blob: 2896ff3841cfa4634e561081df102e3aff5ae142 [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
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002691static Object* Runtime_SetProperty(Arguments args) {
2692 NoHandleAllocation ha;
2693 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2694
2695 Handle<Object> object = args.at<Object>(0);
2696 Handle<Object> key = args.at<Object>(1);
2697 Handle<Object> value = args.at<Object>(2);
2698
2699 // Compute attributes.
2700 PropertyAttributes attributes = NONE;
2701 if (args.length() == 4) {
2702 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002703 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002704 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002705 RUNTIME_ASSERT(
2706 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2707 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002708 }
2709 return Runtime::SetObjectProperty(object, key, value, attributes);
2710}
2711
2712
2713// Set a local property, even if it is READ_ONLY. If the property does not
2714// exist, it will be added with attributes NONE.
2715static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2716 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002717 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002718 CONVERT_CHECKED(JSObject, object, args[0]);
2719 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002720 // Compute attributes.
2721 PropertyAttributes attributes = NONE;
2722 if (args.length() == 4) {
2723 CONVERT_CHECKED(Smi, value_obj, args[3]);
2724 int unchecked_value = value_obj->value();
2725 // Only attribute bits should be set.
2726 RUNTIME_ASSERT(
2727 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2728 attributes = static_cast<PropertyAttributes>(unchecked_value);
2729 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002730
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002731 return object->
2732 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002733}
2734
2735
2736static Object* Runtime_DeleteProperty(Arguments args) {
2737 NoHandleAllocation ha;
2738 ASSERT(args.length() == 2);
2739
2740 CONVERT_CHECKED(JSObject, object, args[0]);
2741 CONVERT_CHECKED(String, key, args[1]);
2742 return object->DeleteProperty(key);
2743}
2744
2745
2746static Object* Runtime_HasLocalProperty(Arguments args) {
2747 NoHandleAllocation ha;
2748 ASSERT(args.length() == 2);
2749 CONVERT_CHECKED(String, key, args[1]);
2750
2751 // Only JS objects can have properties.
2752 if (args[0]->IsJSObject()) {
2753 JSObject* object = JSObject::cast(args[0]);
2754 if (object->HasLocalProperty(key)) return Heap::true_value();
2755 } else if (args[0]->IsString()) {
2756 // Well, there is one exception: Handle [] on strings.
2757 uint32_t index;
2758 if (key->AsArrayIndex(&index)) {
2759 String* string = String::cast(args[0]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002760 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002761 return Heap::true_value();
2762 }
2763 }
2764 return Heap::false_value();
2765}
2766
2767
2768static Object* Runtime_HasProperty(Arguments args) {
2769 NoHandleAllocation na;
2770 ASSERT(args.length() == 2);
2771
2772 // Only JS objects can have properties.
2773 if (args[0]->IsJSObject()) {
2774 JSObject* object = JSObject::cast(args[0]);
2775 CONVERT_CHECKED(String, key, args[1]);
2776 if (object->HasProperty(key)) return Heap::true_value();
2777 }
2778 return Heap::false_value();
2779}
2780
2781
2782static Object* Runtime_HasElement(Arguments args) {
2783 NoHandleAllocation na;
2784 ASSERT(args.length() == 2);
2785
2786 // Only JS objects can have elements.
2787 if (args[0]->IsJSObject()) {
2788 JSObject* object = JSObject::cast(args[0]);
2789 CONVERT_CHECKED(Smi, index_obj, args[1]);
2790 uint32_t index = index_obj->value();
2791 if (object->HasElement(index)) return Heap::true_value();
2792 }
2793 return Heap::false_value();
2794}
2795
2796
2797static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2798 NoHandleAllocation ha;
2799 ASSERT(args.length() == 2);
2800
2801 CONVERT_CHECKED(JSObject, object, args[0]);
2802 CONVERT_CHECKED(String, key, args[1]);
2803
2804 uint32_t index;
2805 if (key->AsArrayIndex(&index)) {
2806 return Heap::ToBoolean(object->HasElement(index));
2807 }
2808
ager@chromium.org870a0b62008-11-04 11:43:05 +00002809 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2810 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002811}
2812
2813
2814static Object* Runtime_GetPropertyNames(Arguments args) {
2815 HandleScope scope;
2816 ASSERT(args.length() == 1);
2817
2818 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2819 Handle<JSObject> object(raw_object);
2820 return *GetKeysFor(object);
2821}
2822
2823
2824// Returns either a FixedArray as Runtime_GetPropertyNames,
2825// or, if the given object has an enum cache that contains
2826// all enumerable properties of the object and its prototypes
2827// have none, the map of the object. This is used to speed up
2828// the check for deletions during a for-in.
2829static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2830 ASSERT(args.length() == 1);
2831
2832 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2833
2834 if (raw_object->IsSimpleEnum()) return raw_object->map();
2835
2836 HandleScope scope;
2837 Handle<JSObject> object(raw_object);
2838 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2839
2840 // Test again, since cache may have been built by preceding call.
2841 if (object->IsSimpleEnum()) return object->map();
2842
2843 return *content;
2844}
2845
2846
2847static Object* Runtime_GetArgumentsProperty(Arguments args) {
2848 NoHandleAllocation ha;
2849 ASSERT(args.length() == 1);
2850
2851 // Compute the frame holding the arguments.
2852 JavaScriptFrameIterator it;
2853 it.AdvanceToArgumentsFrame();
2854 JavaScriptFrame* frame = it.frame();
2855
2856 // Get the actual number of provided arguments.
2857 const uint32_t n = frame->GetProvidedParametersCount();
2858
2859 // Try to convert the key to an index. If successful and within
2860 // index return the the argument from the frame.
2861 uint32_t index;
2862 if (Array::IndexFromObject(args[0], &index) && index < n) {
2863 return frame->GetParameter(index);
2864 }
2865
2866 // Convert the key to a string.
2867 HandleScope scope;
2868 bool exception = false;
2869 Handle<Object> converted =
2870 Execution::ToString(args.at<Object>(0), &exception);
2871 if (exception) return Failure::Exception();
2872 Handle<String> key = Handle<String>::cast(converted);
2873
2874 // Try to convert the string key into an array index.
2875 if (key->AsArrayIndex(&index)) {
2876 if (index < n) {
2877 return frame->GetParameter(index);
2878 } else {
2879 return Top::initial_object_prototype()->GetElement(index);
2880 }
2881 }
2882
2883 // Handle special arguments properties.
2884 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
2885 if (key->Equals(Heap::callee_symbol())) return frame->function();
2886
2887 // Lookup in the initial Object.prototype object.
2888 return Top::initial_object_prototype()->GetProperty(*key);
2889}
2890
2891
kasperl@chromium.org061ef742009-02-27 12:16:20 +00002892static Object* Runtime_ToFastProperties(Arguments args) {
2893 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00002894 Handle<Object> object = args.at<Object>(0);
2895 if (object->IsJSObject()) {
2896 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2897 js_object->TransformToFastProperties(0);
2898 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00002899 return *object;
2900}
2901
2902
2903static Object* Runtime_ToSlowProperties(Arguments args) {
2904 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00002905 Handle<Object> object = args.at<Object>(0);
2906 if (object->IsJSObject()) {
2907 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2908 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
2909 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00002910 return *object;
2911}
2912
2913
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002914static Object* Runtime_ToBool(Arguments args) {
2915 NoHandleAllocation ha;
2916 ASSERT(args.length() == 1);
2917
2918 return args[0]->ToBoolean();
2919}
2920
2921
2922// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
2923// Possible optimizations: put the type string into the oddballs.
2924static Object* Runtime_Typeof(Arguments args) {
2925 NoHandleAllocation ha;
2926
2927 Object* obj = args[0];
2928 if (obj->IsNumber()) return Heap::number_symbol();
2929 HeapObject* heap_obj = HeapObject::cast(obj);
2930
2931 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002932 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002933
2934 InstanceType instance_type = heap_obj->map()->instance_type();
2935 if (instance_type < FIRST_NONSTRING_TYPE) {
2936 return Heap::string_symbol();
2937 }
2938
2939 switch (instance_type) {
2940 case ODDBALL_TYPE:
2941 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
2942 return Heap::boolean_symbol();
2943 }
2944 if (heap_obj->IsNull()) {
2945 return Heap::object_symbol();
2946 }
2947 ASSERT(heap_obj->IsUndefined());
2948 return Heap::undefined_symbol();
2949 case JS_FUNCTION_TYPE:
2950 return Heap::function_symbol();
2951 default:
2952 // For any kind of object not handled above, the spec rule for
2953 // host objects gives that it is okay to return "object"
2954 return Heap::object_symbol();
2955 }
2956}
2957
2958
2959static Object* Runtime_StringToNumber(Arguments args) {
2960 NoHandleAllocation ha;
2961 ASSERT(args.length() == 1);
2962 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002963 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002964 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
2965}
2966
2967
2968static Object* Runtime_StringFromCharCodeArray(Arguments args) {
2969 NoHandleAllocation ha;
2970 ASSERT(args.length() == 1);
2971
2972 CONVERT_CHECKED(JSArray, codes, args[0]);
2973 int length = Smi::cast(codes->length())->value();
2974
2975 // Check if the string can be ASCII.
2976 int i;
2977 for (i = 0; i < length; i++) {
2978 Object* element = codes->GetElement(i);
2979 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
2980 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
2981 break;
2982 }
2983
2984 Object* object = NULL;
2985 if (i == length) { // The string is ASCII.
2986 object = Heap::AllocateRawAsciiString(length);
2987 } else { // The string is not ASCII.
2988 object = Heap::AllocateRawTwoByteString(length);
2989 }
2990
2991 if (object->IsFailure()) return object;
2992 String* result = String::cast(object);
2993 for (int i = 0; i < length; i++) {
2994 Object* element = codes->GetElement(i);
2995 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002996 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002997 }
2998 return result;
2999}
3000
3001
3002// kNotEscaped is generated by the following:
3003//
3004// #!/bin/perl
3005// for (my $i = 0; $i < 256; $i++) {
3006// print "\n" if $i % 16 == 0;
3007// my $c = chr($i);
3008// my $escaped = 1;
3009// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3010// print $escaped ? "0, " : "1, ";
3011// }
3012
3013
3014static bool IsNotEscaped(uint16_t character) {
3015 // Only for 8 bit characters, the rest are always escaped (in a different way)
3016 ASSERT(character < 256);
3017 static const char kNotEscaped[256] = {
3018 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3019 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3020 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3021 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3022 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3023 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3024 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3025 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3026 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3027 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3028 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3029 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3030 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3031 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3032 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3033 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3034 };
3035 return kNotEscaped[character] != 0;
3036}
3037
3038
3039static Object* Runtime_URIEscape(Arguments args) {
3040 const char hex_chars[] = "0123456789ABCDEF";
3041 NoHandleAllocation ha;
3042 ASSERT(args.length() == 1);
3043 CONVERT_CHECKED(String, source, args[0]);
3044
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003045 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003046
3047 int escaped_length = 0;
3048 int length = source->length();
3049 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003050 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003051 buffer->Reset(source);
3052 while (buffer->has_more()) {
3053 uint16_t character = buffer->GetNext();
3054 if (character >= 256) {
3055 escaped_length += 6;
3056 } else if (IsNotEscaped(character)) {
3057 escaped_length++;
3058 } else {
3059 escaped_length += 3;
3060 }
3061 // We don't allow strings that are longer than Smi range.
3062 if (!Smi::IsValid(escaped_length)) {
3063 Top::context()->mark_out_of_memory();
3064 return Failure::OutOfMemoryException();
3065 }
3066 }
3067 }
3068 // No length change implies no change. Return original string if no change.
3069 if (escaped_length == length) {
3070 return source;
3071 }
3072 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3073 if (o->IsFailure()) return o;
3074 String* destination = String::cast(o);
3075 int dest_position = 0;
3076
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003077 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003078 buffer->Rewind();
3079 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003080 uint16_t chr = buffer->GetNext();
3081 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003082 destination->Set(dest_position, '%');
3083 destination->Set(dest_position+1, 'u');
3084 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3085 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3086 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3087 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003088 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003089 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003090 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003091 dest_position++;
3092 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003093 destination->Set(dest_position, '%');
3094 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3095 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003096 dest_position += 3;
3097 }
3098 }
3099 return destination;
3100}
3101
3102
3103static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3104 static const signed char kHexValue['g'] = {
3105 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3106 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3107 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3108 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3109 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3110 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3111 -1, 10, 11, 12, 13, 14, 15 };
3112
3113 if (character1 > 'f') return -1;
3114 int hi = kHexValue[character1];
3115 if (hi == -1) return -1;
3116 if (character2 > 'f') return -1;
3117 int lo = kHexValue[character2];
3118 if (lo == -1) return -1;
3119 return (hi << 4) + lo;
3120}
3121
3122
ager@chromium.org870a0b62008-11-04 11:43:05 +00003123static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003124 int i,
3125 int length,
3126 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003127 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003128 int32_t hi = 0;
3129 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003130 if (character == '%' &&
3131 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003132 source->Get(i + 1) == 'u' &&
3133 (hi = TwoDigitHex(source->Get(i + 2),
3134 source->Get(i + 3))) != -1 &&
3135 (lo = TwoDigitHex(source->Get(i + 4),
3136 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003137 *step = 6;
3138 return (hi << 8) + lo;
3139 } else if (character == '%' &&
3140 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003141 (lo = TwoDigitHex(source->Get(i + 1),
3142 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003143 *step = 3;
3144 return lo;
3145 } else {
3146 *step = 1;
3147 return character;
3148 }
3149}
3150
3151
3152static Object* Runtime_URIUnescape(Arguments args) {
3153 NoHandleAllocation ha;
3154 ASSERT(args.length() == 1);
3155 CONVERT_CHECKED(String, source, args[0]);
3156
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003157 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003158
3159 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003160 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003161
3162 int unescaped_length = 0;
3163 for (int i = 0; i < length; unescaped_length++) {
3164 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003165 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003166 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003167 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003168 i += step;
3169 }
3170
3171 // No length change implies no change. Return original string if no change.
3172 if (unescaped_length == length)
3173 return source;
3174
3175 Object* o = ascii ?
3176 Heap::AllocateRawAsciiString(unescaped_length) :
3177 Heap::AllocateRawTwoByteString(unescaped_length);
3178 if (o->IsFailure()) return o;
3179 String* destination = String::cast(o);
3180
3181 int dest_position = 0;
3182 for (int i = 0; i < length; dest_position++) {
3183 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003184 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003185 i += step;
3186 }
3187 return destination;
3188}
3189
3190
3191static Object* Runtime_StringParseInt(Arguments args) {
3192 NoHandleAllocation ha;
3193
3194 CONVERT_CHECKED(String, s, args[0]);
3195 CONVERT_DOUBLE_CHECKED(n, args[1]);
3196 int radix = FastD2I(n);
3197
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003198 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003199
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003200 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003201 int i;
3202
3203 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003204 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003205 if (i == len) return Heap::nan_value();
3206
3207 // Compute the sign (default to +).
3208 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003209 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003210 sign = -1;
3211 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003212 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003213 i++;
3214 }
3215
3216 // Compute the radix if 0.
3217 if (radix == 0) {
3218 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003219 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003220 radix = 8;
3221 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003222 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003223 if (c == 'x' || c == 'X') {
3224 radix = 16;
3225 i += 2;
3226 }
3227 }
3228 }
3229 } else if (radix == 16) {
3230 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003231 if (i + 1 < len && s->Get(i) == '0') {
3232 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003233 if (c == 'x' || c == 'X') i += 2;
3234 }
3235 }
3236
3237 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3238 double value;
3239 int end_index = StringToInt(s, i, radix, &value);
3240 if (end_index != i) {
3241 return Heap::NumberFromDouble(sign * value);
3242 }
3243 return Heap::nan_value();
3244}
3245
3246
3247static Object* Runtime_StringParseFloat(Arguments args) {
3248 NoHandleAllocation ha;
3249 CONVERT_CHECKED(String, str, args[0]);
3250
3251 // ECMA-262 section 15.1.2.3, empty string is NaN
3252 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3253
3254 // Create a number object from the value.
3255 return Heap::NumberFromDouble(value);
3256}
3257
3258
3259static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3260static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3261
3262
3263template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003264static Object* ConvertCaseHelper(String* s,
3265 int length,
3266 int input_string_length,
3267 unibrow::Mapping<Converter, 128>* mapping) {
3268 // We try this twice, once with the assumption that the result is no longer
3269 // than the input and, if that assumption breaks, again with the exact
3270 // length. This may not be pretty, but it is nicer than what was here before
3271 // and I hereby claim my vaffel-is.
3272 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003273 // Allocate the resulting string.
3274 //
3275 // NOTE: This assumes that the upper/lower case of an ascii
3276 // character is also ascii. This is currently the case, but it
3277 // might break in the future if we implement more context and locale
3278 // dependent upper/lower conversions.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003279 Object* o = StringShape(s).IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003280 ? Heap::AllocateRawAsciiString(length)
3281 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003282 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003283 String* result = String::cast(o);
3284 bool has_changed_character = false;
3285
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003286 // Convert all characters to upper case, assuming that they will fit
3287 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003288 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003289 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003290 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003291 // We can assume that the string is not empty
3292 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003293 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003294 bool has_next = buffer->has_more();
3295 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003296 int char_length = mapping->get(current, next, chars);
3297 if (char_length == 0) {
3298 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003299 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003300 i++;
3301 } else if (char_length == 1) {
3302 // Common case: converting the letter resulted in one character.
3303 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003304 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003305 has_changed_character = true;
3306 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003307 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003308 // We've assumed that the result would be as long as the
3309 // input but here is a character that converts to several
3310 // characters. No matter, we calculate the exact length
3311 // of the result and try the whole thing again.
3312 //
3313 // Note that this leaves room for optimization. We could just
3314 // memcpy what we already have to the result string. Also,
3315 // the result string is the last object allocated we could
3316 // "realloc" it and probably, in the vast majority of cases,
3317 // extend the existing string to be able to hold the full
3318 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003319 int next_length = 0;
3320 if (has_next) {
3321 next_length = mapping->get(next, 0, chars);
3322 if (next_length == 0) next_length = 1;
3323 }
3324 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003325 while (buffer->has_more()) {
3326 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003327 // NOTE: we use 0 as the next character here because, while
3328 // the next character may affect what a character converts to,
3329 // it does not in any case affect the length of what it convert
3330 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003331 int char_length = mapping->get(current, 0, chars);
3332 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003333 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003334 if (current_length > Smi::kMaxValue) {
3335 Top::context()->mark_out_of_memory();
3336 return Failure::OutOfMemoryException();
3337 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003338 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003339 // Try again with the real length.
3340 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003341 } else {
3342 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003343 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003344 i++;
3345 }
3346 has_changed_character = true;
3347 }
3348 current = next;
3349 }
3350 if (has_changed_character) {
3351 return result;
3352 } else {
3353 // If we didn't actually change anything in doing the conversion
3354 // we simple return the result and let the converted string
3355 // become garbage; there is no reason to keep two identical strings
3356 // alive.
3357 return s;
3358 }
3359}
3360
3361
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003362template <class Converter>
3363static Object* ConvertCase(Arguments args,
3364 unibrow::Mapping<Converter, 128>* mapping) {
3365 NoHandleAllocation ha;
3366
3367 CONVERT_CHECKED(String, s, args[0]);
3368 s->TryFlattenIfNotFlat();
3369
3370 int input_string_length = s->length();
3371 // Assume that the string is not empty; we need this assumption later
3372 if (input_string_length == 0) return s;
3373 int length = input_string_length;
3374
3375 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3376 if (answer->IsSmi()) {
3377 // Retry with correct length.
3378 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3379 }
3380 return answer; // This may be a failure.
3381}
3382
3383
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003384static Object* Runtime_StringToLowerCase(Arguments args) {
3385 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3386}
3387
3388
3389static Object* Runtime_StringToUpperCase(Arguments args) {
3390 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3391}
3392
3393
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003394bool Runtime::IsUpperCaseChar(uint16_t ch) {
3395 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3396 int char_length = to_upper_mapping.get(ch, 0, chars);
3397 return char_length == 0;
3398}
3399
3400
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003401static Object* Runtime_NumberToString(Arguments args) {
3402 NoHandleAllocation ha;
3403 ASSERT(args.length() == 1);
3404
3405 Object* number = args[0];
3406 RUNTIME_ASSERT(number->IsNumber());
3407
3408 Object* cached = Heap::GetNumberStringCache(number);
3409 if (cached != Heap::undefined_value()) {
3410 return cached;
3411 }
3412
3413 char arr[100];
3414 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3415 const char* str;
3416 if (number->IsSmi()) {
3417 int num = Smi::cast(number)->value();
3418 str = IntToCString(num, buffer);
3419 } else {
3420 double num = HeapNumber::cast(number)->value();
3421 str = DoubleToCString(num, buffer);
3422 }
3423 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3424
3425 if (!result->IsFailure()) {
3426 Heap::SetNumberStringCache(number, String::cast(result));
3427 }
3428 return result;
3429}
3430
3431
3432static Object* Runtime_NumberToInteger(Arguments args) {
3433 NoHandleAllocation ha;
3434 ASSERT(args.length() == 1);
3435
3436 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003437 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003438 CONVERT_DOUBLE_CHECKED(number, obj);
3439 return Heap::NumberFromDouble(DoubleToInteger(number));
3440}
3441
3442
3443static Object* Runtime_NumberToJSUint32(Arguments args) {
3444 NoHandleAllocation ha;
3445 ASSERT(args.length() == 1);
3446
3447 Object* obj = args[0];
3448 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3449 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3450 return Heap::NumberFromUint32(number);
3451}
3452
3453
3454static Object* Runtime_NumberToJSInt32(Arguments args) {
3455 NoHandleAllocation ha;
3456 ASSERT(args.length() == 1);
3457
3458 Object* obj = args[0];
3459 if (obj->IsSmi()) return obj;
3460 CONVERT_DOUBLE_CHECKED(number, obj);
3461 return Heap::NumberFromInt32(DoubleToInt32(number));
3462}
3463
3464
ager@chromium.org870a0b62008-11-04 11:43:05 +00003465// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3466// a small integer.
3467static Object* Runtime_NumberToSmi(Arguments args) {
3468 NoHandleAllocation ha;
3469 ASSERT(args.length() == 1);
3470
3471 Object* obj = args[0];
3472 if (obj->IsSmi()) {
3473 return obj;
3474 }
3475 if (obj->IsHeapNumber()) {
3476 double value = HeapNumber::cast(obj)->value();
3477 int int_value = FastD2I(value);
3478 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3479 return Smi::FromInt(int_value);
3480 }
3481 }
3482 return Heap::nan_value();
3483}
3484
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003485static Object* Runtime_NumberAdd(Arguments args) {
3486 NoHandleAllocation ha;
3487 ASSERT(args.length() == 2);
3488
3489 CONVERT_DOUBLE_CHECKED(x, args[0]);
3490 CONVERT_DOUBLE_CHECKED(y, args[1]);
3491 return Heap::AllocateHeapNumber(x + y);
3492}
3493
3494
3495static Object* Runtime_NumberSub(Arguments args) {
3496 NoHandleAllocation ha;
3497 ASSERT(args.length() == 2);
3498
3499 CONVERT_DOUBLE_CHECKED(x, args[0]);
3500 CONVERT_DOUBLE_CHECKED(y, args[1]);
3501 return Heap::AllocateHeapNumber(x - y);
3502}
3503
3504
3505static Object* Runtime_NumberMul(Arguments args) {
3506 NoHandleAllocation ha;
3507 ASSERT(args.length() == 2);
3508
3509 CONVERT_DOUBLE_CHECKED(x, args[0]);
3510 CONVERT_DOUBLE_CHECKED(y, args[1]);
3511 return Heap::AllocateHeapNumber(x * y);
3512}
3513
3514
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003515static Object* Runtime_NumberUnaryMinus(Arguments args) {
3516 NoHandleAllocation ha;
3517 ASSERT(args.length() == 1);
3518
3519 CONVERT_DOUBLE_CHECKED(x, args[0]);
3520 return Heap::AllocateHeapNumber(-x);
3521}
3522
3523
3524static Object* Runtime_NumberDiv(Arguments args) {
3525 NoHandleAllocation ha;
3526 ASSERT(args.length() == 2);
3527
3528 CONVERT_DOUBLE_CHECKED(x, args[0]);
3529 CONVERT_DOUBLE_CHECKED(y, args[1]);
3530 return Heap::NewNumberFromDouble(x / y);
3531}
3532
3533
3534static Object* Runtime_NumberMod(Arguments args) {
3535 NoHandleAllocation ha;
3536 ASSERT(args.length() == 2);
3537
3538 CONVERT_DOUBLE_CHECKED(x, args[0]);
3539 CONVERT_DOUBLE_CHECKED(y, args[1]);
3540
3541#ifdef WIN32
3542 // Workaround MS fmod bugs. ECMA-262 says:
3543 // dividend is finite and divisor is an infinity => result equals dividend
3544 // dividend is a zero and divisor is nonzero finite => result equals dividend
3545 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3546 !(x == 0 && (y != 0 && isfinite(y))))
3547#endif
3548 x = fmod(x, y);
3549 // NewNumberFromDouble may return a Smi instead of a Number object
3550 return Heap::NewNumberFromDouble(x);
3551}
3552
3553
3554static Object* Runtime_StringAdd(Arguments args) {
3555 NoHandleAllocation ha;
3556 ASSERT(args.length() == 2);
3557
3558 CONVERT_CHECKED(String, str1, args[0]);
3559 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003560 int len1 = str1->length();
3561 int len2 = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003562 if (len1 == 0) return str2;
3563 if (len2 == 0) return str1;
3564 int length_sum = len1 + len2;
3565 // Make sure that an out of memory exception is thrown if the length
3566 // of the new cons string is too large to fit in a Smi.
3567 if (length_sum > Smi::kMaxValue || length_sum < 0) {
3568 Top::context()->mark_out_of_memory();
3569 return Failure::OutOfMemoryException();
3570 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003571 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003572}
3573
3574
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003575template<typename sinkchar>
3576static inline void StringBuilderConcatHelper(String* special,
3577 sinkchar* sink,
3578 FixedArray* fixed_array,
3579 int array_length) {
3580 int position = 0;
3581 for (int i = 0; i < array_length; i++) {
3582 Object* element = fixed_array->get(i);
3583 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003584 int encoded_slice = Smi::cast(element)->value();
3585 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3586 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003587 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003588 sink + position,
3589 pos,
3590 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003591 position += len;
3592 } else {
3593 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003594 int element_length = string->length();
3595 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003596 position += element_length;
3597 }
3598 }
3599}
3600
3601
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003602static Object* Runtime_StringBuilderConcat(Arguments args) {
3603 NoHandleAllocation ha;
3604 ASSERT(args.length() == 2);
3605 CONVERT_CHECKED(JSArray, array, args[0]);
3606 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003607 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003608 Object* smi_array_length = array->length();
3609 if (!smi_array_length->IsSmi()) {
3610 Top::context()->mark_out_of_memory();
3611 return Failure::OutOfMemoryException();
3612 }
3613 int array_length = Smi::cast(smi_array_length)->value();
3614 if (!array->HasFastElements()) {
3615 return Top::Throw(Heap::illegal_argument_symbol());
3616 }
3617 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003618 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003619 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003620 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003621
3622 if (array_length == 0) {
3623 return Heap::empty_string();
3624 } else if (array_length == 1) {
3625 Object* first = fixed_array->get(0);
3626 if (first->IsString()) return first;
3627 }
3628
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003629 bool ascii = StringShape(special).IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003630 int position = 0;
3631 for (int i = 0; i < array_length; i++) {
3632 Object* elt = fixed_array->get(i);
3633 if (elt->IsSmi()) {
3634 int len = Smi::cast(elt)->value();
3635 int pos = len >> 11;
3636 len &= 0x7ff;
3637 if (pos + len > special_length) {
3638 return Top::Throw(Heap::illegal_argument_symbol());
3639 }
3640 position += len;
3641 } else if (elt->IsString()) {
3642 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003643 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003644 if (!Smi::IsValid(element_length + position)) {
3645 Top::context()->mark_out_of_memory();
3646 return Failure::OutOfMemoryException();
3647 }
3648 position += element_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003649 if (ascii && !StringShape(element).IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003650 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003651 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003652 } else {
3653 return Top::Throw(Heap::illegal_argument_symbol());
3654 }
3655 }
3656
3657 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003658 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003659
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003660 if (ascii) {
3661 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003662 if (object->IsFailure()) return object;
3663 SeqAsciiString* answer = SeqAsciiString::cast(object);
3664 StringBuilderConcatHelper(special,
3665 answer->GetChars(),
3666 fixed_array,
3667 array_length);
3668 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003669 } else {
3670 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003671 if (object->IsFailure()) return object;
3672 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3673 StringBuilderConcatHelper(special,
3674 answer->GetChars(),
3675 fixed_array,
3676 array_length);
3677 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003678 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003679}
3680
3681
3682static Object* Runtime_NumberOr(Arguments args) {
3683 NoHandleAllocation ha;
3684 ASSERT(args.length() == 2);
3685
3686 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3687 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3688 return Heap::NumberFromInt32(x | y);
3689}
3690
3691
3692static Object* Runtime_NumberAnd(Arguments args) {
3693 NoHandleAllocation ha;
3694 ASSERT(args.length() == 2);
3695
3696 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3697 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3698 return Heap::NumberFromInt32(x & y);
3699}
3700
3701
3702static Object* Runtime_NumberXor(Arguments args) {
3703 NoHandleAllocation ha;
3704 ASSERT(args.length() == 2);
3705
3706 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3707 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3708 return Heap::NumberFromInt32(x ^ y);
3709}
3710
3711
3712static Object* Runtime_NumberNot(Arguments args) {
3713 NoHandleAllocation ha;
3714 ASSERT(args.length() == 1);
3715
3716 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3717 return Heap::NumberFromInt32(~x);
3718}
3719
3720
3721static Object* Runtime_NumberShl(Arguments args) {
3722 NoHandleAllocation ha;
3723 ASSERT(args.length() == 2);
3724
3725 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3726 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3727 return Heap::NumberFromInt32(x << (y & 0x1f));
3728}
3729
3730
3731static Object* Runtime_NumberShr(Arguments args) {
3732 NoHandleAllocation ha;
3733 ASSERT(args.length() == 2);
3734
3735 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3736 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3737 return Heap::NumberFromUint32(x >> (y & 0x1f));
3738}
3739
3740
3741static Object* Runtime_NumberSar(Arguments args) {
3742 NoHandleAllocation ha;
3743 ASSERT(args.length() == 2);
3744
3745 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3746 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3747 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3748}
3749
3750
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003751static Object* Runtime_NumberEquals(Arguments args) {
3752 NoHandleAllocation ha;
3753 ASSERT(args.length() == 2);
3754
3755 CONVERT_DOUBLE_CHECKED(x, args[0]);
3756 CONVERT_DOUBLE_CHECKED(y, args[1]);
3757 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3758 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3759 if (x == y) return Smi::FromInt(EQUAL);
3760 Object* result;
3761 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3762 result = Smi::FromInt(EQUAL);
3763 } else {
3764 result = Smi::FromInt(NOT_EQUAL);
3765 }
3766 return result;
3767}
3768
3769
3770static Object* Runtime_StringEquals(Arguments args) {
3771 NoHandleAllocation ha;
3772 ASSERT(args.length() == 2);
3773
3774 CONVERT_CHECKED(String, x, args[0]);
3775 CONVERT_CHECKED(String, y, args[1]);
3776
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003777 bool not_equal = !x->Equals(y);
3778 // This is slightly convoluted because the value that signifies
3779 // equality is 0 and inequality is 1 so we have to negate the result
3780 // from String::Equals.
3781 ASSERT(not_equal == 0 || not_equal == 1);
3782 STATIC_CHECK(EQUAL == 0);
3783 STATIC_CHECK(NOT_EQUAL == 1);
3784 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003785}
3786
3787
3788static Object* Runtime_NumberCompare(Arguments args) {
3789 NoHandleAllocation ha;
3790 ASSERT(args.length() == 3);
3791
3792 CONVERT_DOUBLE_CHECKED(x, args[0]);
3793 CONVERT_DOUBLE_CHECKED(y, args[1]);
3794 if (isnan(x) || isnan(y)) return args[2];
3795 if (x == y) return Smi::FromInt(EQUAL);
3796 if (isless(x, y)) return Smi::FromInt(LESS);
3797 return Smi::FromInt(GREATER);
3798}
3799
3800
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003801// Compare two Smis as if they were converted to strings and then
3802// compared lexicographically.
3803static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3804 NoHandleAllocation ha;
3805 ASSERT(args.length() == 2);
3806
3807 // Arrays for the individual characters of the two Smis. Smis are
3808 // 31 bit integers and 10 decimal digits are therefore enough.
3809 static int x_elms[10];
3810 static int y_elms[10];
3811
3812 // Extract the integer values from the Smis.
3813 CONVERT_CHECKED(Smi, x, args[0]);
3814 CONVERT_CHECKED(Smi, y, args[1]);
3815 int x_value = x->value();
3816 int y_value = y->value();
3817
3818 // If the integers are equal so are the string representations.
3819 if (x_value == y_value) return Smi::FromInt(EQUAL);
3820
3821 // If one of the integers are zero the normal integer order is the
3822 // same as the lexicographic order of the string representations.
3823 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3824
ager@chromium.org32912102009-01-16 10:38:43 +00003825 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003826 // smallest because the char code of '-' is less than the char code
3827 // of any digit. Otherwise, we make both values positive.
3828 if (x_value < 0 || y_value < 0) {
3829 if (y_value >= 0) return Smi::FromInt(LESS);
3830 if (x_value >= 0) return Smi::FromInt(GREATER);
3831 x_value = -x_value;
3832 y_value = -y_value;
3833 }
3834
3835 // Convert the integers to arrays of their decimal digits.
3836 int x_index = 0;
3837 int y_index = 0;
3838 while (x_value > 0) {
3839 x_elms[x_index++] = x_value % 10;
3840 x_value /= 10;
3841 }
3842 while (y_value > 0) {
3843 y_elms[y_index++] = y_value % 10;
3844 y_value /= 10;
3845 }
3846
3847 // Loop through the arrays of decimal digits finding the first place
3848 // where they differ.
3849 while (--x_index >= 0 && --y_index >= 0) {
3850 int diff = x_elms[x_index] - y_elms[y_index];
3851 if (diff != 0) return Smi::FromInt(diff);
3852 }
3853
3854 // If one array is a suffix of the other array, the longest array is
3855 // the representation of the largest of the Smis in the
3856 // lexicographic ordering.
3857 return Smi::FromInt(x_index - y_index);
3858}
3859
3860
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003861static Object* Runtime_StringCompare(Arguments args) {
3862 NoHandleAllocation ha;
3863 ASSERT(args.length() == 2);
3864
3865 CONVERT_CHECKED(String, x, args[0]);
3866 CONVERT_CHECKED(String, y, args[1]);
3867
3868 // A few fast case tests before we flatten.
3869 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003870 if (y->length() == 0) {
3871 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003872 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003873 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003874 return Smi::FromInt(LESS);
3875 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003876
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003877 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003878 if (d < 0) return Smi::FromInt(LESS);
3879 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003880
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003881 x->TryFlattenIfNotFlat();
3882 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003883
3884 static StringInputBuffer bufx;
3885 static StringInputBuffer bufy;
3886 bufx.Reset(x);
3887 bufy.Reset(y);
3888 while (bufx.has_more() && bufy.has_more()) {
3889 int d = bufx.GetNext() - bufy.GetNext();
3890 if (d < 0) return Smi::FromInt(LESS);
3891 else if (d > 0) return Smi::FromInt(GREATER);
3892 }
3893
3894 // x is (non-trivial) prefix of y:
3895 if (bufy.has_more()) return Smi::FromInt(LESS);
3896 // y is prefix of x:
3897 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
3898}
3899
3900
3901static Object* Runtime_Math_abs(Arguments args) {
3902 NoHandleAllocation ha;
3903 ASSERT(args.length() == 1);
3904
3905 CONVERT_DOUBLE_CHECKED(x, args[0]);
3906 return Heap::AllocateHeapNumber(fabs(x));
3907}
3908
3909
3910static Object* Runtime_Math_acos(Arguments args) {
3911 NoHandleAllocation ha;
3912 ASSERT(args.length() == 1);
3913
3914 CONVERT_DOUBLE_CHECKED(x, args[0]);
3915 return Heap::AllocateHeapNumber(acos(x));
3916}
3917
3918
3919static Object* Runtime_Math_asin(Arguments args) {
3920 NoHandleAllocation ha;
3921 ASSERT(args.length() == 1);
3922
3923 CONVERT_DOUBLE_CHECKED(x, args[0]);
3924 return Heap::AllocateHeapNumber(asin(x));
3925}
3926
3927
3928static Object* Runtime_Math_atan(Arguments args) {
3929 NoHandleAllocation ha;
3930 ASSERT(args.length() == 1);
3931
3932 CONVERT_DOUBLE_CHECKED(x, args[0]);
3933 return Heap::AllocateHeapNumber(atan(x));
3934}
3935
3936
3937static Object* Runtime_Math_atan2(Arguments args) {
3938 NoHandleAllocation ha;
3939 ASSERT(args.length() == 2);
3940
3941 CONVERT_DOUBLE_CHECKED(x, args[0]);
3942 CONVERT_DOUBLE_CHECKED(y, args[1]);
3943 double result;
3944 if (isinf(x) && isinf(y)) {
3945 // Make sure that the result in case of two infinite arguments
3946 // is a multiple of Pi / 4. The sign of the result is determined
3947 // by the first argument (x) and the sign of the second argument
3948 // determines the multiplier: one or three.
3949 static double kPiDividedBy4 = 0.78539816339744830962;
3950 int multiplier = (x < 0) ? -1 : 1;
3951 if (y < 0) multiplier *= 3;
3952 result = multiplier * kPiDividedBy4;
3953 } else {
3954 result = atan2(x, y);
3955 }
3956 return Heap::AllocateHeapNumber(result);
3957}
3958
3959
3960static Object* Runtime_Math_ceil(Arguments args) {
3961 NoHandleAllocation ha;
3962 ASSERT(args.length() == 1);
3963
3964 CONVERT_DOUBLE_CHECKED(x, args[0]);
3965 return Heap::NumberFromDouble(ceiling(x));
3966}
3967
3968
3969static Object* Runtime_Math_cos(Arguments args) {
3970 NoHandleAllocation ha;
3971 ASSERT(args.length() == 1);
3972
3973 CONVERT_DOUBLE_CHECKED(x, args[0]);
3974 return Heap::AllocateHeapNumber(cos(x));
3975}
3976
3977
3978static Object* Runtime_Math_exp(Arguments args) {
3979 NoHandleAllocation ha;
3980 ASSERT(args.length() == 1);
3981
3982 CONVERT_DOUBLE_CHECKED(x, args[0]);
3983 return Heap::AllocateHeapNumber(exp(x));
3984}
3985
3986
3987static Object* Runtime_Math_floor(Arguments args) {
3988 NoHandleAllocation ha;
3989 ASSERT(args.length() == 1);
3990
3991 CONVERT_DOUBLE_CHECKED(x, args[0]);
3992 return Heap::NumberFromDouble(floor(x));
3993}
3994
3995
3996static Object* Runtime_Math_log(Arguments args) {
3997 NoHandleAllocation ha;
3998 ASSERT(args.length() == 1);
3999
4000 CONVERT_DOUBLE_CHECKED(x, args[0]);
4001 return Heap::AllocateHeapNumber(log(x));
4002}
4003
4004
4005static Object* Runtime_Math_pow(Arguments args) {
4006 NoHandleAllocation ha;
4007 ASSERT(args.length() == 2);
4008
4009 CONVERT_DOUBLE_CHECKED(x, args[0]);
4010 CONVERT_DOUBLE_CHECKED(y, args[1]);
4011 if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4012 return Heap::nan_value();
4013 } else if (y == 0) {
4014 return Smi::FromInt(1);
4015 } else {
4016 return Heap::AllocateHeapNumber(pow(x, y));
4017 }
4018}
4019
4020// Returns a number value with positive sign, greater than or equal to
4021// 0 but less than 1, chosen randomly.
mads.s.ager31e71382008-08-13 09:32:07 +00004022static Object* Runtime_Math_random(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004023 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004024 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004025
4026 // To get much better precision, we combine the results of two
4027 // invocations of random(). The result is computed by normalizing a
4028 // double in the range [0, RAND_MAX + 1) obtained by adding the
4029 // high-order bits in the range [0, RAND_MAX] with the low-order
4030 // bits in the range [0, 1).
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004031 double lo = static_cast<double>(random()) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004032 double hi = static_cast<double>(random());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004033 double result = (hi + lo) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004034 ASSERT(result >= 0 && result < 1);
4035 return Heap::AllocateHeapNumber(result);
4036}
4037
4038
4039static Object* Runtime_Math_round(Arguments args) {
4040 NoHandleAllocation ha;
4041 ASSERT(args.length() == 1);
4042
4043 CONVERT_DOUBLE_CHECKED(x, args[0]);
4044 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4045 return Heap::NumberFromDouble(floor(x + 0.5));
4046}
4047
4048
4049static Object* Runtime_Math_sin(Arguments args) {
4050 NoHandleAllocation ha;
4051 ASSERT(args.length() == 1);
4052
4053 CONVERT_DOUBLE_CHECKED(x, args[0]);
4054 return Heap::AllocateHeapNumber(sin(x));
4055}
4056
4057
4058static Object* Runtime_Math_sqrt(Arguments args) {
4059 NoHandleAllocation ha;
4060 ASSERT(args.length() == 1);
4061
4062 CONVERT_DOUBLE_CHECKED(x, args[0]);
4063 return Heap::AllocateHeapNumber(sqrt(x));
4064}
4065
4066
4067static Object* Runtime_Math_tan(Arguments args) {
4068 NoHandleAllocation ha;
4069 ASSERT(args.length() == 1);
4070
4071 CONVERT_DOUBLE_CHECKED(x, args[0]);
4072 return Heap::AllocateHeapNumber(tan(x));
4073}
4074
4075
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004076// The NewArguments function is only used when constructing the
4077// arguments array when calling non-functions from JavaScript in
4078// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004079static Object* Runtime_NewArguments(Arguments args) {
4080 NoHandleAllocation ha;
4081 ASSERT(args.length() == 1);
4082
4083 // ECMA-262, 3rd., 10.1.8, p.39
4084 CONVERT_CHECKED(JSFunction, callee, args[0]);
4085
4086 // Compute the frame holding the arguments.
4087 JavaScriptFrameIterator it;
4088 it.AdvanceToArgumentsFrame();
4089 JavaScriptFrame* frame = it.frame();
4090
4091 const int length = frame->GetProvidedParametersCount();
4092 Object* result = Heap::AllocateArgumentsObject(callee, length);
4093 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004094 if (length > 0) {
4095 Object* obj = Heap::AllocateFixedArray(length);
4096 if (obj->IsFailure()) return obj;
4097 FixedArray* array = FixedArray::cast(obj);
4098 ASSERT(array->length() == length);
4099 WriteBarrierMode mode = array->GetWriteBarrierMode();
4100 for (int i = 0; i < length; i++) {
4101 array->set(i, frame->GetParameter(i), mode);
4102 }
4103 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004104 }
4105 return result;
4106}
4107
4108
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004109static Object* Runtime_NewArgumentsFast(Arguments args) {
4110 NoHandleAllocation ha;
4111 ASSERT(args.length() == 3);
4112
4113 JSFunction* callee = JSFunction::cast(args[0]);
4114 Object** parameters = reinterpret_cast<Object**>(args[1]);
4115 const int length = Smi::cast(args[2])->value();
4116
4117 Object* result = Heap::AllocateArgumentsObject(callee, length);
4118 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004119 ASSERT(Heap::InNewSpace(result));
4120
4121 // Allocate the elements if needed.
4122 if (length > 0) {
4123 // Allocate the fixed array.
4124 Object* obj = Heap::AllocateRawFixedArray(length);
4125 if (obj->IsFailure()) return obj;
4126 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4127 FixedArray* array = FixedArray::cast(obj);
4128 array->set_length(length);
4129 WriteBarrierMode mode = array->GetWriteBarrierMode();
4130 for (int i = 0; i < length; i++) {
4131 array->set(i, *--parameters, mode);
4132 }
4133 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4134 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004135 }
4136 return result;
4137}
4138
4139
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004140static Object* Runtime_NewClosure(Arguments args) {
4141 HandleScope scope;
4142 ASSERT(args.length() == 2);
4143 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4144 CONVERT_ARG_CHECKED(Context, context, 1);
4145
4146 Handle<JSFunction> result =
4147 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4148 return *result;
4149}
4150
4151
4152static Object* Runtime_NewObject(Arguments args) {
4153 NoHandleAllocation ha;
4154 ASSERT(args.length() == 1);
4155
4156 Object* constructor = args[0];
4157 if (constructor->IsJSFunction()) {
4158 JSFunction* function = JSFunction::cast(constructor);
4159
ager@chromium.org32912102009-01-16 10:38:43 +00004160 // Handle stepping into constructors if step into is active.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004161 if (Debug::StepInActive()) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004162 HandleScope scope;
4163 Debug::HandleStepIn(Handle<JSFunction>(function), 0, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004164 }
4165
4166 if (function->has_initial_map() &&
4167 function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
4168 // The 'Function' function ignores the receiver object when
4169 // called using 'new' and creates a new JSFunction object that
4170 // is returned. The receiver object is only used for error
4171 // reporting if an error occurs when constructing the new
4172 // JSFunction. AllocateJSObject should not be used to allocate
4173 // JSFunctions since it does not properly initialize the shared
4174 // part of the function. Since the receiver is ignored anyway,
4175 // we use the global object as the receiver instead of a new
4176 // JSFunction object. This way, errors are reported the same
4177 // way whether or not 'Function' is called using 'new'.
4178 return Top::context()->global();
4179 }
4180 return Heap::AllocateJSObject(function);
4181 }
4182
4183 HandleScope scope;
4184 Handle<Object> cons(constructor);
4185 // The constructor is not a function; throw a type error.
4186 Handle<Object> type_error =
4187 Factory::NewTypeError("not_constructor", HandleVector(&cons, 1));
4188 return Top::Throw(*type_error);
4189}
4190
4191
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004192static Object* Runtime_LazyCompile(Arguments args) {
4193 HandleScope scope;
4194 ASSERT(args.length() == 1);
4195
4196 Handle<JSFunction> function = args.at<JSFunction>(0);
4197#ifdef DEBUG
4198 if (FLAG_trace_lazy) {
4199 PrintF("[lazy: ");
4200 function->shared()->name()->Print();
4201 PrintF("]\n");
4202 }
4203#endif
4204
4205 // Compile the target function.
4206 ASSERT(!function->is_compiled());
4207 if (!CompileLazy(function, KEEP_EXCEPTION)) {
4208 return Failure::Exception();
4209 }
4210
4211 return function->code();
4212}
4213
4214
4215static Object* Runtime_GetCalledFunction(Arguments args) {
4216 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004217 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004218 StackFrameIterator it;
4219 // Get past the JS-to-C exit frame.
4220 ASSERT(it.frame()->is_exit());
4221 it.Advance();
4222 // Get past the CALL_NON_FUNCTION activation frame.
4223 ASSERT(it.frame()->is_java_script());
4224 it.Advance();
4225 // Argument adaptor frames do not copy the function; we have to skip
4226 // past them to get to the real calling frame.
4227 if (it.frame()->is_arguments_adaptor()) it.Advance();
4228 // Get the function from the top of the expression stack of the
4229 // calling frame.
4230 StandardFrame* frame = StandardFrame::cast(it.frame());
4231 int index = frame->ComputeExpressionsCount() - 1;
4232 Object* result = frame->GetExpression(index);
4233 return result;
4234}
4235
4236
4237static Object* Runtime_GetFunctionDelegate(Arguments args) {
4238 HandleScope scope;
4239 ASSERT(args.length() == 1);
4240 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4241 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4242}
4243
4244
4245static Object* Runtime_NewContext(Arguments args) {
4246 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004247 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004248
kasper.lund7276f142008-07-30 08:49:36 +00004249 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004250 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4251 Object* result = Heap::AllocateFunctionContext(length, function);
4252 if (result->IsFailure()) return result;
4253
4254 Top::set_context(Context::cast(result));
4255
kasper.lund7276f142008-07-30 08:49:36 +00004256 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004257}
4258
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004259static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004260 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004261 Object* js_object = object;
4262 if (!js_object->IsJSObject()) {
4263 js_object = js_object->ToObject();
4264 if (js_object->IsFailure()) {
4265 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004266 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004267 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004268 Handle<Object> result =
4269 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4270 return Top::Throw(*result);
4271 }
4272 }
4273
4274 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004275 Heap::AllocateWithContext(Top::context(),
4276 JSObject::cast(js_object),
4277 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004278 if (result->IsFailure()) return result;
4279
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004280 Context* context = Context::cast(result);
4281 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004282
kasper.lund7276f142008-07-30 08:49:36 +00004283 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004284}
4285
4286
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004287static Object* Runtime_PushContext(Arguments args) {
4288 NoHandleAllocation ha;
4289 ASSERT(args.length() == 1);
4290 return PushContextHelper(args[0], false);
4291}
4292
4293
4294static Object* Runtime_PushCatchContext(Arguments args) {
4295 NoHandleAllocation ha;
4296 ASSERT(args.length() == 1);
4297 return PushContextHelper(args[0], true);
4298}
4299
4300
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004301static Object* Runtime_LookupContext(Arguments args) {
4302 HandleScope scope;
4303 ASSERT(args.length() == 2);
4304
4305 CONVERT_ARG_CHECKED(Context, context, 0);
4306 CONVERT_ARG_CHECKED(String, name, 1);
4307
4308 int index;
4309 PropertyAttributes attributes;
4310 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004311 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004312 context->Lookup(name, flags, &index, &attributes);
4313
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004314 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004315 ASSERT(holder->IsJSObject());
4316 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004317 }
4318
4319 // No intermediate context found. Use global object by default.
4320 return Top::context()->global();
4321}
4322
4323
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004324// A mechanism to return pairs of Object*'s. This is somewhat
4325// compiler-dependent as it assumes that a 64-bit value (a long long)
4326// is returned via two registers (edx:eax on ia32). Both the ia32 and
4327// arm platform support this; it is mostly an issue of "coaxing" the
4328// compiler to do the right thing.
4329//
4330// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004331typedef uint64_t ObjectPair;
4332static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004333 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004334 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004335}
4336
4337
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004338static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004339 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4340 USE(attributes);
4341 return x->IsTheHole() ? Heap::undefined_value() : x;
4342}
4343
4344
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004345static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4346 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004347 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004348 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004349 JSFunction* context_extension_function =
4350 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004351 // If the holder isn't a context extension object, we just return it
4352 // as the receiver. This allows arguments objects to be used as
4353 // receivers, but only if they are put in the context scope chain
4354 // explicitly via a with-statement.
4355 Object* constructor = holder->map()->constructor();
4356 if (constructor != context_extension_function) return holder;
4357 // Fall back to using the global object as the receiver if the
4358 // property turns out to be a local variable allocated in a context
4359 // extension object - introduced via eval.
4360 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004361}
4362
4363
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004364static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004365 HandleScope scope;
4366 ASSERT(args.length() == 2);
4367
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004368 if (!args[0]->IsContext() || !args[1]->IsString()) {
4369 return MakePair(IllegalOperation(), NULL);
4370 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004371 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004372 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004373
4374 int index;
4375 PropertyAttributes attributes;
4376 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004377 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004378 context->Lookup(name, flags, &index, &attributes);
4379
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004380 // If the index is non-negative, the slot has been found in a local
4381 // variable or a parameter. Read it from the context object or the
4382 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004383 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004384 // If the "property" we were looking for is a local variable or an
4385 // argument in a context, the receiver is the global object; see
4386 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4387 JSObject* receiver = Top::context()->global()->global_receiver();
4388 Object* value = (holder->IsContext())
4389 ? Context::cast(*holder)->get(index)
4390 : JSObject::cast(*holder)->GetElement(index);
4391 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004392 }
4393
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004394 // If the holder is found, we read the property from it.
4395 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004396 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004397 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004398 JSObject* receiver;
4399 if (object->IsGlobalObject()) {
4400 receiver = GlobalObject::cast(object)->global_receiver();
4401 } else if (context->is_exception_holder(*holder)) {
4402 receiver = Top::context()->global()->global_receiver();
4403 } else {
4404 receiver = ComputeReceiverForNonGlobal(object);
4405 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004406 // No need to unhole the value here. This is taken care of by the
4407 // GetProperty function.
4408 Object* value = object->GetProperty(*name);
4409 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004410 }
4411
4412 if (throw_error) {
4413 // The property doesn't exist - throw exception.
4414 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004415 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004416 return MakePair(Top::Throw(*reference_error), NULL);
4417 } else {
4418 // The property doesn't exist - return undefined
4419 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4420 }
4421}
4422
4423
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004424static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004425 return LoadContextSlotHelper(args, true);
4426}
4427
4428
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004429static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004430 return LoadContextSlotHelper(args, false);
4431}
4432
4433
4434static Object* Runtime_StoreContextSlot(Arguments args) {
4435 HandleScope scope;
4436 ASSERT(args.length() == 3);
4437
4438 Handle<Object> value(args[0]);
4439 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004440 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004441
4442 int index;
4443 PropertyAttributes attributes;
4444 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004445 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004446 context->Lookup(name, flags, &index, &attributes);
4447
4448 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004449 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004450 // Ignore if read_only variable.
4451 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004452 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004453 }
4454 } else {
4455 ASSERT((attributes & READ_ONLY) == 0);
4456 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004457 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004458 USE(result);
4459 ASSERT(!result->IsFailure());
4460 }
4461 return *value;
4462 }
4463
4464 // Slow case: The property is not in a FixedArray context.
4465 // It is either in an JSObject extension context or it was not found.
4466 Handle<JSObject> context_ext;
4467
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004468 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004469 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004470 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004471 } else {
4472 // The property was not found. It needs to be stored in the global context.
4473 ASSERT(attributes == ABSENT);
4474 attributes = NONE;
4475 context_ext = Handle<JSObject>(Top::context()->global());
4476 }
4477
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004478 // Set the property, but ignore if read_only variable on the context
4479 // extension object itself.
4480 if ((attributes & READ_ONLY) == 0 ||
4481 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004482 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4483 if (set.is_null()) {
4484 // Failure::Exception is converted to a null handle in the
4485 // handle-based methods such as SetProperty. We therefore need
4486 // to convert null handles back to exceptions.
4487 ASSERT(Top::has_pending_exception());
4488 return Failure::Exception();
4489 }
4490 }
4491 return *value;
4492}
4493
4494
4495static Object* Runtime_Throw(Arguments args) {
4496 HandleScope scope;
4497 ASSERT(args.length() == 1);
4498
4499 return Top::Throw(args[0]);
4500}
4501
4502
4503static Object* Runtime_ReThrow(Arguments args) {
4504 HandleScope scope;
4505 ASSERT(args.length() == 1);
4506
4507 return Top::ReThrow(args[0]);
4508}
4509
4510
4511static Object* Runtime_ThrowReferenceError(Arguments args) {
4512 HandleScope scope;
4513 ASSERT(args.length() == 1);
4514
4515 Handle<Object> name(args[0]);
4516 Handle<Object> reference_error =
4517 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4518 return Top::Throw(*reference_error);
4519}
4520
4521
4522static Object* Runtime_StackOverflow(Arguments args) {
4523 NoHandleAllocation na;
4524 return Top::StackOverflow();
4525}
4526
4527
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004528static Object* Runtime_DebugBreak(Arguments args) {
4529 ASSERT(args.length() == 0);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004530 return Execution::DebugBreakHelper();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004531}
4532
4533
4534static Object* Runtime_StackGuard(Arguments args) {
4535 ASSERT(args.length() == 1);
4536
4537 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004538 if (StackGuard::IsStackOverflow()) {
4539 return Runtime_StackOverflow(args);
4540 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004541
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004542 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004543}
4544
4545
4546// NOTE: These PrintXXX functions are defined for all builds (not just
4547// DEBUG builds) because we may want to be able to trace function
4548// calls in all modes.
4549static void PrintString(String* str) {
4550 // not uncommon to have empty strings
4551 if (str->length() > 0) {
4552 SmartPointer<char> s =
4553 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4554 PrintF("%s", *s);
4555 }
4556}
4557
4558
4559static void PrintObject(Object* obj) {
4560 if (obj->IsSmi()) {
4561 PrintF("%d", Smi::cast(obj)->value());
4562 } else if (obj->IsString() || obj->IsSymbol()) {
4563 PrintString(String::cast(obj));
4564 } else if (obj->IsNumber()) {
4565 PrintF("%g", obj->Number());
4566 } else if (obj->IsFailure()) {
4567 PrintF("<failure>");
4568 } else if (obj->IsUndefined()) {
4569 PrintF("<undefined>");
4570 } else if (obj->IsNull()) {
4571 PrintF("<null>");
4572 } else if (obj->IsTrue()) {
4573 PrintF("<true>");
4574 } else if (obj->IsFalse()) {
4575 PrintF("<false>");
4576 } else {
4577 PrintF("%p", obj);
4578 }
4579}
4580
4581
4582static int StackSize() {
4583 int n = 0;
4584 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4585 return n;
4586}
4587
4588
4589static void PrintTransition(Object* result) {
4590 // indentation
4591 { const int nmax = 80;
4592 int n = StackSize();
4593 if (n <= nmax)
4594 PrintF("%4d:%*s", n, n, "");
4595 else
4596 PrintF("%4d:%*s", n, nmax, "...");
4597 }
4598
4599 if (result == NULL) {
4600 // constructor calls
4601 JavaScriptFrameIterator it;
4602 JavaScriptFrame* frame = it.frame();
4603 if (frame->IsConstructor()) PrintF("new ");
4604 // function name
4605 Object* fun = frame->function();
4606 if (fun->IsJSFunction()) {
4607 PrintObject(JSFunction::cast(fun)->shared()->name());
4608 } else {
4609 PrintObject(fun);
4610 }
4611 // function arguments
4612 // (we are intentionally only printing the actually
4613 // supplied parameters, not all parameters required)
4614 PrintF("(this=");
4615 PrintObject(frame->receiver());
4616 const int length = frame->GetProvidedParametersCount();
4617 for (int i = 0; i < length; i++) {
4618 PrintF(", ");
4619 PrintObject(frame->GetParameter(i));
4620 }
4621 PrintF(") {\n");
4622
4623 } else {
4624 // function result
4625 PrintF("} -> ");
4626 PrintObject(result);
4627 PrintF("\n");
4628 }
4629}
4630
4631
4632static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004633 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004634 NoHandleAllocation ha;
4635 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004636 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004637}
4638
4639
4640static Object* Runtime_TraceExit(Arguments args) {
4641 NoHandleAllocation ha;
4642 PrintTransition(args[0]);
4643 return args[0]; // return TOS
4644}
4645
4646
4647static Object* Runtime_DebugPrint(Arguments args) {
4648 NoHandleAllocation ha;
4649 ASSERT(args.length() == 1);
4650
4651#ifdef DEBUG
4652 if (args[0]->IsString()) {
4653 // If we have a string, assume it's a code "marker"
4654 // and print some interesting cpu debugging info.
4655 JavaScriptFrameIterator it;
4656 JavaScriptFrame* frame = it.frame();
4657 PrintF("fp = %p, sp = %p, pp = %p: ",
4658 frame->fp(), frame->sp(), frame->pp());
4659 } else {
4660 PrintF("DebugPrint: ");
4661 }
4662 args[0]->Print();
4663#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004664 // ShortPrint is available in release mode. Print is not.
4665 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004666#endif
4667 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004668 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004669
4670 return args[0]; // return TOS
4671}
4672
4673
4674static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004675 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004676 NoHandleAllocation ha;
4677 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004678 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004679}
4680
4681
mads.s.ager31e71382008-08-13 09:32:07 +00004682static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004683 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004684 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004685
4686 // According to ECMA-262, section 15.9.1, page 117, the precision of
4687 // the number in a Date object representing a particular instant in
4688 // time is milliseconds. Therefore, we floor the result of getting
4689 // the OS time.
4690 double millis = floor(OS::TimeCurrentMillis());
4691 return Heap::NumberFromDouble(millis);
4692}
4693
4694
4695static Object* Runtime_DateParseString(Arguments args) {
4696 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004697 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004698
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004699 CONVERT_ARG_CHECKED(String, str, 0);
4700 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004701
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004702 CONVERT_ARG_CHECKED(JSArray, output, 1);
4703 RUNTIME_ASSERT(output->HasFastElements());
4704
4705 AssertNoAllocation no_allocation;
4706
4707 FixedArray* output_array = output->elements();
4708 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4709 bool result;
4710 if (StringShape(*str).IsAsciiRepresentation()) {
4711 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004712 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004713 ASSERT(StringShape(*str).IsTwoByteRepresentation());
4714 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4715 }
4716
4717 if (result) {
4718 return *output;
4719 } else {
4720 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004721 }
4722}
4723
4724
4725static Object* Runtime_DateLocalTimezone(Arguments args) {
4726 NoHandleAllocation ha;
4727 ASSERT(args.length() == 1);
4728
4729 CONVERT_DOUBLE_CHECKED(x, args[0]);
4730 char* zone = OS::LocalTimezone(x);
4731 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4732}
4733
4734
4735static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4736 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004737 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004738
4739 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4740}
4741
4742
4743static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4744 NoHandleAllocation ha;
4745 ASSERT(args.length() == 1);
4746
4747 CONVERT_DOUBLE_CHECKED(x, args[0]);
4748 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4749}
4750
4751
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004752static Object* Runtime_NumberIsFinite(Arguments args) {
4753 NoHandleAllocation ha;
4754 ASSERT(args.length() == 1);
4755
4756 CONVERT_DOUBLE_CHECKED(value, args[0]);
4757 Object* result;
4758 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4759 result = Heap::false_value();
4760 } else {
4761 result = Heap::true_value();
4762 }
4763 return result;
4764}
4765
4766
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004767static Object* Runtime_GlobalReceiver(Arguments args) {
4768 ASSERT(args.length() == 1);
4769 Object* global = args[0];
4770 if (!global->IsJSGlobalObject()) return Heap::null_value();
4771 return JSGlobalObject::cast(global)->global_receiver();
4772}
4773
4774
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004775static Object* Runtime_CompileString(Arguments args) {
4776 HandleScope scope;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004777 ASSERT(args.length() == 2);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004778 CONVERT_ARG_CHECKED(String, source, 0);
ager@chromium.org236ad962008-09-25 09:45:57 +00004779 CONVERT_ARG_CHECKED(Smi, line_offset, 1);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004780
ager@chromium.org381abbb2009-02-25 13:23:22 +00004781 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004782 Handle<Context> context(Top::context()->global_context());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004783 Handle<JSFunction> boilerplate =
4784 Compiler::CompileEval(source, context, line_offset->value(), true);
4785 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004786 Handle<JSFunction> fun =
4787 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4788 return *fun;
4789}
4790
4791
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004792static Handle<JSFunction> GetBuiltinFunction(String* name) {
4793 LookupResult result;
4794 Top::global_context()->builtins()->LocalLookup(name, &result);
4795 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
4796}
4797
4798
4799static Object* CompileDirectEval(Handle<String> source) {
4800 // Compute the eval context.
4801 HandleScope scope;
4802 StackFrameLocator locator;
4803 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4804 Handle<Context> context(Context::cast(frame->context()));
4805 bool is_global = context->IsGlobalContext();
4806
ager@chromium.org381abbb2009-02-25 13:23:22 +00004807 // Compile source string in the current context.
4808 Handle<JSFunction> boilerplate =
4809 Compiler::CompileEval(source, context, 0, is_global);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004810 if (boilerplate.is_null()) return Failure::Exception();
4811 Handle<JSFunction> fun =
4812 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4813 return *fun;
4814}
4815
4816
4817static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
4818 ASSERT(args.length() == 2);
4819
4820 HandleScope scope;
4821
4822 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
4823
4824 Handle<Object> receiver;
4825
4826 // Find where the 'eval' symbol is bound. It is unaliased only if
4827 // it is bound in the global context.
4828 StackFrameLocator locator;
4829 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4830 Handle<Context> context(Context::cast(frame->context()));
4831 int index;
4832 PropertyAttributes attributes;
4833 while (!context.is_null()) {
4834 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
4835 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00004836 // Stop search when eval is found or when the global context is
4837 // reached.
4838 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004839 if (context->is_function_context()) {
4840 context = Handle<Context>(Context::cast(context->closure()->context()));
4841 } else {
4842 context = Handle<Context>(context->previous());
4843 }
4844 }
4845
iposva@chromium.org245aa852009-02-10 00:49:54 +00004846 // If eval could not be resolved, it has been deleted and we need to
4847 // throw a reference error.
4848 if (attributes == ABSENT) {
4849 Handle<Object> name = Factory::eval_symbol();
4850 Handle<Object> reference_error =
4851 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4852 return Top::Throw(*reference_error);
4853 }
4854
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004855 if (context->IsGlobalContext()) {
4856 // 'eval' is bound in the global context, but it may have been overwritten.
4857 // Compare it to the builtin 'GlobalEval' function to make sure.
4858 Handle<JSFunction> global_eval =
4859 GetBuiltinFunction(Heap::global_eval_symbol());
4860 if (global_eval.is_identical_to(callee)) {
4861 // A direct eval call.
4862 if (args[1]->IsString()) {
4863 CONVERT_ARG_CHECKED(String, source, 1);
4864 // A normal eval call on a string. Compile it and return the
4865 // compiled function bound in the local context.
4866 Object* compiled_source = CompileDirectEval(source);
4867 if (compiled_source->IsFailure()) return compiled_source;
4868 receiver = Handle<Object>(frame->receiver());
4869 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
4870 } else {
4871 // An eval call that is not called on a string. Global eval
4872 // deals better with this.
4873 receiver = Handle<Object>(Top::global_context()->global());
4874 }
4875 } else {
4876 // 'eval' is overwritten. Just call the function with the given arguments.
4877 receiver = Handle<Object>(Top::global_context()->global());
4878 }
4879 } else {
4880 // 'eval' is not bound in the global context. Just call the function
4881 // with the given arguments. This is not necessarily the global eval.
4882 if (receiver->IsContext()) {
4883 context = Handle<Context>::cast(receiver);
4884 receiver = Handle<Object>(context->get(index));
4885 }
4886 }
4887
4888 Handle<FixedArray> call = Factory::NewFixedArray(2);
4889 call->set(0, *callee);
4890 call->set(1, *receiver);
4891 return *call;
4892}
4893
4894
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004895static Object* Runtime_CompileScript(Arguments args) {
4896 HandleScope scope;
4897 ASSERT(args.length() == 4);
4898
4899 CONVERT_ARG_CHECKED(String, source, 0);
4900 CONVERT_ARG_CHECKED(String, script, 1);
4901 CONVERT_CHECKED(Smi, line_attrs, args[2]);
4902 int line = line_attrs->value();
4903 CONVERT_CHECKED(Smi, col_attrs, args[3]);
4904 int col = col_attrs->value();
4905 Handle<JSFunction> boilerplate =
4906 Compiler::Compile(source, script, line, col, NULL, NULL);
4907 if (boilerplate.is_null()) return Failure::Exception();
4908 Handle<JSFunction> fun =
4909 Factory::NewFunctionFromBoilerplate(boilerplate,
4910 Handle<Context>(Top::context()));
4911 return *fun;
4912}
4913
4914
4915static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
4916 // This utility adjusts the property attributes for newly created Function
4917 // object ("new Function(...)") by changing the map.
4918 // All it does is changing the prototype property to enumerable
4919 // as specified in ECMA262, 15.3.5.2.
4920 HandleScope scope;
4921 ASSERT(args.length() == 1);
4922 CONVERT_ARG_CHECKED(JSFunction, func, 0);
4923 ASSERT(func->map()->instance_type() ==
4924 Top::function_instance_map()->instance_type());
4925 ASSERT(func->map()->instance_size() ==
4926 Top::function_instance_map()->instance_size());
4927 func->set_map(*Top::function_instance_map());
4928 return *func;
4929}
4930
4931
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004932// Push an array unto an array of arrays if it is not already in the
4933// array. Returns true if the element was pushed on the stack and
4934// false otherwise.
4935static Object* Runtime_PushIfAbsent(Arguments args) {
4936 ASSERT(args.length() == 2);
4937 CONVERT_CHECKED(JSArray, array, args[0]);
4938 CONVERT_CHECKED(JSArray, element, args[1]);
4939 RUNTIME_ASSERT(array->HasFastElements());
4940 int length = Smi::cast(array->length())->value();
4941 FixedArray* elements = FixedArray::cast(array->elements());
4942 for (int i = 0; i < length; i++) {
4943 if (elements->get(i) == element) return Heap::false_value();
4944 }
4945 Object* obj = array->SetFastElement(length, element);
4946 if (obj->IsFailure()) return obj;
4947 return Heap::true_value();
4948}
4949
4950
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004951/**
4952 * A simple visitor visits every element of Array's.
4953 * The backend storage can be a fixed array for fast elements case,
4954 * or a dictionary for sparse array. Since Dictionary is a subtype
4955 * of FixedArray, the class can be used by both fast and slow cases.
4956 * The second parameter of the constructor, fast_elements, specifies
4957 * whether the storage is a FixedArray or Dictionary.
4958 *
4959 * An index limit is used to deal with the situation that a result array
4960 * length overflows 32-bit non-negative integer.
4961 */
4962class ArrayConcatVisitor {
4963 public:
4964 ArrayConcatVisitor(Handle<FixedArray> storage,
4965 uint32_t index_limit,
4966 bool fast_elements) :
4967 storage_(storage), index_limit_(index_limit),
4968 fast_elements_(fast_elements), index_offset_(0) { }
4969
4970 void visit(uint32_t i, Handle<Object> elm) {
4971 uint32_t index = i + index_offset_;
4972 if (index >= index_limit_) return;
4973
4974 if (fast_elements_) {
4975 ASSERT(index < static_cast<uint32_t>(storage_->length()));
4976 storage_->set(index, *elm);
4977
4978 } else {
4979 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
4980 Handle<Dictionary> result =
4981 Factory::DictionaryAtNumberPut(dict, index, elm);
4982 if (!result.is_identical_to(dict))
4983 storage_ = result;
4984 }
4985 }
4986
4987 void increase_index_offset(uint32_t delta) {
4988 index_offset_ += delta;
4989 }
4990
4991 private:
4992 Handle<FixedArray> storage_;
4993 uint32_t index_limit_;
4994 bool fast_elements_;
4995 uint32_t index_offset_;
4996};
4997
4998
4999/**
5000 * A helper function that visits elements of a JSObject. Only elements
5001 * whose index between 0 and range (exclusive) are visited.
5002 *
5003 * If the third parameter, visitor, is not NULL, the visitor is called
5004 * with parameters, 'visitor_index_offset + element index' and the element.
5005 *
5006 * It returns the number of visisted elements.
5007 */
5008static uint32_t IterateElements(Handle<JSObject> receiver,
5009 uint32_t range,
5010 ArrayConcatVisitor* visitor) {
5011 uint32_t num_of_elements = 0;
5012
5013 if (receiver->HasFastElements()) {
5014 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5015 uint32_t len = elements->length();
5016 if (range < len) len = range;
5017
5018 for (uint32_t j = 0; j < len; j++) {
5019 Handle<Object> e(elements->get(j));
5020 if (!e->IsTheHole()) {
5021 num_of_elements++;
5022 if (visitor)
5023 visitor->visit(j, e);
5024 }
5025 }
5026
5027 } else {
5028 Handle<Dictionary> dict(receiver->element_dictionary());
5029 uint32_t capacity = dict->Capacity();
5030 for (uint32_t j = 0; j < capacity; j++) {
5031 Handle<Object> k(dict->KeyAt(j));
5032 if (dict->IsKey(*k)) {
5033 ASSERT(k->IsNumber());
5034 uint32_t index = static_cast<uint32_t>(k->Number());
5035 if (index < range) {
5036 num_of_elements++;
5037 if (visitor) {
5038 visitor->visit(index,
5039 Handle<Object>(dict->ValueAt(j)));
5040 }
5041 }
5042 }
5043 }
5044 }
5045
5046 return num_of_elements;
5047}
5048
5049
5050/**
5051 * A helper function that visits elements of an Array object, and elements
5052 * on its prototypes.
5053 *
5054 * Elements on prototypes are visited first, and only elements whose indices
5055 * less than Array length are visited.
5056 *
5057 * If a ArrayConcatVisitor object is given, the visitor is called with
5058 * parameters, element's index + visitor_index_offset and the element.
5059 */
5060static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5061 ArrayConcatVisitor* visitor) {
5062 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5063 Handle<Object> obj = array;
5064
5065 static const int kEstimatedPrototypes = 3;
5066 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5067
5068 // Visit prototype first. If an element on the prototype is shadowed by
5069 // the inheritor using the same index, the ArrayConcatVisitor visits
5070 // the prototype element before the shadowing element.
5071 // The visitor can simply overwrite the old value by new value using
5072 // the same index. This follows Array::concat semantics.
5073 while (!obj->IsNull()) {
5074 objects.Add(Handle<JSObject>::cast(obj));
5075 obj = Handle<Object>(obj->GetPrototype());
5076 }
5077
5078 uint32_t nof_elements = 0;
5079 for (int i = objects.length() - 1; i >= 0; i--) {
5080 Handle<JSObject> obj = objects[i];
5081 nof_elements +=
5082 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5083 }
5084
5085 return nof_elements;
5086}
5087
5088
5089/**
5090 * A helper function of Runtime_ArrayConcat.
5091 *
5092 * The first argument is an Array of arrays and objects. It is the
5093 * same as the arguments array of Array::concat JS function.
5094 *
5095 * If an argument is an Array object, the function visits array
5096 * elements. If an argument is not an Array object, the function
5097 * visits the object as if it is an one-element array.
5098 *
5099 * If the result array index overflows 32-bit integer, the rounded
5100 * non-negative number is used as new length. For example, if one
5101 * array length is 2^32 - 1, second array length is 1, the
5102 * concatenated array length is 0.
5103 */
5104static uint32_t IterateArguments(Handle<JSArray> arguments,
5105 ArrayConcatVisitor* visitor) {
5106 uint32_t visited_elements = 0;
5107 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5108
5109 for (uint32_t i = 0; i < num_of_args; i++) {
5110 Handle<Object> obj(arguments->GetElement(i));
5111 if (obj->IsJSArray()) {
5112 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5113 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5114 uint32_t nof_elements =
5115 IterateArrayAndPrototypeElements(array, visitor);
5116 // Total elements of array and its prototype chain can be more than
5117 // the array length, but ArrayConcat can only concatenate at most
5118 // the array length number of elements.
5119 visited_elements += (nof_elements > len) ? len : nof_elements;
5120 if (visitor) visitor->increase_index_offset(len);
5121
5122 } else {
5123 if (visitor) {
5124 visitor->visit(0, obj);
5125 visitor->increase_index_offset(1);
5126 }
5127 visited_elements++;
5128 }
5129 }
5130 return visited_elements;
5131}
5132
5133
5134/**
5135 * Array::concat implementation.
5136 * See ECMAScript 262, 15.4.4.4.
5137 */
5138static Object* Runtime_ArrayConcat(Arguments args) {
5139 ASSERT(args.length() == 1);
5140 HandleScope handle_scope;
5141
5142 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5143 Handle<JSArray> arguments(arg_arrays);
5144
5145 // Pass 1: estimate the number of elements of the result
5146 // (it could be more than real numbers if prototype has elements).
5147 uint32_t result_length = 0;
5148 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5149
5150 { AssertNoAllocation nogc;
5151 for (uint32_t i = 0; i < num_of_args; i++) {
5152 Object* obj = arguments->GetElement(i);
5153 if (obj->IsJSArray()) {
5154 result_length +=
5155 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5156 } else {
5157 result_length++;
5158 }
5159 }
5160 }
5161
5162 // Allocate an empty array, will set length and content later.
5163 Handle<JSArray> result = Factory::NewJSArray(0);
5164
5165 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5166 // If estimated number of elements is more than half of length, a
5167 // fixed array (fast case) is more time and space-efficient than a
5168 // dictionary.
5169 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5170
5171 Handle<FixedArray> storage;
5172 if (fast_case) {
5173 // The backing storage array must have non-existing elements to
5174 // preserve holes across concat operations.
5175 storage = Factory::NewFixedArrayWithHoles(result_length);
5176
5177 } else {
5178 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5179 uint32_t at_least_space_for = estimate_nof_elements +
5180 (estimate_nof_elements >> 2);
5181 storage = Handle<FixedArray>::cast(
5182 Factory::NewDictionary(at_least_space_for));
5183 }
5184
5185 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5186
5187 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5188
5189 IterateArguments(arguments, &visitor);
5190
5191 result->set_length(*len);
5192 result->set_elements(*storage);
5193
5194 return *result;
5195}
5196
5197
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005198// This will not allocate (flatten the string), but it may run
5199// very slowly for very deeply nested ConsStrings. For debugging use only.
5200static Object* Runtime_GlobalPrint(Arguments args) {
5201 NoHandleAllocation ha;
5202 ASSERT(args.length() == 1);
5203
5204 CONVERT_CHECKED(String, string, args[0]);
5205 StringInputBuffer buffer(string);
5206 while (buffer.has_more()) {
5207 uint16_t character = buffer.GetNext();
5208 PrintF("%c", character);
5209 }
5210 return string;
5211}
5212
5213
5214static Object* Runtime_RemoveArrayHoles(Arguments args) {
5215 ASSERT(args.length() == 1);
5216 // Ignore the case if this is not a JSArray.
5217 if (!args[0]->IsJSArray()) return args[0];
5218 return JSArray::cast(args[0])->RemoveHoles();
5219}
5220
5221
5222// Move contents of argument 0 (an array) to argument 1 (an array)
5223static Object* Runtime_MoveArrayContents(Arguments args) {
5224 ASSERT(args.length() == 2);
5225 CONVERT_CHECKED(JSArray, from, args[0]);
5226 CONVERT_CHECKED(JSArray, to, args[1]);
5227 to->SetContent(FixedArray::cast(from->elements()));
5228 to->set_length(from->length());
5229 from->SetContent(Heap::empty_fixed_array());
5230 from->set_length(0);
5231 return to;
5232}
5233
5234
5235// How many elements does this array have?
5236static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5237 ASSERT(args.length() == 1);
5238 CONVERT_CHECKED(JSArray, array, args[0]);
5239 HeapObject* elements = array->elements();
5240 if (elements->IsDictionary()) {
5241 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
5242 } else {
5243 return array->length();
5244 }
5245}
5246
5247
5248// Returns an array that tells you where in the [0, length) interval an array
5249// might have elements. Can either return keys or intervals. Keys can have
5250// gaps in (undefined). Intervals can also span over some undefined keys.
5251static Object* Runtime_GetArrayKeys(Arguments args) {
5252 ASSERT(args.length() == 2);
5253 HandleScope scope;
5254 CONVERT_CHECKED(JSArray, raw_array, args[0]);
5255 Handle<JSArray> array(raw_array);
5256 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005257 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005258 // Create an array and get all the keys into it, then remove all the
5259 // keys that are not integers in the range 0 to length-1.
5260 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5261 int keys_length = keys->length();
5262 for (int i = 0; i < keys_length; i++) {
5263 Object* key = keys->get(i);
5264 uint32_t index;
5265 if (!Array::IndexFromObject(key, &index) || index >= length) {
5266 // Zap invalid keys.
5267 keys->set_undefined(i);
5268 }
5269 }
5270 return *Factory::NewJSArrayWithElements(keys);
5271 } else {
5272 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5273 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005274 single_interval->set(0,
5275 Smi::FromInt(-1),
5276 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005277 Handle<Object> length_object =
5278 Factory::NewNumber(static_cast<double>(length));
5279 single_interval->set(1, *length_object);
5280 return *Factory::NewJSArrayWithElements(single_interval);
5281 }
5282}
5283
5284
5285// DefineAccessor takes an optional final argument which is the
5286// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5287// to the way accessors are implemented, it is set for both the getter
5288// and setter on the first call to DefineAccessor and ignored on
5289// subsequent calls.
5290static Object* Runtime_DefineAccessor(Arguments args) {
5291 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5292 // Compute attributes.
5293 PropertyAttributes attributes = NONE;
5294 if (args.length() == 5) {
5295 CONVERT_CHECKED(Smi, attrs, args[4]);
5296 int value = attrs->value();
5297 // Only attribute bits should be set.
5298 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5299 attributes = static_cast<PropertyAttributes>(value);
5300 }
5301
5302 CONVERT_CHECKED(JSObject, obj, args[0]);
5303 CONVERT_CHECKED(String, name, args[1]);
5304 CONVERT_CHECKED(Smi, flag, args[2]);
5305 CONVERT_CHECKED(JSFunction, fun, args[3]);
5306 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5307}
5308
5309
5310static Object* Runtime_LookupAccessor(Arguments args) {
5311 ASSERT(args.length() == 3);
5312 CONVERT_CHECKED(JSObject, obj, args[0]);
5313 CONVERT_CHECKED(String, name, args[1]);
5314 CONVERT_CHECKED(Smi, flag, args[2]);
5315 return obj->LookupAccessor(name, flag->value() == 0);
5316}
5317
5318
5319// Helper functions for wrapping and unwrapping stack frame ids.
5320static Smi* WrapFrameId(StackFrame::Id id) {
5321 ASSERT(IsAligned(OffsetFrom(id), 4));
5322 return Smi::FromInt(id >> 2);
5323}
5324
5325
5326static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5327 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5328}
5329
5330
5331// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005332// args[0]: debug event listener function to set or null or undefined for
5333// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005334// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005335static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005336 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005337 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5338 args[0]->IsUndefined() ||
5339 args[0]->IsNull());
5340 Handle<Object> callback = args.at<Object>(0);
5341 Handle<Object> data = args.at<Object>(1);
5342 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005343
5344 return Heap::undefined_value();
5345}
5346
5347
5348static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005349 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005350 StackGuard::DebugBreak();
5351 return Heap::undefined_value();
5352}
5353
5354
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005355// Find the length of the prototype chain that is to to handled as one. If a
5356// prototype object is hidden it is to be viewed as part of the the object it
5357// is prototype for.
5358static int LocalPrototypeChainLength(JSObject* obj) {
5359 int count = 1;
5360 Object* proto = obj->GetPrototype();
5361 while (proto->IsJSObject() &&
5362 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5363 count++;
5364 proto = JSObject::cast(proto)->GetPrototype();
5365 }
5366 return count;
5367}
5368
5369
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005370static Object* DebugLookupResultValue(Object* receiver, LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005371 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005372 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005373 switch (result->type()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005374 case NORMAL: {
5375 Dictionary* dict =
5376 JSObject::cast(result->holder())->property_dictionary();
5377 value = dict->ValueAt(result->GetDictionaryEntry());
5378 if (value->IsTheHole()) {
5379 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005380 }
5381 return value;
5382 }
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005383 case FIELD:
5384 value =
5385 JSObject::cast(
5386 result->holder())->FastPropertyAt(result->GetFieldIndex());
5387 if (value->IsTheHole()) {
5388 return Heap::undefined_value();
5389 }
5390 return value;
5391 case CONSTANT_FUNCTION:
5392 return result->GetConstantFunction();
5393 case CALLBACKS: {
5394 Object* structure = result->GetCallbackObject();
5395 if (structure->IsProxy()) {
5396 AccessorDescriptor* callback =
5397 reinterpret_cast<AccessorDescriptor*>(
5398 Proxy::cast(structure)->proxy());
5399 value = (callback->getter)(receiver, callback->data);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005400 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005401 value = Top::pending_exception();
5402 Top::clear_pending_exception();
5403 if (caught_exception != NULL) {
5404 *caught_exception = true;
5405 }
5406 }
5407 return value;
5408 } else {
5409 return Heap::undefined_value();
5410 }
5411 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005412 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005413 case MAP_TRANSITION:
5414 case CONSTANT_TRANSITION:
5415 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005416 return Heap::undefined_value();
5417 default:
5418 UNREACHABLE();
5419 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005420 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005421 return Heap::undefined_value();
5422}
5423
5424
ager@chromium.org32912102009-01-16 10:38:43 +00005425// Get debugger related details for an object property.
5426// args[0]: object holding property
5427// args[1]: name of the property
5428//
5429// The array returned contains the following information:
5430// 0: Property value
5431// 1: Property details
5432// 2: Property value is exception
5433// 3: Getter function if defined
5434// 4: Setter function if defined
5435// Items 2-4 are only filled if the property has either a getter or a setter
5436// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005437static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005438 HandleScope scope;
5439
5440 ASSERT(args.length() == 2);
5441
5442 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5443 CONVERT_ARG_CHECKED(String, name, 1);
5444
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005445 // Skip the global proxy as it has no properties and always delegates to the
5446 // real global object.
5447 if (obj->IsJSGlobalProxy()) {
5448 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5449 }
5450
5451
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005452 // Check if the name is trivially convertible to an index and get the element
5453 // if so.
5454 uint32_t index;
5455 if (name->AsArrayIndex(&index)) {
5456 Handle<FixedArray> details = Factory::NewFixedArray(2);
5457 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5458 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5459 return *Factory::NewJSArrayWithElements(details);
5460 }
5461
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005462 // Find the number of objects making up this.
5463 int length = LocalPrototypeChainLength(*obj);
5464
5465 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005466 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005467 Handle<JSObject> jsproto = obj;
5468 for (int i = 0; i < length; i++) {
5469 jsproto->LocalLookup(*name, &result);
5470 if (result.IsProperty()) {
5471 break;
5472 }
5473 if (i < length - 1) {
5474 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5475 }
5476 }
5477
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005478 if (result.IsProperty()) {
ager@chromium.org32912102009-01-16 10:38:43 +00005479 bool caught_exception = false;
ager@chromium.org381abbb2009-02-25 13:23:22 +00005480 Object* value = DebugLookupResultValue(*obj, &result,
5481 &caught_exception);
5482 if (value->IsFailure()) return value;
5483 Handle<Object> value_handle(value);
ager@chromium.org32912102009-01-16 10:38:43 +00005484 // If the callback object is a fixed array then it contains JavaScript
5485 // getter and/or setter.
5486 bool hasJavaScriptAccessors = result.type() == CALLBACKS &&
5487 result.GetCallbackObject()->IsFixedArray();
5488 Handle<FixedArray> details =
5489 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005490 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005491 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005492 if (hasJavaScriptAccessors) {
5493 details->set(2,
5494 caught_exception ? Heap::true_value() : Heap::false_value());
5495 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5496 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5497 }
5498
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005499 return *Factory::NewJSArrayWithElements(details);
5500 }
5501 return Heap::undefined_value();
5502}
5503
5504
5505static Object* Runtime_DebugGetProperty(Arguments args) {
5506 HandleScope scope;
5507
5508 ASSERT(args.length() == 2);
5509
5510 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5511 CONVERT_ARG_CHECKED(String, name, 1);
5512
5513 LookupResult result;
5514 obj->Lookup(*name, &result);
5515 if (result.IsProperty()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005516 return DebugLookupResultValue(*obj, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005517 }
5518 return Heap::undefined_value();
5519}
5520
5521
5522// Return the names of the local named properties.
5523// args[0]: object
5524static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5525 HandleScope scope;
5526 ASSERT(args.length() == 1);
5527 if (!args[0]->IsJSObject()) {
5528 return Heap::undefined_value();
5529 }
5530 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5531
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005532 // Skip the global proxy as it has no properties and always delegates to the
5533 // real global object.
5534 if (obj->IsJSGlobalProxy()) {
5535 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5536 }
5537
5538 // Find the number of objects making up this.
5539 int length = LocalPrototypeChainLength(*obj);
5540
5541 // Find the number of local properties for each of the objects.
5542 int* local_property_count = NewArray<int>(length);
5543 int total_property_count = 0;
5544 Handle<JSObject> jsproto = obj;
5545 for (int i = 0; i < length; i++) {
5546 int n;
5547 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5548 local_property_count[i] = n;
5549 total_property_count += n;
5550 if (i < length - 1) {
5551 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5552 }
5553 }
5554
5555 // Allocate an array with storage for all the property names.
5556 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5557
5558 // Get the property names.
5559 jsproto = obj;
5560 for (int i = 0; i < length; i++) {
5561 jsproto->GetLocalPropertyNames(*names,
5562 i == 0 ? 0 : local_property_count[i - 1]);
5563 if (i < length - 1) {
5564 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5565 }
5566 }
5567
5568 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005569 return *Factory::NewJSArrayWithElements(names);
5570}
5571
5572
5573// Return the names of the local indexed properties.
5574// args[0]: object
5575static Object* Runtime_DebugLocalElementNames(Arguments args) {
5576 HandleScope scope;
5577 ASSERT(args.length() == 1);
5578 if (!args[0]->IsJSObject()) {
5579 return Heap::undefined_value();
5580 }
5581 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5582
5583 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5584 Handle<FixedArray> names = Factory::NewFixedArray(n);
5585 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5586 return *Factory::NewJSArrayWithElements(names);
5587}
5588
5589
5590// Return the property type calculated from the property details.
5591// args[0]: smi with property details.
5592static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5593 ASSERT(args.length() == 1);
5594 CONVERT_CHECKED(Smi, details, args[0]);
5595 PropertyType type = PropertyDetails(details).type();
5596 return Smi::FromInt(static_cast<int>(type));
5597}
5598
5599
5600// Return the property attribute calculated from the property details.
5601// args[0]: smi with property details.
5602static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5603 ASSERT(args.length() == 1);
5604 CONVERT_CHECKED(Smi, details, args[0]);
5605 PropertyAttributes attributes = PropertyDetails(details).attributes();
5606 return Smi::FromInt(static_cast<int>(attributes));
5607}
5608
5609
5610// Return the property insertion index calculated from the property details.
5611// args[0]: smi with property details.
5612static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5613 ASSERT(args.length() == 1);
5614 CONVERT_CHECKED(Smi, details, args[0]);
5615 int index = PropertyDetails(details).index();
5616 return Smi::FromInt(index);
5617}
5618
5619
5620// Return information on whether an object has a named or indexed interceptor.
5621// args[0]: object
5622static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5623 HandleScope scope;
5624 ASSERT(args.length() == 1);
5625 if (!args[0]->IsJSObject()) {
5626 return Smi::FromInt(0);
5627 }
5628 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5629
5630 int result = 0;
5631 if (obj->HasNamedInterceptor()) result |= 2;
5632 if (obj->HasIndexedInterceptor()) result |= 1;
5633
5634 return Smi::FromInt(result);
5635}
5636
5637
5638// Return property names from named interceptor.
5639// args[0]: object
5640static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5641 HandleScope scope;
5642 ASSERT(args.length() == 1);
5643 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005644
ager@chromium.org32912102009-01-16 10:38:43 +00005645 if (obj->HasNamedInterceptor()) {
5646 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5647 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5648 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005649 return Heap::undefined_value();
5650}
5651
5652
5653// Return element names from indexed interceptor.
5654// args[0]: object
5655static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5656 HandleScope scope;
5657 ASSERT(args.length() == 1);
5658 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005659
ager@chromium.org32912102009-01-16 10:38:43 +00005660 if (obj->HasIndexedInterceptor()) {
5661 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5662 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5663 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005664 return Heap::undefined_value();
5665}
5666
5667
5668// Return property value from named interceptor.
5669// args[0]: object
5670// args[1]: property name
5671static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5672 HandleScope scope;
5673 ASSERT(args.length() == 2);
5674 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5675 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5676 CONVERT_ARG_CHECKED(String, name, 1);
5677
5678 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005679 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005680}
5681
5682
5683// Return element value from indexed interceptor.
5684// args[0]: object
5685// args[1]: index
5686static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5687 HandleScope scope;
5688 ASSERT(args.length() == 2);
5689 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5690 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5691 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5692
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005693 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005694}
5695
5696
5697static Object* Runtime_CheckExecutionState(Arguments args) {
5698 ASSERT(args.length() >= 1);
5699 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005700 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005701 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005702 return Top::Throw(Heap::illegal_execution_state_symbol());
5703 }
5704
5705 return Heap::true_value();
5706}
5707
5708
5709static Object* Runtime_GetFrameCount(Arguments args) {
5710 HandleScope scope;
5711 ASSERT(args.length() == 1);
5712
5713 // Check arguments.
5714 Object* result = Runtime_CheckExecutionState(args);
5715 if (result->IsFailure()) return result;
5716
5717 // Count all frames which are relevant to debugging stack trace.
5718 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005719 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005720 if (id == StackFrame::NO_ID) {
5721 // If there is no JavaScript stack frame count is 0.
5722 return Smi::FromInt(0);
5723 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005724 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5725 return Smi::FromInt(n);
5726}
5727
5728
5729static const int kFrameDetailsFrameIdIndex = 0;
5730static const int kFrameDetailsReceiverIndex = 1;
5731static const int kFrameDetailsFunctionIndex = 2;
5732static const int kFrameDetailsArgumentCountIndex = 3;
5733static const int kFrameDetailsLocalCountIndex = 4;
5734static const int kFrameDetailsSourcePositionIndex = 5;
5735static const int kFrameDetailsConstructCallIndex = 6;
5736static const int kFrameDetailsDebuggerFrameIndex = 7;
5737static const int kFrameDetailsFirstDynamicIndex = 8;
5738
5739// Return an array with frame details
5740// args[0]: number: break id
5741// args[1]: number: frame index
5742//
5743// The array returned contains the following information:
5744// 0: Frame id
5745// 1: Receiver
5746// 2: Function
5747// 3: Argument count
5748// 4: Local count
5749// 5: Source position
5750// 6: Constructor call
5751// 7: Debugger frame
5752// Arguments name, value
5753// Locals name, value
5754static Object* Runtime_GetFrameDetails(Arguments args) {
5755 HandleScope scope;
5756 ASSERT(args.length() == 2);
5757
5758 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005759 Object* check = Runtime_CheckExecutionState(args);
5760 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005761 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5762
5763 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005764 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005765 if (id == StackFrame::NO_ID) {
5766 // If there are no JavaScript stack frames return undefined.
5767 return Heap::undefined_value();
5768 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005769 int count = 0;
5770 JavaScriptFrameIterator it(id);
5771 for (; !it.done(); it.Advance()) {
5772 if (count == index) break;
5773 count++;
5774 }
5775 if (it.done()) return Heap::undefined_value();
5776
5777 // Traverse the saved contexts chain to find the active context for the
5778 // selected frame.
5779 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005780 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005781 save = save->prev();
5782 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005783 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005784
5785 // Get the frame id.
5786 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
5787
5788 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005789 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005790
5791 // Check for constructor frame.
5792 bool constructor = it.frame()->IsConstructor();
5793
5794 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005795 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005796 ScopeInfo<> info(*code);
5797
5798 // Get the context.
5799 Handle<Context> context(Context::cast(it.frame()->context()));
5800
5801 // Get the locals names and values into a temporary array.
5802 //
5803 // TODO(1240907): Hide compiler-introduced stack variables
5804 // (e.g. .result)? For users of the debugger, they will probably be
5805 // confusing.
5806 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
5807 for (int i = 0; i < info.NumberOfLocals(); i++) {
5808 // Name of the local.
5809 locals->set(i * 2, *info.LocalName(i));
5810
5811 // Fetch the value of the local - either from the stack or from a
5812 // heap-allocated context.
5813 if (i < info.number_of_stack_slots()) {
5814 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
5815 } else {
5816 Handle<String> name = info.LocalName(i);
5817 // Traverse the context chain to the function context as all local
5818 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005819 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005820 context = Handle<Context>(context->previous());
5821 }
5822 ASSERT(context->is_function_context());
5823 locals->set(i * 2 + 1,
5824 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
5825 NULL)));
5826 }
5827 }
5828
5829 // Now advance to the arguments adapter frame (if any). If contains all
5830 // the provided parameters and
5831
5832 // Now advance to the arguments adapter frame (if any). It contains all
5833 // the provided parameters whereas the function frame always have the number
5834 // of arguments matching the functions parameters. The rest of the
5835 // information (except for what is collected above) is the same.
5836 it.AdvanceToArgumentsFrame();
5837
5838 // Find the number of arguments to fill. At least fill the number of
5839 // parameters for the function and fill more if more parameters are provided.
5840 int argument_count = info.number_of_parameters();
5841 if (argument_count < it.frame()->GetProvidedParametersCount()) {
5842 argument_count = it.frame()->GetProvidedParametersCount();
5843 }
5844
5845 // Calculate the size of the result.
5846 int details_size = kFrameDetailsFirstDynamicIndex +
5847 2 * (argument_count + info.NumberOfLocals());
5848 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
5849
5850 // Add the frame id.
5851 details->set(kFrameDetailsFrameIdIndex, *frame_id);
5852
5853 // Add the function (same as in function frame).
5854 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
5855
5856 // Add the arguments count.
5857 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
5858
5859 // Add the locals count
5860 details->set(kFrameDetailsLocalCountIndex,
5861 Smi::FromInt(info.NumberOfLocals()));
5862
5863 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00005864 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005865 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
5866 } else {
5867 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
5868 }
5869
5870 // Add the constructor information.
5871 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
5872
5873 // Add information on whether this frame is invoked in the debugger context.
5874 details->set(kFrameDetailsDebuggerFrameIndex,
5875 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
5876
5877 // Fill the dynamic part.
5878 int details_index = kFrameDetailsFirstDynamicIndex;
5879
5880 // Add arguments name and value.
5881 for (int i = 0; i < argument_count; i++) {
5882 // Name of the argument.
5883 if (i < info.number_of_parameters()) {
5884 details->set(details_index++, *info.parameter_name(i));
5885 } else {
5886 details->set(details_index++, Heap::undefined_value());
5887 }
5888
5889 // Parameter value.
5890 if (i < it.frame()->GetProvidedParametersCount()) {
5891 details->set(details_index++, it.frame()->GetParameter(i));
5892 } else {
5893 details->set(details_index++, Heap::undefined_value());
5894 }
5895 }
5896
5897 // Add locals name and value from the temporary copy from the function frame.
5898 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
5899 details->set(details_index++, locals->get(i));
5900 }
5901
5902 // Add the receiver (same as in function frame).
5903 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
5904 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
5905 Handle<Object> receiver(it.frame()->receiver());
5906 if (!receiver->IsJSObject()) {
5907 // If the receiver is NOT a JSObject we have hit an optimization
5908 // where a value object is not converted into a wrapped JS objects.
5909 // To hide this optimization from the debugger, we wrap the receiver
5910 // by creating correct wrapper object based on the calling frame's
5911 // global context.
5912 it.Advance();
5913 Handle<Context> calling_frames_global_context(
5914 Context::cast(Context::cast(it.frame()->context())->global_context()));
5915 receiver = Factory::ToObject(receiver, calling_frames_global_context);
5916 }
5917 details->set(kFrameDetailsReceiverIndex, *receiver);
5918
5919 ASSERT_EQ(details_size, details_index);
5920 return *Factory::NewJSArrayWithElements(details);
5921}
5922
5923
5924static Object* Runtime_GetCFrames(Arguments args) {
5925 HandleScope scope;
5926 ASSERT(args.length() == 1);
5927 Object* result = Runtime_CheckExecutionState(args);
5928 if (result->IsFailure()) return result;
5929
5930 static const int kMaxCFramesSize = 200;
5931 OS::StackFrame frames[kMaxCFramesSize];
5932 int frames_count = OS::StackWalk(frames, kMaxCFramesSize);
5933 if (frames_count == OS::kStackWalkError) {
5934 return Heap::undefined_value();
5935 }
5936
5937 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
5938 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
5939 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
5940 for (int i = 0; i < frames_count; i++) {
5941 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
5942 frame_value->SetProperty(
5943 *address_str,
5944 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
5945 NONE);
5946
5947 // Get the stack walk text for this frame.
5948 Handle<String> frame_text;
5949 if (strlen(frames[i].text) > 0) {
5950 Vector<const char> str(frames[i].text, strlen(frames[i].text));
5951 frame_text = Factory::NewStringFromAscii(str);
5952 }
5953
5954 if (!frame_text.is_null()) {
5955 frame_value->SetProperty(*text_str, *frame_text, NONE);
5956 }
5957
5958 frames_array->set(i, *frame_value);
5959 }
5960 return *Factory::NewJSArrayWithElements(frames_array);
5961}
5962
5963
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005964static Object* Runtime_GetThreadCount(Arguments args) {
5965 HandleScope scope;
5966 ASSERT(args.length() == 1);
5967
5968 // Check arguments.
5969 Object* result = Runtime_CheckExecutionState(args);
5970 if (result->IsFailure()) return result;
5971
5972 // Count all archived V8 threads.
5973 int n = 0;
5974 for (ThreadState* thread = ThreadState::FirstInUse();
5975 thread != NULL;
5976 thread = thread->Next()) {
5977 n++;
5978 }
5979
5980 // Total number of threads is current thread and archived threads.
5981 return Smi::FromInt(n + 1);
5982}
5983
5984
5985static const int kThreadDetailsCurrentThreadIndex = 0;
5986static const int kThreadDetailsThreadIdIndex = 1;
5987static const int kThreadDetailsSize = 2;
5988
5989// Return an array with thread details
5990// args[0]: number: break id
5991// args[1]: number: thread index
5992//
5993// The array returned contains the following information:
5994// 0: Is current thread?
5995// 1: Thread id
5996static Object* Runtime_GetThreadDetails(Arguments args) {
5997 HandleScope scope;
5998 ASSERT(args.length() == 2);
5999
6000 // Check arguments.
6001 Object* check = Runtime_CheckExecutionState(args);
6002 if (check->IsFailure()) return check;
6003 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6004
6005 // Allocate array for result.
6006 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6007
6008 // Thread index 0 is current thread.
6009 if (index == 0) {
6010 // Fill the details.
6011 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6012 details->set(kThreadDetailsThreadIdIndex,
6013 Smi::FromInt(ThreadManager::CurrentId()));
6014 } else {
6015 // Find the thread with the requested index.
6016 int n = 1;
6017 ThreadState* thread = ThreadState::FirstInUse();
6018 while (index != n && thread != NULL) {
6019 thread = thread->Next();
6020 n++;
6021 }
6022 if (thread == NULL) {
6023 return Heap::undefined_value();
6024 }
6025
6026 // Fill the details.
6027 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6028 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6029 }
6030
6031 // Convert to JS array and return.
6032 return *Factory::NewJSArrayWithElements(details);
6033}
6034
6035
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006036static Object* Runtime_GetBreakLocations(Arguments args) {
6037 HandleScope scope;
6038 ASSERT(args.length() == 1);
6039
6040 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6041 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6042 // Find the number of break points
6043 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6044 if (break_locations->IsUndefined()) return Heap::undefined_value();
6045 // Return array as JS array
6046 return *Factory::NewJSArrayWithElements(
6047 Handle<FixedArray>::cast(break_locations));
6048}
6049
6050
6051// Set a break point in a function
6052// args[0]: function
6053// args[1]: number: break source position (within the function source)
6054// args[2]: number: break point object
6055static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6056 HandleScope scope;
6057 ASSERT(args.length() == 3);
6058 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6059 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6060 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6061 RUNTIME_ASSERT(source_position >= 0);
6062 Handle<Object> break_point_object_arg = args.at<Object>(2);
6063
6064 // Set break point.
6065 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6066
6067 return Heap::undefined_value();
6068}
6069
6070
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006071Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6072 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006073 // Iterate the heap looking for SharedFunctionInfo generated from the
6074 // script. The inner most SharedFunctionInfo containing the source position
6075 // for the requested break point is found.
6076 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6077 // which is found is not compiled it is compiled and the heap is iterated
6078 // again as the compilation might create inner functions from the newly
6079 // compiled function and the actual requested break point might be in one of
6080 // these functions.
6081 bool done = false;
6082 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006083 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006084 Handle<SharedFunctionInfo> target;
6085 // The current candidate for the last function in script:
6086 Handle<SharedFunctionInfo> last;
6087 while (!done) {
6088 HeapIterator iterator;
6089 while (iterator.has_next()) {
6090 HeapObject* obj = iterator.next();
6091 ASSERT(obj != NULL);
6092 if (obj->IsSharedFunctionInfo()) {
6093 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6094 if (shared->script() == *script) {
6095 // If the SharedFunctionInfo found has the requested script data and
6096 // contains the source position it is a candidate.
6097 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006098 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006099 start_position = shared->start_position();
6100 }
6101 if (start_position <= position &&
6102 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006103 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006104 // candidate this is the new candidate.
6105 if (target.is_null()) {
6106 target_start_position = start_position;
6107 target = shared;
6108 } else {
6109 if (target_start_position < start_position &&
6110 shared->end_position() < target->end_position()) {
6111 target_start_position = start_position;
6112 target = shared;
6113 }
6114 }
6115 }
6116
6117 // Keep track of the last function in the script.
6118 if (last.is_null() ||
6119 shared->end_position() > last->start_position()) {
6120 last = shared;
6121 }
6122 }
6123 }
6124 }
6125
6126 // Make sure some candidate is selected.
6127 if (target.is_null()) {
6128 if (!last.is_null()) {
6129 // Position after the last function - use last.
6130 target = last;
6131 } else {
6132 // Unable to find function - possibly script without any function.
6133 return Heap::undefined_value();
6134 }
6135 }
6136
6137 // If the candidate found is compiled we are done. NOTE: when lazy
6138 // compilation of inner functions is introduced some additional checking
6139 // needs to be done here to compile inner functions.
6140 done = target->is_compiled();
6141 if (!done) {
6142 // If the candidate is not compiled compile it to reveal any inner
6143 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006144 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006145 }
6146 }
6147
6148 return *target;
6149}
6150
6151
6152// Change the state of a break point in a script. NOTE: Regarding performance
6153// see the NOTE for GetScriptFromScriptData.
6154// args[0]: script to set break point in
6155// args[1]: number: break source position (within the script source)
6156// args[2]: number: break point object
6157static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6158 HandleScope scope;
6159 ASSERT(args.length() == 3);
6160 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6161 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6162 RUNTIME_ASSERT(source_position >= 0);
6163 Handle<Object> break_point_object_arg = args.at<Object>(2);
6164
6165 // Get the script from the script wrapper.
6166 RUNTIME_ASSERT(wrapper->value()->IsScript());
6167 Handle<Script> script(Script::cast(wrapper->value()));
6168
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006169 Object* result = Runtime::FindSharedFunctionInfoInScript(
6170 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006171 if (!result->IsUndefined()) {
6172 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6173 // Find position within function. The script position might be before the
6174 // source position of the first function.
6175 int position;
6176 if (shared->start_position() > source_position) {
6177 position = 0;
6178 } else {
6179 position = source_position - shared->start_position();
6180 }
6181 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6182 }
6183 return Heap::undefined_value();
6184}
6185
6186
6187// Clear a break point
6188// args[0]: number: break point object
6189static Object* Runtime_ClearBreakPoint(Arguments args) {
6190 HandleScope scope;
6191 ASSERT(args.length() == 1);
6192 Handle<Object> break_point_object_arg = args.at<Object>(0);
6193
6194 // Clear break point.
6195 Debug::ClearBreakPoint(break_point_object_arg);
6196
6197 return Heap::undefined_value();
6198}
6199
6200
6201// Change the state of break on exceptions
6202// args[0]: boolean indicating uncaught exceptions
6203// args[1]: boolean indicating on/off
6204static Object* Runtime_ChangeBreakOnException(Arguments args) {
6205 HandleScope scope;
6206 ASSERT(args.length() == 2);
6207 ASSERT(args[0]->IsNumber());
6208 ASSERT(args[1]->IsBoolean());
6209
6210 // Update break point state
6211 ExceptionBreakType type =
6212 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6213 bool enable = args[1]->ToBoolean()->IsTrue();
6214 Debug::ChangeBreakOnException(type, enable);
6215 return Heap::undefined_value();
6216}
6217
6218
6219// Prepare for stepping
6220// args[0]: break id for checking execution state
6221// args[1]: step action from the enumeration StepAction
6222// args[2]: number of times to perform the step
6223static Object* Runtime_PrepareStep(Arguments args) {
6224 HandleScope scope;
6225 ASSERT(args.length() == 3);
6226 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006227 Object* check = Runtime_CheckExecutionState(args);
6228 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006229 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6230 return Top::Throw(Heap::illegal_argument_symbol());
6231 }
6232
6233 // Get the step action and check validity.
6234 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6235 if (step_action != StepIn &&
6236 step_action != StepNext &&
6237 step_action != StepOut &&
6238 step_action != StepInMin &&
6239 step_action != StepMin) {
6240 return Top::Throw(Heap::illegal_argument_symbol());
6241 }
6242
6243 // Get the number of steps.
6244 int step_count = NumberToInt32(args[2]);
6245 if (step_count < 1) {
6246 return Top::Throw(Heap::illegal_argument_symbol());
6247 }
6248
6249 // Prepare step.
6250 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6251 return Heap::undefined_value();
6252}
6253
6254
6255// Clear all stepping set by PrepareStep.
6256static Object* Runtime_ClearStepping(Arguments args) {
6257 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006258 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006259 Debug::ClearStepping();
6260 return Heap::undefined_value();
6261}
6262
6263
6264// Creates a copy of the with context chain. The copy of the context chain is
6265// is linked to the function context supplied.
6266static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6267 Handle<Context> function_context) {
6268 // At the bottom of the chain. Return the function context to link to.
6269 if (context_chain->is_function_context()) {
6270 return function_context;
6271 }
6272
6273 // Recursively copy the with contexts.
6274 Handle<Context> previous(context_chain->previous());
6275 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6276 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006277 CopyWithContextChain(function_context, previous),
6278 extension,
6279 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006280}
6281
6282
6283// Helper function to find or create the arguments object for
6284// Runtime_DebugEvaluate.
6285static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6286 Handle<JSFunction> function,
6287 Handle<Code> code,
6288 const ScopeInfo<>* sinfo,
6289 Handle<Context> function_context) {
6290 // Try to find the value of 'arguments' to pass as parameter. If it is not
6291 // found (that is the debugged function does not reference 'arguments' and
6292 // does not support eval) then create an 'arguments' object.
6293 int index;
6294 if (sinfo->number_of_stack_slots() > 0) {
6295 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6296 if (index != -1) {
6297 return Handle<Object>(frame->GetExpression(index));
6298 }
6299 }
6300
6301 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6302 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6303 NULL);
6304 if (index != -1) {
6305 return Handle<Object>(function_context->get(index));
6306 }
6307 }
6308
6309 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006310 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6311 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006312 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006313 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006314 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006315 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006316 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006317 return arguments;
6318}
6319
6320
6321// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006322// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006323// extension part has all the parameters and locals of the function on the
6324// stack frame. A function which calls eval with the code to evaluate is then
6325// compiled in this context and called in this context. As this context
6326// replaces the context of the function on the stack frame a new (empty)
6327// function is created as well to be used as the closure for the context.
6328// This function and the context acts as replacements for the function on the
6329// stack frame presenting the same view of the values of parameters and
6330// local variables as if the piece of JavaScript was evaluated at the point
6331// where the function on the stack frame is currently stopped.
6332static Object* Runtime_DebugEvaluate(Arguments args) {
6333 HandleScope scope;
6334
6335 // Check the execution state and decode arguments frame and source to be
6336 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006337 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006338 Object* check_result = Runtime_CheckExecutionState(args);
6339 if (check_result->IsFailure()) return check_result;
6340 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6341 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006342 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6343
6344 // Handle the processing of break.
6345 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006346
6347 // Get the frame where the debugging is performed.
6348 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6349 JavaScriptFrameIterator it(id);
6350 JavaScriptFrame* frame = it.frame();
6351 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6352 Handle<Code> code(function->code());
6353 ScopeInfo<> sinfo(*code);
6354
6355 // Traverse the saved contexts chain to find the active context for the
6356 // selected frame.
6357 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006358 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006359 save = save->prev();
6360 }
6361 ASSERT(save != NULL);
6362 SaveContext savex;
6363 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006364
6365 // Create the (empty) function replacing the function on the stack frame for
6366 // the purpose of evaluating in the context created below. It is important
6367 // that this function does not describe any parameters and local variables
6368 // in the context. If it does then this will cause problems with the lookup
6369 // in Context::Lookup, where context slots for parameters and local variables
6370 // are looked at before the extension object.
6371 Handle<JSFunction> go_between =
6372 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
6373 go_between->set_context(function->context());
6374#ifdef DEBUG
6375 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
6376 ASSERT(go_between_sinfo.number_of_parameters() == 0);
6377 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
6378#endif
6379
6380 // Allocate and initialize a context extension object with all the
6381 // arguments, stack locals heap locals and extension properties of the
6382 // debugged function.
6383 Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function());
6384 // First fill all parameters to the context extension.
6385 for (int i = 0; i < sinfo.number_of_parameters(); ++i) {
6386 SetProperty(context_ext,
6387 sinfo.parameter_name(i),
6388 Handle<Object>(frame->GetParameter(i)), NONE);
6389 }
6390 // Second fill all stack locals to the context extension.
6391 for (int i = 0; i < sinfo.number_of_stack_slots(); i++) {
6392 SetProperty(context_ext,
6393 sinfo.stack_slot_name(i),
6394 Handle<Object>(frame->GetExpression(i)), NONE);
6395 }
6396 // Third fill all context locals to the context extension.
6397 Handle<Context> frame_context(Context::cast(frame->context()));
6398 Handle<Context> function_context(frame_context->fcontext());
6399 for (int i = Context::MIN_CONTEXT_SLOTS;
6400 i < sinfo.number_of_context_slots();
6401 ++i) {
6402 int context_index =
6403 ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL);
6404 SetProperty(context_ext,
6405 sinfo.context_slot_name(i),
6406 Handle<Object>(function_context->get(context_index)), NONE);
6407 }
6408 // Finally copy any properties from the function context extension. This will
6409 // be variables introduced by eval.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006410 if (function_context->has_extension() &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006411 !function_context->IsGlobalContext()) {
6412 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6413 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6414 for (int i = 0; i < keys->length(); i++) {
6415 // Names of variables introduced by eval are strings.
6416 ASSERT(keys->get(i)->IsString());
6417 Handle<String> key(String::cast(keys->get(i)));
6418 SetProperty(context_ext, key, GetProperty(ext, key), NONE);
6419 }
6420 }
6421
6422 // Allocate a new context for the debug evaluation and set the extension
6423 // object build.
6424 Handle<Context> context =
6425 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
6426 context->set_extension(*context_ext);
6427 // Copy any with contexts present and chain them in front of this context.
6428 context = CopyWithContextChain(frame_context, context);
6429
6430 // Wrap the evaluation statement in a new function compiled in the newly
6431 // created context. The function has one parameter which has to be called
6432 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00006433 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006434 // function(arguments,__source__) {return eval(__source__);}
6435 static const char* source_str =
6436 "function(arguments,__source__){return eval(__source__);}";
6437 static const int source_str_length = strlen(source_str);
6438 Handle<String> function_source =
6439 Factory::NewStringFromAscii(Vector<const char>(source_str,
6440 source_str_length));
6441 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00006442 Compiler::CompileEval(function_source,
6443 context,
6444 0,
6445 context->IsGlobalContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006446 if (boilerplate.is_null()) return Failure::Exception();
6447 Handle<JSFunction> compiled_function =
6448 Factory::NewFunctionFromBoilerplate(boilerplate, context);
6449
6450 // Invoke the result of the compilation to get the evaluation function.
6451 bool has_pending_exception;
6452 Handle<Object> receiver(frame->receiver());
6453 Handle<Object> evaluation_function =
6454 Execution::Call(compiled_function, receiver, 0, NULL,
6455 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006456 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006457
6458 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
6459 function_context);
6460
6461 // Invoke the evaluation function and return the result.
6462 const int argc = 2;
6463 Object** argv[argc] = { arguments.location(),
6464 Handle<Object>::cast(source).location() };
6465 Handle<Object> result =
6466 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
6467 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006468 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006469 return *result;
6470}
6471
6472
6473static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
6474 HandleScope scope;
6475
6476 // Check the execution state and decode arguments frame and source to be
6477 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006478 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006479 Object* check_result = Runtime_CheckExecutionState(args);
6480 if (check_result->IsFailure()) return check_result;
6481 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006482 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
6483
6484 // Handle the processing of break.
6485 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006486
6487 // Enter the top context from before the debugger was invoked.
6488 SaveContext save;
6489 SaveContext* top = &save;
6490 while (top != NULL && *top->context() == *Debug::debug_context()) {
6491 top = top->prev();
6492 }
6493 if (top != NULL) {
6494 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006495 }
6496
6497 // Get the global context now set to the top context from before the
6498 // debugger was invoked.
6499 Handle<Context> context = Top::global_context();
6500
6501 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00006502 Handle<JSFunction> boilerplate =
6503 Handle<JSFunction>(Compiler::CompileEval(source, context, 0, true));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006504 if (boilerplate.is_null()) return Failure::Exception();
6505 Handle<JSFunction> compiled_function =
6506 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
6507 context));
6508
6509 // Invoke the result of the compilation to get the evaluation function.
6510 bool has_pending_exception;
6511 Handle<Object> receiver = Top::global();
6512 Handle<Object> result =
6513 Execution::Call(compiled_function, receiver, 0, NULL,
6514 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006515 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006516 return *result;
6517}
6518
6519
ager@chromium.org41826e72009-03-30 13:30:57 +00006520// If an object given is an external string, check that the underlying
6521// resource is accessible. For other kinds of objects, always return true.
6522static bool IsExternalStringValid(Object* str) {
6523 if (!str->IsString() || !StringShape(String::cast(str)).IsExternal()) {
6524 return true;
6525 }
6526 if (StringShape(String::cast(str)).IsAsciiRepresentation()) {
6527 return ExternalAsciiString::cast(str)->resource() != 0;
6528 } else if (StringShape(String::cast(str)).IsTwoByteRepresentation()) {
6529 return ExternalTwoByteString::cast(str)->resource() != 0;
6530 } else {
6531 return true;
6532 }
6533}
6534
6535
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006536// Helper function used by Runtime_DebugGetLoadedScripts below.
6537static int DebugGetLoadedScripts(FixedArray* instances, int instances_size) {
6538 NoHandleAllocation ha;
6539 AssertNoAllocation no_alloc;
6540
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006541 // Scan heap for Script objects.
6542 int count = 0;
6543 HeapIterator iterator;
6544 while (iterator.has_next()) {
6545 HeapObject* obj = iterator.next();
6546 ASSERT(obj != NULL);
ager@chromium.org41826e72009-03-30 13:30:57 +00006547 if (obj->IsScript() && IsExternalStringValid(Script::cast(obj)->source())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006548 if (instances != NULL && count < instances_size) {
6549 instances->set(count, obj);
6550 }
6551 count++;
6552 }
6553 }
6554
6555 return count;
6556}
6557
6558
6559static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
6560 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006561 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006562
6563 // Perform two GCs to get rid of all unreferenced scripts. The first GC gets
ager@chromium.org32912102009-01-16 10:38:43 +00006564 // rid of all the cached script wrappers and the second gets rid of the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006565 // scripts which is no longer referenced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006566 Heap::CollectAllGarbage();
6567 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006568
6569 // Get the number of scripts.
6570 int count;
6571 count = DebugGetLoadedScripts(NULL, 0);
6572
6573 // Allocate an array to hold the result.
6574 Handle<FixedArray> instances = Factory::NewFixedArray(count);
6575
6576 // Fill the script objects.
6577 count = DebugGetLoadedScripts(*instances, count);
6578
6579 // Convert the script objects to proper JS objects.
6580 for (int i = 0; i < count; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006581 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
6582 // Get the script wrapper in a local handle before calling GetScriptWrapper,
6583 // because using
6584 // instances->set(i, *GetScriptWrapper(script))
6585 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
6586 // already have deferenced the instances handle.
6587 Handle<JSValue> wrapper = GetScriptWrapper(script);
6588 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006589 }
6590
6591 // Return result as a JS array.
6592 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
6593 Handle<JSArray>::cast(result)->SetContent(*instances);
6594 return *result;
6595}
6596
6597
6598// Helper function used by Runtime_DebugReferencedBy below.
6599static int DebugReferencedBy(JSObject* target,
6600 Object* instance_filter, int max_references,
6601 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006602 JSFunction* arguments_function) {
6603 NoHandleAllocation ha;
6604 AssertNoAllocation no_alloc;
6605
6606 // Iterate the heap.
6607 int count = 0;
6608 JSObject* last = NULL;
6609 HeapIterator iterator;
6610 while (iterator.has_next() &&
6611 (max_references == 0 || count < max_references)) {
6612 // Only look at all JSObjects.
6613 HeapObject* heap_obj = iterator.next();
6614 if (heap_obj->IsJSObject()) {
6615 // Skip context extension objects and argument arrays as these are
6616 // checked in the context of functions using them.
6617 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006618 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006619 obj->map()->constructor() == arguments_function) {
6620 continue;
6621 }
6622
6623 // Check if the JS object has a reference to the object looked for.
6624 if (obj->ReferencesObject(target)) {
6625 // Check instance filter if supplied. This is normally used to avoid
6626 // references from mirror objects (see Runtime_IsInPrototypeChain).
6627 if (!instance_filter->IsUndefined()) {
6628 Object* V = obj;
6629 while (true) {
6630 Object* prototype = V->GetPrototype();
6631 if (prototype->IsNull()) {
6632 break;
6633 }
6634 if (instance_filter == prototype) {
6635 obj = NULL; // Don't add this object.
6636 break;
6637 }
6638 V = prototype;
6639 }
6640 }
6641
6642 if (obj != NULL) {
6643 // Valid reference found add to instance array if supplied an update
6644 // count.
6645 if (instances != NULL && count < instances_size) {
6646 instances->set(count, obj);
6647 }
6648 last = obj;
6649 count++;
6650 }
6651 }
6652 }
6653 }
6654
6655 // Check for circular reference only. This can happen when the object is only
6656 // referenced from mirrors and has a circular reference in which case the
6657 // object is not really alive and would have been garbage collected if not
6658 // referenced from the mirror.
6659 if (count == 1 && last == target) {
6660 count = 0;
6661 }
6662
6663 // Return the number of referencing objects found.
6664 return count;
6665}
6666
6667
6668// Scan the heap for objects with direct references to an object
6669// args[0]: the object to find references to
6670// args[1]: constructor function for instances to exclude (Mirror)
6671// args[2]: the the maximum number of objects to return
6672static Object* Runtime_DebugReferencedBy(Arguments args) {
6673 ASSERT(args.length() == 3);
6674
6675 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006676 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006677
6678 // Check parameters.
6679 CONVERT_CHECKED(JSObject, target, args[0]);
6680 Object* instance_filter = args[1];
6681 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
6682 instance_filter->IsJSObject());
6683 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
6684 RUNTIME_ASSERT(max_references >= 0);
6685
6686 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006687 JSObject* arguments_boilerplate =
6688 Top::context()->global_context()->arguments_boilerplate();
6689 JSFunction* arguments_function =
6690 JSFunction::cast(arguments_boilerplate->map()->constructor());
6691
6692 // Get the number of referencing objects.
6693 int count;
6694 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006695 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006696
6697 // Allocate an array to hold the result.
6698 Object* object = Heap::AllocateFixedArray(count);
6699 if (object->IsFailure()) return object;
6700 FixedArray* instances = FixedArray::cast(object);
6701
6702 // Fill the referencing objects.
6703 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006704 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006705
6706 // Return result as JS array.
6707 Object* result =
6708 Heap::AllocateJSObject(
6709 Top::context()->global_context()->array_function());
6710 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6711 return result;
6712}
6713
6714
6715// Helper function used by Runtime_DebugConstructedBy below.
6716static int DebugConstructedBy(JSFunction* constructor, int max_references,
6717 FixedArray* instances, int instances_size) {
6718 AssertNoAllocation no_alloc;
6719
6720 // Iterate the heap.
6721 int count = 0;
6722 HeapIterator iterator;
6723 while (iterator.has_next() &&
6724 (max_references == 0 || count < max_references)) {
6725 // Only look at all JSObjects.
6726 HeapObject* heap_obj = iterator.next();
6727 if (heap_obj->IsJSObject()) {
6728 JSObject* obj = JSObject::cast(heap_obj);
6729 if (obj->map()->constructor() == constructor) {
6730 // Valid reference found add to instance array if supplied an update
6731 // count.
6732 if (instances != NULL && count < instances_size) {
6733 instances->set(count, obj);
6734 }
6735 count++;
6736 }
6737 }
6738 }
6739
6740 // Return the number of referencing objects found.
6741 return count;
6742}
6743
6744
6745// Scan the heap for objects constructed by a specific function.
6746// args[0]: the constructor to find instances of
6747// args[1]: the the maximum number of objects to return
6748static Object* Runtime_DebugConstructedBy(Arguments args) {
6749 ASSERT(args.length() == 2);
6750
6751 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006752 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006753
6754 // Check parameters.
6755 CONVERT_CHECKED(JSFunction, constructor, args[0]);
6756 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
6757 RUNTIME_ASSERT(max_references >= 0);
6758
6759 // Get the number of referencing objects.
6760 int count;
6761 count = DebugConstructedBy(constructor, max_references, NULL, 0);
6762
6763 // Allocate an array to hold the result.
6764 Object* object = Heap::AllocateFixedArray(count);
6765 if (object->IsFailure()) return object;
6766 FixedArray* instances = FixedArray::cast(object);
6767
6768 // Fill the referencing objects.
6769 count = DebugConstructedBy(constructor, max_references, instances, count);
6770
6771 // Return result as JS array.
6772 Object* result =
6773 Heap::AllocateJSObject(
6774 Top::context()->global_context()->array_function());
6775 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6776 return result;
6777}
6778
6779
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006780// Find the effective prototype object as returned by __proto__.
6781// args[0]: the object to find the prototype for.
6782static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006783 ASSERT(args.length() == 1);
6784
6785 CONVERT_CHECKED(JSObject, obj, args[0]);
6786
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006787 // Use the __proto__ accessor.
6788 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006789}
6790
6791
6792static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006793 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006794 CPU::DebugBreak();
6795 return Heap::undefined_value();
6796}
6797
6798
6799// Finds the script object from the script data. NOTE: This operation uses
6800// heap traversal to find the function generated for the source position
6801// for the requested break point. For lazily compiled functions several heap
6802// traversals might be required rendering this operation as a rather slow
6803// operation. However for setting break points which is normally done through
6804// some kind of user interaction the performance is not crucial.
6805static Handle<Object> Runtime_GetScriptFromScriptName(
6806 Handle<String> script_name) {
6807 // Scan the heap for Script objects to find the script with the requested
6808 // script data.
6809 Handle<Script> script;
6810 HeapIterator iterator;
6811 while (script.is_null() && iterator.has_next()) {
6812 HeapObject* obj = iterator.next();
6813 // If a script is found check if it has the script data requested.
6814 if (obj->IsScript()) {
6815 if (Script::cast(obj)->name()->IsString()) {
6816 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
6817 script = Handle<Script>(Script::cast(obj));
6818 }
6819 }
6820 }
6821 }
6822
6823 // If no script with the requested script data is found return undefined.
6824 if (script.is_null()) return Factory::undefined_value();
6825
6826 // Return the script found.
6827 return GetScriptWrapper(script);
6828}
6829
6830
6831// Get the script object from script data. NOTE: Regarding performance
6832// see the NOTE for GetScriptFromScriptData.
6833// args[0]: script data for the script to find the source for
6834static Object* Runtime_GetScript(Arguments args) {
6835 HandleScope scope;
6836
6837 ASSERT(args.length() == 1);
6838
6839 CONVERT_CHECKED(String, script_name, args[0]);
6840
6841 // Find the requested script.
6842 Handle<Object> result =
6843 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
6844 return *result;
6845}
6846
6847
6848static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
6849#ifdef DEBUG
6850 HandleScope scope;
6851 ASSERT(args.length() == 1);
6852 // Get the function and make sure it is compiled.
6853 CONVERT_ARG_CHECKED(JSFunction, func, 0);
6854 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
6855 return Failure::Exception();
6856 }
6857 func->code()->PrintLn();
6858#endif // DEBUG
6859 return Heap::undefined_value();
6860}
6861
6862
6863static Object* Runtime_Abort(Arguments args) {
6864 ASSERT(args.length() == 2);
6865 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
6866 Smi::cast(args[1])->value());
6867 Top::PrintStack();
6868 OS::Abort();
6869 UNREACHABLE();
6870 return NULL;
6871}
6872
6873
kasper.lund44510672008-07-25 07:37:58 +00006874#ifdef DEBUG
6875// ListNatives is ONLY used by the fuzz-natives.js in debug mode
6876// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006877static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006878 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006879 HandleScope scope;
6880 Handle<JSArray> result = Factory::NewJSArray(0);
6881 int index = 0;
6882#define ADD_ENTRY(Name, argc) \
6883 { \
6884 HandleScope inner; \
6885 Handle<String> name = \
6886 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
6887 Handle<JSArray> pair = Factory::NewJSArray(0); \
6888 SetElement(pair, 0, name); \
6889 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
6890 SetElement(result, index++, pair); \
6891 }
6892 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
6893#undef ADD_ENTRY
6894 return *result;
6895}
kasper.lund44510672008-07-25 07:37:58 +00006896#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006897
6898
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006899static Object* Runtime_Log(Arguments args) {
6900 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00006901 CONVERT_CHECKED(String, format, args[0]);
6902 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006903 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006904 Logger::LogRuntime(chars, elms);
6905 return Heap::undefined_value();
6906}
6907
6908
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006909static Object* Runtime_IS_VAR(Arguments args) {
6910 UNREACHABLE(); // implemented as macro in the parser
6911 return NULL;
6912}
6913
6914
6915// ----------------------------------------------------------------------------
6916// Implementation of Runtime
6917
6918#define F(name, nargs) \
6919 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
6920 static_cast<int>(Runtime::k##name) },
6921
6922static Runtime::Function Runtime_functions[] = {
6923 RUNTIME_FUNCTION_LIST(F)
6924 { NULL, NULL, NULL, 0, -1 }
6925};
6926
6927#undef F
6928
6929
6930Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
6931 ASSERT(0 <= fid && fid < kNofFunctions);
6932 return &Runtime_functions[fid];
6933}
6934
6935
6936Runtime::Function* Runtime::FunctionForName(const char* name) {
6937 for (Function* f = Runtime_functions; f->name != NULL; f++) {
6938 if (strcmp(f->name, name) == 0) {
6939 return f;
6940 }
6941 }
6942 return NULL;
6943}
6944
6945
6946void Runtime::PerformGC(Object* result) {
6947 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006948 if (failure->IsRetryAfterGC()) {
6949 // Try to do a garbage collection; ignore it if it fails. The C
6950 // entry stub will throw an out-of-memory exception in that case.
6951 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
6952 } else {
6953 // Handle last resort GC and make sure to allow future allocations
6954 // to grow the heap without causing GCs (if possible).
6955 Counters::gc_last_resort_from_js.Increment();
6956 Heap::CollectAllGarbage();
6957 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006958}
6959
6960
6961} } // namespace v8::internal