blob: a1e23b4aabae1c4549aa70dbc92b1eb2aa28cfc0 [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"
35#include "compiler.h"
36#include "cpu.h"
37#include "dateparser.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"
42#include "platform.h"
43#include "runtime.h"
44#include "scopeinfo.h"
45#include "v8threads.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000046#include "smart-pointer.h"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000047#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000048
49namespace v8 { namespace internal {
50
51
52#define RUNTIME_ASSERT(value) do { \
53 if (!(value)) return IllegalOperation(); \
54} while (false)
55
56// Cast the given object to a value of the specified type and store
57// it in a variable with the given name. If the object is not of the
58// expected type call IllegalOperation and return.
59#define CONVERT_CHECKED(Type, name, obj) \
60 RUNTIME_ASSERT(obj->Is##Type()); \
61 Type* name = Type::cast(obj);
62
63#define CONVERT_ARG_CHECKED(Type, name, index) \
64 RUNTIME_ASSERT(args[index]->Is##Type()); \
65 Handle<Type> name = args.at<Type>(index);
66
kasper.lundbd3ec4e2008-07-09 11:06:54 +000067// Cast the given object to a boolean and store it in a variable with
68// the given name. If the object is not a boolean call IllegalOperation
69// and return.
70#define CONVERT_BOOLEAN_CHECKED(name, obj) \
71 RUNTIME_ASSERT(obj->IsBoolean()); \
72 bool name = (obj)->IsTrue();
73
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000074// Cast the given object to a Smi and store its value in an int variable
75// with the given name. If the object is not a Smi call IllegalOperation
76// and return.
77#define CONVERT_SMI_CHECKED(name, obj) \
78 RUNTIME_ASSERT(obj->IsSmi()); \
79 int name = Smi::cast(obj)->value();
80
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000081// Cast the given object to a double and store it in a variable with
82// the given name. If the object is not a number (as opposed to
83// the number not-a-number) call IllegalOperation and return.
84#define CONVERT_DOUBLE_CHECKED(name, obj) \
85 RUNTIME_ASSERT(obj->IsNumber()); \
86 double name = (obj)->Number();
87
88// Call the specified converter on the object *comand store the result in
89// a variable of the specified type with the given name. If the
90// object is not a Number call IllegalOperation and return.
91#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
92 RUNTIME_ASSERT(obj->IsNumber()); \
93 type name = NumberTo##Type(obj);
94
95// Non-reentrant string buffer for efficient general use in this file.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +000096static StaticResource<StringInputBuffer> runtime_string_input_buffer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000097
98
99static Object* IllegalOperation() {
100 return Top::Throw(Heap::illegal_access_symbol());
101}
102
103
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000104static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
105 StackLimitCheck check;
106 if (check.HasOverflowed()) return Top::StackOverflow();
107
108 Object* result = Heap::CopyJSObject(boilerplate);
109 if (result->IsFailure()) return result;
110 JSObject* copy = JSObject::cast(result);
111
112 // Deep copy local properties.
113 if (copy->HasFastProperties()) {
114 FixedArray* properties = copy->properties();
115 WriteBarrierMode mode = properties->GetWriteBarrierMode();
116 for (int i = 0; i < properties->length(); i++) {
117 Object* value = properties->get(i);
118 if (value->IsJSObject()) {
119 JSObject* jsObject = JSObject::cast(value);
120 result = DeepCopyBoilerplate(jsObject);
121 if (result->IsFailure()) return result;
122 properties->set(i, result, mode);
123 }
124 }
125 mode = copy->GetWriteBarrierMode();
126 for (int i = 0; i < copy->map()->inobject_properties(); i++) {
127 Object* value = copy->InObjectPropertyAt(i);
128 if (value->IsJSObject()) {
129 JSObject* jsObject = JSObject::cast(value);
130 result = DeepCopyBoilerplate(jsObject);
131 if (result->IsFailure()) return result;
132 copy->InObjectPropertyAtPut(i, result, mode);
133 }
134 }
135 } else {
136 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
137 if (result->IsFailure()) return result;
138 FixedArray* names = FixedArray::cast(result);
139 copy->GetLocalPropertyNames(names, 0);
140 for (int i = 0; i < names->length(); i++) {
141 ASSERT(names->get(i)->IsString());
142 String* keyString = String::cast(names->get(i));
143 PropertyAttributes attributes =
144 copy->GetLocalPropertyAttribute(keyString);
145 // Only deep copy fields from the object literal expression.
146 // In particular, don't try to copy the length attribute of
147 // an array.
148 if (attributes != NONE) continue;
149 Object* value = copy->GetProperty(keyString, &attributes);
150 ASSERT(!value->IsFailure());
151 if (value->IsJSObject()) {
152 JSObject* jsObject = JSObject::cast(value);
153 result = DeepCopyBoilerplate(jsObject);
154 if (result->IsFailure()) return result;
155 result = copy->SetProperty(keyString, result, NONE);
156 if (result->IsFailure()) return result;
157 }
158 }
159 }
160
161 // Deep copy local elements.
162 if (copy->HasFastElements()) {
163 FixedArray* elements = copy->elements();
164 WriteBarrierMode mode = elements->GetWriteBarrierMode();
165 for (int i = 0; i < elements->length(); i++) {
166 Object* value = elements->get(i);
167 if (value->IsJSObject()) {
168 JSObject* jsObject = JSObject::cast(value);
169 result = DeepCopyBoilerplate(jsObject);
170 if (result->IsFailure()) return result;
171 elements->set(i, result, mode);
172 }
173 }
174 } else {
175 Dictionary* 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()) {
182 JSObject* jsObject = JSObject::cast(value);
183 result = DeepCopyBoilerplate(jsObject);
184 if (result->IsFailure()) return result;
185 element_dictionary->ValueAtPut(i, result);
186 }
187 }
188 }
189 }
190 return copy;
191}
192
193
194static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
195 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
196 return DeepCopyBoilerplate(boilerplate);
197}
198
199
200static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000201 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000202 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000203}
204
205
ager@chromium.org236ad962008-09-25 09:45:57 +0000206static Handle<Map> ComputeObjectLiteralMap(
207 Handle<Context> context,
208 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000209 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000210 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000211 if (FLAG_canonicalize_object_literal_maps) {
212 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000213 int number_of_symbol_keys = 0;
214 while ((number_of_symbol_keys < number_of_properties) &&
215 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
216 number_of_symbol_keys++;
217 }
218 // Based on the number of prefix symbols key we decide whether
219 // to use the map cache in the global context.
220 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000221 if ((number_of_symbol_keys == number_of_properties) &&
222 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000223 // Create the fixed array with the key.
224 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
225 for (int i = 0; i < number_of_symbol_keys; i++) {
226 keys->set(i, constant_properties->get(i*2));
227 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000228 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000229 return Factory::ObjectLiteralMapFromCache(context, keys);
230 }
231 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000232 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000233 return Factory::CopyMap(
234 Handle<Map>(context->object_function()->initial_map()),
235 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000236}
237
238
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000239static Handle<Object> CreateLiteralBoilerplate(
240 Handle<FixedArray> literals,
241 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000242
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000243
244static Handle<Object> CreateObjectLiteralBoilerplate(
245 Handle<FixedArray> literals,
246 Handle<FixedArray> constant_properties) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000247 // Get the global context from the literals array. This is the
248 // context in which the function was created and we use the object
249 // function from this context to create the object literal. We do
250 // not use the object function from the current global context
251 // because this might be the object function from another context
252 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000253 Handle<Context> context =
254 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
255
256 bool is_result_from_cache;
257 Handle<Map> map = ComputeObjectLiteralMap(context,
258 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000259 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000260
ager@chromium.org236ad962008-09-25 09:45:57 +0000261 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
ager@chromium.org32912102009-01-16 10:38:43 +0000262 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000263 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000264 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
265 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266 for (int index = 0; index < length; index +=2) {
267 Handle<Object> key(constant_properties->get(index+0));
268 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000269 if (value->IsFixedArray()) {
270 // The value contains the constant_properties of a
271 // simple object literal.
272 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
273 value = CreateLiteralBoilerplate(literals, array);
274 if (value.is_null()) return value;
275 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000276 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000277 uint32_t element_index = 0;
278 if (key->IsSymbol()) {
279 // If key is a symbol it is not an array element.
280 Handle<String> name(String::cast(*key));
281 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000282 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000283 } else if (Array::IndexFromObject(*key, &element_index)) {
284 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000285 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000286 } else {
287 // Non-uint32 number.
288 ASSERT(key->IsNumber());
289 double num = key->Number();
290 char arr[100];
291 Vector<char> buffer(arr, ARRAY_SIZE(arr));
292 const char* str = DoubleToCString(num, buffer);
293 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000294 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000296 // If setting the property on the boilerplate throws an
297 // exception, the exception is converted to an empty handle in
298 // the handle based operations. In that case, we need to
299 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000300 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000301 }
302 }
303
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000304 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000305}
306
307
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000308static Handle<Object> CreateArrayLiteralBoilerplate(
309 Handle<FixedArray> literals,
310 Handle<FixedArray> elements) {
311 // Create the JSArray.
312 Handle<JSFunction> constructor(
313 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
314 Handle<Object> object = Factory::NewJSObject(constructor);
315
316 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
317
318 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
319 for (int i = 0; i < content->length(); i++) {
320 if (content->get(i)->IsFixedArray()) {
321 // The value contains the constant_properties of a
322 // simple object literal.
323 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
324 Handle<Object> result =
325 CreateLiteralBoilerplate(literals, fa);
326 if (result.is_null()) return result;
327 content->set(i, *result);
328 }
329 }
330
331 // Set the elements.
332 Handle<JSArray>::cast(object)->SetContent(*content);
333 return object;
334}
335
336
337static Handle<Object> CreateLiteralBoilerplate(
338 Handle<FixedArray> literals,
339 Handle<FixedArray> array) {
340 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
341 switch (CompileTimeValue::GetType(array)) {
342 case CompileTimeValue::OBJECT_LITERAL:
343 return CreateObjectLiteralBoilerplate(literals, elements);
344 case CompileTimeValue::ARRAY_LITERAL:
345 return CreateArrayLiteralBoilerplate(literals, elements);
346 default:
347 UNREACHABLE();
348 return Handle<Object>::null();
349 }
350}
351
352
353static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
354 HandleScope scope;
355 ASSERT(args.length() == 3);
356 // Copy the arguments.
357 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
358 CONVERT_SMI_CHECKED(literals_index, args[1]);
359 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
360
361 Handle<Object> result =
362 CreateObjectLiteralBoilerplate(literals, constant_properties);
363
364 if (result.is_null()) return Failure::Exception();
365
366 // Update the functions literal and return the boilerplate.
367 literals->set(literals_index, *result);
368
369 return *result;
370}
371
372
373static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000374 // Takes a FixedArray of elements containing the literal elements of
375 // the array literal and produces JSArray with those elements.
376 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000377 // which contains the context from which to get the Array function
378 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000379 HandleScope scope;
380 ASSERT(args.length() == 3);
381 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
382 CONVERT_SMI_CHECKED(literals_index, args[1]);
383 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000384
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000385 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
386 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000387
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000388 // Update the functions literal and return the boilerplate.
389 literals->set(literals_index, *object);
390 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000391}
392
393
ager@chromium.org32912102009-01-16 10:38:43 +0000394static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
395 ASSERT(args.length() == 2);
396 CONVERT_CHECKED(String, key, args[0]);
397 Object* value = args[1];
398 // Create a catch context extension object.
399 JSFunction* constructor =
400 Top::context()->global_context()->context_extension_function();
401 Object* object = Heap::AllocateJSObject(constructor);
402 if (object->IsFailure()) return object;
403 // Assign the exception value to the catch variable and make sure
404 // that the catch variable is DontDelete.
405 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
406 if (value->IsFailure()) return value;
407 return object;
408}
409
410
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000411static Object* Runtime_ClassOf(Arguments args) {
412 NoHandleAllocation ha;
413 ASSERT(args.length() == 1);
414 Object* obj = args[0];
415 if (!obj->IsJSObject()) return Heap::null_value();
416 return JSObject::cast(obj)->class_name();
417}
418
ager@chromium.org7c537e22008-10-16 08:43:32 +0000419
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000420static Object* Runtime_HasStringClass(Arguments args) {
421 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::String_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000422}
423
424
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000425static Object* Runtime_HasDateClass(Arguments args) {
426 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Date_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000427}
428
429
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000430static Object* Runtime_HasArrayClass(Arguments args) {
431 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Array_symbol()));
432}
433
434
435static Object* Runtime_HasFunctionClass(Arguments args) {
436 return Heap::ToBoolean(
437 args[0]->HasSpecificClassOf(Heap::function_class_symbol()));
438}
439
440
441static Object* Runtime_HasNumberClass(Arguments args) {
442 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Number_symbol()));
443}
444
445
446static Object* Runtime_HasBooleanClass(Arguments args) {
447 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Boolean_symbol()));
448}
449
450
451static Object* Runtime_HasArgumentsClass(Arguments args) {
452 return Heap::ToBoolean(
453 args[0]->HasSpecificClassOf(Heap::Arguments_symbol()));
454}
455
456
457static Object* Runtime_HasRegExpClass(Arguments args) {
458 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::RegExp_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000459}
460
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000461
462static Object* Runtime_IsInPrototypeChain(Arguments args) {
463 NoHandleAllocation ha;
464 ASSERT(args.length() == 2);
465 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
466 Object* O = args[0];
467 Object* V = args[1];
468 while (true) {
469 Object* prototype = V->GetPrototype();
470 if (prototype->IsNull()) return Heap::false_value();
471 if (O == prototype) return Heap::true_value();
472 V = prototype;
473 }
474}
475
476
477static Object* Runtime_IsConstructCall(Arguments args) {
478 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000479 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000480 JavaScriptFrameIterator it;
481 return Heap::ToBoolean(it.frame()->IsConstructor());
482}
483
484
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000485static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000486 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000487 ASSERT(args.length() == 3);
ager@chromium.org236ad962008-09-25 09:45:57 +0000488 CONVERT_CHECKED(JSRegExp, raw_re, args[0]);
489 Handle<JSRegExp> re(raw_re);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000490 CONVERT_CHECKED(String, raw_pattern, args[1]);
491 Handle<String> pattern(raw_pattern);
492 CONVERT_CHECKED(String, raw_flags, args[2]);
493 Handle<String> flags(raw_flags);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000494 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
495 if (result.is_null()) return Failure::Exception();
496 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000497}
498
499
500static Object* Runtime_CreateApiFunction(Arguments args) {
501 HandleScope scope;
502 ASSERT(args.length() == 1);
503 CONVERT_CHECKED(FunctionTemplateInfo, raw_data, args[0]);
504 Handle<FunctionTemplateInfo> data(raw_data);
505 return *Factory::CreateApiFunction(data);
506}
507
508
509static Object* Runtime_IsTemplate(Arguments args) {
510 ASSERT(args.length() == 1);
511 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000512 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000513 return Heap::ToBoolean(result);
514}
515
516
517static Object* Runtime_GetTemplateField(Arguments args) {
518 ASSERT(args.length() == 2);
519 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000520 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000521 int index = field->value();
522 int offset = index * kPointerSize + HeapObject::kHeaderSize;
523 InstanceType type = templ->map()->instance_type();
524 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
525 type == OBJECT_TEMPLATE_INFO_TYPE);
526 RUNTIME_ASSERT(offset > 0);
527 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
528 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
529 } else {
530 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
531 }
532 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000533}
534
535
ager@chromium.org870a0b62008-11-04 11:43:05 +0000536static Object* Runtime_DisableAccessChecks(Arguments args) {
537 ASSERT(args.length() == 1);
538 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000539 Map* old_map = object->map();
540 bool needs_access_checks = old_map->is_access_check_needed();
541 if (needs_access_checks) {
542 // Copy map so it won't interfere constructor's initial map.
543 Object* new_map = old_map->CopyDropTransitions();
544 if (new_map->IsFailure()) return new_map;
545
546 Map::cast(new_map)->set_is_access_check_needed(false);
547 object->set_map(Map::cast(new_map));
548 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000549 return needs_access_checks ? Heap::true_value() : Heap::false_value();
550}
551
552
553static Object* Runtime_EnableAccessChecks(Arguments args) {
554 ASSERT(args.length() == 1);
555 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000556 Map* old_map = object->map();
557 if (!old_map->is_access_check_needed()) {
558 // Copy map so it won't interfere constructor's initial map.
559 Object* new_map = old_map->CopyDropTransitions();
560 if (new_map->IsFailure()) return new_map;
561
562 Map::cast(new_map)->set_is_access_check_needed(true);
563 object->set_map(Map::cast(new_map));
564 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000565 return Heap::undefined_value();
566}
567
568
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000569static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
570 HandleScope scope;
571 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
572 Handle<Object> args[2] = { type_handle, name };
573 Handle<Object> error =
574 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
575 return Top::Throw(*error);
576}
577
578
579static Object* Runtime_DeclareGlobals(Arguments args) {
580 HandleScope scope;
581 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
582
583 CONVERT_ARG_CHECKED(FixedArray, pairs, 0);
584 Handle<Context> context = args.at<Context>(1);
585 bool is_eval = Smi::cast(args[2])->value() == 1;
586
587 // Compute the property attributes. According to ECMA-262, section
588 // 13, page 71, the property must be read-only and
589 // non-deletable. However, neither SpiderMonkey nor KJS creates the
590 // property as read-only, so we don't either.
591 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
592
593 // Only optimize the object if we intend to add more than 5 properties.
594 OptimizedObjectForAddingMultipleProperties ba(global, pairs->length()/2 > 5);
595
596 // Traverse the name/value pairs and set the properties.
597 int length = pairs->length();
598 for (int i = 0; i < length; i += 2) {
599 HandleScope scope;
600 Handle<String> name(String::cast(pairs->get(i)));
601 Handle<Object> value(pairs->get(i + 1));
602
603 // We have to declare a global const property. To capture we only
604 // assign to it when evaluating the assignment for "const x =
605 // <expr>" the initial value is the hole.
606 bool is_const_property = value->IsTheHole();
607
608 if (value->IsUndefined() || is_const_property) {
609 // Lookup the property in the global object, and don't set the
610 // value of the variable if the property is already there.
611 LookupResult lookup;
612 global->Lookup(*name, &lookup);
613 if (lookup.IsProperty()) {
614 // Determine if the property is local by comparing the holder
615 // against the global object. The information will be used to
616 // avoid throwing re-declaration errors when declaring
617 // variables or constants that exist in the prototype chain.
618 bool is_local = (*global == lookup.holder());
619 // Get the property attributes and determine if the property is
620 // read-only.
621 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
622 bool is_read_only = (attributes & READ_ONLY) != 0;
623 if (lookup.type() == INTERCEPTOR) {
624 // If the interceptor says the property is there, we
625 // just return undefined without overwriting the property.
626 // Otherwise, we continue to setting the property.
627 if (attributes != ABSENT) {
628 // Check if the existing property conflicts with regards to const.
629 if (is_local && (is_read_only || is_const_property)) {
630 const char* type = (is_read_only) ? "const" : "var";
631 return ThrowRedeclarationError(type, name);
632 };
633 // The property already exists without conflicting: Go to
634 // the next declaration.
635 continue;
636 }
637 // Fall-through and introduce the absent property by using
638 // SetProperty.
639 } else {
640 if (is_local && (is_read_only || is_const_property)) {
641 const char* type = (is_read_only) ? "const" : "var";
642 return ThrowRedeclarationError(type, name);
643 }
644 // The property already exists without conflicting: Go to
645 // the next declaration.
646 continue;
647 }
648 }
649 } else {
650 // Copy the function and update its context. Use it as value.
651 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
652 Handle<JSFunction> function =
653 Factory::NewFunctionFromBoilerplate(boilerplate, context);
654 value = function;
655 }
656
657 LookupResult lookup;
658 global->LocalLookup(*name, &lookup);
659
660 PropertyAttributes attributes = is_const_property
661 ? static_cast<PropertyAttributes>(base | READ_ONLY)
662 : base;
663
664 if (lookup.IsProperty()) {
665 // There's a local property that we need to overwrite because
666 // we're either declaring a function or there's an interceptor
667 // that claims the property is absent.
668
669 // Check for conflicting re-declarations. We cannot have
670 // conflicting types in case of intercepted properties because
671 // they are absent.
672 if (lookup.type() != INTERCEPTOR &&
673 (lookup.IsReadOnly() || is_const_property)) {
674 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
675 return ThrowRedeclarationError(type, name);
676 }
677 SetProperty(global, name, value, attributes);
678 } else {
679 // If a property with this name does not already exist on the
680 // global object add the property locally. We take special
681 // precautions to always add it as a local property even in case
682 // of callbacks in the prototype chain (this rules out using
683 // SetProperty). Also, we must use the handle-based version to
684 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000685 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000686 }
687 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000688
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000689 return Heap::undefined_value();
690}
691
692
693static Object* Runtime_DeclareContextSlot(Arguments args) {
694 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000695 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000696
ager@chromium.org7c537e22008-10-16 08:43:32 +0000697 CONVERT_ARG_CHECKED(Context, context, 0);
698 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000699 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000700 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000701 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000702 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000703
704 // Declarations are always done in the function context.
705 context = Handle<Context>(context->fcontext());
706
707 int index;
708 PropertyAttributes attributes;
709 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000710 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000711 context->Lookup(name, flags, &index, &attributes);
712
713 if (attributes != ABSENT) {
714 // The name was declared before; check for conflicting
715 // re-declarations: This is similar to the code in parser.cc in
716 // the AstBuildingParser::Declare function.
717 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
718 // Functions are not read-only.
719 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
720 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
721 return ThrowRedeclarationError(type, name);
722 }
723
724 // Initialize it if necessary.
725 if (*initial_value != NULL) {
726 if (index >= 0) {
727 // The variable or constant context slot should always be in
728 // the function context; not in any outer context nor in the
729 // arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000730 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000731 if (((attributes & READ_ONLY) == 0) ||
732 context->get(index)->IsTheHole()) {
733 context->set(index, *initial_value);
734 }
735 } else {
736 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000737 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000738 SetProperty(context_ext, name, initial_value, mode);
739 }
740 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000741
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000742 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000743 // The property is not in the function context. It needs to be
744 // "declared" in the function context's extension context, or in the
745 // global context.
746 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000747 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000748 // The function context's extension context exists - use it.
749 context_ext = Handle<JSObject>(context->extension());
750 } else {
751 // The function context's extension context does not exists - allocate
752 // it.
753 context_ext = Factory::NewJSObject(Top::context_extension_function());
754 // And store it in the extension slot.
755 context->set_extension(*context_ext);
756 }
757 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000758
ager@chromium.org7c537e22008-10-16 08:43:32 +0000759 // Declare the property by setting it to the initial value if provided,
760 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
761 // constant declarations).
762 ASSERT(!context_ext->HasLocalProperty(*name));
763 Handle<Object> value(Heap::undefined_value());
764 if (*initial_value != NULL) value = initial_value;
765 SetProperty(context_ext, name, value, mode);
766 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
767 }
768
769 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000770}
771
772
773static Object* Runtime_InitializeVarGlobal(Arguments args) {
774 NoHandleAllocation nha;
775
776 // Determine if we need to assign to the variable if it already
777 // exists (based on the number of arguments).
778 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
779 bool assign = args.length() == 2;
780
781 CONVERT_ARG_CHECKED(String, name, 0);
782 GlobalObject* global = Top::context()->global();
783
784 // According to ECMA-262, section 12.2, page 62, the property must
785 // not be deletable.
786 PropertyAttributes attributes = DONT_DELETE;
787
788 // Lookup the property locally in the global object. If it isn't
789 // there, we add the property and take special precautions to always
790 // add it as a local property even in case of callbacks in the
791 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000792 // We have IgnoreAttributesAndSetLocalProperty for this.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000793 LookupResult lookup;
794 global->LocalLookup(*name, &lookup);
795 if (!lookup.IsProperty()) {
796 Object* value = (assign) ? args[1] : Heap::undefined_value();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000797 return global->IgnoreAttributesAndSetLocalProperty(*name,
798 value,
799 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000800 }
801
802 // Determine if this is a redeclaration of something read-only.
803 if (lookup.IsReadOnly()) {
804 return ThrowRedeclarationError("const", name);
805 }
806
807 // Determine if this is a redeclaration of an intercepted read-only
808 // property and figure out if the property exists at all.
809 bool found = true;
810 PropertyType type = lookup.type();
811 if (type == INTERCEPTOR) {
812 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
813 if (intercepted == ABSENT) {
814 // The interceptor claims the property isn't there. We need to
815 // make sure to introduce it.
816 found = false;
817 } else if ((intercepted & READ_ONLY) != 0) {
818 // The property is present, but read-only. Since we're trying to
819 // overwrite it with a variable declaration we must throw a
820 // re-declaration error.
821 return ThrowRedeclarationError("const", name);
822 }
823 // Restore global object from context (in case of GC).
824 global = Top::context()->global();
825 }
826
827 if (found && !assign) {
828 // The global property is there and we're not assigning any value
829 // to it. Just return.
830 return Heap::undefined_value();
831 }
832
833 // Assign the value (or undefined) to the property.
834 Object* value = (assign) ? args[1] : Heap::undefined_value();
835 return global->SetProperty(&lookup, *name, value, attributes);
836}
837
838
839static Object* Runtime_InitializeConstGlobal(Arguments args) {
840 // All constants are declared with an initial value. The name
841 // of the constant is the first argument and the initial value
842 // is the second.
843 RUNTIME_ASSERT(args.length() == 2);
844 CONVERT_ARG_CHECKED(String, name, 0);
845 Handle<Object> value = args.at<Object>(1);
846
847 // Get the current global object from top.
848 GlobalObject* global = Top::context()->global();
849
850 // According to ECMA-262, section 12.2, page 62, the property must
851 // not be deletable. Since it's a const, it must be READ_ONLY too.
852 PropertyAttributes attributes =
853 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
854
855 // Lookup the property locally in the global object. If it isn't
856 // there, we add the property and take special precautions to always
857 // add it as a local property even in case of callbacks in the
858 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000859 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000860 LookupResult lookup;
861 global->LocalLookup(*name, &lookup);
862 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000863 return global->IgnoreAttributesAndSetLocalProperty(*name,
864 *value,
865 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000866 }
867
868 // Determine if this is a redeclaration of something not
869 // read-only. In case the result is hidden behind an interceptor we
870 // need to ask it for the property attributes.
871 if (!lookup.IsReadOnly()) {
872 if (lookup.type() != INTERCEPTOR) {
873 return ThrowRedeclarationError("var", name);
874 }
875
876 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
877
878 // Throw re-declaration error if the intercepted property is present
879 // but not read-only.
880 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
881 return ThrowRedeclarationError("var", name);
882 }
883
884 // Restore global object from context (in case of GC) and continue
885 // with setting the value because the property is either absent or
886 // read-only. We also have to do redo the lookup.
887 global = Top::context()->global();
888
889 // BUG 1213579: Handle the case where we have to set a read-only
890 // property through an interceptor and only do it if it's
891 // uninitialized, e.g. the hole. Nirk...
892 global->SetProperty(*name, *value, attributes);
893 return *value;
894 }
895
896 // Set the value, but only we're assigning the initial value to a
897 // constant. For now, we determine this by checking if the
898 // current value is the hole.
899 PropertyType type = lookup.type();
900 if (type == FIELD) {
901 FixedArray* properties = global->properties();
902 int index = lookup.GetFieldIndex();
903 if (properties->get(index)->IsTheHole()) {
904 properties->set(index, *value);
905 }
906 } else if (type == NORMAL) {
907 Dictionary* dictionary = global->property_dictionary();
908 int entry = lookup.GetDictionaryEntry();
909 if (dictionary->ValueAt(entry)->IsTheHole()) {
910 dictionary->ValueAtPut(entry, *value);
911 }
912 } else {
913 // Ignore re-initialization of constants that have already been
914 // assigned a function value.
915 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
916 }
917
918 // Use the set value as the result of the operation.
919 return *value;
920}
921
922
923static Object* Runtime_InitializeConstContextSlot(Arguments args) {
924 HandleScope scope;
925 ASSERT(args.length() == 3);
926
927 Handle<Object> value(args[0]);
928 ASSERT(!value->IsTheHole());
929 CONVERT_ARG_CHECKED(Context, context, 1);
930 Handle<String> name(String::cast(args[2]));
931
932 // Initializations are always done in the function context.
933 context = Handle<Context>(context->fcontext());
934
935 int index;
936 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000937 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000938 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000939 context->Lookup(name, flags, &index, &attributes);
940
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000941 // In most situations, the property introduced by the const
942 // declaration should be present in the context extension object.
943 // However, because declaration and initialization are separate, the
944 // property might have been deleted (if it was introduced by eval)
945 // before we reach the initialization point.
946 //
947 // Example:
948 //
949 // function f() { eval("delete x; const x;"); }
950 //
951 // In that case, the initialization behaves like a normal assignment
952 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000953 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000954 // Property was found in a context.
955 if (holder->IsContext()) {
956 // The holder cannot be the function context. If it is, there
957 // should have been a const redeclaration error when declaring
958 // the const property.
959 ASSERT(!holder.is_identical_to(context));
960 if ((attributes & READ_ONLY) == 0) {
961 Handle<Context>::cast(holder)->set(index, *value);
962 }
963 } else {
964 // The holder is an arguments object.
965 ASSERT((attributes & READ_ONLY) == 0);
966 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000967 }
968 return *value;
969 }
970
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000971 // The property could not be found, we introduce it in the global
972 // context.
973 if (attributes == ABSENT) {
974 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
975 SetProperty(global, name, value, NONE);
976 return *value;
977 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000978
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000979 // The property was present in a context extension object.
980 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000981
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000982 if (*context_ext == context->extension()) {
983 // This is the property that was introduced by the const
984 // declaration. Set it if it hasn't been set before. NOTE: We
985 // cannot use GetProperty() to get the current value as it
986 // 'unholes' the value.
987 LookupResult lookup;
988 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
989 ASSERT(lookup.IsProperty()); // the property was declared
990 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
991
992 PropertyType type = lookup.type();
993 if (type == FIELD) {
994 FixedArray* properties = context_ext->properties();
995 int index = lookup.GetFieldIndex();
996 if (properties->get(index)->IsTheHole()) {
997 properties->set(index, *value);
998 }
999 } else if (type == NORMAL) {
1000 Dictionary* dictionary = context_ext->property_dictionary();
1001 int entry = lookup.GetDictionaryEntry();
1002 if (dictionary->ValueAt(entry)->IsTheHole()) {
1003 dictionary->ValueAtPut(entry, *value);
1004 }
1005 } else {
1006 // We should not reach here. Any real, named property should be
1007 // either a field or a dictionary slot.
1008 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001009 }
1010 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001011 // The property was found in a different context extension object.
1012 // Set it if it is not a read-only property.
1013 if ((attributes & READ_ONLY) == 0) {
1014 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1015 // Setting a property might throw an exception. Exceptions
1016 // are converted to empty handles in handle operations. We
1017 // need to convert back to exceptions here.
1018 if (set.is_null()) {
1019 ASSERT(Top::has_pending_exception());
1020 return Failure::Exception();
1021 }
1022 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001023 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001024
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001025 return *value;
1026}
1027
1028
1029static Object* Runtime_RegExpExec(Arguments args) {
1030 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001031 ASSERT(args.length() == 4);
ager@chromium.org236ad962008-09-25 09:45:57 +00001032 CONVERT_CHECKED(JSRegExp, raw_regexp, args[0]);
1033 Handle<JSRegExp> regexp(raw_regexp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001034 CONVERT_CHECKED(String, raw_subject, args[1]);
1035 Handle<String> subject(raw_subject);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001036 // Due to the way the JS files are constructed this must be less than the
1037 // length of a string, i.e. it is always a Smi. We check anyway for security.
1038 CONVERT_CHECKED(Smi, index, args[2]);
1039 CONVERT_CHECKED(JSArray, raw_last_match_info, args[3]);
1040 Handle<JSArray> last_match_info(raw_last_match_info);
ager@chromium.org41826e72009-03-30 13:30:57 +00001041 RUNTIME_ASSERT(last_match_info->HasFastElements());
1042 RUNTIME_ASSERT(index->value() >= 0);
1043 RUNTIME_ASSERT(index->value() <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001044 Handle<Object> result = RegExpImpl::Exec(regexp,
1045 subject,
1046 index->value(),
1047 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001048 if (result.is_null()) return Failure::Exception();
1049 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001050}
1051
1052
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001053static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1054 HandleScope scope;
1055 ASSERT(args.length() == 4);
1056 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1057 int index = Smi::cast(args[1])->value();
1058 Handle<String> pattern = args.at<String>(2);
1059 Handle<String> flags = args.at<String>(3);
1060
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001061 // Get the RegExp function from the context in the literals array.
1062 // This is the RegExp function from the context in which the
1063 // function was created. We do not use the RegExp function from the
1064 // current global context because this might be the RegExp function
1065 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001066 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001067 Handle<JSFunction>(
1068 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001069 // Compute the regular expression literal.
1070 bool has_pending_exception;
1071 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001072 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1073 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001074 if (has_pending_exception) {
1075 ASSERT(Top::has_pending_exception());
1076 return Failure::Exception();
1077 }
1078 literals->set(index, *regexp);
1079 return *regexp;
1080}
1081
1082
1083static Object* Runtime_FunctionGetName(Arguments args) {
1084 NoHandleAllocation ha;
1085 ASSERT(args.length() == 1);
1086
1087 CONVERT_CHECKED(JSFunction, f, args[0]);
1088 return f->shared()->name();
1089}
1090
1091
ager@chromium.org236ad962008-09-25 09:45:57 +00001092static Object* Runtime_FunctionSetName(Arguments args) {
1093 NoHandleAllocation ha;
1094 ASSERT(args.length() == 2);
1095
1096 CONVERT_CHECKED(JSFunction, f, args[0]);
1097 CONVERT_CHECKED(String, name, args[1]);
1098 f->shared()->set_name(name);
1099 return Heap::undefined_value();
1100}
1101
1102
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001103static Object* Runtime_FunctionGetScript(Arguments args) {
1104 HandleScope scope;
1105 ASSERT(args.length() == 1);
1106
1107 CONVERT_CHECKED(JSFunction, fun, args[0]);
1108 Handle<Object> script = Handle<Object>(fun->shared()->script());
1109 if (!script->IsScript()) return Heap::undefined_value();
1110
1111 return *GetScriptWrapper(Handle<Script>::cast(script));
1112}
1113
1114
1115static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1116 NoHandleAllocation ha;
1117 ASSERT(args.length() == 1);
1118
1119 CONVERT_CHECKED(JSFunction, f, args[0]);
1120 return f->shared()->GetSourceCode();
1121}
1122
1123
1124static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1125 NoHandleAllocation ha;
1126 ASSERT(args.length() == 1);
1127
1128 CONVERT_CHECKED(JSFunction, fun, args[0]);
1129 int pos = fun->shared()->start_position();
1130 return Smi::FromInt(pos);
1131}
1132
1133
1134static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1135 NoHandleAllocation ha;
1136 ASSERT(args.length() == 2);
1137
1138 CONVERT_CHECKED(JSFunction, fun, args[0]);
1139 CONVERT_CHECKED(String, name, args[1]);
1140 fun->SetInstanceClassName(name);
1141 return Heap::undefined_value();
1142}
1143
1144
1145static Object* Runtime_FunctionSetLength(Arguments args) {
1146 NoHandleAllocation ha;
1147 ASSERT(args.length() == 2);
1148
1149 CONVERT_CHECKED(JSFunction, fun, args[0]);
1150 CONVERT_CHECKED(Smi, length, args[1]);
1151 fun->shared()->set_length(length->value());
1152 return length;
1153}
1154
1155
1156static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001157 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001158 ASSERT(args.length() == 2);
1159
1160 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001161 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1162 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001163 return args[0]; // return TOS
1164}
1165
1166
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001167static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1168 NoHandleAllocation ha;
1169 ASSERT(args.length() == 1);
1170
1171 CONVERT_CHECKED(JSFunction, f, args[0]);
1172 // The function_data field of the shared function info is used exclusively by
1173 // the API.
1174 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1175 : Heap::false_value();
1176}
1177
1178
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001179static Object* Runtime_SetCode(Arguments args) {
1180 HandleScope scope;
1181 ASSERT(args.length() == 2);
1182
1183 CONVERT_CHECKED(JSFunction, raw_target, args[0]);
1184 Handle<JSFunction> target(raw_target);
1185 Handle<Object> code = args.at<Object>(1);
1186
1187 Handle<Context> context(target->context());
1188
1189 if (!code->IsNull()) {
1190 RUNTIME_ASSERT(code->IsJSFunction());
1191 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1192 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1193 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1194 return Failure::Exception();
1195 }
1196 // Set the code, formal parameter count, and the length of the target
1197 // function.
1198 target->set_code(fun->code());
1199 target->shared()->set_length(fun->shared()->length());
1200 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001201 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001202 // Set the source code of the target function to undefined.
1203 // SetCode is only used for built-in constructors like String,
1204 // Array, and Object, and some web code
1205 // doesn't like seeing source code for constructors.
1206 target->shared()->set_script(Heap::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001207 context = Handle<Context>(fun->context());
1208
1209 // Make sure we get a fresh copy of the literal vector to avoid
1210 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001211 int number_of_literals = fun->NumberOfLiterals();
1212 Handle<FixedArray> literals =
1213 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001214 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001215 // Insert the object, regexp and array functions in the literals
1216 // array prefix. These are the functions that will be used when
1217 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001218 literals->set(JSFunction::kLiteralGlobalContextIndex,
1219 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001220 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001221 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001222 }
1223
1224 target->set_context(*context);
1225 return *target;
1226}
1227
1228
1229static Object* CharCodeAt(String* subject, Object* index) {
1230 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001231 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001232 // Flatten the string. If someone wants to get a char at an index
1233 // in a cons string, it is likely that more indices will be
1234 // accessed.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001235 subject->TryFlattenIfNotFlat();
1236 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001237 return Heap::nan_value();
1238 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001239 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001240}
1241
1242
1243static Object* Runtime_StringCharCodeAt(Arguments args) {
1244 NoHandleAllocation ha;
1245 ASSERT(args.length() == 2);
1246
1247 CONVERT_CHECKED(String, subject, args[0]);
1248 Object* index = args[1];
1249 return CharCodeAt(subject, index);
1250}
1251
1252
1253static Object* Runtime_CharFromCode(Arguments args) {
1254 NoHandleAllocation ha;
1255 ASSERT(args.length() == 1);
1256 uint32_t code;
1257 if (Array::IndexFromObject(args[0], &code)) {
1258 if (code <= 0xffff) {
1259 return Heap::LookupSingleCharacterStringFromCode(code);
1260 }
1261 }
1262 return Heap::empty_string();
1263}
1264
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001265// Forward declarations.
1266static const int kStringBuilderConcatHelperLengthBits = 11;
1267static const int kStringBuilderConcatHelperPositionBits = 19;
1268
1269template <typename schar>
1270static inline void StringBuilderConcatHelper(String*,
1271 schar*,
1272 FixedArray*,
1273 int);
1274
1275typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1276typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1277
1278class ReplacementStringBuilder {
1279 public:
1280 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1281 : subject_(subject),
1282 parts_(Factory::NewFixedArray(estimated_part_count)),
1283 part_count_(0),
1284 character_count_(0),
1285 is_ascii_(StringShape(*subject).IsAsciiRepresentation()) {
1286 // Require a non-zero initial size. Ensures that doubling the size to
1287 // extend the array will work.
1288 ASSERT(estimated_part_count > 0);
1289 }
1290
1291 void EnsureCapacity(int elements) {
1292 int length = parts_->length();
1293 int required_length = part_count_ + elements;
1294 if (length < required_length) {
1295 int new_length = length;
1296 do {
1297 new_length *= 2;
1298 } while (new_length < required_length);
1299 Handle<FixedArray> extended_array =
1300 Factory::NewFixedArray(new_length);
1301 parts_->CopyTo(0, *extended_array, 0, part_count_);
1302 parts_ = extended_array;
1303 }
1304 }
1305
1306 void AddSubjectSlice(int from, int to) {
1307 ASSERT(from >= 0);
1308 int length = to - from;
1309 ASSERT(length > 0);
1310 // Can we encode the slice in 11 bits for length and 19 bits for
1311 // start position - as used by StringBuilderConcatHelper?
1312 if (StringBuilderSubstringLength::is_valid(length) &&
1313 StringBuilderSubstringPosition::is_valid(from)) {
1314 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1315 StringBuilderSubstringPosition::encode(from);
1316 AddElement(Smi::FromInt(encoded_slice));
1317 } else {
1318 Handle<String> slice = Factory::NewStringSlice(subject_, from, to);
1319 AddElement(*slice);
1320 }
1321 IncrementCharacterCount(length);
1322 }
1323
1324
1325 void AddString(Handle<String> string) {
1326 int length = string->length();
1327 ASSERT(length > 0);
1328 AddElement(*string);
1329 if (!StringShape(*string).IsAsciiRepresentation()) {
1330 is_ascii_ = false;
1331 }
1332 IncrementCharacterCount(length);
1333 }
1334
1335
1336 Handle<String> ToString() {
1337 if (part_count_ == 0) {
1338 return Factory::empty_string();
1339 }
1340
1341 Handle<String> joined_string;
1342 if (is_ascii_) {
1343 joined_string = NewRawAsciiString(character_count_);
1344 AssertNoAllocation no_alloc;
1345 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1346 char* char_buffer = seq->GetChars();
1347 StringBuilderConcatHelper(*subject_,
1348 char_buffer,
1349 *parts_,
1350 part_count_);
1351 } else {
1352 // Non-ASCII.
1353 joined_string = NewRawTwoByteString(character_count_);
1354 AssertNoAllocation no_alloc;
1355 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1356 uc16* char_buffer = seq->GetChars();
1357 StringBuilderConcatHelper(*subject_,
1358 char_buffer,
1359 *parts_,
1360 part_count_);
1361 }
1362 return joined_string;
1363 }
1364
1365
1366 void IncrementCharacterCount(int by) {
1367 if (character_count_ > Smi::kMaxValue - by) {
1368 V8::FatalProcessOutOfMemory("String.replace result too large.");
1369 }
1370 character_count_ += by;
1371 }
1372
1373 private:
1374
1375 Handle<String> NewRawAsciiString(int size) {
1376 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1377 }
1378
1379
1380 Handle<String> NewRawTwoByteString(int size) {
1381 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1382 }
1383
1384
1385 void AddElement(Object* element) {
1386 ASSERT(element->IsSmi() || element->IsString());
1387 parts_->set(part_count_, element);
1388 part_count_++;
1389 }
1390
1391 Handle<String> subject_;
1392 Handle<FixedArray> parts_;
1393 int part_count_;
1394 int character_count_;
1395 bool is_ascii_;
1396};
1397
1398
1399class CompiledReplacement {
1400 public:
1401 CompiledReplacement()
1402 : parts_(1), replacement_substrings_(0) {}
1403
1404 void Compile(Handle<String> replacement,
1405 int capture_count,
1406 int subject_length);
1407
1408 void Apply(ReplacementStringBuilder* builder,
1409 int match_from,
1410 int match_to,
1411 Handle<JSArray> last_match_info);
1412
1413 // Number of distinct parts of the replacement pattern.
1414 int parts() {
1415 return parts_.length();
1416 }
1417 private:
1418 enum PartType {
1419 SUBJECT_PREFIX = 1,
1420 SUBJECT_SUFFIX,
1421 SUBJECT_CAPTURE,
1422 REPLACEMENT_SUBSTRING,
1423 REPLACEMENT_STRING,
1424
1425 NUMBER_OF_PART_TYPES
1426 };
1427
1428 struct ReplacementPart {
1429 static inline ReplacementPart SubjectMatch() {
1430 return ReplacementPart(SUBJECT_CAPTURE, 0);
1431 }
1432 static inline ReplacementPart SubjectCapture(int capture_index) {
1433 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1434 }
1435 static inline ReplacementPart SubjectPrefix() {
1436 return ReplacementPart(SUBJECT_PREFIX, 0);
1437 }
1438 static inline ReplacementPart SubjectSuffix(int subject_length) {
1439 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1440 }
1441 static inline ReplacementPart ReplacementString() {
1442 return ReplacementPart(REPLACEMENT_STRING, 0);
1443 }
1444 static inline ReplacementPart ReplacementSubString(int from, int to) {
1445 ASSERT(from >= 0);
1446 ASSERT(to > from);
1447 return ReplacementPart(-from, to);
1448 }
1449
1450 // If tag <= 0 then it is the negation of a start index of a substring of
1451 // the replacement pattern, otherwise it's a value from PartType.
1452 ReplacementPart(int tag, int data)
1453 : tag(tag), data(data) {
1454 // Must be non-positive or a PartType value.
1455 ASSERT(tag < NUMBER_OF_PART_TYPES);
1456 }
1457 // Either a value of PartType or a non-positive number that is
1458 // the negation of an index into the replacement string.
1459 int tag;
1460 // The data value's interpretation depends on the value of tag:
1461 // tag == SUBJECT_PREFIX ||
1462 // tag == SUBJECT_SUFFIX: data is unused.
1463 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1464 // tag == REPLACEMENT_SUBSTRING ||
1465 // tag == REPLACEMENT_STRING: data is index into array of substrings
1466 // of the replacement string.
1467 // tag <= 0: Temporary representation of the substring of the replacement
1468 // string ranging over -tag .. data.
1469 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1470 // substring objects.
1471 int data;
1472 };
1473
1474 template<typename Char>
1475 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1476 Vector<Char> characters,
1477 int capture_count,
1478 int subject_length) {
1479 int length = characters.length();
1480 int last = 0;
1481 for (int i = 0; i < length; i++) {
1482 Char c = characters[i];
1483 if (c == '$') {
1484 int next_index = i + 1;
1485 if (next_index == length) { // No next character!
1486 break;
1487 }
1488 Char c2 = characters[next_index];
1489 switch (c2) {
1490 case '$':
1491 if (i > last) {
1492 // There is a substring before. Include the first "$".
1493 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1494 last = next_index + 1; // Continue after the second "$".
1495 } else {
1496 // Let the next substring start with the second "$".
1497 last = next_index;
1498 }
1499 i = next_index;
1500 break;
1501 case '`':
1502 if (i > last) {
1503 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1504 }
1505 parts->Add(ReplacementPart::SubjectPrefix());
1506 i = next_index;
1507 last = i + 1;
1508 break;
1509 case '\'':
1510 if (i > last) {
1511 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1512 }
1513 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1514 i = next_index;
1515 last = i + 1;
1516 break;
1517 case '&':
1518 if (i > last) {
1519 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1520 }
1521 parts->Add(ReplacementPart::SubjectMatch());
1522 i = next_index;
1523 last = i + 1;
1524 break;
1525 case '0':
1526 case '1':
1527 case '2':
1528 case '3':
1529 case '4':
1530 case '5':
1531 case '6':
1532 case '7':
1533 case '8':
1534 case '9': {
1535 int capture_ref = c2 - '0';
1536 if (capture_ref > capture_count) {
1537 i = next_index;
1538 continue;
1539 }
1540 int second_digit_index = next_index + 1;
1541 if (second_digit_index < length) {
1542 // Peek ahead to see if we have two digits.
1543 Char c3 = characters[second_digit_index];
1544 if ('0' <= c3 && c3 <= '9') { // Double digits.
1545 int double_digit_ref = capture_ref * 10 + c3 - '0';
1546 if (double_digit_ref <= capture_count) {
1547 next_index = second_digit_index;
1548 capture_ref = double_digit_ref;
1549 }
1550 }
1551 }
1552 if (capture_ref > 0) {
1553 if (i > last) {
1554 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1555 }
1556 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1557 last = next_index + 1;
1558 }
1559 i = next_index;
1560 break;
1561 }
1562 default:
1563 i = next_index;
1564 break;
1565 }
1566 }
1567 }
1568 if (length > last) {
1569 if (last == 0) {
1570 parts->Add(ReplacementPart::ReplacementString());
1571 } else {
1572 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1573 }
1574 }
1575 }
1576
1577 ZoneList<ReplacementPart> parts_;
1578 ZoneList<Handle<String> > replacement_substrings_;
1579};
1580
1581
1582void CompiledReplacement::Compile(Handle<String> replacement,
1583 int capture_count,
1584 int subject_length) {
1585 ASSERT(replacement->IsFlat());
1586 if (StringShape(*replacement).IsAsciiRepresentation()) {
1587 AssertNoAllocation no_alloc;
1588 ParseReplacementPattern(&parts_,
1589 replacement->ToAsciiVector(),
1590 capture_count,
1591 subject_length);
1592 } else {
1593 ASSERT(StringShape(*replacement).IsTwoByteRepresentation());
1594 AssertNoAllocation no_alloc;
1595
1596 ParseReplacementPattern(&parts_,
1597 replacement->ToUC16Vector(),
1598 capture_count,
1599 subject_length);
1600 }
1601 // Find substrings of replacement string and create them as String objects..
1602 int substring_index = 0;
1603 for (int i = 0, n = parts_.length(); i < n; i++) {
1604 int tag = parts_[i].tag;
1605 if (tag <= 0) { // A replacement string slice.
1606 int from = -tag;
1607 int to = parts_[i].data;
1608 replacement_substrings_.Add(Factory::NewStringSlice(replacement,
1609 from,
1610 to));
1611 parts_[i].tag = REPLACEMENT_SUBSTRING;
1612 parts_[i].data = substring_index;
1613 substring_index++;
1614 } else if (tag == REPLACEMENT_STRING) {
1615 replacement_substrings_.Add(replacement);
1616 parts_[i].data = substring_index;
1617 substring_index++;
1618 }
1619 }
1620}
1621
1622
1623void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1624 int match_from,
1625 int match_to,
1626 Handle<JSArray> last_match_info) {
1627 for (int i = 0, n = parts_.length(); i < n; i++) {
1628 ReplacementPart part = parts_[i];
1629 switch (part.tag) {
1630 case SUBJECT_PREFIX:
1631 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1632 break;
1633 case SUBJECT_SUFFIX: {
1634 int subject_length = part.data;
1635 if (match_to < subject_length) {
1636 builder->AddSubjectSlice(match_to, subject_length);
1637 }
1638 break;
1639 }
1640 case SUBJECT_CAPTURE: {
1641 int capture = part.data;
1642 FixedArray* match_info = last_match_info->elements();
1643 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1644 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1645 if (from >= 0 && to > from) {
1646 builder->AddSubjectSlice(from, to);
1647 }
1648 break;
1649 }
1650 case REPLACEMENT_SUBSTRING:
1651 case REPLACEMENT_STRING:
1652 builder->AddString(replacement_substrings_[part.data]);
1653 break;
1654 default:
1655 UNREACHABLE();
1656 }
1657 }
1658}
1659
1660
1661
1662static Object* StringReplaceRegExpWithString(String* subject,
1663 JSRegExp* regexp,
1664 String* replacement,
1665 JSArray* last_match_info) {
1666 ASSERT(subject->IsFlat());
1667 ASSERT(replacement->IsFlat());
1668
1669 HandleScope handles;
1670
1671 int length = subject->length();
1672 Handle<String> subject_handle(subject);
1673 Handle<JSRegExp> regexp_handle(regexp);
1674 Handle<String> replacement_handle(replacement);
1675 Handle<JSArray> last_match_info_handle(last_match_info);
1676 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1677 subject_handle,
1678 0,
1679 last_match_info_handle);
1680 if (match.is_null()) {
1681 return Failure::Exception();
1682 }
1683 if (match->IsNull()) {
1684 return *subject_handle;
1685 }
1686
1687 int capture_count = regexp_handle->CaptureCount();
1688
1689 // CompiledReplacement uses zone allocation.
1690 ZoneScope zone(DELETE_ON_EXIT);
1691 CompiledReplacement compiled_replacement;
1692 compiled_replacement.Compile(replacement_handle,
1693 capture_count,
1694 length);
1695
1696 bool is_global = regexp_handle->GetFlags().is_global();
1697
1698 // Guessing the number of parts that the final result string is built
1699 // from. Global regexps can match any number of times, so we guess
1700 // conservatively.
1701 int expected_parts =
1702 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1703 ReplacementStringBuilder builder(subject_handle, expected_parts);
1704
1705 // Index of end of last match.
1706 int prev = 0;
1707
1708 // Number of parts added by compiled replacement plus preceeding string
1709 // and possibly suffix after last match.
1710 const int parts_added_per_loop = compiled_replacement.parts() + 2;
1711 bool matched = true;
1712 do {
1713 ASSERT(last_match_info_handle->HasFastElements());
1714 // Increase the capacity of the builder before entering local handle-scope,
1715 // so its internal buffer can safely allocate a new handle if it grows.
1716 builder.EnsureCapacity(parts_added_per_loop);
1717
1718 HandleScope loop_scope;
1719 int start, end;
1720 {
1721 AssertNoAllocation match_info_array_is_not_in_a_handle;
1722 FixedArray* match_info_array = last_match_info_handle->elements();
1723
1724 ASSERT_EQ(capture_count * 2 + 2,
1725 RegExpImpl::GetLastCaptureCount(match_info_array));
1726 start = RegExpImpl::GetCapture(match_info_array, 0);
1727 end = RegExpImpl::GetCapture(match_info_array, 1);
1728 }
1729
1730 if (prev < start) {
1731 builder.AddSubjectSlice(prev, start);
1732 }
1733 compiled_replacement.Apply(&builder,
1734 start,
1735 end,
1736 last_match_info_handle);
1737 prev = end;
1738
1739 // Only continue checking for global regexps.
1740 if (!is_global) break;
1741
1742 // Continue from where the match ended, unless it was an empty match.
1743 int next = end;
1744 if (start == end) {
1745 next = end + 1;
1746 if (next > length) break;
1747 }
1748
1749 match = RegExpImpl::Exec(regexp_handle,
1750 subject_handle,
1751 next,
1752 last_match_info_handle);
1753 if (match.is_null()) {
1754 return Failure::Exception();
1755 }
1756 matched = !match->IsNull();
1757 } while (matched);
1758
1759 if (prev < length) {
1760 builder.AddSubjectSlice(prev, length);
1761 }
1762
1763 return *(builder.ToString());
1764}
1765
1766
1767static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1768 ASSERT(args.length() == 4);
1769
1770 CONVERT_CHECKED(String, subject, args[0]);
1771 if (!subject->IsFlat()) {
1772 Object* flat_subject = subject->TryFlatten();
1773 if (flat_subject->IsFailure()) {
1774 return flat_subject;
1775 }
1776 subject = String::cast(flat_subject);
1777 }
1778
1779 CONVERT_CHECKED(String, replacement, args[2]);
1780 if (!replacement->IsFlat()) {
1781 Object* flat_replacement = replacement->TryFlatten();
1782 if (flat_replacement->IsFailure()) {
1783 return flat_replacement;
1784 }
1785 replacement = String::cast(flat_replacement);
1786 }
1787
1788 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1789 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1790
1791 ASSERT(last_match_info->HasFastElements());
1792
1793 return StringReplaceRegExpWithString(subject,
1794 regexp,
1795 replacement,
1796 last_match_info);
1797}
1798
1799
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001800
ager@chromium.org7c537e22008-10-16 08:43:32 +00001801// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1802// limit, we can fix the size of tables.
1803static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001804// Reduce alphabet to this size.
1805static const int kBMAlphabetSize = 0x100;
1806// For patterns below this length, the skip length of Boyer-Moore is too short
1807// to compensate for the algorithmic overhead compared to simple brute force.
1808static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001809
ager@chromium.org7c537e22008-10-16 08:43:32 +00001810// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1811// shift. Only allows the last kBMMaxShift characters of the needle
1812// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001813class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001814 public:
1815 BMGoodSuffixBuffers() {}
1816 inline void init(int needle_length) {
1817 ASSERT(needle_length > 1);
1818 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1819 int len = needle_length - start;
1820 biased_suffixes_ = suffixes_ - start;
1821 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1822 for (int i = 0; i <= len; i++) {
1823 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001824 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001825 }
1826 inline int& suffix(int index) {
1827 ASSERT(biased_suffixes_ + index >= suffixes_);
1828 return biased_suffixes_[index];
1829 }
1830 inline int& shift(int index) {
1831 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1832 return biased_good_suffix_shift_[index];
1833 }
1834 private:
1835 int suffixes_[kBMMaxShift + 1];
1836 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001837 int* biased_suffixes_;
1838 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001839 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1840};
1841
1842// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001843static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001844static BMGoodSuffixBuffers bmgs_buffers;
1845
1846// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001847template <typename pchar>
1848static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1849 int start) {
1850 // Run forwards to populate bad_char_table, so that *last* instance
1851 // of character equivalence class is the one registered.
1852 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001853 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1854 : kBMAlphabetSize;
1855 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001856 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001857 } else {
1858 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001859 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001860 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001861 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001862 for (int i = start; i < pattern.length() - 1; i++) {
1863 pchar c = pattern[i];
1864 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001865 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001866 }
1867}
1868
1869template <typename pchar>
1870static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001871 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001872 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001873 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001874 // Compute Good Suffix tables.
1875 bmgs_buffers.init(m);
1876
1877 bmgs_buffers.shift(m-1) = 1;
1878 bmgs_buffers.suffix(m) = m + 1;
1879 pchar last_char = pattern[m - 1];
1880 int suffix = m + 1;
1881 for (int i = m; i > start;) {
1882 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1883 if (bmgs_buffers.shift(suffix) == len) {
1884 bmgs_buffers.shift(suffix) = suffix - i;
1885 }
1886 suffix = bmgs_buffers.suffix(suffix);
1887 }
1888 i--;
1889 suffix--;
1890 bmgs_buffers.suffix(i) = suffix;
1891 if (suffix == m) {
1892 // No suffix to extend, so we check against last_char only.
1893 while (i > start && pattern[i - 1] != last_char) {
1894 if (bmgs_buffers.shift(m) == len) {
1895 bmgs_buffers.shift(m) = m - i;
1896 }
1897 i--;
1898 bmgs_buffers.suffix(i) = m;
1899 }
1900 if (i > start) {
1901 i--;
1902 suffix--;
1903 bmgs_buffers.suffix(i) = suffix;
1904 }
1905 }
1906 }
1907 if (suffix < m) {
1908 for (int i = start; i <= m; i++) {
1909 if (bmgs_buffers.shift(i) == len) {
1910 bmgs_buffers.shift(i) = suffix - start;
1911 }
1912 if (i == suffix) {
1913 suffix = bmgs_buffers.suffix(suffix);
1914 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001915 }
1916 }
1917}
1918
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001919template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001920static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001921 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001922 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001923 }
1924 if (sizeof(pchar) == 1) {
1925 if (char_code > String::kMaxAsciiCharCode) {
1926 return -1;
1927 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001928 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001929 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001930 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001931}
1932
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001933// Restricted simplified Boyer-Moore string matching.
1934// Uses only the bad-shift table of Boyer-Moore and only uses it
1935// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001936template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001937static int BoyerMooreHorspool(Vector<const schar> subject,
1938 Vector<const pchar> pattern,
1939 int start_index,
1940 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001941 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001942 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001943 // Only preprocess at most kBMMaxShift last characters of pattern.
1944 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001945
ager@chromium.org7c537e22008-10-16 08:43:32 +00001946 BoyerMoorePopulateBadCharTable(pattern, start);
1947
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001948 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001949 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001950 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001951 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001952 // Perform search
1953 for (idx = start_index; idx <= n - m;) {
1954 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001955 int c;
1956 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001957 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001958 int shift = j - bc_occ;
1959 idx += shift;
1960 badness += 1 - shift; // at most zero, so badness cannot increase.
1961 if (idx > n - m) {
1962 *complete = true;
1963 return -1;
1964 }
1965 }
1966 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001967 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001968 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001969 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001970 return idx;
1971 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001972 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001973 // Badness increases by the number of characters we have
1974 // checked, and decreases by the number of characters we
1975 // can skip by shifting. It's a measure of how we are doing
1976 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001977 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001978 if (badness > 0) {
1979 *complete = false;
1980 return idx;
1981 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001982 }
1983 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001984 *complete = true;
1985 return -1;
1986}
ager@chromium.org7c537e22008-10-16 08:43:32 +00001987
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001988
1989template <typename schar, typename pchar>
1990static int BoyerMooreIndexOf(Vector<const schar> subject,
1991 Vector<const pchar> pattern,
1992 int idx) {
1993 int n = subject.length();
1994 int m = pattern.length();
1995 // Only preprocess at most kBMMaxShift last characters of pattern.
1996 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
1997
1998 // Build the Good Suffix table and continue searching.
1999 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2000 pchar last_char = pattern[m - 1];
2001 // Continue search from i.
2002 do {
2003 int j = m - 1;
2004 schar c;
2005 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002006 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002007 idx += shift;
2008 if (idx > n - m) {
2009 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002010 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002011 }
2012 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2013 if (j < 0) {
2014 return idx;
2015 } else if (j < start) {
2016 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002017 // Fall back on BMH shift.
2018 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002019 } else {
2020 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002021 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002022 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002023 if (gs_shift > shift) {
2024 shift = gs_shift;
2025 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002026 idx += shift;
2027 }
2028 } while (idx <= n - m);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002029
2030 return -1;
2031}
2032
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002033
2034template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002035static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002036 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002037 int start_index) {
2038 for (int i = start_index, n = string.length(); i < n; i++) {
2039 if (pattern_char == string[i]) {
2040 return i;
2041 }
2042 }
2043 return -1;
2044}
2045
2046// Trivial string search for shorter strings.
2047// On return, if "complete" is set to true, the return value is the
2048// final result of searching for the patter in the subject.
2049// If "complete" is set to false, the return value is the index where
2050// further checking should start, i.e., it's guaranteed that the pattern
2051// does not occur at a position prior to the returned index.
2052template <typename pchar, typename schar>
2053static int SimpleIndexOf(Vector<const schar> subject,
2054 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002055 int idx,
2056 bool* complete) {
2057 // Badness is a count of how much work we have done. When we have
2058 // done enough work we decide it's probably worth switching to a better
2059 // algorithm.
2060 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002061 // We know our pattern is at least 2 characters, we cache the first so
2062 // the common case of the first character not matching is faster.
2063 pchar pattern_first_char = pattern[0];
2064
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002065 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2066 badness++;
2067 if (badness > 0) {
2068 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002069 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002070 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002071 if (subject[i] != pattern_first_char) continue;
2072 int j = 1;
2073 do {
2074 if (pattern[j] != subject[i+j]) {
2075 break;
2076 }
2077 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002078 } while (j < pattern.length());
2079 if (j == pattern.length()) {
2080 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002081 return i;
2082 }
2083 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002084 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002085 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002086 return -1;
2087}
2088
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002089// Simple indexOf that never bails out. For short patterns only.
2090template <typename pchar, typename schar>
2091static int SimpleIndexOf(Vector<const schar> subject,
2092 Vector<const pchar> pattern,
2093 int idx) {
2094 pchar pattern_first_char = pattern[0];
2095 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2096 if (subject[i] != pattern_first_char) continue;
2097 int j = 1;
2098 do {
2099 if (pattern[j] != subject[i+j]) {
2100 break;
2101 }
2102 j++;
2103 } while (j < pattern.length());
2104 if (j == pattern.length()) {
2105 return i;
2106 }
2107 }
2108 return -1;
2109}
2110
2111
2112// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002113template <typename schar, typename pchar>
2114static int StringMatchStrategy(Vector<const schar> sub,
2115 Vector<const pchar> pat,
2116 int start_index) {
2117 ASSERT(pat.length() > 1);
2118
2119 // We have an ASCII haystack and a non-ASCII needle. Check if there
2120 // really is a non-ASCII character in the needle and bail out if there
2121 // is.
2122 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2123 for (int i = 0; i < pat.length(); i++) {
2124 uc16 c = pat[i];
2125 if (c > String::kMaxAsciiCharCode) {
2126 return -1;
2127 }
2128 }
2129 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002130 if (pat.length() < kBMMinPatternLength) {
2131 // We don't believe fancy searching can ever be more efficient.
2132 // The max shift of Boyer-Moore on a pattern of this length does
2133 // not compensate for the overhead.
2134 return SimpleIndexOf(sub, pat, start_index);
2135 }
2136 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002137 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002138 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2139 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002140 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002141 if (complete) return idx;
2142 return BoyerMooreIndexOf(sub, pat, idx);
2143}
2144
2145// Perform string match of pattern on subject, starting at start index.
2146// Caller must ensure that 0 <= start_index <= sub->length(),
2147// and should check that pat->length() + start_index <= sub->length()
2148int Runtime::StringMatch(Handle<String> sub,
2149 Handle<String> pat,
2150 int start_index) {
2151 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002152 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002153
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002154 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002155 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002156
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002157 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002158 if (start_index + pattern_length > subject_length) return -1;
2159
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002160 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002161 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002162 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002163 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002164 // character patterns linear search is necessary, so any smart
2165 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002166 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002167 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002168 if (StringShape(*sub).IsAsciiRepresentation()) {
2169 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002170 if (pchar > String::kMaxAsciiCharCode) {
2171 return -1;
2172 }
2173 Vector<const char> ascii_vector =
2174 sub->ToAsciiVector().SubVector(start_index, subject_length);
2175 const void* pos = memchr(ascii_vector.start(),
2176 static_cast<const char>(pchar),
2177 static_cast<size_t>(ascii_vector.length()));
2178 if (pos == NULL) {
2179 return -1;
2180 }
2181 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
2182 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002183 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002184 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002185 }
2186
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002187 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002188 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002189 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002190
ager@chromium.org7c537e22008-10-16 08:43:32 +00002191 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2192 // dispatch on type of strings
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002193 if (StringShape(*pat).IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002194 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002195 if (StringShape(*sub).IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002196 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002197 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002198 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002199 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002200 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002201 if (StringShape(*sub).IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002202 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002203 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002204 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002205}
2206
2207
2208static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002209 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002210 ASSERT(args.length() == 3);
2211
ager@chromium.org7c537e22008-10-16 08:43:32 +00002212 CONVERT_ARG_CHECKED(String, sub, 0);
2213 CONVERT_ARG_CHECKED(String, pat, 1);
2214
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002215 Object* index = args[2];
2216 uint32_t start_index;
2217 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2218
ager@chromium.org870a0b62008-11-04 11:43:05 +00002219 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002220 int position = Runtime::StringMatch(sub, pat, start_index);
2221 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002222}
2223
2224
2225static Object* Runtime_StringLastIndexOf(Arguments args) {
2226 NoHandleAllocation ha;
2227 ASSERT(args.length() == 3);
2228
2229 CONVERT_CHECKED(String, sub, args[0]);
2230 CONVERT_CHECKED(String, pat, args[1]);
2231 Object* index = args[2];
2232
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002233 sub->TryFlattenIfNotFlat();
2234 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002235
2236 uint32_t start_index;
2237 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2238
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002239 uint32_t pattern_length = pat->length();
2240 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002241
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002242 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002243 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002244 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002245
2246 for (int i = start_index; i >= 0; i--) {
2247 bool found = true;
2248 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002249 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002250 found = false;
2251 break;
2252 }
2253 }
2254 if (found) return Smi::FromInt(i);
2255 }
2256
2257 return Smi::FromInt(-1);
2258}
2259
2260
2261static Object* Runtime_StringLocaleCompare(Arguments args) {
2262 NoHandleAllocation ha;
2263 ASSERT(args.length() == 2);
2264
2265 CONVERT_CHECKED(String, str1, args[0]);
2266 CONVERT_CHECKED(String, str2, args[1]);
2267
2268 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002269 int str1_length = str1->length();
2270 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002271
2272 // Decide trivial cases without flattening.
2273 if (str1_length == 0) {
2274 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2275 return Smi::FromInt(-str2_length);
2276 } else {
2277 if (str2_length == 0) return Smi::FromInt(str1_length);
2278 }
2279
2280 int end = str1_length < str2_length ? str1_length : str2_length;
2281
2282 // No need to flatten if we are going to find the answer on the first
2283 // character. At this point we know there is at least one character
2284 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002285 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002286 if (d != 0) return Smi::FromInt(d);
2287
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002288 str1->TryFlattenIfNotFlat();
2289 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002290
2291 static StringInputBuffer buf1;
2292 static StringInputBuffer buf2;
2293
2294 buf1.Reset(str1);
2295 buf2.Reset(str2);
2296
2297 for (int i = 0; i < end; i++) {
2298 uint16_t char1 = buf1.GetNext();
2299 uint16_t char2 = buf2.GetNext();
2300 if (char1 != char2) return Smi::FromInt(char1 - char2);
2301 }
2302
2303 return Smi::FromInt(str1_length - str2_length);
2304}
2305
2306
2307static Object* Runtime_StringSlice(Arguments args) {
2308 NoHandleAllocation ha;
2309 ASSERT(args.length() == 3);
2310
2311 CONVERT_CHECKED(String, value, args[0]);
2312 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
2313 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
2314
2315 int start = FastD2I(from_number);
2316 int end = FastD2I(to_number);
2317
2318 RUNTIME_ASSERT(end >= start);
2319 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002320 RUNTIME_ASSERT(end <= value->length());
2321 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002322}
2323
2324
ager@chromium.org41826e72009-03-30 13:30:57 +00002325static Object* Runtime_StringMatch(Arguments args) {
2326 ASSERT_EQ(3, args.length());
2327
2328 CONVERT_ARG_CHECKED(String, subject, 0);
2329 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2330 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2331 HandleScope handles;
2332
2333 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2334
2335 if (match.is_null()) {
2336 return Failure::Exception();
2337 }
2338 if (match->IsNull()) {
2339 return Heap::null_value();
2340 }
2341 int length = subject->length();
2342
2343 ZoneScope zone_space(DELETE_ON_EXIT);
2344 ZoneList<int> offsets(8);
2345 do {
2346 int start;
2347 int end;
2348 {
2349 AssertNoAllocation no_alloc;
2350 FixedArray* elements = regexp_info->elements();
2351 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2352 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2353 }
2354 offsets.Add(start);
2355 offsets.Add(end);
2356 int index = start < end ? end : end + 1;
2357 if (index > length) break;
2358 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2359 if (match.is_null()) {
2360 return Failure::Exception();
2361 }
2362 } while (!match->IsNull());
2363 int matches = offsets.length() / 2;
2364 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2365 for (int i = 0; i < matches ; i++) {
2366 int from = offsets.at(i * 2);
2367 int to = offsets.at(i * 2 + 1);
2368 elements->set(i, *Factory::NewStringSlice(subject, from, to));
2369 }
2370 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2371 result->set_length(Smi::FromInt(matches));
2372 return *result;
2373}
2374
2375
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002376static Object* Runtime_NumberToRadixString(Arguments args) {
2377 NoHandleAllocation ha;
2378 ASSERT(args.length() == 2);
2379
2380 CONVERT_DOUBLE_CHECKED(value, args[0]);
2381 if (isnan(value)) {
2382 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2383 }
2384 if (isinf(value)) {
2385 if (value < 0) {
2386 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2387 }
2388 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2389 }
2390 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2391 int radix = FastD2I(radix_number);
2392 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2393 char* str = DoubleToRadixCString(value, radix);
2394 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2395 DeleteArray(str);
2396 return result;
2397}
2398
2399
2400static Object* Runtime_NumberToFixed(Arguments args) {
2401 NoHandleAllocation ha;
2402 ASSERT(args.length() == 2);
2403
2404 CONVERT_DOUBLE_CHECKED(value, args[0]);
2405 if (isnan(value)) {
2406 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2407 }
2408 if (isinf(value)) {
2409 if (value < 0) {
2410 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2411 }
2412 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2413 }
2414 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2415 int f = FastD2I(f_number);
2416 RUNTIME_ASSERT(f >= 0);
2417 char* str = DoubleToFixedCString(value, f);
2418 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2419 DeleteArray(str);
2420 return res;
2421}
2422
2423
2424static Object* Runtime_NumberToExponential(Arguments args) {
2425 NoHandleAllocation ha;
2426 ASSERT(args.length() == 2);
2427
2428 CONVERT_DOUBLE_CHECKED(value, args[0]);
2429 if (isnan(value)) {
2430 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2431 }
2432 if (isinf(value)) {
2433 if (value < 0) {
2434 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2435 }
2436 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2437 }
2438 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2439 int f = FastD2I(f_number);
2440 RUNTIME_ASSERT(f >= -1 && f <= 20);
2441 char* str = DoubleToExponentialCString(value, f);
2442 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2443 DeleteArray(str);
2444 return res;
2445}
2446
2447
2448static Object* Runtime_NumberToPrecision(Arguments args) {
2449 NoHandleAllocation ha;
2450 ASSERT(args.length() == 2);
2451
2452 CONVERT_DOUBLE_CHECKED(value, args[0]);
2453 if (isnan(value)) {
2454 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2455 }
2456 if (isinf(value)) {
2457 if (value < 0) {
2458 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2459 }
2460 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2461 }
2462 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2463 int f = FastD2I(f_number);
2464 RUNTIME_ASSERT(f >= 1 && f <= 21);
2465 char* str = DoubleToPrecisionCString(value, f);
2466 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2467 DeleteArray(str);
2468 return res;
2469}
2470
2471
2472// Returns a single character string where first character equals
2473// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002474static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002475 if (index < static_cast<uint32_t>(string->length())) {
2476 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002477 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002478 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002479 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002480 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002481}
2482
2483
2484Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2485 // Handle [] indexing on Strings
2486 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002487 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2488 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002489 }
2490
2491 // Handle [] indexing on String objects
2492 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002493 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2494 Handle<Object> result =
2495 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2496 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002497 }
2498
2499 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002500 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002501 return prototype->GetElement(index);
2502 }
2503
2504 return object->GetElement(index);
2505}
2506
2507
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002508Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2509 HandleScope scope;
2510
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002511 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002512 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002513 Handle<Object> error =
2514 Factory::NewTypeError("non_object_property_load",
2515 HandleVector(args, 2));
2516 return Top::Throw(*error);
2517 }
2518
2519 // Check if the given key is an array index.
2520 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002521 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002522 return GetElementOrCharAt(object, index);
2523 }
2524
2525 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002526 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002527 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002528 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002529 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002530 bool has_pending_exception = false;
2531 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002532 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002533 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002534 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002535 }
2536
ager@chromium.org32912102009-01-16 10:38:43 +00002537 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002538 // the element if so.
2539 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002540 return GetElementOrCharAt(object, index);
2541 } else {
2542 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002543 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002544 }
2545}
2546
2547
2548static Object* Runtime_GetProperty(Arguments args) {
2549 NoHandleAllocation ha;
2550 ASSERT(args.length() == 2);
2551
2552 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002553 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002554
2555 return Runtime::GetObjectProperty(object, key);
2556}
2557
2558
ager@chromium.org7c537e22008-10-16 08:43:32 +00002559
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002560// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002561static Object* Runtime_KeyedGetProperty(Arguments args) {
2562 NoHandleAllocation ha;
2563 ASSERT(args.length() == 2);
2564
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002565 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002566 // itself.
2567 //
2568 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002569 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002570 // global proxy object never has properties. This is the case
2571 // because the global proxy object forwards everything to its hidden
2572 // prototype including local lookups.
2573 //
2574 // Additionally, we need to make sure that we do not cache results
2575 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002576 if (args[0]->IsJSObject() &&
2577 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002578 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002579 args[1]->IsString()) {
2580 JSObject* receiver = JSObject::cast(args[0]);
2581 String* key = String::cast(args[1]);
2582 if (receiver->HasFastProperties()) {
2583 // Attempt to use lookup cache.
2584 Object* obj = Heap::GetKeyedLookupCache();
2585 if (obj->IsFailure()) return obj;
2586 LookupCache* cache = LookupCache::cast(obj);
2587 Map* receiver_map = receiver->map();
2588 int offset = cache->Lookup(receiver_map, key);
2589 if (offset != LookupCache::kNotFound) {
2590 Object* value = receiver->FastPropertyAt(offset);
2591 return value->IsTheHole() ? Heap::undefined_value() : value;
2592 }
2593 // Lookup cache miss. Perform lookup and update the cache if
2594 // appropriate.
2595 LookupResult result;
2596 receiver->LocalLookup(key, &result);
2597 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2598 int offset = result.GetFieldIndex();
2599 Object* obj = cache->Put(receiver_map, key, offset);
2600 if (obj->IsFailure()) return obj;
2601 Heap::SetKeyedLookupCache(LookupCache::cast(obj));
2602 Object* value = receiver->FastPropertyAt(offset);
2603 return value->IsTheHole() ? Heap::undefined_value() : value;
2604 }
2605 } else {
2606 // Attempt dictionary lookup.
2607 Dictionary* dictionary = receiver->property_dictionary();
2608 int entry = dictionary->FindStringEntry(key);
2609 if ((entry != DescriptorArray::kNotFound) &&
2610 (dictionary->DetailsAt(entry).type() == NORMAL)) {
2611 return dictionary->ValueAt(entry);
2612 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002613 }
2614 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002615
2616 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002617 return Runtime::GetObjectProperty(args.at<Object>(0),
2618 args.at<Object>(1));
2619}
2620
2621
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002622Object* Runtime::SetObjectProperty(Handle<Object> object,
2623 Handle<Object> key,
2624 Handle<Object> value,
2625 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002626 HandleScope scope;
2627
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002628 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002629 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002630 Handle<Object> error =
2631 Factory::NewTypeError("non_object_property_store",
2632 HandleVector(args, 2));
2633 return Top::Throw(*error);
2634 }
2635
2636 // If the object isn't a JavaScript object, we ignore the store.
2637 if (!object->IsJSObject()) return *value;
2638
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002639 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2640
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002641 // Check if the given key is an array index.
2642 uint32_t index;
2643 if (Array::IndexFromObject(*key, &index)) {
2644 ASSERT(attr == NONE);
2645
2646 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2647 // of a string using [] notation. We need to support this too in
2648 // JavaScript.
2649 // In the case of a String object we just need to redirect the assignment to
2650 // the underlying string if the index is in range. Since the underlying
2651 // string does nothing with the assignment then we can ignore such
2652 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002653 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002654 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002655 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002656
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002657 Handle<Object> result = SetElement(js_object, index, value);
2658 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002659 return *value;
2660 }
2661
2662 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002663 Handle<Object> result;
2664 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002665 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002666 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002667 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002668 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002669 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002670 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002671 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002672 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002673 return *value;
2674 }
2675
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002676 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002677 bool has_pending_exception = false;
2678 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2679 if (has_pending_exception) return Failure::Exception();
2680 Handle<String> name = Handle<String>::cast(converted);
2681
2682 if (name->AsArrayIndex(&index)) {
2683 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002684 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002685 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002686 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002687 }
2688}
2689
2690
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002691Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2692 Handle<Object> key,
2693 Handle<Object> value,
2694 PropertyAttributes attr) {
2695 HandleScope scope;
2696
2697 // Check if the given key is an array index.
2698 uint32_t index;
2699 if (Array::IndexFromObject(*key, &index)) {
2700 ASSERT(attr == NONE);
2701
2702 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2703 // of a string using [] notation. We need to support this too in
2704 // JavaScript.
2705 // In the case of a String object we just need to redirect the assignment to
2706 // the underlying string if the index is in range. Since the underlying
2707 // string does nothing with the assignment then we can ignore such
2708 // assignments.
2709 if (js_object->IsStringObjectWithCharacterAt(index)) {
2710 return *value;
2711 }
2712
2713 return js_object->SetElement(index, *value);
2714 }
2715
2716 if (key->IsString()) {
2717 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2718 ASSERT(attr == NONE);
2719 return js_object->SetElement(index, *value);
2720 } else {
2721 Handle<String> key_string = Handle<String>::cast(key);
2722 key_string->TryFlattenIfNotFlat();
2723 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2724 *value,
2725 attr);
2726 }
2727 }
2728
2729 // Call-back into JavaScript to convert the key to a string.
2730 bool has_pending_exception = false;
2731 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2732 if (has_pending_exception) return Failure::Exception();
2733 Handle<String> name = Handle<String>::cast(converted);
2734
2735 if (name->AsArrayIndex(&index)) {
2736 ASSERT(attr == NONE);
2737 return js_object->SetElement(index, *value);
2738 } else {
2739 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2740 }
2741}
2742
2743
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002744static Object* Runtime_SetProperty(Arguments args) {
2745 NoHandleAllocation ha;
2746 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2747
2748 Handle<Object> object = args.at<Object>(0);
2749 Handle<Object> key = args.at<Object>(1);
2750 Handle<Object> value = args.at<Object>(2);
2751
2752 // Compute attributes.
2753 PropertyAttributes attributes = NONE;
2754 if (args.length() == 4) {
2755 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002756 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002757 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002758 RUNTIME_ASSERT(
2759 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2760 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002761 }
2762 return Runtime::SetObjectProperty(object, key, value, attributes);
2763}
2764
2765
2766// Set a local property, even if it is READ_ONLY. If the property does not
2767// exist, it will be added with attributes NONE.
2768static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2769 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002770 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002771 CONVERT_CHECKED(JSObject, object, args[0]);
2772 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002773 // Compute attributes.
2774 PropertyAttributes attributes = NONE;
2775 if (args.length() == 4) {
2776 CONVERT_CHECKED(Smi, value_obj, args[3]);
2777 int unchecked_value = value_obj->value();
2778 // Only attribute bits should be set.
2779 RUNTIME_ASSERT(
2780 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2781 attributes = static_cast<PropertyAttributes>(unchecked_value);
2782 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002783
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002784 return object->
2785 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002786}
2787
2788
2789static Object* Runtime_DeleteProperty(Arguments args) {
2790 NoHandleAllocation ha;
2791 ASSERT(args.length() == 2);
2792
2793 CONVERT_CHECKED(JSObject, object, args[0]);
2794 CONVERT_CHECKED(String, key, args[1]);
2795 return object->DeleteProperty(key);
2796}
2797
2798
2799static Object* Runtime_HasLocalProperty(Arguments args) {
2800 NoHandleAllocation ha;
2801 ASSERT(args.length() == 2);
2802 CONVERT_CHECKED(String, key, args[1]);
2803
2804 // Only JS objects can have properties.
2805 if (args[0]->IsJSObject()) {
2806 JSObject* object = JSObject::cast(args[0]);
2807 if (object->HasLocalProperty(key)) return Heap::true_value();
2808 } else if (args[0]->IsString()) {
2809 // Well, there is one exception: Handle [] on strings.
2810 uint32_t index;
2811 if (key->AsArrayIndex(&index)) {
2812 String* string = String::cast(args[0]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002813 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002814 return Heap::true_value();
2815 }
2816 }
2817 return Heap::false_value();
2818}
2819
2820
2821static Object* Runtime_HasProperty(Arguments args) {
2822 NoHandleAllocation na;
2823 ASSERT(args.length() == 2);
2824
2825 // Only JS objects can have properties.
2826 if (args[0]->IsJSObject()) {
2827 JSObject* object = JSObject::cast(args[0]);
2828 CONVERT_CHECKED(String, key, args[1]);
2829 if (object->HasProperty(key)) return Heap::true_value();
2830 }
2831 return Heap::false_value();
2832}
2833
2834
2835static Object* Runtime_HasElement(Arguments args) {
2836 NoHandleAllocation na;
2837 ASSERT(args.length() == 2);
2838
2839 // Only JS objects can have elements.
2840 if (args[0]->IsJSObject()) {
2841 JSObject* object = JSObject::cast(args[0]);
2842 CONVERT_CHECKED(Smi, index_obj, args[1]);
2843 uint32_t index = index_obj->value();
2844 if (object->HasElement(index)) return Heap::true_value();
2845 }
2846 return Heap::false_value();
2847}
2848
2849
2850static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2851 NoHandleAllocation ha;
2852 ASSERT(args.length() == 2);
2853
2854 CONVERT_CHECKED(JSObject, object, args[0]);
2855 CONVERT_CHECKED(String, key, args[1]);
2856
2857 uint32_t index;
2858 if (key->AsArrayIndex(&index)) {
2859 return Heap::ToBoolean(object->HasElement(index));
2860 }
2861
ager@chromium.org870a0b62008-11-04 11:43:05 +00002862 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2863 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002864}
2865
2866
2867static Object* Runtime_GetPropertyNames(Arguments args) {
2868 HandleScope scope;
2869 ASSERT(args.length() == 1);
2870
2871 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2872 Handle<JSObject> object(raw_object);
2873 return *GetKeysFor(object);
2874}
2875
2876
2877// Returns either a FixedArray as Runtime_GetPropertyNames,
2878// or, if the given object has an enum cache that contains
2879// all enumerable properties of the object and its prototypes
2880// have none, the map of the object. This is used to speed up
2881// the check for deletions during a for-in.
2882static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2883 ASSERT(args.length() == 1);
2884
2885 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2886
2887 if (raw_object->IsSimpleEnum()) return raw_object->map();
2888
2889 HandleScope scope;
2890 Handle<JSObject> object(raw_object);
2891 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2892
2893 // Test again, since cache may have been built by preceding call.
2894 if (object->IsSimpleEnum()) return object->map();
2895
2896 return *content;
2897}
2898
2899
2900static Object* Runtime_GetArgumentsProperty(Arguments args) {
2901 NoHandleAllocation ha;
2902 ASSERT(args.length() == 1);
2903
2904 // Compute the frame holding the arguments.
2905 JavaScriptFrameIterator it;
2906 it.AdvanceToArgumentsFrame();
2907 JavaScriptFrame* frame = it.frame();
2908
2909 // Get the actual number of provided arguments.
2910 const uint32_t n = frame->GetProvidedParametersCount();
2911
2912 // Try to convert the key to an index. If successful and within
2913 // index return the the argument from the frame.
2914 uint32_t index;
2915 if (Array::IndexFromObject(args[0], &index) && index < n) {
2916 return frame->GetParameter(index);
2917 }
2918
2919 // Convert the key to a string.
2920 HandleScope scope;
2921 bool exception = false;
2922 Handle<Object> converted =
2923 Execution::ToString(args.at<Object>(0), &exception);
2924 if (exception) return Failure::Exception();
2925 Handle<String> key = Handle<String>::cast(converted);
2926
2927 // Try to convert the string key into an array index.
2928 if (key->AsArrayIndex(&index)) {
2929 if (index < n) {
2930 return frame->GetParameter(index);
2931 } else {
2932 return Top::initial_object_prototype()->GetElement(index);
2933 }
2934 }
2935
2936 // Handle special arguments properties.
2937 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
2938 if (key->Equals(Heap::callee_symbol())) return frame->function();
2939
2940 // Lookup in the initial Object.prototype object.
2941 return Top::initial_object_prototype()->GetProperty(*key);
2942}
2943
2944
kasperl@chromium.org061ef742009-02-27 12:16:20 +00002945static Object* Runtime_ToFastProperties(Arguments args) {
2946 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00002947 Handle<Object> object = args.at<Object>(0);
2948 if (object->IsJSObject()) {
2949 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2950 js_object->TransformToFastProperties(0);
2951 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00002952 return *object;
2953}
2954
2955
2956static Object* Runtime_ToSlowProperties(Arguments args) {
2957 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00002958 Handle<Object> object = args.at<Object>(0);
2959 if (object->IsJSObject()) {
2960 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2961 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
2962 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00002963 return *object;
2964}
2965
2966
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002967static Object* Runtime_ToBool(Arguments args) {
2968 NoHandleAllocation ha;
2969 ASSERT(args.length() == 1);
2970
2971 return args[0]->ToBoolean();
2972}
2973
2974
2975// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
2976// Possible optimizations: put the type string into the oddballs.
2977static Object* Runtime_Typeof(Arguments args) {
2978 NoHandleAllocation ha;
2979
2980 Object* obj = args[0];
2981 if (obj->IsNumber()) return Heap::number_symbol();
2982 HeapObject* heap_obj = HeapObject::cast(obj);
2983
2984 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002985 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002986
2987 InstanceType instance_type = heap_obj->map()->instance_type();
2988 if (instance_type < FIRST_NONSTRING_TYPE) {
2989 return Heap::string_symbol();
2990 }
2991
2992 switch (instance_type) {
2993 case ODDBALL_TYPE:
2994 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
2995 return Heap::boolean_symbol();
2996 }
2997 if (heap_obj->IsNull()) {
2998 return Heap::object_symbol();
2999 }
3000 ASSERT(heap_obj->IsUndefined());
3001 return Heap::undefined_symbol();
3002 case JS_FUNCTION_TYPE:
3003 return Heap::function_symbol();
3004 default:
3005 // For any kind of object not handled above, the spec rule for
3006 // host objects gives that it is okay to return "object"
3007 return Heap::object_symbol();
3008 }
3009}
3010
3011
3012static Object* Runtime_StringToNumber(Arguments args) {
3013 NoHandleAllocation ha;
3014 ASSERT(args.length() == 1);
3015 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003016 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003017 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3018}
3019
3020
3021static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3022 NoHandleAllocation ha;
3023 ASSERT(args.length() == 1);
3024
3025 CONVERT_CHECKED(JSArray, codes, args[0]);
3026 int length = Smi::cast(codes->length())->value();
3027
3028 // Check if the string can be ASCII.
3029 int i;
3030 for (i = 0; i < length; i++) {
3031 Object* element = codes->GetElement(i);
3032 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3033 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3034 break;
3035 }
3036
3037 Object* object = NULL;
3038 if (i == length) { // The string is ASCII.
3039 object = Heap::AllocateRawAsciiString(length);
3040 } else { // The string is not ASCII.
3041 object = Heap::AllocateRawTwoByteString(length);
3042 }
3043
3044 if (object->IsFailure()) return object;
3045 String* result = String::cast(object);
3046 for (int i = 0; i < length; i++) {
3047 Object* element = codes->GetElement(i);
3048 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003049 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003050 }
3051 return result;
3052}
3053
3054
3055// kNotEscaped is generated by the following:
3056//
3057// #!/bin/perl
3058// for (my $i = 0; $i < 256; $i++) {
3059// print "\n" if $i % 16 == 0;
3060// my $c = chr($i);
3061// my $escaped = 1;
3062// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3063// print $escaped ? "0, " : "1, ";
3064// }
3065
3066
3067static bool IsNotEscaped(uint16_t character) {
3068 // Only for 8 bit characters, the rest are always escaped (in a different way)
3069 ASSERT(character < 256);
3070 static const char kNotEscaped[256] = {
3071 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3072 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3073 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3074 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3075 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3076 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3077 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3078 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3079 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3080 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3081 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3082 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3083 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3084 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3085 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3086 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3087 };
3088 return kNotEscaped[character] != 0;
3089}
3090
3091
3092static Object* Runtime_URIEscape(Arguments args) {
3093 const char hex_chars[] = "0123456789ABCDEF";
3094 NoHandleAllocation ha;
3095 ASSERT(args.length() == 1);
3096 CONVERT_CHECKED(String, source, args[0]);
3097
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003098 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003099
3100 int escaped_length = 0;
3101 int length = source->length();
3102 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003103 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003104 buffer->Reset(source);
3105 while (buffer->has_more()) {
3106 uint16_t character = buffer->GetNext();
3107 if (character >= 256) {
3108 escaped_length += 6;
3109 } else if (IsNotEscaped(character)) {
3110 escaped_length++;
3111 } else {
3112 escaped_length += 3;
3113 }
3114 // We don't allow strings that are longer than Smi range.
3115 if (!Smi::IsValid(escaped_length)) {
3116 Top::context()->mark_out_of_memory();
3117 return Failure::OutOfMemoryException();
3118 }
3119 }
3120 }
3121 // No length change implies no change. Return original string if no change.
3122 if (escaped_length == length) {
3123 return source;
3124 }
3125 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3126 if (o->IsFailure()) return o;
3127 String* destination = String::cast(o);
3128 int dest_position = 0;
3129
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003130 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003131 buffer->Rewind();
3132 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003133 uint16_t chr = buffer->GetNext();
3134 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003135 destination->Set(dest_position, '%');
3136 destination->Set(dest_position+1, 'u');
3137 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3138 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3139 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3140 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003141 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003142 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003143 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003144 dest_position++;
3145 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003146 destination->Set(dest_position, '%');
3147 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3148 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003149 dest_position += 3;
3150 }
3151 }
3152 return destination;
3153}
3154
3155
3156static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3157 static const signed char kHexValue['g'] = {
3158 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3159 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3160 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3161 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3162 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3163 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3164 -1, 10, 11, 12, 13, 14, 15 };
3165
3166 if (character1 > 'f') return -1;
3167 int hi = kHexValue[character1];
3168 if (hi == -1) return -1;
3169 if (character2 > 'f') return -1;
3170 int lo = kHexValue[character2];
3171 if (lo == -1) return -1;
3172 return (hi << 4) + lo;
3173}
3174
3175
ager@chromium.org870a0b62008-11-04 11:43:05 +00003176static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003177 int i,
3178 int length,
3179 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003180 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003181 int32_t hi = 0;
3182 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003183 if (character == '%' &&
3184 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003185 source->Get(i + 1) == 'u' &&
3186 (hi = TwoDigitHex(source->Get(i + 2),
3187 source->Get(i + 3))) != -1 &&
3188 (lo = TwoDigitHex(source->Get(i + 4),
3189 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003190 *step = 6;
3191 return (hi << 8) + lo;
3192 } else if (character == '%' &&
3193 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003194 (lo = TwoDigitHex(source->Get(i + 1),
3195 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003196 *step = 3;
3197 return lo;
3198 } else {
3199 *step = 1;
3200 return character;
3201 }
3202}
3203
3204
3205static Object* Runtime_URIUnescape(Arguments args) {
3206 NoHandleAllocation ha;
3207 ASSERT(args.length() == 1);
3208 CONVERT_CHECKED(String, source, args[0]);
3209
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003210 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003211
3212 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003213 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003214
3215 int unescaped_length = 0;
3216 for (int i = 0; i < length; unescaped_length++) {
3217 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003218 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003219 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003220 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003221 i += step;
3222 }
3223
3224 // No length change implies no change. Return original string if no change.
3225 if (unescaped_length == length)
3226 return source;
3227
3228 Object* o = ascii ?
3229 Heap::AllocateRawAsciiString(unescaped_length) :
3230 Heap::AllocateRawTwoByteString(unescaped_length);
3231 if (o->IsFailure()) return o;
3232 String* destination = String::cast(o);
3233
3234 int dest_position = 0;
3235 for (int i = 0; i < length; dest_position++) {
3236 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003237 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003238 i += step;
3239 }
3240 return destination;
3241}
3242
3243
3244static Object* Runtime_StringParseInt(Arguments args) {
3245 NoHandleAllocation ha;
3246
3247 CONVERT_CHECKED(String, s, args[0]);
3248 CONVERT_DOUBLE_CHECKED(n, args[1]);
3249 int radix = FastD2I(n);
3250
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003251 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003252
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003253 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003254 int i;
3255
3256 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003257 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003258 if (i == len) return Heap::nan_value();
3259
3260 // Compute the sign (default to +).
3261 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003262 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003263 sign = -1;
3264 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003265 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003266 i++;
3267 }
3268
3269 // Compute the radix if 0.
3270 if (radix == 0) {
3271 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003272 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003273 radix = 8;
3274 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003275 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003276 if (c == 'x' || c == 'X') {
3277 radix = 16;
3278 i += 2;
3279 }
3280 }
3281 }
3282 } else if (radix == 16) {
3283 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003284 if (i + 1 < len && s->Get(i) == '0') {
3285 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003286 if (c == 'x' || c == 'X') i += 2;
3287 }
3288 }
3289
3290 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3291 double value;
3292 int end_index = StringToInt(s, i, radix, &value);
3293 if (end_index != i) {
3294 return Heap::NumberFromDouble(sign * value);
3295 }
3296 return Heap::nan_value();
3297}
3298
3299
3300static Object* Runtime_StringParseFloat(Arguments args) {
3301 NoHandleAllocation ha;
3302 CONVERT_CHECKED(String, str, args[0]);
3303
3304 // ECMA-262 section 15.1.2.3, empty string is NaN
3305 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3306
3307 // Create a number object from the value.
3308 return Heap::NumberFromDouble(value);
3309}
3310
3311
3312static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3313static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3314
3315
3316template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003317static Object* ConvertCaseHelper(String* s,
3318 int length,
3319 int input_string_length,
3320 unibrow::Mapping<Converter, 128>* mapping) {
3321 // We try this twice, once with the assumption that the result is no longer
3322 // than the input and, if that assumption breaks, again with the exact
3323 // length. This may not be pretty, but it is nicer than what was here before
3324 // and I hereby claim my vaffel-is.
3325 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003326 // Allocate the resulting string.
3327 //
3328 // NOTE: This assumes that the upper/lower case of an ascii
3329 // character is also ascii. This is currently the case, but it
3330 // might break in the future if we implement more context and locale
3331 // dependent upper/lower conversions.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003332 Object* o = StringShape(s).IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003333 ? Heap::AllocateRawAsciiString(length)
3334 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003335 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003336 String* result = String::cast(o);
3337 bool has_changed_character = false;
3338
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003339 // Convert all characters to upper case, assuming that they will fit
3340 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003341 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003342 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003343 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003344 // We can assume that the string is not empty
3345 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003346 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003347 bool has_next = buffer->has_more();
3348 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003349 int char_length = mapping->get(current, next, chars);
3350 if (char_length == 0) {
3351 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003352 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003353 i++;
3354 } else if (char_length == 1) {
3355 // Common case: converting the letter resulted in one character.
3356 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003357 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003358 has_changed_character = true;
3359 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003360 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003361 // We've assumed that the result would be as long as the
3362 // input but here is a character that converts to several
3363 // characters. No matter, we calculate the exact length
3364 // of the result and try the whole thing again.
3365 //
3366 // Note that this leaves room for optimization. We could just
3367 // memcpy what we already have to the result string. Also,
3368 // the result string is the last object allocated we could
3369 // "realloc" it and probably, in the vast majority of cases,
3370 // extend the existing string to be able to hold the full
3371 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003372 int next_length = 0;
3373 if (has_next) {
3374 next_length = mapping->get(next, 0, chars);
3375 if (next_length == 0) next_length = 1;
3376 }
3377 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003378 while (buffer->has_more()) {
3379 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003380 // NOTE: we use 0 as the next character here because, while
3381 // the next character may affect what a character converts to,
3382 // it does not in any case affect the length of what it convert
3383 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003384 int char_length = mapping->get(current, 0, chars);
3385 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003386 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003387 if (current_length > Smi::kMaxValue) {
3388 Top::context()->mark_out_of_memory();
3389 return Failure::OutOfMemoryException();
3390 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003391 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003392 // Try again with the real length.
3393 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003394 } else {
3395 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003396 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003397 i++;
3398 }
3399 has_changed_character = true;
3400 }
3401 current = next;
3402 }
3403 if (has_changed_character) {
3404 return result;
3405 } else {
3406 // If we didn't actually change anything in doing the conversion
3407 // we simple return the result and let the converted string
3408 // become garbage; there is no reason to keep two identical strings
3409 // alive.
3410 return s;
3411 }
3412}
3413
3414
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003415template <class Converter>
3416static Object* ConvertCase(Arguments args,
3417 unibrow::Mapping<Converter, 128>* mapping) {
3418 NoHandleAllocation ha;
3419
3420 CONVERT_CHECKED(String, s, args[0]);
3421 s->TryFlattenIfNotFlat();
3422
3423 int input_string_length = s->length();
3424 // Assume that the string is not empty; we need this assumption later
3425 if (input_string_length == 0) return s;
3426 int length = input_string_length;
3427
3428 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3429 if (answer->IsSmi()) {
3430 // Retry with correct length.
3431 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3432 }
3433 return answer; // This may be a failure.
3434}
3435
3436
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003437static Object* Runtime_StringToLowerCase(Arguments args) {
3438 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3439}
3440
3441
3442static Object* Runtime_StringToUpperCase(Arguments args) {
3443 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3444}
3445
3446
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003447bool Runtime::IsUpperCaseChar(uint16_t ch) {
3448 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3449 int char_length = to_upper_mapping.get(ch, 0, chars);
3450 return char_length == 0;
3451}
3452
3453
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003454static Object* Runtime_NumberToString(Arguments args) {
3455 NoHandleAllocation ha;
3456 ASSERT(args.length() == 1);
3457
3458 Object* number = args[0];
3459 RUNTIME_ASSERT(number->IsNumber());
3460
3461 Object* cached = Heap::GetNumberStringCache(number);
3462 if (cached != Heap::undefined_value()) {
3463 return cached;
3464 }
3465
3466 char arr[100];
3467 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3468 const char* str;
3469 if (number->IsSmi()) {
3470 int num = Smi::cast(number)->value();
3471 str = IntToCString(num, buffer);
3472 } else {
3473 double num = HeapNumber::cast(number)->value();
3474 str = DoubleToCString(num, buffer);
3475 }
3476 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3477
3478 if (!result->IsFailure()) {
3479 Heap::SetNumberStringCache(number, String::cast(result));
3480 }
3481 return result;
3482}
3483
3484
3485static Object* Runtime_NumberToInteger(Arguments args) {
3486 NoHandleAllocation ha;
3487 ASSERT(args.length() == 1);
3488
3489 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003490 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003491 CONVERT_DOUBLE_CHECKED(number, obj);
3492 return Heap::NumberFromDouble(DoubleToInteger(number));
3493}
3494
3495
3496static Object* Runtime_NumberToJSUint32(Arguments args) {
3497 NoHandleAllocation ha;
3498 ASSERT(args.length() == 1);
3499
3500 Object* obj = args[0];
3501 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3502 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3503 return Heap::NumberFromUint32(number);
3504}
3505
3506
3507static Object* Runtime_NumberToJSInt32(Arguments args) {
3508 NoHandleAllocation ha;
3509 ASSERT(args.length() == 1);
3510
3511 Object* obj = args[0];
3512 if (obj->IsSmi()) return obj;
3513 CONVERT_DOUBLE_CHECKED(number, obj);
3514 return Heap::NumberFromInt32(DoubleToInt32(number));
3515}
3516
3517
ager@chromium.org870a0b62008-11-04 11:43:05 +00003518// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3519// a small integer.
3520static Object* Runtime_NumberToSmi(Arguments args) {
3521 NoHandleAllocation ha;
3522 ASSERT(args.length() == 1);
3523
3524 Object* obj = args[0];
3525 if (obj->IsSmi()) {
3526 return obj;
3527 }
3528 if (obj->IsHeapNumber()) {
3529 double value = HeapNumber::cast(obj)->value();
3530 int int_value = FastD2I(value);
3531 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3532 return Smi::FromInt(int_value);
3533 }
3534 }
3535 return Heap::nan_value();
3536}
3537
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003538
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003539static Object* Runtime_NumberAdd(Arguments args) {
3540 NoHandleAllocation ha;
3541 ASSERT(args.length() == 2);
3542
3543 CONVERT_DOUBLE_CHECKED(x, args[0]);
3544 CONVERT_DOUBLE_CHECKED(y, args[1]);
3545 return Heap::AllocateHeapNumber(x + y);
3546}
3547
3548
3549static Object* Runtime_NumberSub(Arguments args) {
3550 NoHandleAllocation ha;
3551 ASSERT(args.length() == 2);
3552
3553 CONVERT_DOUBLE_CHECKED(x, args[0]);
3554 CONVERT_DOUBLE_CHECKED(y, args[1]);
3555 return Heap::AllocateHeapNumber(x - y);
3556}
3557
3558
3559static Object* Runtime_NumberMul(Arguments args) {
3560 NoHandleAllocation ha;
3561 ASSERT(args.length() == 2);
3562
3563 CONVERT_DOUBLE_CHECKED(x, args[0]);
3564 CONVERT_DOUBLE_CHECKED(y, args[1]);
3565 return Heap::AllocateHeapNumber(x * y);
3566}
3567
3568
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003569static Object* Runtime_NumberUnaryMinus(Arguments args) {
3570 NoHandleAllocation ha;
3571 ASSERT(args.length() == 1);
3572
3573 CONVERT_DOUBLE_CHECKED(x, args[0]);
3574 return Heap::AllocateHeapNumber(-x);
3575}
3576
3577
3578static Object* Runtime_NumberDiv(Arguments args) {
3579 NoHandleAllocation ha;
3580 ASSERT(args.length() == 2);
3581
3582 CONVERT_DOUBLE_CHECKED(x, args[0]);
3583 CONVERT_DOUBLE_CHECKED(y, args[1]);
3584 return Heap::NewNumberFromDouble(x / y);
3585}
3586
3587
3588static Object* Runtime_NumberMod(Arguments args) {
3589 NoHandleAllocation ha;
3590 ASSERT(args.length() == 2);
3591
3592 CONVERT_DOUBLE_CHECKED(x, args[0]);
3593 CONVERT_DOUBLE_CHECKED(y, args[1]);
3594
3595#ifdef WIN32
3596 // Workaround MS fmod bugs. ECMA-262 says:
3597 // dividend is finite and divisor is an infinity => result equals dividend
3598 // dividend is a zero and divisor is nonzero finite => result equals dividend
3599 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3600 !(x == 0 && (y != 0 && isfinite(y))))
3601#endif
3602 x = fmod(x, y);
3603 // NewNumberFromDouble may return a Smi instead of a Number object
3604 return Heap::NewNumberFromDouble(x);
3605}
3606
3607
3608static Object* Runtime_StringAdd(Arguments args) {
3609 NoHandleAllocation ha;
3610 ASSERT(args.length() == 2);
3611
3612 CONVERT_CHECKED(String, str1, args[0]);
3613 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003614 int len1 = str1->length();
3615 int len2 = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003616 if (len1 == 0) return str2;
3617 if (len2 == 0) return str1;
3618 int length_sum = len1 + len2;
3619 // Make sure that an out of memory exception is thrown if the length
3620 // of the new cons string is too large to fit in a Smi.
3621 if (length_sum > Smi::kMaxValue || length_sum < 0) {
3622 Top::context()->mark_out_of_memory();
3623 return Failure::OutOfMemoryException();
3624 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003625 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003626}
3627
3628
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003629template<typename sinkchar>
3630static inline void StringBuilderConcatHelper(String* special,
3631 sinkchar* sink,
3632 FixedArray* fixed_array,
3633 int array_length) {
3634 int position = 0;
3635 for (int i = 0; i < array_length; i++) {
3636 Object* element = fixed_array->get(i);
3637 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003638 int encoded_slice = Smi::cast(element)->value();
3639 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3640 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003641 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003642 sink + position,
3643 pos,
3644 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003645 position += len;
3646 } else {
3647 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003648 int element_length = string->length();
3649 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003650 position += element_length;
3651 }
3652 }
3653}
3654
3655
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003656static Object* Runtime_StringBuilderConcat(Arguments args) {
3657 NoHandleAllocation ha;
3658 ASSERT(args.length() == 2);
3659 CONVERT_CHECKED(JSArray, array, args[0]);
3660 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003661 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003662 Object* smi_array_length = array->length();
3663 if (!smi_array_length->IsSmi()) {
3664 Top::context()->mark_out_of_memory();
3665 return Failure::OutOfMemoryException();
3666 }
3667 int array_length = Smi::cast(smi_array_length)->value();
3668 if (!array->HasFastElements()) {
3669 return Top::Throw(Heap::illegal_argument_symbol());
3670 }
3671 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003672 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003673 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003674 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003675
3676 if (array_length == 0) {
3677 return Heap::empty_string();
3678 } else if (array_length == 1) {
3679 Object* first = fixed_array->get(0);
3680 if (first->IsString()) return first;
3681 }
3682
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003683 bool ascii = StringShape(special).IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003684 int position = 0;
3685 for (int i = 0; i < array_length; i++) {
3686 Object* elt = fixed_array->get(i);
3687 if (elt->IsSmi()) {
3688 int len = Smi::cast(elt)->value();
3689 int pos = len >> 11;
3690 len &= 0x7ff;
3691 if (pos + len > special_length) {
3692 return Top::Throw(Heap::illegal_argument_symbol());
3693 }
3694 position += len;
3695 } else if (elt->IsString()) {
3696 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003697 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003698 if (!Smi::IsValid(element_length + position)) {
3699 Top::context()->mark_out_of_memory();
3700 return Failure::OutOfMemoryException();
3701 }
3702 position += element_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003703 if (ascii && !StringShape(element).IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003704 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003705 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003706 } else {
3707 return Top::Throw(Heap::illegal_argument_symbol());
3708 }
3709 }
3710
3711 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003712 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003713
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003714 if (ascii) {
3715 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003716 if (object->IsFailure()) return object;
3717 SeqAsciiString* answer = SeqAsciiString::cast(object);
3718 StringBuilderConcatHelper(special,
3719 answer->GetChars(),
3720 fixed_array,
3721 array_length);
3722 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003723 } else {
3724 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003725 if (object->IsFailure()) return object;
3726 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3727 StringBuilderConcatHelper(special,
3728 answer->GetChars(),
3729 fixed_array,
3730 array_length);
3731 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003732 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003733}
3734
3735
3736static Object* Runtime_NumberOr(Arguments args) {
3737 NoHandleAllocation ha;
3738 ASSERT(args.length() == 2);
3739
3740 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3741 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3742 return Heap::NumberFromInt32(x | y);
3743}
3744
3745
3746static Object* Runtime_NumberAnd(Arguments args) {
3747 NoHandleAllocation ha;
3748 ASSERT(args.length() == 2);
3749
3750 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3751 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3752 return Heap::NumberFromInt32(x & y);
3753}
3754
3755
3756static Object* Runtime_NumberXor(Arguments args) {
3757 NoHandleAllocation ha;
3758 ASSERT(args.length() == 2);
3759
3760 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3761 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3762 return Heap::NumberFromInt32(x ^ y);
3763}
3764
3765
3766static Object* Runtime_NumberNot(Arguments args) {
3767 NoHandleAllocation ha;
3768 ASSERT(args.length() == 1);
3769
3770 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3771 return Heap::NumberFromInt32(~x);
3772}
3773
3774
3775static Object* Runtime_NumberShl(Arguments args) {
3776 NoHandleAllocation ha;
3777 ASSERT(args.length() == 2);
3778
3779 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3780 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3781 return Heap::NumberFromInt32(x << (y & 0x1f));
3782}
3783
3784
3785static Object* Runtime_NumberShr(Arguments args) {
3786 NoHandleAllocation ha;
3787 ASSERT(args.length() == 2);
3788
3789 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3790 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3791 return Heap::NumberFromUint32(x >> (y & 0x1f));
3792}
3793
3794
3795static Object* Runtime_NumberSar(Arguments args) {
3796 NoHandleAllocation ha;
3797 ASSERT(args.length() == 2);
3798
3799 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3800 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3801 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3802}
3803
3804
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003805static Object* Runtime_NumberEquals(Arguments args) {
3806 NoHandleAllocation ha;
3807 ASSERT(args.length() == 2);
3808
3809 CONVERT_DOUBLE_CHECKED(x, args[0]);
3810 CONVERT_DOUBLE_CHECKED(y, args[1]);
3811 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3812 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3813 if (x == y) return Smi::FromInt(EQUAL);
3814 Object* result;
3815 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3816 result = Smi::FromInt(EQUAL);
3817 } else {
3818 result = Smi::FromInt(NOT_EQUAL);
3819 }
3820 return result;
3821}
3822
3823
3824static Object* Runtime_StringEquals(Arguments args) {
3825 NoHandleAllocation ha;
3826 ASSERT(args.length() == 2);
3827
3828 CONVERT_CHECKED(String, x, args[0]);
3829 CONVERT_CHECKED(String, y, args[1]);
3830
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003831 bool not_equal = !x->Equals(y);
3832 // This is slightly convoluted because the value that signifies
3833 // equality is 0 and inequality is 1 so we have to negate the result
3834 // from String::Equals.
3835 ASSERT(not_equal == 0 || not_equal == 1);
3836 STATIC_CHECK(EQUAL == 0);
3837 STATIC_CHECK(NOT_EQUAL == 1);
3838 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003839}
3840
3841
3842static Object* Runtime_NumberCompare(Arguments args) {
3843 NoHandleAllocation ha;
3844 ASSERT(args.length() == 3);
3845
3846 CONVERT_DOUBLE_CHECKED(x, args[0]);
3847 CONVERT_DOUBLE_CHECKED(y, args[1]);
3848 if (isnan(x) || isnan(y)) return args[2];
3849 if (x == y) return Smi::FromInt(EQUAL);
3850 if (isless(x, y)) return Smi::FromInt(LESS);
3851 return Smi::FromInt(GREATER);
3852}
3853
3854
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003855// Compare two Smis as if they were converted to strings and then
3856// compared lexicographically.
3857static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3858 NoHandleAllocation ha;
3859 ASSERT(args.length() == 2);
3860
3861 // Arrays for the individual characters of the two Smis. Smis are
3862 // 31 bit integers and 10 decimal digits are therefore enough.
3863 static int x_elms[10];
3864 static int y_elms[10];
3865
3866 // Extract the integer values from the Smis.
3867 CONVERT_CHECKED(Smi, x, args[0]);
3868 CONVERT_CHECKED(Smi, y, args[1]);
3869 int x_value = x->value();
3870 int y_value = y->value();
3871
3872 // If the integers are equal so are the string representations.
3873 if (x_value == y_value) return Smi::FromInt(EQUAL);
3874
3875 // If one of the integers are zero the normal integer order is the
3876 // same as the lexicographic order of the string representations.
3877 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3878
ager@chromium.org32912102009-01-16 10:38:43 +00003879 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003880 // smallest because the char code of '-' is less than the char code
3881 // of any digit. Otherwise, we make both values positive.
3882 if (x_value < 0 || y_value < 0) {
3883 if (y_value >= 0) return Smi::FromInt(LESS);
3884 if (x_value >= 0) return Smi::FromInt(GREATER);
3885 x_value = -x_value;
3886 y_value = -y_value;
3887 }
3888
3889 // Convert the integers to arrays of their decimal digits.
3890 int x_index = 0;
3891 int y_index = 0;
3892 while (x_value > 0) {
3893 x_elms[x_index++] = x_value % 10;
3894 x_value /= 10;
3895 }
3896 while (y_value > 0) {
3897 y_elms[y_index++] = y_value % 10;
3898 y_value /= 10;
3899 }
3900
3901 // Loop through the arrays of decimal digits finding the first place
3902 // where they differ.
3903 while (--x_index >= 0 && --y_index >= 0) {
3904 int diff = x_elms[x_index] - y_elms[y_index];
3905 if (diff != 0) return Smi::FromInt(diff);
3906 }
3907
3908 // If one array is a suffix of the other array, the longest array is
3909 // the representation of the largest of the Smis in the
3910 // lexicographic ordering.
3911 return Smi::FromInt(x_index - y_index);
3912}
3913
3914
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003915static Object* Runtime_StringCompare(Arguments args) {
3916 NoHandleAllocation ha;
3917 ASSERT(args.length() == 2);
3918
3919 CONVERT_CHECKED(String, x, args[0]);
3920 CONVERT_CHECKED(String, y, args[1]);
3921
3922 // A few fast case tests before we flatten.
3923 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003924 if (y->length() == 0) {
3925 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003926 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003927 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003928 return Smi::FromInt(LESS);
3929 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003930
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003931 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003932 if (d < 0) return Smi::FromInt(LESS);
3933 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003934
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003935 x->TryFlattenIfNotFlat();
3936 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003937
3938 static StringInputBuffer bufx;
3939 static StringInputBuffer bufy;
3940 bufx.Reset(x);
3941 bufy.Reset(y);
3942 while (bufx.has_more() && bufy.has_more()) {
3943 int d = bufx.GetNext() - bufy.GetNext();
3944 if (d < 0) return Smi::FromInt(LESS);
3945 else if (d > 0) return Smi::FromInt(GREATER);
3946 }
3947
3948 // x is (non-trivial) prefix of y:
3949 if (bufy.has_more()) return Smi::FromInt(LESS);
3950 // y is prefix of x:
3951 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
3952}
3953
3954
3955static Object* Runtime_Math_abs(Arguments args) {
3956 NoHandleAllocation ha;
3957 ASSERT(args.length() == 1);
3958
3959 CONVERT_DOUBLE_CHECKED(x, args[0]);
3960 return Heap::AllocateHeapNumber(fabs(x));
3961}
3962
3963
3964static Object* Runtime_Math_acos(Arguments args) {
3965 NoHandleAllocation ha;
3966 ASSERT(args.length() == 1);
3967
3968 CONVERT_DOUBLE_CHECKED(x, args[0]);
3969 return Heap::AllocateHeapNumber(acos(x));
3970}
3971
3972
3973static Object* Runtime_Math_asin(Arguments args) {
3974 NoHandleAllocation ha;
3975 ASSERT(args.length() == 1);
3976
3977 CONVERT_DOUBLE_CHECKED(x, args[0]);
3978 return Heap::AllocateHeapNumber(asin(x));
3979}
3980
3981
3982static Object* Runtime_Math_atan(Arguments args) {
3983 NoHandleAllocation ha;
3984 ASSERT(args.length() == 1);
3985
3986 CONVERT_DOUBLE_CHECKED(x, args[0]);
3987 return Heap::AllocateHeapNumber(atan(x));
3988}
3989
3990
3991static Object* Runtime_Math_atan2(Arguments args) {
3992 NoHandleAllocation ha;
3993 ASSERT(args.length() == 2);
3994
3995 CONVERT_DOUBLE_CHECKED(x, args[0]);
3996 CONVERT_DOUBLE_CHECKED(y, args[1]);
3997 double result;
3998 if (isinf(x) && isinf(y)) {
3999 // Make sure that the result in case of two infinite arguments
4000 // is a multiple of Pi / 4. The sign of the result is determined
4001 // by the first argument (x) and the sign of the second argument
4002 // determines the multiplier: one or three.
4003 static double kPiDividedBy4 = 0.78539816339744830962;
4004 int multiplier = (x < 0) ? -1 : 1;
4005 if (y < 0) multiplier *= 3;
4006 result = multiplier * kPiDividedBy4;
4007 } else {
4008 result = atan2(x, y);
4009 }
4010 return Heap::AllocateHeapNumber(result);
4011}
4012
4013
4014static Object* Runtime_Math_ceil(Arguments args) {
4015 NoHandleAllocation ha;
4016 ASSERT(args.length() == 1);
4017
4018 CONVERT_DOUBLE_CHECKED(x, args[0]);
4019 return Heap::NumberFromDouble(ceiling(x));
4020}
4021
4022
4023static Object* Runtime_Math_cos(Arguments args) {
4024 NoHandleAllocation ha;
4025 ASSERT(args.length() == 1);
4026
4027 CONVERT_DOUBLE_CHECKED(x, args[0]);
4028 return Heap::AllocateHeapNumber(cos(x));
4029}
4030
4031
4032static Object* Runtime_Math_exp(Arguments args) {
4033 NoHandleAllocation ha;
4034 ASSERT(args.length() == 1);
4035
4036 CONVERT_DOUBLE_CHECKED(x, args[0]);
4037 return Heap::AllocateHeapNumber(exp(x));
4038}
4039
4040
4041static Object* Runtime_Math_floor(Arguments args) {
4042 NoHandleAllocation ha;
4043 ASSERT(args.length() == 1);
4044
4045 CONVERT_DOUBLE_CHECKED(x, args[0]);
4046 return Heap::NumberFromDouble(floor(x));
4047}
4048
4049
4050static Object* Runtime_Math_log(Arguments args) {
4051 NoHandleAllocation ha;
4052 ASSERT(args.length() == 1);
4053
4054 CONVERT_DOUBLE_CHECKED(x, args[0]);
4055 return Heap::AllocateHeapNumber(log(x));
4056}
4057
4058
4059static Object* Runtime_Math_pow(Arguments args) {
4060 NoHandleAllocation ha;
4061 ASSERT(args.length() == 2);
4062
4063 CONVERT_DOUBLE_CHECKED(x, args[0]);
4064 CONVERT_DOUBLE_CHECKED(y, args[1]);
4065 if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4066 return Heap::nan_value();
4067 } else if (y == 0) {
4068 return Smi::FromInt(1);
4069 } else {
4070 return Heap::AllocateHeapNumber(pow(x, y));
4071 }
4072}
4073
4074// Returns a number value with positive sign, greater than or equal to
4075// 0 but less than 1, chosen randomly.
mads.s.ager31e71382008-08-13 09:32:07 +00004076static Object* Runtime_Math_random(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004077 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004078 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004079
4080 // To get much better precision, we combine the results of two
4081 // invocations of random(). The result is computed by normalizing a
4082 // double in the range [0, RAND_MAX + 1) obtained by adding the
4083 // high-order bits in the range [0, RAND_MAX] with the low-order
4084 // bits in the range [0, 1).
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004085 double lo = static_cast<double>(random()) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004086 double hi = static_cast<double>(random());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004087 double result = (hi + lo) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004088 ASSERT(result >= 0 && result < 1);
4089 return Heap::AllocateHeapNumber(result);
4090}
4091
4092
4093static Object* Runtime_Math_round(Arguments args) {
4094 NoHandleAllocation ha;
4095 ASSERT(args.length() == 1);
4096
4097 CONVERT_DOUBLE_CHECKED(x, args[0]);
4098 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4099 return Heap::NumberFromDouble(floor(x + 0.5));
4100}
4101
4102
4103static Object* Runtime_Math_sin(Arguments args) {
4104 NoHandleAllocation ha;
4105 ASSERT(args.length() == 1);
4106
4107 CONVERT_DOUBLE_CHECKED(x, args[0]);
4108 return Heap::AllocateHeapNumber(sin(x));
4109}
4110
4111
4112static Object* Runtime_Math_sqrt(Arguments args) {
4113 NoHandleAllocation ha;
4114 ASSERT(args.length() == 1);
4115
4116 CONVERT_DOUBLE_CHECKED(x, args[0]);
4117 return Heap::AllocateHeapNumber(sqrt(x));
4118}
4119
4120
4121static Object* Runtime_Math_tan(Arguments args) {
4122 NoHandleAllocation ha;
4123 ASSERT(args.length() == 1);
4124
4125 CONVERT_DOUBLE_CHECKED(x, args[0]);
4126 return Heap::AllocateHeapNumber(tan(x));
4127}
4128
4129
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004130// The NewArguments function is only used when constructing the
4131// arguments array when calling non-functions from JavaScript in
4132// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004133static Object* Runtime_NewArguments(Arguments args) {
4134 NoHandleAllocation ha;
4135 ASSERT(args.length() == 1);
4136
4137 // ECMA-262, 3rd., 10.1.8, p.39
4138 CONVERT_CHECKED(JSFunction, callee, args[0]);
4139
4140 // Compute the frame holding the arguments.
4141 JavaScriptFrameIterator it;
4142 it.AdvanceToArgumentsFrame();
4143 JavaScriptFrame* frame = it.frame();
4144
4145 const int length = frame->GetProvidedParametersCount();
4146 Object* result = Heap::AllocateArgumentsObject(callee, length);
4147 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004148 if (length > 0) {
4149 Object* obj = Heap::AllocateFixedArray(length);
4150 if (obj->IsFailure()) return obj;
4151 FixedArray* array = FixedArray::cast(obj);
4152 ASSERT(array->length() == length);
4153 WriteBarrierMode mode = array->GetWriteBarrierMode();
4154 for (int i = 0; i < length; i++) {
4155 array->set(i, frame->GetParameter(i), mode);
4156 }
4157 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004158 }
4159 return result;
4160}
4161
4162
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004163static Object* Runtime_NewArgumentsFast(Arguments args) {
4164 NoHandleAllocation ha;
4165 ASSERT(args.length() == 3);
4166
4167 JSFunction* callee = JSFunction::cast(args[0]);
4168 Object** parameters = reinterpret_cast<Object**>(args[1]);
4169 const int length = Smi::cast(args[2])->value();
4170
4171 Object* result = Heap::AllocateArgumentsObject(callee, length);
4172 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004173 ASSERT(Heap::InNewSpace(result));
4174
4175 // Allocate the elements if needed.
4176 if (length > 0) {
4177 // Allocate the fixed array.
4178 Object* obj = Heap::AllocateRawFixedArray(length);
4179 if (obj->IsFailure()) return obj;
4180 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4181 FixedArray* array = FixedArray::cast(obj);
4182 array->set_length(length);
4183 WriteBarrierMode mode = array->GetWriteBarrierMode();
4184 for (int i = 0; i < length; i++) {
4185 array->set(i, *--parameters, mode);
4186 }
4187 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4188 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004189 }
4190 return result;
4191}
4192
4193
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004194static Object* Runtime_NewClosure(Arguments args) {
4195 HandleScope scope;
4196 ASSERT(args.length() == 2);
4197 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4198 CONVERT_ARG_CHECKED(Context, context, 1);
4199
4200 Handle<JSFunction> result =
4201 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4202 return *result;
4203}
4204
4205
4206static Object* Runtime_NewObject(Arguments args) {
4207 NoHandleAllocation ha;
4208 ASSERT(args.length() == 1);
4209
4210 Object* constructor = args[0];
4211 if (constructor->IsJSFunction()) {
4212 JSFunction* function = JSFunction::cast(constructor);
4213
ager@chromium.org32912102009-01-16 10:38:43 +00004214 // Handle stepping into constructors if step into is active.
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004215#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004216 if (Debug::StepInActive()) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004217 HandleScope scope;
4218 Debug::HandleStepIn(Handle<JSFunction>(function), 0, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004219 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004220#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004221
4222 if (function->has_initial_map() &&
4223 function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
4224 // The 'Function' function ignores the receiver object when
4225 // called using 'new' and creates a new JSFunction object that
4226 // is returned. The receiver object is only used for error
4227 // reporting if an error occurs when constructing the new
4228 // JSFunction. AllocateJSObject should not be used to allocate
4229 // JSFunctions since it does not properly initialize the shared
4230 // part of the function. Since the receiver is ignored anyway,
4231 // we use the global object as the receiver instead of a new
4232 // JSFunction object. This way, errors are reported the same
4233 // way whether or not 'Function' is called using 'new'.
4234 return Top::context()->global();
4235 }
4236 return Heap::AllocateJSObject(function);
4237 }
4238
4239 HandleScope scope;
4240 Handle<Object> cons(constructor);
4241 // The constructor is not a function; throw a type error.
4242 Handle<Object> type_error =
4243 Factory::NewTypeError("not_constructor", HandleVector(&cons, 1));
4244 return Top::Throw(*type_error);
4245}
4246
4247
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004248static Object* Runtime_LazyCompile(Arguments args) {
4249 HandleScope scope;
4250 ASSERT(args.length() == 1);
4251
4252 Handle<JSFunction> function = args.at<JSFunction>(0);
4253#ifdef DEBUG
4254 if (FLAG_trace_lazy) {
4255 PrintF("[lazy: ");
4256 function->shared()->name()->Print();
4257 PrintF("]\n");
4258 }
4259#endif
4260
4261 // Compile the target function.
4262 ASSERT(!function->is_compiled());
4263 if (!CompileLazy(function, KEEP_EXCEPTION)) {
4264 return Failure::Exception();
4265 }
4266
4267 return function->code();
4268}
4269
4270
4271static Object* Runtime_GetCalledFunction(Arguments args) {
4272 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004273 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004274 StackFrameIterator it;
4275 // Get past the JS-to-C exit frame.
4276 ASSERT(it.frame()->is_exit());
4277 it.Advance();
4278 // Get past the CALL_NON_FUNCTION activation frame.
4279 ASSERT(it.frame()->is_java_script());
4280 it.Advance();
4281 // Argument adaptor frames do not copy the function; we have to skip
4282 // past them to get to the real calling frame.
4283 if (it.frame()->is_arguments_adaptor()) it.Advance();
4284 // Get the function from the top of the expression stack of the
4285 // calling frame.
4286 StandardFrame* frame = StandardFrame::cast(it.frame());
4287 int index = frame->ComputeExpressionsCount() - 1;
4288 Object* result = frame->GetExpression(index);
4289 return result;
4290}
4291
4292
4293static Object* Runtime_GetFunctionDelegate(Arguments args) {
4294 HandleScope scope;
4295 ASSERT(args.length() == 1);
4296 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4297 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4298}
4299
4300
4301static Object* Runtime_NewContext(Arguments args) {
4302 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004303 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004304
kasper.lund7276f142008-07-30 08:49:36 +00004305 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004306 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4307 Object* result = Heap::AllocateFunctionContext(length, function);
4308 if (result->IsFailure()) return result;
4309
4310 Top::set_context(Context::cast(result));
4311
kasper.lund7276f142008-07-30 08:49:36 +00004312 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004313}
4314
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004315static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004316 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004317 Object* js_object = object;
4318 if (!js_object->IsJSObject()) {
4319 js_object = js_object->ToObject();
4320 if (js_object->IsFailure()) {
4321 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004322 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004323 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004324 Handle<Object> result =
4325 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4326 return Top::Throw(*result);
4327 }
4328 }
4329
4330 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004331 Heap::AllocateWithContext(Top::context(),
4332 JSObject::cast(js_object),
4333 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004334 if (result->IsFailure()) return result;
4335
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004336 Context* context = Context::cast(result);
4337 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004338
kasper.lund7276f142008-07-30 08:49:36 +00004339 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004340}
4341
4342
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004343static Object* Runtime_PushContext(Arguments args) {
4344 NoHandleAllocation ha;
4345 ASSERT(args.length() == 1);
4346 return PushContextHelper(args[0], false);
4347}
4348
4349
4350static Object* Runtime_PushCatchContext(Arguments args) {
4351 NoHandleAllocation ha;
4352 ASSERT(args.length() == 1);
4353 return PushContextHelper(args[0], true);
4354}
4355
4356
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004357static Object* Runtime_LookupContext(Arguments args) {
4358 HandleScope scope;
4359 ASSERT(args.length() == 2);
4360
4361 CONVERT_ARG_CHECKED(Context, context, 0);
4362 CONVERT_ARG_CHECKED(String, name, 1);
4363
4364 int index;
4365 PropertyAttributes attributes;
4366 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004367 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004368 context->Lookup(name, flags, &index, &attributes);
4369
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004370 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004371 ASSERT(holder->IsJSObject());
4372 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004373 }
4374
4375 // No intermediate context found. Use global object by default.
4376 return Top::context()->global();
4377}
4378
4379
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004380// A mechanism to return pairs of Object*'s. This is somewhat
4381// compiler-dependent as it assumes that a 64-bit value (a long long)
4382// is returned via two registers (edx:eax on ia32). Both the ia32 and
4383// arm platform support this; it is mostly an issue of "coaxing" the
4384// compiler to do the right thing.
4385//
4386// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004387typedef uint64_t ObjectPair;
4388static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004389 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004390 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004391}
4392
4393
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004394static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004395 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4396 USE(attributes);
4397 return x->IsTheHole() ? Heap::undefined_value() : x;
4398}
4399
4400
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004401static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4402 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004403 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004404 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004405 JSFunction* context_extension_function =
4406 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004407 // If the holder isn't a context extension object, we just return it
4408 // as the receiver. This allows arguments objects to be used as
4409 // receivers, but only if they are put in the context scope chain
4410 // explicitly via a with-statement.
4411 Object* constructor = holder->map()->constructor();
4412 if (constructor != context_extension_function) return holder;
4413 // Fall back to using the global object as the receiver if the
4414 // property turns out to be a local variable allocated in a context
4415 // extension object - introduced via eval.
4416 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004417}
4418
4419
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004420static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004421 HandleScope scope;
4422 ASSERT(args.length() == 2);
4423
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004424 if (!args[0]->IsContext() || !args[1]->IsString()) {
4425 return MakePair(IllegalOperation(), NULL);
4426 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004427 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004428 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004429
4430 int index;
4431 PropertyAttributes attributes;
4432 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004433 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004434 context->Lookup(name, flags, &index, &attributes);
4435
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004436 // If the index is non-negative, the slot has been found in a local
4437 // variable or a parameter. Read it from the context object or the
4438 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004439 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004440 // If the "property" we were looking for is a local variable or an
4441 // argument in a context, the receiver is the global object; see
4442 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4443 JSObject* receiver = Top::context()->global()->global_receiver();
4444 Object* value = (holder->IsContext())
4445 ? Context::cast(*holder)->get(index)
4446 : JSObject::cast(*holder)->GetElement(index);
4447 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004448 }
4449
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004450 // If the holder is found, we read the property from it.
4451 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004452 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004453 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004454 JSObject* receiver;
4455 if (object->IsGlobalObject()) {
4456 receiver = GlobalObject::cast(object)->global_receiver();
4457 } else if (context->is_exception_holder(*holder)) {
4458 receiver = Top::context()->global()->global_receiver();
4459 } else {
4460 receiver = ComputeReceiverForNonGlobal(object);
4461 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004462 // No need to unhole the value here. This is taken care of by the
4463 // GetProperty function.
4464 Object* value = object->GetProperty(*name);
4465 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004466 }
4467
4468 if (throw_error) {
4469 // The property doesn't exist - throw exception.
4470 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004471 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004472 return MakePair(Top::Throw(*reference_error), NULL);
4473 } else {
4474 // The property doesn't exist - return undefined
4475 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4476 }
4477}
4478
4479
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004480static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004481 return LoadContextSlotHelper(args, true);
4482}
4483
4484
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004485static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004486 return LoadContextSlotHelper(args, false);
4487}
4488
4489
4490static Object* Runtime_StoreContextSlot(Arguments args) {
4491 HandleScope scope;
4492 ASSERT(args.length() == 3);
4493
4494 Handle<Object> value(args[0]);
4495 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004496 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004497
4498 int index;
4499 PropertyAttributes attributes;
4500 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004501 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004502 context->Lookup(name, flags, &index, &attributes);
4503
4504 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004505 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004506 // Ignore if read_only variable.
4507 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004508 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004509 }
4510 } else {
4511 ASSERT((attributes & READ_ONLY) == 0);
4512 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004513 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004514 USE(result);
4515 ASSERT(!result->IsFailure());
4516 }
4517 return *value;
4518 }
4519
4520 // Slow case: The property is not in a FixedArray context.
4521 // It is either in an JSObject extension context or it was not found.
4522 Handle<JSObject> context_ext;
4523
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004524 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004525 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004526 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004527 } else {
4528 // The property was not found. It needs to be stored in the global context.
4529 ASSERT(attributes == ABSENT);
4530 attributes = NONE;
4531 context_ext = Handle<JSObject>(Top::context()->global());
4532 }
4533
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004534 // Set the property, but ignore if read_only variable on the context
4535 // extension object itself.
4536 if ((attributes & READ_ONLY) == 0 ||
4537 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004538 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4539 if (set.is_null()) {
4540 // Failure::Exception is converted to a null handle in the
4541 // handle-based methods such as SetProperty. We therefore need
4542 // to convert null handles back to exceptions.
4543 ASSERT(Top::has_pending_exception());
4544 return Failure::Exception();
4545 }
4546 }
4547 return *value;
4548}
4549
4550
4551static Object* Runtime_Throw(Arguments args) {
4552 HandleScope scope;
4553 ASSERT(args.length() == 1);
4554
4555 return Top::Throw(args[0]);
4556}
4557
4558
4559static Object* Runtime_ReThrow(Arguments args) {
4560 HandleScope scope;
4561 ASSERT(args.length() == 1);
4562
4563 return Top::ReThrow(args[0]);
4564}
4565
4566
4567static Object* Runtime_ThrowReferenceError(Arguments args) {
4568 HandleScope scope;
4569 ASSERT(args.length() == 1);
4570
4571 Handle<Object> name(args[0]);
4572 Handle<Object> reference_error =
4573 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4574 return Top::Throw(*reference_error);
4575}
4576
4577
4578static Object* Runtime_StackOverflow(Arguments args) {
4579 NoHandleAllocation na;
4580 return Top::StackOverflow();
4581}
4582
4583
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004584static Object* Runtime_StackGuard(Arguments args) {
4585 ASSERT(args.length() == 1);
4586
4587 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004588 if (StackGuard::IsStackOverflow()) {
4589 return Runtime_StackOverflow(args);
4590 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004591
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004592 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004593}
4594
4595
4596// NOTE: These PrintXXX functions are defined for all builds (not just
4597// DEBUG builds) because we may want to be able to trace function
4598// calls in all modes.
4599static void PrintString(String* str) {
4600 // not uncommon to have empty strings
4601 if (str->length() > 0) {
4602 SmartPointer<char> s =
4603 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4604 PrintF("%s", *s);
4605 }
4606}
4607
4608
4609static void PrintObject(Object* obj) {
4610 if (obj->IsSmi()) {
4611 PrintF("%d", Smi::cast(obj)->value());
4612 } else if (obj->IsString() || obj->IsSymbol()) {
4613 PrintString(String::cast(obj));
4614 } else if (obj->IsNumber()) {
4615 PrintF("%g", obj->Number());
4616 } else if (obj->IsFailure()) {
4617 PrintF("<failure>");
4618 } else if (obj->IsUndefined()) {
4619 PrintF("<undefined>");
4620 } else if (obj->IsNull()) {
4621 PrintF("<null>");
4622 } else if (obj->IsTrue()) {
4623 PrintF("<true>");
4624 } else if (obj->IsFalse()) {
4625 PrintF("<false>");
4626 } else {
4627 PrintF("%p", obj);
4628 }
4629}
4630
4631
4632static int StackSize() {
4633 int n = 0;
4634 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4635 return n;
4636}
4637
4638
4639static void PrintTransition(Object* result) {
4640 // indentation
4641 { const int nmax = 80;
4642 int n = StackSize();
4643 if (n <= nmax)
4644 PrintF("%4d:%*s", n, n, "");
4645 else
4646 PrintF("%4d:%*s", n, nmax, "...");
4647 }
4648
4649 if (result == NULL) {
4650 // constructor calls
4651 JavaScriptFrameIterator it;
4652 JavaScriptFrame* frame = it.frame();
4653 if (frame->IsConstructor()) PrintF("new ");
4654 // function name
4655 Object* fun = frame->function();
4656 if (fun->IsJSFunction()) {
4657 PrintObject(JSFunction::cast(fun)->shared()->name());
4658 } else {
4659 PrintObject(fun);
4660 }
4661 // function arguments
4662 // (we are intentionally only printing the actually
4663 // supplied parameters, not all parameters required)
4664 PrintF("(this=");
4665 PrintObject(frame->receiver());
4666 const int length = frame->GetProvidedParametersCount();
4667 for (int i = 0; i < length; i++) {
4668 PrintF(", ");
4669 PrintObject(frame->GetParameter(i));
4670 }
4671 PrintF(") {\n");
4672
4673 } else {
4674 // function result
4675 PrintF("} -> ");
4676 PrintObject(result);
4677 PrintF("\n");
4678 }
4679}
4680
4681
4682static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004683 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004684 NoHandleAllocation ha;
4685 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004686 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004687}
4688
4689
4690static Object* Runtime_TraceExit(Arguments args) {
4691 NoHandleAllocation ha;
4692 PrintTransition(args[0]);
4693 return args[0]; // return TOS
4694}
4695
4696
4697static Object* Runtime_DebugPrint(Arguments args) {
4698 NoHandleAllocation ha;
4699 ASSERT(args.length() == 1);
4700
4701#ifdef DEBUG
4702 if (args[0]->IsString()) {
4703 // If we have a string, assume it's a code "marker"
4704 // and print some interesting cpu debugging info.
4705 JavaScriptFrameIterator it;
4706 JavaScriptFrame* frame = it.frame();
4707 PrintF("fp = %p, sp = %p, pp = %p: ",
4708 frame->fp(), frame->sp(), frame->pp());
4709 } else {
4710 PrintF("DebugPrint: ");
4711 }
4712 args[0]->Print();
4713#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004714 // ShortPrint is available in release mode. Print is not.
4715 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004716#endif
4717 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004718 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004719
4720 return args[0]; // return TOS
4721}
4722
4723
4724static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004725 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004726 NoHandleAllocation ha;
4727 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004728 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004729}
4730
4731
mads.s.ager31e71382008-08-13 09:32:07 +00004732static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004733 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004734 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004735
4736 // According to ECMA-262, section 15.9.1, page 117, the precision of
4737 // the number in a Date object representing a particular instant in
4738 // time is milliseconds. Therefore, we floor the result of getting
4739 // the OS time.
4740 double millis = floor(OS::TimeCurrentMillis());
4741 return Heap::NumberFromDouble(millis);
4742}
4743
4744
4745static Object* Runtime_DateParseString(Arguments args) {
4746 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004747 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004748
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004749 CONVERT_ARG_CHECKED(String, str, 0);
4750 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004751
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004752 CONVERT_ARG_CHECKED(JSArray, output, 1);
4753 RUNTIME_ASSERT(output->HasFastElements());
4754
4755 AssertNoAllocation no_allocation;
4756
4757 FixedArray* output_array = output->elements();
4758 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4759 bool result;
4760 if (StringShape(*str).IsAsciiRepresentation()) {
4761 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004762 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004763 ASSERT(StringShape(*str).IsTwoByteRepresentation());
4764 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4765 }
4766
4767 if (result) {
4768 return *output;
4769 } else {
4770 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004771 }
4772}
4773
4774
4775static Object* Runtime_DateLocalTimezone(Arguments args) {
4776 NoHandleAllocation ha;
4777 ASSERT(args.length() == 1);
4778
4779 CONVERT_DOUBLE_CHECKED(x, args[0]);
4780 char* zone = OS::LocalTimezone(x);
4781 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4782}
4783
4784
4785static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4786 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004787 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004788
4789 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4790}
4791
4792
4793static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4794 NoHandleAllocation ha;
4795 ASSERT(args.length() == 1);
4796
4797 CONVERT_DOUBLE_CHECKED(x, args[0]);
4798 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4799}
4800
4801
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004802static Object* Runtime_NumberIsFinite(Arguments args) {
4803 NoHandleAllocation ha;
4804 ASSERT(args.length() == 1);
4805
4806 CONVERT_DOUBLE_CHECKED(value, args[0]);
4807 Object* result;
4808 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4809 result = Heap::false_value();
4810 } else {
4811 result = Heap::true_value();
4812 }
4813 return result;
4814}
4815
4816
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004817static Object* Runtime_GlobalReceiver(Arguments args) {
4818 ASSERT(args.length() == 1);
4819 Object* global = args[0];
4820 if (!global->IsJSGlobalObject()) return Heap::null_value();
4821 return JSGlobalObject::cast(global)->global_receiver();
4822}
4823
4824
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004825static Object* Runtime_CompileString(Arguments args) {
4826 HandleScope scope;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004827 ASSERT_EQ(3, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004828 CONVERT_ARG_CHECKED(String, source, 0);
ager@chromium.org236ad962008-09-25 09:45:57 +00004829 CONVERT_ARG_CHECKED(Smi, line_offset, 1);
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004830 CONVERT_ARG_CHECKED(Oddball, is_json, 2)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004831
ager@chromium.org381abbb2009-02-25 13:23:22 +00004832 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004833 Handle<Context> context(Top::context()->global_context());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004834 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
4835 context,
4836 line_offset->value(),
4837 true,
4838 is_json->IsTrue());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004839 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004840 Handle<JSFunction> fun =
4841 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4842 return *fun;
4843}
4844
4845
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004846static Handle<JSFunction> GetBuiltinFunction(String* name) {
4847 LookupResult result;
4848 Top::global_context()->builtins()->LocalLookup(name, &result);
4849 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
4850}
4851
4852
4853static Object* CompileDirectEval(Handle<String> source) {
4854 // Compute the eval context.
4855 HandleScope scope;
4856 StackFrameLocator locator;
4857 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4858 Handle<Context> context(Context::cast(frame->context()));
4859 bool is_global = context->IsGlobalContext();
4860
ager@chromium.org381abbb2009-02-25 13:23:22 +00004861 // Compile source string in the current context.
4862 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004863 Compiler::CompileEval(source, context, 0, is_global, false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004864 if (boilerplate.is_null()) return Failure::Exception();
4865 Handle<JSFunction> fun =
4866 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4867 return *fun;
4868}
4869
4870
4871static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
4872 ASSERT(args.length() == 2);
4873
4874 HandleScope scope;
4875
4876 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
4877
4878 Handle<Object> receiver;
4879
4880 // Find where the 'eval' symbol is bound. It is unaliased only if
4881 // it is bound in the global context.
4882 StackFrameLocator locator;
4883 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4884 Handle<Context> context(Context::cast(frame->context()));
4885 int index;
4886 PropertyAttributes attributes;
4887 while (!context.is_null()) {
4888 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
4889 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00004890 // Stop search when eval is found or when the global context is
4891 // reached.
4892 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004893 if (context->is_function_context()) {
4894 context = Handle<Context>(Context::cast(context->closure()->context()));
4895 } else {
4896 context = Handle<Context>(context->previous());
4897 }
4898 }
4899
iposva@chromium.org245aa852009-02-10 00:49:54 +00004900 // If eval could not be resolved, it has been deleted and we need to
4901 // throw a reference error.
4902 if (attributes == ABSENT) {
4903 Handle<Object> name = Factory::eval_symbol();
4904 Handle<Object> reference_error =
4905 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4906 return Top::Throw(*reference_error);
4907 }
4908
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004909 if (context->IsGlobalContext()) {
4910 // 'eval' is bound in the global context, but it may have been overwritten.
4911 // Compare it to the builtin 'GlobalEval' function to make sure.
4912 Handle<JSFunction> global_eval =
4913 GetBuiltinFunction(Heap::global_eval_symbol());
4914 if (global_eval.is_identical_to(callee)) {
4915 // A direct eval call.
4916 if (args[1]->IsString()) {
4917 CONVERT_ARG_CHECKED(String, source, 1);
4918 // A normal eval call on a string. Compile it and return the
4919 // compiled function bound in the local context.
4920 Object* compiled_source = CompileDirectEval(source);
4921 if (compiled_source->IsFailure()) return compiled_source;
4922 receiver = Handle<Object>(frame->receiver());
4923 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
4924 } else {
4925 // An eval call that is not called on a string. Global eval
4926 // deals better with this.
4927 receiver = Handle<Object>(Top::global_context()->global());
4928 }
4929 } else {
4930 // 'eval' is overwritten. Just call the function with the given arguments.
4931 receiver = Handle<Object>(Top::global_context()->global());
4932 }
4933 } else {
4934 // 'eval' is not bound in the global context. Just call the function
4935 // with the given arguments. This is not necessarily the global eval.
4936 if (receiver->IsContext()) {
4937 context = Handle<Context>::cast(receiver);
4938 receiver = Handle<Object>(context->get(index));
4939 }
4940 }
4941
4942 Handle<FixedArray> call = Factory::NewFixedArray(2);
4943 call->set(0, *callee);
4944 call->set(1, *receiver);
4945 return *call;
4946}
4947
4948
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004949static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
4950 // This utility adjusts the property attributes for newly created Function
4951 // object ("new Function(...)") by changing the map.
4952 // All it does is changing the prototype property to enumerable
4953 // as specified in ECMA262, 15.3.5.2.
4954 HandleScope scope;
4955 ASSERT(args.length() == 1);
4956 CONVERT_ARG_CHECKED(JSFunction, func, 0);
4957 ASSERT(func->map()->instance_type() ==
4958 Top::function_instance_map()->instance_type());
4959 ASSERT(func->map()->instance_size() ==
4960 Top::function_instance_map()->instance_size());
4961 func->set_map(*Top::function_instance_map());
4962 return *func;
4963}
4964
4965
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004966// Push an array unto an array of arrays if it is not already in the
4967// array. Returns true if the element was pushed on the stack and
4968// false otherwise.
4969static Object* Runtime_PushIfAbsent(Arguments args) {
4970 ASSERT(args.length() == 2);
4971 CONVERT_CHECKED(JSArray, array, args[0]);
4972 CONVERT_CHECKED(JSArray, element, args[1]);
4973 RUNTIME_ASSERT(array->HasFastElements());
4974 int length = Smi::cast(array->length())->value();
4975 FixedArray* elements = FixedArray::cast(array->elements());
4976 for (int i = 0; i < length; i++) {
4977 if (elements->get(i) == element) return Heap::false_value();
4978 }
4979 Object* obj = array->SetFastElement(length, element);
4980 if (obj->IsFailure()) return obj;
4981 return Heap::true_value();
4982}
4983
4984
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004985/**
4986 * A simple visitor visits every element of Array's.
4987 * The backend storage can be a fixed array for fast elements case,
4988 * or a dictionary for sparse array. Since Dictionary is a subtype
4989 * of FixedArray, the class can be used by both fast and slow cases.
4990 * The second parameter of the constructor, fast_elements, specifies
4991 * whether the storage is a FixedArray or Dictionary.
4992 *
4993 * An index limit is used to deal with the situation that a result array
4994 * length overflows 32-bit non-negative integer.
4995 */
4996class ArrayConcatVisitor {
4997 public:
4998 ArrayConcatVisitor(Handle<FixedArray> storage,
4999 uint32_t index_limit,
5000 bool fast_elements) :
5001 storage_(storage), index_limit_(index_limit),
5002 fast_elements_(fast_elements), index_offset_(0) { }
5003
5004 void visit(uint32_t i, Handle<Object> elm) {
5005 uint32_t index = i + index_offset_;
5006 if (index >= index_limit_) return;
5007
5008 if (fast_elements_) {
5009 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5010 storage_->set(index, *elm);
5011
5012 } else {
5013 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
5014 Handle<Dictionary> result =
5015 Factory::DictionaryAtNumberPut(dict, index, elm);
5016 if (!result.is_identical_to(dict))
5017 storage_ = result;
5018 }
5019 }
5020
5021 void increase_index_offset(uint32_t delta) {
5022 index_offset_ += delta;
5023 }
5024
5025 private:
5026 Handle<FixedArray> storage_;
5027 uint32_t index_limit_;
5028 bool fast_elements_;
5029 uint32_t index_offset_;
5030};
5031
5032
5033/**
5034 * A helper function that visits elements of a JSObject. Only elements
5035 * whose index between 0 and range (exclusive) are visited.
5036 *
5037 * If the third parameter, visitor, is not NULL, the visitor is called
5038 * with parameters, 'visitor_index_offset + element index' and the element.
5039 *
5040 * It returns the number of visisted elements.
5041 */
5042static uint32_t IterateElements(Handle<JSObject> receiver,
5043 uint32_t range,
5044 ArrayConcatVisitor* visitor) {
5045 uint32_t num_of_elements = 0;
5046
5047 if (receiver->HasFastElements()) {
5048 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5049 uint32_t len = elements->length();
5050 if (range < len) len = range;
5051
5052 for (uint32_t j = 0; j < len; j++) {
5053 Handle<Object> e(elements->get(j));
5054 if (!e->IsTheHole()) {
5055 num_of_elements++;
5056 if (visitor)
5057 visitor->visit(j, e);
5058 }
5059 }
5060
5061 } else {
5062 Handle<Dictionary> dict(receiver->element_dictionary());
5063 uint32_t capacity = dict->Capacity();
5064 for (uint32_t j = 0; j < capacity; j++) {
5065 Handle<Object> k(dict->KeyAt(j));
5066 if (dict->IsKey(*k)) {
5067 ASSERT(k->IsNumber());
5068 uint32_t index = static_cast<uint32_t>(k->Number());
5069 if (index < range) {
5070 num_of_elements++;
5071 if (visitor) {
5072 visitor->visit(index,
5073 Handle<Object>(dict->ValueAt(j)));
5074 }
5075 }
5076 }
5077 }
5078 }
5079
5080 return num_of_elements;
5081}
5082
5083
5084/**
5085 * A helper function that visits elements of an Array object, and elements
5086 * on its prototypes.
5087 *
5088 * Elements on prototypes are visited first, and only elements whose indices
5089 * less than Array length are visited.
5090 *
5091 * If a ArrayConcatVisitor object is given, the visitor is called with
5092 * parameters, element's index + visitor_index_offset and the element.
5093 */
5094static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5095 ArrayConcatVisitor* visitor) {
5096 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5097 Handle<Object> obj = array;
5098
5099 static const int kEstimatedPrototypes = 3;
5100 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5101
5102 // Visit prototype first. If an element on the prototype is shadowed by
5103 // the inheritor using the same index, the ArrayConcatVisitor visits
5104 // the prototype element before the shadowing element.
5105 // The visitor can simply overwrite the old value by new value using
5106 // the same index. This follows Array::concat semantics.
5107 while (!obj->IsNull()) {
5108 objects.Add(Handle<JSObject>::cast(obj));
5109 obj = Handle<Object>(obj->GetPrototype());
5110 }
5111
5112 uint32_t nof_elements = 0;
5113 for (int i = objects.length() - 1; i >= 0; i--) {
5114 Handle<JSObject> obj = objects[i];
5115 nof_elements +=
5116 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5117 }
5118
5119 return nof_elements;
5120}
5121
5122
5123/**
5124 * A helper function of Runtime_ArrayConcat.
5125 *
5126 * The first argument is an Array of arrays and objects. It is the
5127 * same as the arguments array of Array::concat JS function.
5128 *
5129 * If an argument is an Array object, the function visits array
5130 * elements. If an argument is not an Array object, the function
5131 * visits the object as if it is an one-element array.
5132 *
5133 * If the result array index overflows 32-bit integer, the rounded
5134 * non-negative number is used as new length. For example, if one
5135 * array length is 2^32 - 1, second array length is 1, the
5136 * concatenated array length is 0.
5137 */
5138static uint32_t IterateArguments(Handle<JSArray> arguments,
5139 ArrayConcatVisitor* visitor) {
5140 uint32_t visited_elements = 0;
5141 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5142
5143 for (uint32_t i = 0; i < num_of_args; i++) {
5144 Handle<Object> obj(arguments->GetElement(i));
5145 if (obj->IsJSArray()) {
5146 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5147 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5148 uint32_t nof_elements =
5149 IterateArrayAndPrototypeElements(array, visitor);
5150 // Total elements of array and its prototype chain can be more than
5151 // the array length, but ArrayConcat can only concatenate at most
5152 // the array length number of elements.
5153 visited_elements += (nof_elements > len) ? len : nof_elements;
5154 if (visitor) visitor->increase_index_offset(len);
5155
5156 } else {
5157 if (visitor) {
5158 visitor->visit(0, obj);
5159 visitor->increase_index_offset(1);
5160 }
5161 visited_elements++;
5162 }
5163 }
5164 return visited_elements;
5165}
5166
5167
5168/**
5169 * Array::concat implementation.
5170 * See ECMAScript 262, 15.4.4.4.
5171 */
5172static Object* Runtime_ArrayConcat(Arguments args) {
5173 ASSERT(args.length() == 1);
5174 HandleScope handle_scope;
5175
5176 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5177 Handle<JSArray> arguments(arg_arrays);
5178
5179 // Pass 1: estimate the number of elements of the result
5180 // (it could be more than real numbers if prototype has elements).
5181 uint32_t result_length = 0;
5182 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5183
5184 { AssertNoAllocation nogc;
5185 for (uint32_t i = 0; i < num_of_args; i++) {
5186 Object* obj = arguments->GetElement(i);
5187 if (obj->IsJSArray()) {
5188 result_length +=
5189 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5190 } else {
5191 result_length++;
5192 }
5193 }
5194 }
5195
5196 // Allocate an empty array, will set length and content later.
5197 Handle<JSArray> result = Factory::NewJSArray(0);
5198
5199 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5200 // If estimated number of elements is more than half of length, a
5201 // fixed array (fast case) is more time and space-efficient than a
5202 // dictionary.
5203 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5204
5205 Handle<FixedArray> storage;
5206 if (fast_case) {
5207 // The backing storage array must have non-existing elements to
5208 // preserve holes across concat operations.
5209 storage = Factory::NewFixedArrayWithHoles(result_length);
5210
5211 } else {
5212 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5213 uint32_t at_least_space_for = estimate_nof_elements +
5214 (estimate_nof_elements >> 2);
5215 storage = Handle<FixedArray>::cast(
5216 Factory::NewDictionary(at_least_space_for));
5217 }
5218
5219 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5220
5221 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5222
5223 IterateArguments(arguments, &visitor);
5224
5225 result->set_length(*len);
5226 result->set_elements(*storage);
5227
5228 return *result;
5229}
5230
5231
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005232// This will not allocate (flatten the string), but it may run
5233// very slowly for very deeply nested ConsStrings. For debugging use only.
5234static Object* Runtime_GlobalPrint(Arguments args) {
5235 NoHandleAllocation ha;
5236 ASSERT(args.length() == 1);
5237
5238 CONVERT_CHECKED(String, string, args[0]);
5239 StringInputBuffer buffer(string);
5240 while (buffer.has_more()) {
5241 uint16_t character = buffer.GetNext();
5242 PrintF("%c", character);
5243 }
5244 return string;
5245}
5246
5247
5248static Object* Runtime_RemoveArrayHoles(Arguments args) {
5249 ASSERT(args.length() == 1);
5250 // Ignore the case if this is not a JSArray.
5251 if (!args[0]->IsJSArray()) return args[0];
5252 return JSArray::cast(args[0])->RemoveHoles();
5253}
5254
5255
5256// Move contents of argument 0 (an array) to argument 1 (an array)
5257static Object* Runtime_MoveArrayContents(Arguments args) {
5258 ASSERT(args.length() == 2);
5259 CONVERT_CHECKED(JSArray, from, args[0]);
5260 CONVERT_CHECKED(JSArray, to, args[1]);
5261 to->SetContent(FixedArray::cast(from->elements()));
5262 to->set_length(from->length());
5263 from->SetContent(Heap::empty_fixed_array());
5264 from->set_length(0);
5265 return to;
5266}
5267
5268
5269// How many elements does this array have?
5270static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5271 ASSERT(args.length() == 1);
5272 CONVERT_CHECKED(JSArray, array, args[0]);
5273 HeapObject* elements = array->elements();
5274 if (elements->IsDictionary()) {
5275 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
5276 } else {
5277 return array->length();
5278 }
5279}
5280
5281
5282// Returns an array that tells you where in the [0, length) interval an array
5283// might have elements. Can either return keys or intervals. Keys can have
5284// gaps in (undefined). Intervals can also span over some undefined keys.
5285static Object* Runtime_GetArrayKeys(Arguments args) {
5286 ASSERT(args.length() == 2);
5287 HandleScope scope;
5288 CONVERT_CHECKED(JSArray, raw_array, args[0]);
5289 Handle<JSArray> array(raw_array);
5290 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005291 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005292 // Create an array and get all the keys into it, then remove all the
5293 // keys that are not integers in the range 0 to length-1.
5294 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5295 int keys_length = keys->length();
5296 for (int i = 0; i < keys_length; i++) {
5297 Object* key = keys->get(i);
5298 uint32_t index;
5299 if (!Array::IndexFromObject(key, &index) || index >= length) {
5300 // Zap invalid keys.
5301 keys->set_undefined(i);
5302 }
5303 }
5304 return *Factory::NewJSArrayWithElements(keys);
5305 } else {
5306 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5307 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005308 single_interval->set(0,
5309 Smi::FromInt(-1),
5310 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005311 Handle<Object> length_object =
5312 Factory::NewNumber(static_cast<double>(length));
5313 single_interval->set(1, *length_object);
5314 return *Factory::NewJSArrayWithElements(single_interval);
5315 }
5316}
5317
5318
5319// DefineAccessor takes an optional final argument which is the
5320// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5321// to the way accessors are implemented, it is set for both the getter
5322// and setter on the first call to DefineAccessor and ignored on
5323// subsequent calls.
5324static Object* Runtime_DefineAccessor(Arguments args) {
5325 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5326 // Compute attributes.
5327 PropertyAttributes attributes = NONE;
5328 if (args.length() == 5) {
5329 CONVERT_CHECKED(Smi, attrs, args[4]);
5330 int value = attrs->value();
5331 // Only attribute bits should be set.
5332 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5333 attributes = static_cast<PropertyAttributes>(value);
5334 }
5335
5336 CONVERT_CHECKED(JSObject, obj, args[0]);
5337 CONVERT_CHECKED(String, name, args[1]);
5338 CONVERT_CHECKED(Smi, flag, args[2]);
5339 CONVERT_CHECKED(JSFunction, fun, args[3]);
5340 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5341}
5342
5343
5344static Object* Runtime_LookupAccessor(Arguments args) {
5345 ASSERT(args.length() == 3);
5346 CONVERT_CHECKED(JSObject, obj, args[0]);
5347 CONVERT_CHECKED(String, name, args[1]);
5348 CONVERT_CHECKED(Smi, flag, args[2]);
5349 return obj->LookupAccessor(name, flag->value() == 0);
5350}
5351
5352
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005353#ifdef ENABLE_DEBUGGER_SUPPORT
5354static Object* Runtime_DebugBreak(Arguments args) {
5355 ASSERT(args.length() == 0);
5356 return Execution::DebugBreakHelper();
5357}
5358
5359
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005360// Helper functions for wrapping and unwrapping stack frame ids.
5361static Smi* WrapFrameId(StackFrame::Id id) {
5362 ASSERT(IsAligned(OffsetFrom(id), 4));
5363 return Smi::FromInt(id >> 2);
5364}
5365
5366
5367static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5368 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5369}
5370
5371
5372// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005373// args[0]: debug event listener function to set or null or undefined for
5374// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005375// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005376static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005377 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005378 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5379 args[0]->IsUndefined() ||
5380 args[0]->IsNull());
5381 Handle<Object> callback = args.at<Object>(0);
5382 Handle<Object> data = args.at<Object>(1);
5383 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005384
5385 return Heap::undefined_value();
5386}
5387
5388
5389static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005390 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005391 StackGuard::DebugBreak();
5392 return Heap::undefined_value();
5393}
5394
5395
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005396// Find the length of the prototype chain that is to to handled as one. If a
5397// prototype object is hidden it is to be viewed as part of the the object it
5398// is prototype for.
5399static int LocalPrototypeChainLength(JSObject* obj) {
5400 int count = 1;
5401 Object* proto = obj->GetPrototype();
5402 while (proto->IsJSObject() &&
5403 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5404 count++;
5405 proto = JSObject::cast(proto)->GetPrototype();
5406 }
5407 return count;
5408}
5409
5410
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005411static Object* DebugLookupResultValue(Object* receiver, LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005412 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005413 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005414 switch (result->type()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005415 case NORMAL: {
5416 Dictionary* dict =
5417 JSObject::cast(result->holder())->property_dictionary();
5418 value = dict->ValueAt(result->GetDictionaryEntry());
5419 if (value->IsTheHole()) {
5420 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005421 }
5422 return value;
5423 }
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005424 case FIELD:
5425 value =
5426 JSObject::cast(
5427 result->holder())->FastPropertyAt(result->GetFieldIndex());
5428 if (value->IsTheHole()) {
5429 return Heap::undefined_value();
5430 }
5431 return value;
5432 case CONSTANT_FUNCTION:
5433 return result->GetConstantFunction();
5434 case CALLBACKS: {
5435 Object* structure = result->GetCallbackObject();
5436 if (structure->IsProxy()) {
5437 AccessorDescriptor* callback =
5438 reinterpret_cast<AccessorDescriptor*>(
5439 Proxy::cast(structure)->proxy());
5440 value = (callback->getter)(receiver, callback->data);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005441 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005442 value = Top::pending_exception();
5443 Top::clear_pending_exception();
5444 if (caught_exception != NULL) {
5445 *caught_exception = true;
5446 }
5447 }
5448 return value;
5449 } else {
5450 return Heap::undefined_value();
5451 }
5452 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005453 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005454 case MAP_TRANSITION:
5455 case CONSTANT_TRANSITION:
5456 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005457 return Heap::undefined_value();
5458 default:
5459 UNREACHABLE();
5460 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005461 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005462 return Heap::undefined_value();
5463}
5464
5465
ager@chromium.org32912102009-01-16 10:38:43 +00005466// Get debugger related details for an object property.
5467// args[0]: object holding property
5468// args[1]: name of the property
5469//
5470// The array returned contains the following information:
5471// 0: Property value
5472// 1: Property details
5473// 2: Property value is exception
5474// 3: Getter function if defined
5475// 4: Setter function if defined
5476// Items 2-4 are only filled if the property has either a getter or a setter
5477// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005478static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005479 HandleScope scope;
5480
5481 ASSERT(args.length() == 2);
5482
5483 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5484 CONVERT_ARG_CHECKED(String, name, 1);
5485
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005486 // Skip the global proxy as it has no properties and always delegates to the
5487 // real global object.
5488 if (obj->IsJSGlobalProxy()) {
5489 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5490 }
5491
5492
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005493 // Check if the name is trivially convertible to an index and get the element
5494 // if so.
5495 uint32_t index;
5496 if (name->AsArrayIndex(&index)) {
5497 Handle<FixedArray> details = Factory::NewFixedArray(2);
5498 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5499 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5500 return *Factory::NewJSArrayWithElements(details);
5501 }
5502
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005503 // Find the number of objects making up this.
5504 int length = LocalPrototypeChainLength(*obj);
5505
5506 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005507 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005508 Handle<JSObject> jsproto = obj;
5509 for (int i = 0; i < length; i++) {
5510 jsproto->LocalLookup(*name, &result);
5511 if (result.IsProperty()) {
5512 break;
5513 }
5514 if (i < length - 1) {
5515 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5516 }
5517 }
5518
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005519 if (result.IsProperty()) {
ager@chromium.org32912102009-01-16 10:38:43 +00005520 bool caught_exception = false;
ager@chromium.org381abbb2009-02-25 13:23:22 +00005521 Object* value = DebugLookupResultValue(*obj, &result,
5522 &caught_exception);
5523 if (value->IsFailure()) return value;
5524 Handle<Object> value_handle(value);
ager@chromium.org32912102009-01-16 10:38:43 +00005525 // If the callback object is a fixed array then it contains JavaScript
5526 // getter and/or setter.
5527 bool hasJavaScriptAccessors = result.type() == CALLBACKS &&
5528 result.GetCallbackObject()->IsFixedArray();
5529 Handle<FixedArray> details =
5530 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005531 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005532 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005533 if (hasJavaScriptAccessors) {
5534 details->set(2,
5535 caught_exception ? Heap::true_value() : Heap::false_value());
5536 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5537 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5538 }
5539
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005540 return *Factory::NewJSArrayWithElements(details);
5541 }
5542 return Heap::undefined_value();
5543}
5544
5545
5546static Object* Runtime_DebugGetProperty(Arguments args) {
5547 HandleScope scope;
5548
5549 ASSERT(args.length() == 2);
5550
5551 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5552 CONVERT_ARG_CHECKED(String, name, 1);
5553
5554 LookupResult result;
5555 obj->Lookup(*name, &result);
5556 if (result.IsProperty()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005557 return DebugLookupResultValue(*obj, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005558 }
5559 return Heap::undefined_value();
5560}
5561
5562
5563// Return the names of the local named properties.
5564// args[0]: object
5565static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5566 HandleScope scope;
5567 ASSERT(args.length() == 1);
5568 if (!args[0]->IsJSObject()) {
5569 return Heap::undefined_value();
5570 }
5571 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5572
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005573 // Skip the global proxy as it has no properties and always delegates to the
5574 // real global object.
5575 if (obj->IsJSGlobalProxy()) {
5576 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5577 }
5578
5579 // Find the number of objects making up this.
5580 int length = LocalPrototypeChainLength(*obj);
5581
5582 // Find the number of local properties for each of the objects.
5583 int* local_property_count = NewArray<int>(length);
5584 int total_property_count = 0;
5585 Handle<JSObject> jsproto = obj;
5586 for (int i = 0; i < length; i++) {
5587 int n;
5588 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5589 local_property_count[i] = n;
5590 total_property_count += n;
5591 if (i < length - 1) {
5592 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5593 }
5594 }
5595
5596 // Allocate an array with storage for all the property names.
5597 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5598
5599 // Get the property names.
5600 jsproto = obj;
5601 for (int i = 0; i < length; i++) {
5602 jsproto->GetLocalPropertyNames(*names,
5603 i == 0 ? 0 : local_property_count[i - 1]);
5604 if (i < length - 1) {
5605 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5606 }
5607 }
5608
5609 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005610 return *Factory::NewJSArrayWithElements(names);
5611}
5612
5613
5614// Return the names of the local indexed properties.
5615// args[0]: object
5616static Object* Runtime_DebugLocalElementNames(Arguments args) {
5617 HandleScope scope;
5618 ASSERT(args.length() == 1);
5619 if (!args[0]->IsJSObject()) {
5620 return Heap::undefined_value();
5621 }
5622 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5623
5624 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5625 Handle<FixedArray> names = Factory::NewFixedArray(n);
5626 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5627 return *Factory::NewJSArrayWithElements(names);
5628}
5629
5630
5631// Return the property type calculated from the property details.
5632// args[0]: smi with property details.
5633static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5634 ASSERT(args.length() == 1);
5635 CONVERT_CHECKED(Smi, details, args[0]);
5636 PropertyType type = PropertyDetails(details).type();
5637 return Smi::FromInt(static_cast<int>(type));
5638}
5639
5640
5641// Return the property attribute calculated from the property details.
5642// args[0]: smi with property details.
5643static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5644 ASSERT(args.length() == 1);
5645 CONVERT_CHECKED(Smi, details, args[0]);
5646 PropertyAttributes attributes = PropertyDetails(details).attributes();
5647 return Smi::FromInt(static_cast<int>(attributes));
5648}
5649
5650
5651// Return the property insertion index calculated from the property details.
5652// args[0]: smi with property details.
5653static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5654 ASSERT(args.length() == 1);
5655 CONVERT_CHECKED(Smi, details, args[0]);
5656 int index = PropertyDetails(details).index();
5657 return Smi::FromInt(index);
5658}
5659
5660
5661// Return information on whether an object has a named or indexed interceptor.
5662// args[0]: object
5663static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5664 HandleScope scope;
5665 ASSERT(args.length() == 1);
5666 if (!args[0]->IsJSObject()) {
5667 return Smi::FromInt(0);
5668 }
5669 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5670
5671 int result = 0;
5672 if (obj->HasNamedInterceptor()) result |= 2;
5673 if (obj->HasIndexedInterceptor()) result |= 1;
5674
5675 return Smi::FromInt(result);
5676}
5677
5678
5679// Return property names from named interceptor.
5680// args[0]: object
5681static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5682 HandleScope scope;
5683 ASSERT(args.length() == 1);
5684 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005685
ager@chromium.org32912102009-01-16 10:38:43 +00005686 if (obj->HasNamedInterceptor()) {
5687 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5688 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5689 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005690 return Heap::undefined_value();
5691}
5692
5693
5694// Return element names from indexed interceptor.
5695// args[0]: object
5696static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5697 HandleScope scope;
5698 ASSERT(args.length() == 1);
5699 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005700
ager@chromium.org32912102009-01-16 10:38:43 +00005701 if (obj->HasIndexedInterceptor()) {
5702 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5703 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5704 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005705 return Heap::undefined_value();
5706}
5707
5708
5709// Return property value from named interceptor.
5710// args[0]: object
5711// args[1]: property name
5712static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5713 HandleScope scope;
5714 ASSERT(args.length() == 2);
5715 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5716 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5717 CONVERT_ARG_CHECKED(String, name, 1);
5718
5719 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005720 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005721}
5722
5723
5724// Return element value from indexed interceptor.
5725// args[0]: object
5726// args[1]: index
5727static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5728 HandleScope scope;
5729 ASSERT(args.length() == 2);
5730 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5731 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5732 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5733
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005734 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005735}
5736
5737
5738static Object* Runtime_CheckExecutionState(Arguments args) {
5739 ASSERT(args.length() >= 1);
5740 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005741 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005742 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005743 return Top::Throw(Heap::illegal_execution_state_symbol());
5744 }
5745
5746 return Heap::true_value();
5747}
5748
5749
5750static Object* Runtime_GetFrameCount(Arguments args) {
5751 HandleScope scope;
5752 ASSERT(args.length() == 1);
5753
5754 // Check arguments.
5755 Object* result = Runtime_CheckExecutionState(args);
5756 if (result->IsFailure()) return result;
5757
5758 // Count all frames which are relevant to debugging stack trace.
5759 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005760 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005761 if (id == StackFrame::NO_ID) {
5762 // If there is no JavaScript stack frame count is 0.
5763 return Smi::FromInt(0);
5764 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005765 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5766 return Smi::FromInt(n);
5767}
5768
5769
5770static const int kFrameDetailsFrameIdIndex = 0;
5771static const int kFrameDetailsReceiverIndex = 1;
5772static const int kFrameDetailsFunctionIndex = 2;
5773static const int kFrameDetailsArgumentCountIndex = 3;
5774static const int kFrameDetailsLocalCountIndex = 4;
5775static const int kFrameDetailsSourcePositionIndex = 5;
5776static const int kFrameDetailsConstructCallIndex = 6;
5777static const int kFrameDetailsDebuggerFrameIndex = 7;
5778static const int kFrameDetailsFirstDynamicIndex = 8;
5779
5780// Return an array with frame details
5781// args[0]: number: break id
5782// args[1]: number: frame index
5783//
5784// The array returned contains the following information:
5785// 0: Frame id
5786// 1: Receiver
5787// 2: Function
5788// 3: Argument count
5789// 4: Local count
5790// 5: Source position
5791// 6: Constructor call
5792// 7: Debugger frame
5793// Arguments name, value
5794// Locals name, value
5795static Object* Runtime_GetFrameDetails(Arguments args) {
5796 HandleScope scope;
5797 ASSERT(args.length() == 2);
5798
5799 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005800 Object* check = Runtime_CheckExecutionState(args);
5801 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005802 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5803
5804 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005805 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005806 if (id == StackFrame::NO_ID) {
5807 // If there are no JavaScript stack frames return undefined.
5808 return Heap::undefined_value();
5809 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005810 int count = 0;
5811 JavaScriptFrameIterator it(id);
5812 for (; !it.done(); it.Advance()) {
5813 if (count == index) break;
5814 count++;
5815 }
5816 if (it.done()) return Heap::undefined_value();
5817
5818 // Traverse the saved contexts chain to find the active context for the
5819 // selected frame.
5820 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005821 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005822 save = save->prev();
5823 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005824 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005825
5826 // Get the frame id.
5827 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
5828
5829 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005830 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005831
5832 // Check for constructor frame.
5833 bool constructor = it.frame()->IsConstructor();
5834
5835 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005836 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005837 ScopeInfo<> info(*code);
5838
5839 // Get the context.
5840 Handle<Context> context(Context::cast(it.frame()->context()));
5841
5842 // Get the locals names and values into a temporary array.
5843 //
5844 // TODO(1240907): Hide compiler-introduced stack variables
5845 // (e.g. .result)? For users of the debugger, they will probably be
5846 // confusing.
5847 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
5848 for (int i = 0; i < info.NumberOfLocals(); i++) {
5849 // Name of the local.
5850 locals->set(i * 2, *info.LocalName(i));
5851
5852 // Fetch the value of the local - either from the stack or from a
5853 // heap-allocated context.
5854 if (i < info.number_of_stack_slots()) {
5855 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
5856 } else {
5857 Handle<String> name = info.LocalName(i);
5858 // Traverse the context chain to the function context as all local
5859 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005860 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005861 context = Handle<Context>(context->previous());
5862 }
5863 ASSERT(context->is_function_context());
5864 locals->set(i * 2 + 1,
5865 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
5866 NULL)));
5867 }
5868 }
5869
5870 // Now advance to the arguments adapter frame (if any). If contains all
5871 // the provided parameters and
5872
5873 // Now advance to the arguments adapter frame (if any). It contains all
5874 // the provided parameters whereas the function frame always have the number
5875 // of arguments matching the functions parameters. The rest of the
5876 // information (except for what is collected above) is the same.
5877 it.AdvanceToArgumentsFrame();
5878
5879 // Find the number of arguments to fill. At least fill the number of
5880 // parameters for the function and fill more if more parameters are provided.
5881 int argument_count = info.number_of_parameters();
5882 if (argument_count < it.frame()->GetProvidedParametersCount()) {
5883 argument_count = it.frame()->GetProvidedParametersCount();
5884 }
5885
5886 // Calculate the size of the result.
5887 int details_size = kFrameDetailsFirstDynamicIndex +
5888 2 * (argument_count + info.NumberOfLocals());
5889 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
5890
5891 // Add the frame id.
5892 details->set(kFrameDetailsFrameIdIndex, *frame_id);
5893
5894 // Add the function (same as in function frame).
5895 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
5896
5897 // Add the arguments count.
5898 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
5899
5900 // Add the locals count
5901 details->set(kFrameDetailsLocalCountIndex,
5902 Smi::FromInt(info.NumberOfLocals()));
5903
5904 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00005905 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005906 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
5907 } else {
5908 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
5909 }
5910
5911 // Add the constructor information.
5912 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
5913
5914 // Add information on whether this frame is invoked in the debugger context.
5915 details->set(kFrameDetailsDebuggerFrameIndex,
5916 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
5917
5918 // Fill the dynamic part.
5919 int details_index = kFrameDetailsFirstDynamicIndex;
5920
5921 // Add arguments name and value.
5922 for (int i = 0; i < argument_count; i++) {
5923 // Name of the argument.
5924 if (i < info.number_of_parameters()) {
5925 details->set(details_index++, *info.parameter_name(i));
5926 } else {
5927 details->set(details_index++, Heap::undefined_value());
5928 }
5929
5930 // Parameter value.
5931 if (i < it.frame()->GetProvidedParametersCount()) {
5932 details->set(details_index++, it.frame()->GetParameter(i));
5933 } else {
5934 details->set(details_index++, Heap::undefined_value());
5935 }
5936 }
5937
5938 // Add locals name and value from the temporary copy from the function frame.
5939 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
5940 details->set(details_index++, locals->get(i));
5941 }
5942
5943 // Add the receiver (same as in function frame).
5944 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
5945 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
5946 Handle<Object> receiver(it.frame()->receiver());
5947 if (!receiver->IsJSObject()) {
5948 // If the receiver is NOT a JSObject we have hit an optimization
5949 // where a value object is not converted into a wrapped JS objects.
5950 // To hide this optimization from the debugger, we wrap the receiver
5951 // by creating correct wrapper object based on the calling frame's
5952 // global context.
5953 it.Advance();
5954 Handle<Context> calling_frames_global_context(
5955 Context::cast(Context::cast(it.frame()->context())->global_context()));
5956 receiver = Factory::ToObject(receiver, calling_frames_global_context);
5957 }
5958 details->set(kFrameDetailsReceiverIndex, *receiver);
5959
5960 ASSERT_EQ(details_size, details_index);
5961 return *Factory::NewJSArrayWithElements(details);
5962}
5963
5964
5965static Object* Runtime_GetCFrames(Arguments args) {
5966 HandleScope scope;
5967 ASSERT(args.length() == 1);
5968 Object* result = Runtime_CheckExecutionState(args);
5969 if (result->IsFailure()) return result;
5970
5971 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005972 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
5973 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005974 if (frames_count == OS::kStackWalkError) {
5975 return Heap::undefined_value();
5976 }
5977
5978 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
5979 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
5980 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
5981 for (int i = 0; i < frames_count; i++) {
5982 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
5983 frame_value->SetProperty(
5984 *address_str,
5985 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
5986 NONE);
5987
5988 // Get the stack walk text for this frame.
5989 Handle<String> frame_text;
5990 if (strlen(frames[i].text) > 0) {
5991 Vector<const char> str(frames[i].text, strlen(frames[i].text));
5992 frame_text = Factory::NewStringFromAscii(str);
5993 }
5994
5995 if (!frame_text.is_null()) {
5996 frame_value->SetProperty(*text_str, *frame_text, NONE);
5997 }
5998
5999 frames_array->set(i, *frame_value);
6000 }
6001 return *Factory::NewJSArrayWithElements(frames_array);
6002}
6003
6004
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006005static Object* Runtime_GetThreadCount(Arguments args) {
6006 HandleScope scope;
6007 ASSERT(args.length() == 1);
6008
6009 // Check arguments.
6010 Object* result = Runtime_CheckExecutionState(args);
6011 if (result->IsFailure()) return result;
6012
6013 // Count all archived V8 threads.
6014 int n = 0;
6015 for (ThreadState* thread = ThreadState::FirstInUse();
6016 thread != NULL;
6017 thread = thread->Next()) {
6018 n++;
6019 }
6020
6021 // Total number of threads is current thread and archived threads.
6022 return Smi::FromInt(n + 1);
6023}
6024
6025
6026static const int kThreadDetailsCurrentThreadIndex = 0;
6027static const int kThreadDetailsThreadIdIndex = 1;
6028static const int kThreadDetailsSize = 2;
6029
6030// Return an array with thread details
6031// args[0]: number: break id
6032// args[1]: number: thread index
6033//
6034// The array returned contains the following information:
6035// 0: Is current thread?
6036// 1: Thread id
6037static Object* Runtime_GetThreadDetails(Arguments args) {
6038 HandleScope scope;
6039 ASSERT(args.length() == 2);
6040
6041 // Check arguments.
6042 Object* check = Runtime_CheckExecutionState(args);
6043 if (check->IsFailure()) return check;
6044 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6045
6046 // Allocate array for result.
6047 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6048
6049 // Thread index 0 is current thread.
6050 if (index == 0) {
6051 // Fill the details.
6052 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6053 details->set(kThreadDetailsThreadIdIndex,
6054 Smi::FromInt(ThreadManager::CurrentId()));
6055 } else {
6056 // Find the thread with the requested index.
6057 int n = 1;
6058 ThreadState* thread = ThreadState::FirstInUse();
6059 while (index != n && thread != NULL) {
6060 thread = thread->Next();
6061 n++;
6062 }
6063 if (thread == NULL) {
6064 return Heap::undefined_value();
6065 }
6066
6067 // Fill the details.
6068 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6069 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6070 }
6071
6072 // Convert to JS array and return.
6073 return *Factory::NewJSArrayWithElements(details);
6074}
6075
6076
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006077static Object* Runtime_GetBreakLocations(Arguments args) {
6078 HandleScope scope;
6079 ASSERT(args.length() == 1);
6080
6081 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6082 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6083 // Find the number of break points
6084 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6085 if (break_locations->IsUndefined()) return Heap::undefined_value();
6086 // Return array as JS array
6087 return *Factory::NewJSArrayWithElements(
6088 Handle<FixedArray>::cast(break_locations));
6089}
6090
6091
6092// Set a break point in a function
6093// args[0]: function
6094// args[1]: number: break source position (within the function source)
6095// args[2]: number: break point object
6096static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6097 HandleScope scope;
6098 ASSERT(args.length() == 3);
6099 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6100 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6101 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6102 RUNTIME_ASSERT(source_position >= 0);
6103 Handle<Object> break_point_object_arg = args.at<Object>(2);
6104
6105 // Set break point.
6106 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6107
6108 return Heap::undefined_value();
6109}
6110
6111
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006112Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6113 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006114 // Iterate the heap looking for SharedFunctionInfo generated from the
6115 // script. The inner most SharedFunctionInfo containing the source position
6116 // for the requested break point is found.
6117 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6118 // which is found is not compiled it is compiled and the heap is iterated
6119 // again as the compilation might create inner functions from the newly
6120 // compiled function and the actual requested break point might be in one of
6121 // these functions.
6122 bool done = false;
6123 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006124 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006125 Handle<SharedFunctionInfo> target;
6126 // The current candidate for the last function in script:
6127 Handle<SharedFunctionInfo> last;
6128 while (!done) {
6129 HeapIterator iterator;
6130 while (iterator.has_next()) {
6131 HeapObject* obj = iterator.next();
6132 ASSERT(obj != NULL);
6133 if (obj->IsSharedFunctionInfo()) {
6134 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6135 if (shared->script() == *script) {
6136 // If the SharedFunctionInfo found has the requested script data and
6137 // contains the source position it is a candidate.
6138 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006139 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006140 start_position = shared->start_position();
6141 }
6142 if (start_position <= position &&
6143 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006144 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006145 // candidate this is the new candidate.
6146 if (target.is_null()) {
6147 target_start_position = start_position;
6148 target = shared;
6149 } else {
6150 if (target_start_position < start_position &&
6151 shared->end_position() < target->end_position()) {
6152 target_start_position = start_position;
6153 target = shared;
6154 }
6155 }
6156 }
6157
6158 // Keep track of the last function in the script.
6159 if (last.is_null() ||
6160 shared->end_position() > last->start_position()) {
6161 last = shared;
6162 }
6163 }
6164 }
6165 }
6166
6167 // Make sure some candidate is selected.
6168 if (target.is_null()) {
6169 if (!last.is_null()) {
6170 // Position after the last function - use last.
6171 target = last;
6172 } else {
6173 // Unable to find function - possibly script without any function.
6174 return Heap::undefined_value();
6175 }
6176 }
6177
6178 // If the candidate found is compiled we are done. NOTE: when lazy
6179 // compilation of inner functions is introduced some additional checking
6180 // needs to be done here to compile inner functions.
6181 done = target->is_compiled();
6182 if (!done) {
6183 // If the candidate is not compiled compile it to reveal any inner
6184 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006185 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006186 }
6187 }
6188
6189 return *target;
6190}
6191
6192
6193// Change the state of a break point in a script. NOTE: Regarding performance
6194// see the NOTE for GetScriptFromScriptData.
6195// args[0]: script to set break point in
6196// args[1]: number: break source position (within the script source)
6197// args[2]: number: break point object
6198static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6199 HandleScope scope;
6200 ASSERT(args.length() == 3);
6201 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6202 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6203 RUNTIME_ASSERT(source_position >= 0);
6204 Handle<Object> break_point_object_arg = args.at<Object>(2);
6205
6206 // Get the script from the script wrapper.
6207 RUNTIME_ASSERT(wrapper->value()->IsScript());
6208 Handle<Script> script(Script::cast(wrapper->value()));
6209
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006210 Object* result = Runtime::FindSharedFunctionInfoInScript(
6211 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006212 if (!result->IsUndefined()) {
6213 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6214 // Find position within function. The script position might be before the
6215 // source position of the first function.
6216 int position;
6217 if (shared->start_position() > source_position) {
6218 position = 0;
6219 } else {
6220 position = source_position - shared->start_position();
6221 }
6222 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6223 }
6224 return Heap::undefined_value();
6225}
6226
6227
6228// Clear a break point
6229// args[0]: number: break point object
6230static Object* Runtime_ClearBreakPoint(Arguments args) {
6231 HandleScope scope;
6232 ASSERT(args.length() == 1);
6233 Handle<Object> break_point_object_arg = args.at<Object>(0);
6234
6235 // Clear break point.
6236 Debug::ClearBreakPoint(break_point_object_arg);
6237
6238 return Heap::undefined_value();
6239}
6240
6241
6242// Change the state of break on exceptions
6243// args[0]: boolean indicating uncaught exceptions
6244// args[1]: boolean indicating on/off
6245static Object* Runtime_ChangeBreakOnException(Arguments args) {
6246 HandleScope scope;
6247 ASSERT(args.length() == 2);
6248 ASSERT(args[0]->IsNumber());
6249 ASSERT(args[1]->IsBoolean());
6250
6251 // Update break point state
6252 ExceptionBreakType type =
6253 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6254 bool enable = args[1]->ToBoolean()->IsTrue();
6255 Debug::ChangeBreakOnException(type, enable);
6256 return Heap::undefined_value();
6257}
6258
6259
6260// Prepare for stepping
6261// args[0]: break id for checking execution state
6262// args[1]: step action from the enumeration StepAction
6263// args[2]: number of times to perform the step
6264static Object* Runtime_PrepareStep(Arguments args) {
6265 HandleScope scope;
6266 ASSERT(args.length() == 3);
6267 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006268 Object* check = Runtime_CheckExecutionState(args);
6269 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006270 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6271 return Top::Throw(Heap::illegal_argument_symbol());
6272 }
6273
6274 // Get the step action and check validity.
6275 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6276 if (step_action != StepIn &&
6277 step_action != StepNext &&
6278 step_action != StepOut &&
6279 step_action != StepInMin &&
6280 step_action != StepMin) {
6281 return Top::Throw(Heap::illegal_argument_symbol());
6282 }
6283
6284 // Get the number of steps.
6285 int step_count = NumberToInt32(args[2]);
6286 if (step_count < 1) {
6287 return Top::Throw(Heap::illegal_argument_symbol());
6288 }
6289
6290 // Prepare step.
6291 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6292 return Heap::undefined_value();
6293}
6294
6295
6296// Clear all stepping set by PrepareStep.
6297static Object* Runtime_ClearStepping(Arguments args) {
6298 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006299 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006300 Debug::ClearStepping();
6301 return Heap::undefined_value();
6302}
6303
6304
6305// Creates a copy of the with context chain. The copy of the context chain is
6306// is linked to the function context supplied.
6307static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6308 Handle<Context> function_context) {
6309 // At the bottom of the chain. Return the function context to link to.
6310 if (context_chain->is_function_context()) {
6311 return function_context;
6312 }
6313
6314 // Recursively copy the with contexts.
6315 Handle<Context> previous(context_chain->previous());
6316 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6317 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006318 CopyWithContextChain(function_context, previous),
6319 extension,
6320 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006321}
6322
6323
6324// Helper function to find or create the arguments object for
6325// Runtime_DebugEvaluate.
6326static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6327 Handle<JSFunction> function,
6328 Handle<Code> code,
6329 const ScopeInfo<>* sinfo,
6330 Handle<Context> function_context) {
6331 // Try to find the value of 'arguments' to pass as parameter. If it is not
6332 // found (that is the debugged function does not reference 'arguments' and
6333 // does not support eval) then create an 'arguments' object.
6334 int index;
6335 if (sinfo->number_of_stack_slots() > 0) {
6336 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6337 if (index != -1) {
6338 return Handle<Object>(frame->GetExpression(index));
6339 }
6340 }
6341
6342 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6343 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6344 NULL);
6345 if (index != -1) {
6346 return Handle<Object>(function_context->get(index));
6347 }
6348 }
6349
6350 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006351 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6352 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006353 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006354 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006355 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006356 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006357 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006358 return arguments;
6359}
6360
6361
6362// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006363// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006364// extension part has all the parameters and locals of the function on the
6365// stack frame. A function which calls eval with the code to evaluate is then
6366// compiled in this context and called in this context. As this context
6367// replaces the context of the function on the stack frame a new (empty)
6368// function is created as well to be used as the closure for the context.
6369// This function and the context acts as replacements for the function on the
6370// stack frame presenting the same view of the values of parameters and
6371// local variables as if the piece of JavaScript was evaluated at the point
6372// where the function on the stack frame is currently stopped.
6373static Object* Runtime_DebugEvaluate(Arguments args) {
6374 HandleScope scope;
6375
6376 // Check the execution state and decode arguments frame and source to be
6377 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006378 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006379 Object* check_result = Runtime_CheckExecutionState(args);
6380 if (check_result->IsFailure()) return check_result;
6381 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6382 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006383 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6384
6385 // Handle the processing of break.
6386 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006387
6388 // Get the frame where the debugging is performed.
6389 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6390 JavaScriptFrameIterator it(id);
6391 JavaScriptFrame* frame = it.frame();
6392 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6393 Handle<Code> code(function->code());
6394 ScopeInfo<> sinfo(*code);
6395
6396 // Traverse the saved contexts chain to find the active context for the
6397 // selected frame.
6398 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006399 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006400 save = save->prev();
6401 }
6402 ASSERT(save != NULL);
6403 SaveContext savex;
6404 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006405
6406 // Create the (empty) function replacing the function on the stack frame for
6407 // the purpose of evaluating in the context created below. It is important
6408 // that this function does not describe any parameters and local variables
6409 // in the context. If it does then this will cause problems with the lookup
6410 // in Context::Lookup, where context slots for parameters and local variables
6411 // are looked at before the extension object.
6412 Handle<JSFunction> go_between =
6413 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
6414 go_between->set_context(function->context());
6415#ifdef DEBUG
6416 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
6417 ASSERT(go_between_sinfo.number_of_parameters() == 0);
6418 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
6419#endif
6420
6421 // Allocate and initialize a context extension object with all the
6422 // arguments, stack locals heap locals and extension properties of the
6423 // debugged function.
6424 Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function());
6425 // First fill all parameters to the context extension.
6426 for (int i = 0; i < sinfo.number_of_parameters(); ++i) {
6427 SetProperty(context_ext,
6428 sinfo.parameter_name(i),
6429 Handle<Object>(frame->GetParameter(i)), NONE);
6430 }
6431 // Second fill all stack locals to the context extension.
6432 for (int i = 0; i < sinfo.number_of_stack_slots(); i++) {
6433 SetProperty(context_ext,
6434 sinfo.stack_slot_name(i),
6435 Handle<Object>(frame->GetExpression(i)), NONE);
6436 }
6437 // Third fill all context locals to the context extension.
6438 Handle<Context> frame_context(Context::cast(frame->context()));
6439 Handle<Context> function_context(frame_context->fcontext());
6440 for (int i = Context::MIN_CONTEXT_SLOTS;
6441 i < sinfo.number_of_context_slots();
6442 ++i) {
6443 int context_index =
6444 ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL);
6445 SetProperty(context_ext,
6446 sinfo.context_slot_name(i),
6447 Handle<Object>(function_context->get(context_index)), NONE);
6448 }
6449 // Finally copy any properties from the function context extension. This will
6450 // be variables introduced by eval.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006451 if (function_context->has_extension() &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006452 !function_context->IsGlobalContext()) {
6453 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6454 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6455 for (int i = 0; i < keys->length(); i++) {
6456 // Names of variables introduced by eval are strings.
6457 ASSERT(keys->get(i)->IsString());
6458 Handle<String> key(String::cast(keys->get(i)));
6459 SetProperty(context_ext, key, GetProperty(ext, key), NONE);
6460 }
6461 }
6462
6463 // Allocate a new context for the debug evaluation and set the extension
6464 // object build.
6465 Handle<Context> context =
6466 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
6467 context->set_extension(*context_ext);
6468 // Copy any with contexts present and chain them in front of this context.
6469 context = CopyWithContextChain(frame_context, context);
6470
6471 // Wrap the evaluation statement in a new function compiled in the newly
6472 // created context. The function has one parameter which has to be called
6473 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00006474 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006475 // function(arguments,__source__) {return eval(__source__);}
6476 static const char* source_str =
6477 "function(arguments,__source__){return eval(__source__);}";
6478 static const int source_str_length = strlen(source_str);
6479 Handle<String> function_source =
6480 Factory::NewStringFromAscii(Vector<const char>(source_str,
6481 source_str_length));
6482 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00006483 Compiler::CompileEval(function_source,
6484 context,
6485 0,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006486 context->IsGlobalContext(),
6487 false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006488 if (boilerplate.is_null()) return Failure::Exception();
6489 Handle<JSFunction> compiled_function =
6490 Factory::NewFunctionFromBoilerplate(boilerplate, context);
6491
6492 // Invoke the result of the compilation to get the evaluation function.
6493 bool has_pending_exception;
6494 Handle<Object> receiver(frame->receiver());
6495 Handle<Object> evaluation_function =
6496 Execution::Call(compiled_function, receiver, 0, NULL,
6497 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006498 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006499
6500 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
6501 function_context);
6502
6503 // Invoke the evaluation function and return the result.
6504 const int argc = 2;
6505 Object** argv[argc] = { arguments.location(),
6506 Handle<Object>::cast(source).location() };
6507 Handle<Object> result =
6508 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
6509 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006510 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006511 return *result;
6512}
6513
6514
6515static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
6516 HandleScope scope;
6517
6518 // Check the execution state and decode arguments frame and source to be
6519 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006520 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006521 Object* check_result = Runtime_CheckExecutionState(args);
6522 if (check_result->IsFailure()) return check_result;
6523 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006524 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
6525
6526 // Handle the processing of break.
6527 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006528
6529 // Enter the top context from before the debugger was invoked.
6530 SaveContext save;
6531 SaveContext* top = &save;
6532 while (top != NULL && *top->context() == *Debug::debug_context()) {
6533 top = top->prev();
6534 }
6535 if (top != NULL) {
6536 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006537 }
6538
6539 // Get the global context now set to the top context from before the
6540 // debugger was invoked.
6541 Handle<Context> context = Top::global_context();
6542
6543 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00006544 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006545 Handle<JSFunction>(Compiler::CompileEval(source,
6546 context,
6547 0,
6548 true,
6549 false));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006550 if (boilerplate.is_null()) return Failure::Exception();
6551 Handle<JSFunction> compiled_function =
6552 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
6553 context));
6554
6555 // Invoke the result of the compilation to get the evaluation function.
6556 bool has_pending_exception;
6557 Handle<Object> receiver = Top::global();
6558 Handle<Object> result =
6559 Execution::Call(compiled_function, receiver, 0, NULL,
6560 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006561 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006562 return *result;
6563}
6564
6565
ager@chromium.org41826e72009-03-30 13:30:57 +00006566// If an object given is an external string, check that the underlying
6567// resource is accessible. For other kinds of objects, always return true.
6568static bool IsExternalStringValid(Object* str) {
6569 if (!str->IsString() || !StringShape(String::cast(str)).IsExternal()) {
6570 return true;
6571 }
6572 if (StringShape(String::cast(str)).IsAsciiRepresentation()) {
ager@chromium.org80787b72009-04-17 10:24:24 +00006573 return ExternalAsciiString::cast(str)->resource() != NULL;
ager@chromium.org41826e72009-03-30 13:30:57 +00006574 } else if (StringShape(String::cast(str)).IsTwoByteRepresentation()) {
ager@chromium.org80787b72009-04-17 10:24:24 +00006575 return ExternalTwoByteString::cast(str)->resource() != NULL;
ager@chromium.org41826e72009-03-30 13:30:57 +00006576 } else {
6577 return true;
6578 }
6579}
6580
6581
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006582// Helper function used by Runtime_DebugGetLoadedScripts below.
6583static int DebugGetLoadedScripts(FixedArray* instances, int instances_size) {
6584 NoHandleAllocation ha;
6585 AssertNoAllocation no_alloc;
6586
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006587 // Scan heap for Script objects.
6588 int count = 0;
6589 HeapIterator iterator;
6590 while (iterator.has_next()) {
6591 HeapObject* obj = iterator.next();
6592 ASSERT(obj != NULL);
ager@chromium.org41826e72009-03-30 13:30:57 +00006593 if (obj->IsScript() && IsExternalStringValid(Script::cast(obj)->source())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006594 if (instances != NULL && count < instances_size) {
6595 instances->set(count, obj);
6596 }
6597 count++;
6598 }
6599 }
6600
6601 return count;
6602}
6603
6604
6605static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
6606 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006607 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006608
6609 // Perform two GCs to get rid of all unreferenced scripts. The first GC gets
ager@chromium.org32912102009-01-16 10:38:43 +00006610 // rid of all the cached script wrappers and the second gets rid of the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006611 // scripts which is no longer referenced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006612 Heap::CollectAllGarbage();
6613 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006614
6615 // Get the number of scripts.
6616 int count;
6617 count = DebugGetLoadedScripts(NULL, 0);
6618
6619 // Allocate an array to hold the result.
6620 Handle<FixedArray> instances = Factory::NewFixedArray(count);
6621
6622 // Fill the script objects.
6623 count = DebugGetLoadedScripts(*instances, count);
6624
6625 // Convert the script objects to proper JS objects.
6626 for (int i = 0; i < count; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006627 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
6628 // Get the script wrapper in a local handle before calling GetScriptWrapper,
6629 // because using
6630 // instances->set(i, *GetScriptWrapper(script))
6631 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
6632 // already have deferenced the instances handle.
6633 Handle<JSValue> wrapper = GetScriptWrapper(script);
6634 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006635 }
6636
6637 // Return result as a JS array.
6638 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
6639 Handle<JSArray>::cast(result)->SetContent(*instances);
6640 return *result;
6641}
6642
6643
6644// Helper function used by Runtime_DebugReferencedBy below.
6645static int DebugReferencedBy(JSObject* target,
6646 Object* instance_filter, int max_references,
6647 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006648 JSFunction* arguments_function) {
6649 NoHandleAllocation ha;
6650 AssertNoAllocation no_alloc;
6651
6652 // Iterate the heap.
6653 int count = 0;
6654 JSObject* last = NULL;
6655 HeapIterator iterator;
6656 while (iterator.has_next() &&
6657 (max_references == 0 || count < max_references)) {
6658 // Only look at all JSObjects.
6659 HeapObject* heap_obj = iterator.next();
6660 if (heap_obj->IsJSObject()) {
6661 // Skip context extension objects and argument arrays as these are
6662 // checked in the context of functions using them.
6663 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006664 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006665 obj->map()->constructor() == arguments_function) {
6666 continue;
6667 }
6668
6669 // Check if the JS object has a reference to the object looked for.
6670 if (obj->ReferencesObject(target)) {
6671 // Check instance filter if supplied. This is normally used to avoid
6672 // references from mirror objects (see Runtime_IsInPrototypeChain).
6673 if (!instance_filter->IsUndefined()) {
6674 Object* V = obj;
6675 while (true) {
6676 Object* prototype = V->GetPrototype();
6677 if (prototype->IsNull()) {
6678 break;
6679 }
6680 if (instance_filter == prototype) {
6681 obj = NULL; // Don't add this object.
6682 break;
6683 }
6684 V = prototype;
6685 }
6686 }
6687
6688 if (obj != NULL) {
6689 // Valid reference found add to instance array if supplied an update
6690 // count.
6691 if (instances != NULL && count < instances_size) {
6692 instances->set(count, obj);
6693 }
6694 last = obj;
6695 count++;
6696 }
6697 }
6698 }
6699 }
6700
6701 // Check for circular reference only. This can happen when the object is only
6702 // referenced from mirrors and has a circular reference in which case the
6703 // object is not really alive and would have been garbage collected if not
6704 // referenced from the mirror.
6705 if (count == 1 && last == target) {
6706 count = 0;
6707 }
6708
6709 // Return the number of referencing objects found.
6710 return count;
6711}
6712
6713
6714// Scan the heap for objects with direct references to an object
6715// args[0]: the object to find references to
6716// args[1]: constructor function for instances to exclude (Mirror)
6717// args[2]: the the maximum number of objects to return
6718static Object* Runtime_DebugReferencedBy(Arguments args) {
6719 ASSERT(args.length() == 3);
6720
6721 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006722 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006723
6724 // Check parameters.
6725 CONVERT_CHECKED(JSObject, target, args[0]);
6726 Object* instance_filter = args[1];
6727 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
6728 instance_filter->IsJSObject());
6729 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
6730 RUNTIME_ASSERT(max_references >= 0);
6731
6732 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006733 JSObject* arguments_boilerplate =
6734 Top::context()->global_context()->arguments_boilerplate();
6735 JSFunction* arguments_function =
6736 JSFunction::cast(arguments_boilerplate->map()->constructor());
6737
6738 // Get the number of referencing objects.
6739 int count;
6740 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006741 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006742
6743 // Allocate an array to hold the result.
6744 Object* object = Heap::AllocateFixedArray(count);
6745 if (object->IsFailure()) return object;
6746 FixedArray* instances = FixedArray::cast(object);
6747
6748 // Fill the referencing objects.
6749 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006750 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006751
6752 // Return result as JS array.
6753 Object* result =
6754 Heap::AllocateJSObject(
6755 Top::context()->global_context()->array_function());
6756 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6757 return result;
6758}
6759
6760
6761// Helper function used by Runtime_DebugConstructedBy below.
6762static int DebugConstructedBy(JSFunction* constructor, int max_references,
6763 FixedArray* instances, int instances_size) {
6764 AssertNoAllocation no_alloc;
6765
6766 // Iterate the heap.
6767 int count = 0;
6768 HeapIterator iterator;
6769 while (iterator.has_next() &&
6770 (max_references == 0 || count < max_references)) {
6771 // Only look at all JSObjects.
6772 HeapObject* heap_obj = iterator.next();
6773 if (heap_obj->IsJSObject()) {
6774 JSObject* obj = JSObject::cast(heap_obj);
6775 if (obj->map()->constructor() == constructor) {
6776 // Valid reference found add to instance array if supplied an update
6777 // count.
6778 if (instances != NULL && count < instances_size) {
6779 instances->set(count, obj);
6780 }
6781 count++;
6782 }
6783 }
6784 }
6785
6786 // Return the number of referencing objects found.
6787 return count;
6788}
6789
6790
6791// Scan the heap for objects constructed by a specific function.
6792// args[0]: the constructor to find instances of
6793// args[1]: the the maximum number of objects to return
6794static Object* Runtime_DebugConstructedBy(Arguments args) {
6795 ASSERT(args.length() == 2);
6796
6797 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006798 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006799
6800 // Check parameters.
6801 CONVERT_CHECKED(JSFunction, constructor, args[0]);
6802 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
6803 RUNTIME_ASSERT(max_references >= 0);
6804
6805 // Get the number of referencing objects.
6806 int count;
6807 count = DebugConstructedBy(constructor, max_references, NULL, 0);
6808
6809 // Allocate an array to hold the result.
6810 Object* object = Heap::AllocateFixedArray(count);
6811 if (object->IsFailure()) return object;
6812 FixedArray* instances = FixedArray::cast(object);
6813
6814 // Fill the referencing objects.
6815 count = DebugConstructedBy(constructor, max_references, instances, count);
6816
6817 // Return result as JS array.
6818 Object* result =
6819 Heap::AllocateJSObject(
6820 Top::context()->global_context()->array_function());
6821 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6822 return result;
6823}
6824
6825
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006826// Find the effective prototype object as returned by __proto__.
6827// args[0]: the object to find the prototype for.
6828static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006829 ASSERT(args.length() == 1);
6830
6831 CONVERT_CHECKED(JSObject, obj, args[0]);
6832
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006833 // Use the __proto__ accessor.
6834 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006835}
6836
6837
6838static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006839 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006840 CPU::DebugBreak();
6841 return Heap::undefined_value();
6842}
6843
6844
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006845static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
6846#ifdef DEBUG
6847 HandleScope scope;
6848 ASSERT(args.length() == 1);
6849 // Get the function and make sure it is compiled.
6850 CONVERT_ARG_CHECKED(JSFunction, func, 0);
6851 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
6852 return Failure::Exception();
6853 }
6854 func->code()->PrintLn();
6855#endif // DEBUG
6856 return Heap::undefined_value();
6857}
6858#endif // ENABLE_DEBUGGER_SUPPORT
6859
6860
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006861// Finds the script object from the script data. NOTE: This operation uses
6862// heap traversal to find the function generated for the source position
6863// for the requested break point. For lazily compiled functions several heap
6864// traversals might be required rendering this operation as a rather slow
6865// operation. However for setting break points which is normally done through
6866// some kind of user interaction the performance is not crucial.
6867static Handle<Object> Runtime_GetScriptFromScriptName(
6868 Handle<String> script_name) {
6869 // Scan the heap for Script objects to find the script with the requested
6870 // script data.
6871 Handle<Script> script;
6872 HeapIterator iterator;
6873 while (script.is_null() && iterator.has_next()) {
6874 HeapObject* obj = iterator.next();
6875 // If a script is found check if it has the script data requested.
6876 if (obj->IsScript()) {
6877 if (Script::cast(obj)->name()->IsString()) {
6878 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
6879 script = Handle<Script>(Script::cast(obj));
6880 }
6881 }
6882 }
6883 }
6884
6885 // If no script with the requested script data is found return undefined.
6886 if (script.is_null()) return Factory::undefined_value();
6887
6888 // Return the script found.
6889 return GetScriptWrapper(script);
6890}
6891
6892
6893// Get the script object from script data. NOTE: Regarding performance
6894// see the NOTE for GetScriptFromScriptData.
6895// args[0]: script data for the script to find the source for
6896static Object* Runtime_GetScript(Arguments args) {
6897 HandleScope scope;
6898
6899 ASSERT(args.length() == 1);
6900
6901 CONVERT_CHECKED(String, script_name, args[0]);
6902
6903 // Find the requested script.
6904 Handle<Object> result =
6905 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
6906 return *result;
6907}
6908
6909
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006910static Object* Runtime_Abort(Arguments args) {
6911 ASSERT(args.length() == 2);
6912 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
6913 Smi::cast(args[1])->value());
6914 Top::PrintStack();
6915 OS::Abort();
6916 UNREACHABLE();
6917 return NULL;
6918}
6919
6920
kasper.lund44510672008-07-25 07:37:58 +00006921#ifdef DEBUG
6922// ListNatives is ONLY used by the fuzz-natives.js in debug mode
6923// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006924static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006925 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006926 HandleScope scope;
6927 Handle<JSArray> result = Factory::NewJSArray(0);
6928 int index = 0;
6929#define ADD_ENTRY(Name, argc) \
6930 { \
6931 HandleScope inner; \
6932 Handle<String> name = \
6933 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
6934 Handle<JSArray> pair = Factory::NewJSArray(0); \
6935 SetElement(pair, 0, name); \
6936 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
6937 SetElement(result, index++, pair); \
6938 }
6939 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
6940#undef ADD_ENTRY
6941 return *result;
6942}
kasper.lund44510672008-07-25 07:37:58 +00006943#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006944
6945
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006946static Object* Runtime_Log(Arguments args) {
6947 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00006948 CONVERT_CHECKED(String, format, args[0]);
6949 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006950 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006951 Logger::LogRuntime(chars, elms);
6952 return Heap::undefined_value();
6953}
6954
6955
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006956static Object* Runtime_IS_VAR(Arguments args) {
6957 UNREACHABLE(); // implemented as macro in the parser
6958 return NULL;
6959}
6960
6961
6962// ----------------------------------------------------------------------------
6963// Implementation of Runtime
6964
6965#define F(name, nargs) \
6966 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
6967 static_cast<int>(Runtime::k##name) },
6968
6969static Runtime::Function Runtime_functions[] = {
6970 RUNTIME_FUNCTION_LIST(F)
6971 { NULL, NULL, NULL, 0, -1 }
6972};
6973
6974#undef F
6975
6976
6977Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
6978 ASSERT(0 <= fid && fid < kNofFunctions);
6979 return &Runtime_functions[fid];
6980}
6981
6982
6983Runtime::Function* Runtime::FunctionForName(const char* name) {
6984 for (Function* f = Runtime_functions; f->name != NULL; f++) {
6985 if (strcmp(f->name, name) == 0) {
6986 return f;
6987 }
6988 }
6989 return NULL;
6990}
6991
6992
6993void Runtime::PerformGC(Object* result) {
6994 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006995 if (failure->IsRetryAfterGC()) {
6996 // Try to do a garbage collection; ignore it if it fails. The C
6997 // entry stub will throw an out-of-memory exception in that case.
6998 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
6999 } else {
7000 // Handle last resort GC and make sure to allow future allocations
7001 // to grow the heap without causing GCs (if possible).
7002 Counters::gc_last_resort_from_js.Increment();
7003 Heap::CollectAllGarbage();
7004 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007005}
7006
7007
7008} } // namespace v8::internal