blob: 38db689a746a46d32f071f9dbc9b2a5c3a1e16fd [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);
1041 CHECK(last_match_info->HasFastElements());
1042 Handle<Object> result = RegExpImpl::Exec(regexp,
1043 subject,
1044 index->value(),
1045 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001046 if (result.is_null()) return Failure::Exception();
1047 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001048}
1049
1050
1051static Object* Runtime_RegExpExecGlobal(Arguments args) {
1052 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001053 ASSERT(args.length() == 3);
ager@chromium.org236ad962008-09-25 09:45:57 +00001054 CONVERT_CHECKED(JSRegExp, raw_regexp, args[0]);
1055 Handle<JSRegExp> regexp(raw_regexp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001056 CONVERT_CHECKED(String, raw_subject, args[1]);
1057 Handle<String> subject(raw_subject);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001058 CONVERT_CHECKED(JSArray, raw_last_match_info, args[2]);
1059 Handle<JSArray> last_match_info(raw_last_match_info);
1060 CHECK(last_match_info->HasFastElements());
1061 Handle<Object> result =
1062 RegExpImpl::ExecGlobal(regexp, subject, last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001063 if (result.is_null()) return Failure::Exception();
1064 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001065}
1066
1067
1068static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1069 HandleScope scope;
1070 ASSERT(args.length() == 4);
1071 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1072 int index = Smi::cast(args[1])->value();
1073 Handle<String> pattern = args.at<String>(2);
1074 Handle<String> flags = args.at<String>(3);
1075
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001076 // Get the RegExp function from the context in the literals array.
1077 // This is the RegExp function from the context in which the
1078 // function was created. We do not use the RegExp function from the
1079 // current global context because this might be the RegExp function
1080 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001081 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001082 Handle<JSFunction>(
1083 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001084 // Compute the regular expression literal.
1085 bool has_pending_exception;
1086 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001087 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1088 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001089 if (has_pending_exception) {
1090 ASSERT(Top::has_pending_exception());
1091 return Failure::Exception();
1092 }
1093 literals->set(index, *regexp);
1094 return *regexp;
1095}
1096
1097
1098static Object* Runtime_FunctionGetName(Arguments args) {
1099 NoHandleAllocation ha;
1100 ASSERT(args.length() == 1);
1101
1102 CONVERT_CHECKED(JSFunction, f, args[0]);
1103 return f->shared()->name();
1104}
1105
1106
ager@chromium.org236ad962008-09-25 09:45:57 +00001107static Object* Runtime_FunctionSetName(Arguments args) {
1108 NoHandleAllocation ha;
1109 ASSERT(args.length() == 2);
1110
1111 CONVERT_CHECKED(JSFunction, f, args[0]);
1112 CONVERT_CHECKED(String, name, args[1]);
1113 f->shared()->set_name(name);
1114 return Heap::undefined_value();
1115}
1116
1117
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001118static Object* Runtime_FunctionGetScript(Arguments args) {
1119 HandleScope scope;
1120 ASSERT(args.length() == 1);
1121
1122 CONVERT_CHECKED(JSFunction, fun, args[0]);
1123 Handle<Object> script = Handle<Object>(fun->shared()->script());
1124 if (!script->IsScript()) return Heap::undefined_value();
1125
1126 return *GetScriptWrapper(Handle<Script>::cast(script));
1127}
1128
1129
1130static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1131 NoHandleAllocation ha;
1132 ASSERT(args.length() == 1);
1133
1134 CONVERT_CHECKED(JSFunction, f, args[0]);
1135 return f->shared()->GetSourceCode();
1136}
1137
1138
1139static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1140 NoHandleAllocation ha;
1141 ASSERT(args.length() == 1);
1142
1143 CONVERT_CHECKED(JSFunction, fun, args[0]);
1144 int pos = fun->shared()->start_position();
1145 return Smi::FromInt(pos);
1146}
1147
1148
1149static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1150 NoHandleAllocation ha;
1151 ASSERT(args.length() == 2);
1152
1153 CONVERT_CHECKED(JSFunction, fun, args[0]);
1154 CONVERT_CHECKED(String, name, args[1]);
1155 fun->SetInstanceClassName(name);
1156 return Heap::undefined_value();
1157}
1158
1159
1160static Object* Runtime_FunctionSetLength(Arguments args) {
1161 NoHandleAllocation ha;
1162 ASSERT(args.length() == 2);
1163
1164 CONVERT_CHECKED(JSFunction, fun, args[0]);
1165 CONVERT_CHECKED(Smi, length, args[1]);
1166 fun->shared()->set_length(length->value());
1167 return length;
1168}
1169
1170
1171static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001172 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001173 ASSERT(args.length() == 2);
1174
1175 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001176 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1177 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001178 return args[0]; // return TOS
1179}
1180
1181
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001182static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1183 NoHandleAllocation ha;
1184 ASSERT(args.length() == 1);
1185
1186 CONVERT_CHECKED(JSFunction, f, args[0]);
1187 // The function_data field of the shared function info is used exclusively by
1188 // the API.
1189 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1190 : Heap::false_value();
1191}
1192
1193
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001194static Object* Runtime_SetCode(Arguments args) {
1195 HandleScope scope;
1196 ASSERT(args.length() == 2);
1197
1198 CONVERT_CHECKED(JSFunction, raw_target, args[0]);
1199 Handle<JSFunction> target(raw_target);
1200 Handle<Object> code = args.at<Object>(1);
1201
1202 Handle<Context> context(target->context());
1203
1204 if (!code->IsNull()) {
1205 RUNTIME_ASSERT(code->IsJSFunction());
1206 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1207 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1208 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1209 return Failure::Exception();
1210 }
1211 // Set the code, formal parameter count, and the length of the target
1212 // function.
1213 target->set_code(fun->code());
1214 target->shared()->set_length(fun->shared()->length());
1215 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001216 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001217 // Set the source code of the target function to undefined.
1218 // SetCode is only used for built-in constructors like String,
1219 // Array, and Object, and some web code
1220 // doesn't like seeing source code for constructors.
1221 target->shared()->set_script(Heap::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001222 context = Handle<Context>(fun->context());
1223
1224 // Make sure we get a fresh copy of the literal vector to avoid
1225 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001226 int number_of_literals = fun->NumberOfLiterals();
1227 Handle<FixedArray> literals =
1228 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001229 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001230 // Insert the object, regexp and array functions in the literals
1231 // array prefix. These are the functions that will be used when
1232 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001233 literals->set(JSFunction::kLiteralGlobalContextIndex,
1234 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001235 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001236 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001237 }
1238
1239 target->set_context(*context);
1240 return *target;
1241}
1242
1243
1244static Object* CharCodeAt(String* subject, Object* index) {
1245 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001246 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001247 // Flatten the string. If someone wants to get a char at an index
1248 // in a cons string, it is likely that more indices will be
1249 // accessed.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001250 subject->TryFlattenIfNotFlat();
1251 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001252 return Heap::nan_value();
1253 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001254 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001255}
1256
1257
1258static Object* Runtime_StringCharCodeAt(Arguments args) {
1259 NoHandleAllocation ha;
1260 ASSERT(args.length() == 2);
1261
1262 CONVERT_CHECKED(String, subject, args[0]);
1263 Object* index = args[1];
1264 return CharCodeAt(subject, index);
1265}
1266
1267
1268static Object* Runtime_CharFromCode(Arguments args) {
1269 NoHandleAllocation ha;
1270 ASSERT(args.length() == 1);
1271 uint32_t code;
1272 if (Array::IndexFromObject(args[0], &code)) {
1273 if (code <= 0xffff) {
1274 return Heap::LookupSingleCharacterStringFromCode(code);
1275 }
1276 }
1277 return Heap::empty_string();
1278}
1279
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001280// Forward declarations.
1281static const int kStringBuilderConcatHelperLengthBits = 11;
1282static const int kStringBuilderConcatHelperPositionBits = 19;
1283
1284template <typename schar>
1285static inline void StringBuilderConcatHelper(String*,
1286 schar*,
1287 FixedArray*,
1288 int);
1289
1290typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1291typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1292
1293class ReplacementStringBuilder {
1294 public:
1295 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1296 : subject_(subject),
1297 parts_(Factory::NewFixedArray(estimated_part_count)),
1298 part_count_(0),
1299 character_count_(0),
1300 is_ascii_(StringShape(*subject).IsAsciiRepresentation()) {
1301 // Require a non-zero initial size. Ensures that doubling the size to
1302 // extend the array will work.
1303 ASSERT(estimated_part_count > 0);
1304 }
1305
1306 void EnsureCapacity(int elements) {
1307 int length = parts_->length();
1308 int required_length = part_count_ + elements;
1309 if (length < required_length) {
1310 int new_length = length;
1311 do {
1312 new_length *= 2;
1313 } while (new_length < required_length);
1314 Handle<FixedArray> extended_array =
1315 Factory::NewFixedArray(new_length);
1316 parts_->CopyTo(0, *extended_array, 0, part_count_);
1317 parts_ = extended_array;
1318 }
1319 }
1320
1321 void AddSubjectSlice(int from, int to) {
1322 ASSERT(from >= 0);
1323 int length = to - from;
1324 ASSERT(length > 0);
1325 // Can we encode the slice in 11 bits for length and 19 bits for
1326 // start position - as used by StringBuilderConcatHelper?
1327 if (StringBuilderSubstringLength::is_valid(length) &&
1328 StringBuilderSubstringPosition::is_valid(from)) {
1329 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1330 StringBuilderSubstringPosition::encode(from);
1331 AddElement(Smi::FromInt(encoded_slice));
1332 } else {
1333 Handle<String> slice = Factory::NewStringSlice(subject_, from, to);
1334 AddElement(*slice);
1335 }
1336 IncrementCharacterCount(length);
1337 }
1338
1339
1340 void AddString(Handle<String> string) {
1341 int length = string->length();
1342 ASSERT(length > 0);
1343 AddElement(*string);
1344 if (!StringShape(*string).IsAsciiRepresentation()) {
1345 is_ascii_ = false;
1346 }
1347 IncrementCharacterCount(length);
1348 }
1349
1350
1351 Handle<String> ToString() {
1352 if (part_count_ == 0) {
1353 return Factory::empty_string();
1354 }
1355
1356 Handle<String> joined_string;
1357 if (is_ascii_) {
1358 joined_string = NewRawAsciiString(character_count_);
1359 AssertNoAllocation no_alloc;
1360 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1361 char* char_buffer = seq->GetChars();
1362 StringBuilderConcatHelper(*subject_,
1363 char_buffer,
1364 *parts_,
1365 part_count_);
1366 } else {
1367 // Non-ASCII.
1368 joined_string = NewRawTwoByteString(character_count_);
1369 AssertNoAllocation no_alloc;
1370 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1371 uc16* char_buffer = seq->GetChars();
1372 StringBuilderConcatHelper(*subject_,
1373 char_buffer,
1374 *parts_,
1375 part_count_);
1376 }
1377 return joined_string;
1378 }
1379
1380
1381 void IncrementCharacterCount(int by) {
1382 if (character_count_ > Smi::kMaxValue - by) {
1383 V8::FatalProcessOutOfMemory("String.replace result too large.");
1384 }
1385 character_count_ += by;
1386 }
1387
1388 private:
1389
1390 Handle<String> NewRawAsciiString(int size) {
1391 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1392 }
1393
1394
1395 Handle<String> NewRawTwoByteString(int size) {
1396 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1397 }
1398
1399
1400 void AddElement(Object* element) {
1401 ASSERT(element->IsSmi() || element->IsString());
1402 parts_->set(part_count_, element);
1403 part_count_++;
1404 }
1405
1406 Handle<String> subject_;
1407 Handle<FixedArray> parts_;
1408 int part_count_;
1409 int character_count_;
1410 bool is_ascii_;
1411};
1412
1413
1414class CompiledReplacement {
1415 public:
1416 CompiledReplacement()
1417 : parts_(1), replacement_substrings_(0) {}
1418
1419 void Compile(Handle<String> replacement,
1420 int capture_count,
1421 int subject_length);
1422
1423 void Apply(ReplacementStringBuilder* builder,
1424 int match_from,
1425 int match_to,
1426 Handle<JSArray> last_match_info);
1427
1428 // Number of distinct parts of the replacement pattern.
1429 int parts() {
1430 return parts_.length();
1431 }
1432 private:
1433 enum PartType {
1434 SUBJECT_PREFIX = 1,
1435 SUBJECT_SUFFIX,
1436 SUBJECT_CAPTURE,
1437 REPLACEMENT_SUBSTRING,
1438 REPLACEMENT_STRING,
1439
1440 NUMBER_OF_PART_TYPES
1441 };
1442
1443 struct ReplacementPart {
1444 static inline ReplacementPart SubjectMatch() {
1445 return ReplacementPart(SUBJECT_CAPTURE, 0);
1446 }
1447 static inline ReplacementPart SubjectCapture(int capture_index) {
1448 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1449 }
1450 static inline ReplacementPart SubjectPrefix() {
1451 return ReplacementPart(SUBJECT_PREFIX, 0);
1452 }
1453 static inline ReplacementPart SubjectSuffix(int subject_length) {
1454 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1455 }
1456 static inline ReplacementPart ReplacementString() {
1457 return ReplacementPart(REPLACEMENT_STRING, 0);
1458 }
1459 static inline ReplacementPart ReplacementSubString(int from, int to) {
1460 ASSERT(from >= 0);
1461 ASSERT(to > from);
1462 return ReplacementPart(-from, to);
1463 }
1464
1465 // If tag <= 0 then it is the negation of a start index of a substring of
1466 // the replacement pattern, otherwise it's a value from PartType.
1467 ReplacementPart(int tag, int data)
1468 : tag(tag), data(data) {
1469 // Must be non-positive or a PartType value.
1470 ASSERT(tag < NUMBER_OF_PART_TYPES);
1471 }
1472 // Either a value of PartType or a non-positive number that is
1473 // the negation of an index into the replacement string.
1474 int tag;
1475 // The data value's interpretation depends on the value of tag:
1476 // tag == SUBJECT_PREFIX ||
1477 // tag == SUBJECT_SUFFIX: data is unused.
1478 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1479 // tag == REPLACEMENT_SUBSTRING ||
1480 // tag == REPLACEMENT_STRING: data is index into array of substrings
1481 // of the replacement string.
1482 // tag <= 0: Temporary representation of the substring of the replacement
1483 // string ranging over -tag .. data.
1484 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1485 // substring objects.
1486 int data;
1487 };
1488
1489 template<typename Char>
1490 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1491 Vector<Char> characters,
1492 int capture_count,
1493 int subject_length) {
1494 int length = characters.length();
1495 int last = 0;
1496 for (int i = 0; i < length; i++) {
1497 Char c = characters[i];
1498 if (c == '$') {
1499 int next_index = i + 1;
1500 if (next_index == length) { // No next character!
1501 break;
1502 }
1503 Char c2 = characters[next_index];
1504 switch (c2) {
1505 case '$':
1506 if (i > last) {
1507 // There is a substring before. Include the first "$".
1508 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1509 last = next_index + 1; // Continue after the second "$".
1510 } else {
1511 // Let the next substring start with the second "$".
1512 last = next_index;
1513 }
1514 i = next_index;
1515 break;
1516 case '`':
1517 if (i > last) {
1518 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1519 }
1520 parts->Add(ReplacementPart::SubjectPrefix());
1521 i = next_index;
1522 last = i + 1;
1523 break;
1524 case '\'':
1525 if (i > last) {
1526 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1527 }
1528 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1529 i = next_index;
1530 last = i + 1;
1531 break;
1532 case '&':
1533 if (i > last) {
1534 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1535 }
1536 parts->Add(ReplacementPart::SubjectMatch());
1537 i = next_index;
1538 last = i + 1;
1539 break;
1540 case '0':
1541 case '1':
1542 case '2':
1543 case '3':
1544 case '4':
1545 case '5':
1546 case '6':
1547 case '7':
1548 case '8':
1549 case '9': {
1550 int capture_ref = c2 - '0';
1551 if (capture_ref > capture_count) {
1552 i = next_index;
1553 continue;
1554 }
1555 int second_digit_index = next_index + 1;
1556 if (second_digit_index < length) {
1557 // Peek ahead to see if we have two digits.
1558 Char c3 = characters[second_digit_index];
1559 if ('0' <= c3 && c3 <= '9') { // Double digits.
1560 int double_digit_ref = capture_ref * 10 + c3 - '0';
1561 if (double_digit_ref <= capture_count) {
1562 next_index = second_digit_index;
1563 capture_ref = double_digit_ref;
1564 }
1565 }
1566 }
1567 if (capture_ref > 0) {
1568 if (i > last) {
1569 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1570 }
1571 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1572 last = next_index + 1;
1573 }
1574 i = next_index;
1575 break;
1576 }
1577 default:
1578 i = next_index;
1579 break;
1580 }
1581 }
1582 }
1583 if (length > last) {
1584 if (last == 0) {
1585 parts->Add(ReplacementPart::ReplacementString());
1586 } else {
1587 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1588 }
1589 }
1590 }
1591
1592 ZoneList<ReplacementPart> parts_;
1593 ZoneList<Handle<String> > replacement_substrings_;
1594};
1595
1596
1597void CompiledReplacement::Compile(Handle<String> replacement,
1598 int capture_count,
1599 int subject_length) {
1600 ASSERT(replacement->IsFlat());
1601 if (StringShape(*replacement).IsAsciiRepresentation()) {
1602 AssertNoAllocation no_alloc;
1603 ParseReplacementPattern(&parts_,
1604 replacement->ToAsciiVector(),
1605 capture_count,
1606 subject_length);
1607 } else {
1608 ASSERT(StringShape(*replacement).IsTwoByteRepresentation());
1609 AssertNoAllocation no_alloc;
1610
1611 ParseReplacementPattern(&parts_,
1612 replacement->ToUC16Vector(),
1613 capture_count,
1614 subject_length);
1615 }
1616 // Find substrings of replacement string and create them as String objects..
1617 int substring_index = 0;
1618 for (int i = 0, n = parts_.length(); i < n; i++) {
1619 int tag = parts_[i].tag;
1620 if (tag <= 0) { // A replacement string slice.
1621 int from = -tag;
1622 int to = parts_[i].data;
1623 replacement_substrings_.Add(Factory::NewStringSlice(replacement,
1624 from,
1625 to));
1626 parts_[i].tag = REPLACEMENT_SUBSTRING;
1627 parts_[i].data = substring_index;
1628 substring_index++;
1629 } else if (tag == REPLACEMENT_STRING) {
1630 replacement_substrings_.Add(replacement);
1631 parts_[i].data = substring_index;
1632 substring_index++;
1633 }
1634 }
1635}
1636
1637
1638void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1639 int match_from,
1640 int match_to,
1641 Handle<JSArray> last_match_info) {
1642 for (int i = 0, n = parts_.length(); i < n; i++) {
1643 ReplacementPart part = parts_[i];
1644 switch (part.tag) {
1645 case SUBJECT_PREFIX:
1646 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1647 break;
1648 case SUBJECT_SUFFIX: {
1649 int subject_length = part.data;
1650 if (match_to < subject_length) {
1651 builder->AddSubjectSlice(match_to, subject_length);
1652 }
1653 break;
1654 }
1655 case SUBJECT_CAPTURE: {
1656 int capture = part.data;
1657 FixedArray* match_info = last_match_info->elements();
1658 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1659 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1660 if (from >= 0 && to > from) {
1661 builder->AddSubjectSlice(from, to);
1662 }
1663 break;
1664 }
1665 case REPLACEMENT_SUBSTRING:
1666 case REPLACEMENT_STRING:
1667 builder->AddString(replacement_substrings_[part.data]);
1668 break;
1669 default:
1670 UNREACHABLE();
1671 }
1672 }
1673}
1674
1675
1676
1677static Object* StringReplaceRegExpWithString(String* subject,
1678 JSRegExp* regexp,
1679 String* replacement,
1680 JSArray* last_match_info) {
1681 ASSERT(subject->IsFlat());
1682 ASSERT(replacement->IsFlat());
1683
1684 HandleScope handles;
1685
1686 int length = subject->length();
1687 Handle<String> subject_handle(subject);
1688 Handle<JSRegExp> regexp_handle(regexp);
1689 Handle<String> replacement_handle(replacement);
1690 Handle<JSArray> last_match_info_handle(last_match_info);
1691 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1692 subject_handle,
1693 0,
1694 last_match_info_handle);
1695 if (match.is_null()) {
1696 return Failure::Exception();
1697 }
1698 if (match->IsNull()) {
1699 return *subject_handle;
1700 }
1701
1702 int capture_count = regexp_handle->CaptureCount();
1703
1704 // CompiledReplacement uses zone allocation.
1705 ZoneScope zone(DELETE_ON_EXIT);
1706 CompiledReplacement compiled_replacement;
1707 compiled_replacement.Compile(replacement_handle,
1708 capture_count,
1709 length);
1710
1711 bool is_global = regexp_handle->GetFlags().is_global();
1712
1713 // Guessing the number of parts that the final result string is built
1714 // from. Global regexps can match any number of times, so we guess
1715 // conservatively.
1716 int expected_parts =
1717 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1718 ReplacementStringBuilder builder(subject_handle, expected_parts);
1719
1720 // Index of end of last match.
1721 int prev = 0;
1722
1723 // Number of parts added by compiled replacement plus preceeding string
1724 // and possibly suffix after last match.
1725 const int parts_added_per_loop = compiled_replacement.parts() + 2;
1726 bool matched = true;
1727 do {
1728 ASSERT(last_match_info_handle->HasFastElements());
1729 // Increase the capacity of the builder before entering local handle-scope,
1730 // so its internal buffer can safely allocate a new handle if it grows.
1731 builder.EnsureCapacity(parts_added_per_loop);
1732
1733 HandleScope loop_scope;
1734 int start, end;
1735 {
1736 AssertNoAllocation match_info_array_is_not_in_a_handle;
1737 FixedArray* match_info_array = last_match_info_handle->elements();
1738
1739 ASSERT_EQ(capture_count * 2 + 2,
1740 RegExpImpl::GetLastCaptureCount(match_info_array));
1741 start = RegExpImpl::GetCapture(match_info_array, 0);
1742 end = RegExpImpl::GetCapture(match_info_array, 1);
1743 }
1744
1745 if (prev < start) {
1746 builder.AddSubjectSlice(prev, start);
1747 }
1748 compiled_replacement.Apply(&builder,
1749 start,
1750 end,
1751 last_match_info_handle);
1752 prev = end;
1753
1754 // Only continue checking for global regexps.
1755 if (!is_global) break;
1756
1757 // Continue from where the match ended, unless it was an empty match.
1758 int next = end;
1759 if (start == end) {
1760 next = end + 1;
1761 if (next > length) break;
1762 }
1763
1764 match = RegExpImpl::Exec(regexp_handle,
1765 subject_handle,
1766 next,
1767 last_match_info_handle);
1768 if (match.is_null()) {
1769 return Failure::Exception();
1770 }
1771 matched = !match->IsNull();
1772 } while (matched);
1773
1774 if (prev < length) {
1775 builder.AddSubjectSlice(prev, length);
1776 }
1777
1778 return *(builder.ToString());
1779}
1780
1781
1782static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1783 ASSERT(args.length() == 4);
1784
1785 CONVERT_CHECKED(String, subject, args[0]);
1786 if (!subject->IsFlat()) {
1787 Object* flat_subject = subject->TryFlatten();
1788 if (flat_subject->IsFailure()) {
1789 return flat_subject;
1790 }
1791 subject = String::cast(flat_subject);
1792 }
1793
1794 CONVERT_CHECKED(String, replacement, args[2]);
1795 if (!replacement->IsFlat()) {
1796 Object* flat_replacement = replacement->TryFlatten();
1797 if (flat_replacement->IsFailure()) {
1798 return flat_replacement;
1799 }
1800 replacement = String::cast(flat_replacement);
1801 }
1802
1803 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1804 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1805
1806 ASSERT(last_match_info->HasFastElements());
1807
1808 return StringReplaceRegExpWithString(subject,
1809 regexp,
1810 replacement,
1811 last_match_info);
1812}
1813
1814
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001815
ager@chromium.org7c537e22008-10-16 08:43:32 +00001816// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1817// limit, we can fix the size of tables.
1818static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001819// Reduce alphabet to this size.
1820static const int kBMAlphabetSize = 0x100;
1821// For patterns below this length, the skip length of Boyer-Moore is too short
1822// to compensate for the algorithmic overhead compared to simple brute force.
1823static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001824
ager@chromium.org7c537e22008-10-16 08:43:32 +00001825// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1826// shift. Only allows the last kBMMaxShift characters of the needle
1827// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001828class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001829 public:
1830 BMGoodSuffixBuffers() {}
1831 inline void init(int needle_length) {
1832 ASSERT(needle_length > 1);
1833 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1834 int len = needle_length - start;
1835 biased_suffixes_ = suffixes_ - start;
1836 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1837 for (int i = 0; i <= len; i++) {
1838 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001839 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001840 }
1841 inline int& suffix(int index) {
1842 ASSERT(biased_suffixes_ + index >= suffixes_);
1843 return biased_suffixes_[index];
1844 }
1845 inline int& shift(int index) {
1846 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1847 return biased_good_suffix_shift_[index];
1848 }
1849 private:
1850 int suffixes_[kBMMaxShift + 1];
1851 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001852 int* biased_suffixes_;
1853 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001854 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1855};
1856
1857// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001858static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001859static BMGoodSuffixBuffers bmgs_buffers;
1860
1861// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001862template <typename pchar>
1863static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1864 int start) {
1865 // Run forwards to populate bad_char_table, so that *last* instance
1866 // of character equivalence class is the one registered.
1867 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001868 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1869 : kBMAlphabetSize;
1870 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001871 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001872 } else {
1873 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001874 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001875 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001876 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001877 for (int i = start; i < pattern.length() - 1; i++) {
1878 pchar c = pattern[i];
1879 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001880 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001881 }
1882}
1883
1884template <typename pchar>
1885static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001886 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001887 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001888 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001889 // Compute Good Suffix tables.
1890 bmgs_buffers.init(m);
1891
1892 bmgs_buffers.shift(m-1) = 1;
1893 bmgs_buffers.suffix(m) = m + 1;
1894 pchar last_char = pattern[m - 1];
1895 int suffix = m + 1;
1896 for (int i = m; i > start;) {
1897 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1898 if (bmgs_buffers.shift(suffix) == len) {
1899 bmgs_buffers.shift(suffix) = suffix - i;
1900 }
1901 suffix = bmgs_buffers.suffix(suffix);
1902 }
1903 i--;
1904 suffix--;
1905 bmgs_buffers.suffix(i) = suffix;
1906 if (suffix == m) {
1907 // No suffix to extend, so we check against last_char only.
1908 while (i > start && pattern[i - 1] != last_char) {
1909 if (bmgs_buffers.shift(m) == len) {
1910 bmgs_buffers.shift(m) = m - i;
1911 }
1912 i--;
1913 bmgs_buffers.suffix(i) = m;
1914 }
1915 if (i > start) {
1916 i--;
1917 suffix--;
1918 bmgs_buffers.suffix(i) = suffix;
1919 }
1920 }
1921 }
1922 if (suffix < m) {
1923 for (int i = start; i <= m; i++) {
1924 if (bmgs_buffers.shift(i) == len) {
1925 bmgs_buffers.shift(i) = suffix - start;
1926 }
1927 if (i == suffix) {
1928 suffix = bmgs_buffers.suffix(suffix);
1929 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001930 }
1931 }
1932}
1933
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001934template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001935static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001936 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001937 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001938 }
1939 if (sizeof(pchar) == 1) {
1940 if (char_code > String::kMaxAsciiCharCode) {
1941 return -1;
1942 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001943 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001944 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001945 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001946}
1947
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001948// Restricted simplified Boyer-Moore string matching.
1949// Uses only the bad-shift table of Boyer-Moore and only uses it
1950// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001951template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001952static int BoyerMooreHorspool(Vector<const schar> subject,
1953 Vector<const pchar> pattern,
1954 int start_index,
1955 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001956 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001957 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001958 // Only preprocess at most kBMMaxShift last characters of pattern.
1959 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001960
ager@chromium.org7c537e22008-10-16 08:43:32 +00001961 BoyerMoorePopulateBadCharTable(pattern, start);
1962
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001963 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001964 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001965 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001966 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001967 // Perform search
1968 for (idx = start_index; idx <= n - m;) {
1969 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001970 int c;
1971 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001972 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001973 int shift = j - bc_occ;
1974 idx += shift;
1975 badness += 1 - shift; // at most zero, so badness cannot increase.
1976 if (idx > n - m) {
1977 *complete = true;
1978 return -1;
1979 }
1980 }
1981 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001982 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001983 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001984 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001985 return idx;
1986 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001987 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001988 // Badness increases by the number of characters we have
1989 // checked, and decreases by the number of characters we
1990 // can skip by shifting. It's a measure of how we are doing
1991 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001992 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001993 if (badness > 0) {
1994 *complete = false;
1995 return idx;
1996 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001997 }
1998 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001999 *complete = true;
2000 return -1;
2001}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002002
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002003
2004template <typename schar, typename pchar>
2005static int BoyerMooreIndexOf(Vector<const schar> subject,
2006 Vector<const pchar> pattern,
2007 int idx) {
2008 int n = subject.length();
2009 int m = pattern.length();
2010 // Only preprocess at most kBMMaxShift last characters of pattern.
2011 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2012
2013 // Build the Good Suffix table and continue searching.
2014 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2015 pchar last_char = pattern[m - 1];
2016 // Continue search from i.
2017 do {
2018 int j = m - 1;
2019 schar c;
2020 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002021 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002022 idx += shift;
2023 if (idx > n - m) {
2024 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002025 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002026 }
2027 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2028 if (j < 0) {
2029 return idx;
2030 } else if (j < start) {
2031 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002032 // Fall back on BMH shift.
2033 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002034 } else {
2035 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002036 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002037 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002038 if (gs_shift > shift) {
2039 shift = gs_shift;
2040 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002041 idx += shift;
2042 }
2043 } while (idx <= n - m);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002044
2045 return -1;
2046}
2047
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002048
2049template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002050static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002051 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002052 int start_index) {
2053 for (int i = start_index, n = string.length(); i < n; i++) {
2054 if (pattern_char == string[i]) {
2055 return i;
2056 }
2057 }
2058 return -1;
2059}
2060
2061// Trivial string search for shorter strings.
2062// On return, if "complete" is set to true, the return value is the
2063// final result of searching for the patter in the subject.
2064// If "complete" is set to false, the return value is the index where
2065// further checking should start, i.e., it's guaranteed that the pattern
2066// does not occur at a position prior to the returned index.
2067template <typename pchar, typename schar>
2068static int SimpleIndexOf(Vector<const schar> subject,
2069 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002070 int idx,
2071 bool* complete) {
2072 // Badness is a count of how much work we have done. When we have
2073 // done enough work we decide it's probably worth switching to a better
2074 // algorithm.
2075 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002076 // We know our pattern is at least 2 characters, we cache the first so
2077 // the common case of the first character not matching is faster.
2078 pchar pattern_first_char = pattern[0];
2079
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002080 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2081 badness++;
2082 if (badness > 0) {
2083 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002084 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002085 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002086 if (subject[i] != pattern_first_char) continue;
2087 int j = 1;
2088 do {
2089 if (pattern[j] != subject[i+j]) {
2090 break;
2091 }
2092 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002093 } while (j < pattern.length());
2094 if (j == pattern.length()) {
2095 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002096 return i;
2097 }
2098 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002099 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002100 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002101 return -1;
2102}
2103
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002104// Simple indexOf that never bails out. For short patterns only.
2105template <typename pchar, typename schar>
2106static int SimpleIndexOf(Vector<const schar> subject,
2107 Vector<const pchar> pattern,
2108 int idx) {
2109 pchar pattern_first_char = pattern[0];
2110 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2111 if (subject[i] != pattern_first_char) continue;
2112 int j = 1;
2113 do {
2114 if (pattern[j] != subject[i+j]) {
2115 break;
2116 }
2117 j++;
2118 } while (j < pattern.length());
2119 if (j == pattern.length()) {
2120 return i;
2121 }
2122 }
2123 return -1;
2124}
2125
2126
2127// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002128template <typename schar, typename pchar>
2129static int StringMatchStrategy(Vector<const schar> sub,
2130 Vector<const pchar> pat,
2131 int start_index) {
2132 ASSERT(pat.length() > 1);
2133
2134 // We have an ASCII haystack and a non-ASCII needle. Check if there
2135 // really is a non-ASCII character in the needle and bail out if there
2136 // is.
2137 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2138 for (int i = 0; i < pat.length(); i++) {
2139 uc16 c = pat[i];
2140 if (c > String::kMaxAsciiCharCode) {
2141 return -1;
2142 }
2143 }
2144 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002145 if (pat.length() < kBMMinPatternLength) {
2146 // We don't believe fancy searching can ever be more efficient.
2147 // The max shift of Boyer-Moore on a pattern of this length does
2148 // not compensate for the overhead.
2149 return SimpleIndexOf(sub, pat, start_index);
2150 }
2151 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002152 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002153 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2154 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002155 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002156 if (complete) return idx;
2157 return BoyerMooreIndexOf(sub, pat, idx);
2158}
2159
2160// Perform string match of pattern on subject, starting at start index.
2161// Caller must ensure that 0 <= start_index <= sub->length(),
2162// and should check that pat->length() + start_index <= sub->length()
2163int Runtime::StringMatch(Handle<String> sub,
2164 Handle<String> pat,
2165 int start_index) {
2166 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002167 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002168
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002169 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002170 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002171
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002172 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002173 if (start_index + pattern_length > subject_length) return -1;
2174
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002175 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002176 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002177 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002178 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002179 // character patterns linear search is necessary, so any smart
2180 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002181 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002182 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002183 if (StringShape(*sub).IsAsciiRepresentation()) {
2184 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002185 if (pchar > String::kMaxAsciiCharCode) {
2186 return -1;
2187 }
2188 Vector<const char> ascii_vector =
2189 sub->ToAsciiVector().SubVector(start_index, subject_length);
2190 const void* pos = memchr(ascii_vector.start(),
2191 static_cast<const char>(pchar),
2192 static_cast<size_t>(ascii_vector.length()));
2193 if (pos == NULL) {
2194 return -1;
2195 }
2196 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
2197 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002198 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002199 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002200 }
2201
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002202 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002203 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002204 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002205
ager@chromium.org7c537e22008-10-16 08:43:32 +00002206 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2207 // dispatch on type of strings
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002208 if (StringShape(*pat).IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002209 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002210 if (StringShape(*sub).IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002211 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002212 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002213 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002214 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002215 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002216 if (StringShape(*sub).IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002217 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002218 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002219 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002220}
2221
2222
2223static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002224 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002225 ASSERT(args.length() == 3);
2226
ager@chromium.org7c537e22008-10-16 08:43:32 +00002227 CONVERT_ARG_CHECKED(String, sub, 0);
2228 CONVERT_ARG_CHECKED(String, pat, 1);
2229
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002230 Object* index = args[2];
2231 uint32_t start_index;
2232 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2233
ager@chromium.org870a0b62008-11-04 11:43:05 +00002234 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002235 int position = Runtime::StringMatch(sub, pat, start_index);
2236 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002237}
2238
2239
2240static Object* Runtime_StringLastIndexOf(Arguments args) {
2241 NoHandleAllocation ha;
2242 ASSERT(args.length() == 3);
2243
2244 CONVERT_CHECKED(String, sub, args[0]);
2245 CONVERT_CHECKED(String, pat, args[1]);
2246 Object* index = args[2];
2247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002248 sub->TryFlattenIfNotFlat();
2249 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002250
2251 uint32_t start_index;
2252 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2253
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002254 uint32_t pattern_length = pat->length();
2255 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002256
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002257 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002258 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002259 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002260
2261 for (int i = start_index; i >= 0; i--) {
2262 bool found = true;
2263 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002264 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002265 found = false;
2266 break;
2267 }
2268 }
2269 if (found) return Smi::FromInt(i);
2270 }
2271
2272 return Smi::FromInt(-1);
2273}
2274
2275
2276static Object* Runtime_StringLocaleCompare(Arguments args) {
2277 NoHandleAllocation ha;
2278 ASSERT(args.length() == 2);
2279
2280 CONVERT_CHECKED(String, str1, args[0]);
2281 CONVERT_CHECKED(String, str2, args[1]);
2282
2283 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002284 int str1_length = str1->length();
2285 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002286
2287 // Decide trivial cases without flattening.
2288 if (str1_length == 0) {
2289 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2290 return Smi::FromInt(-str2_length);
2291 } else {
2292 if (str2_length == 0) return Smi::FromInt(str1_length);
2293 }
2294
2295 int end = str1_length < str2_length ? str1_length : str2_length;
2296
2297 // No need to flatten if we are going to find the answer on the first
2298 // character. At this point we know there is at least one character
2299 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002300 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002301 if (d != 0) return Smi::FromInt(d);
2302
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002303 str1->TryFlattenIfNotFlat();
2304 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002305
2306 static StringInputBuffer buf1;
2307 static StringInputBuffer buf2;
2308
2309 buf1.Reset(str1);
2310 buf2.Reset(str2);
2311
2312 for (int i = 0; i < end; i++) {
2313 uint16_t char1 = buf1.GetNext();
2314 uint16_t char2 = buf2.GetNext();
2315 if (char1 != char2) return Smi::FromInt(char1 - char2);
2316 }
2317
2318 return Smi::FromInt(str1_length - str2_length);
2319}
2320
2321
2322static Object* Runtime_StringSlice(Arguments args) {
2323 NoHandleAllocation ha;
2324 ASSERT(args.length() == 3);
2325
2326 CONVERT_CHECKED(String, value, args[0]);
2327 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
2328 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
2329
2330 int start = FastD2I(from_number);
2331 int end = FastD2I(to_number);
2332
2333 RUNTIME_ASSERT(end >= start);
2334 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002335 RUNTIME_ASSERT(end <= value->length());
2336 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002337}
2338
2339
2340static Object* Runtime_NumberToRadixString(Arguments args) {
2341 NoHandleAllocation ha;
2342 ASSERT(args.length() == 2);
2343
2344 CONVERT_DOUBLE_CHECKED(value, args[0]);
2345 if (isnan(value)) {
2346 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2347 }
2348 if (isinf(value)) {
2349 if (value < 0) {
2350 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2351 }
2352 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2353 }
2354 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2355 int radix = FastD2I(radix_number);
2356 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2357 char* str = DoubleToRadixCString(value, radix);
2358 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2359 DeleteArray(str);
2360 return result;
2361}
2362
2363
2364static Object* Runtime_NumberToFixed(Arguments args) {
2365 NoHandleAllocation ha;
2366 ASSERT(args.length() == 2);
2367
2368 CONVERT_DOUBLE_CHECKED(value, args[0]);
2369 if (isnan(value)) {
2370 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2371 }
2372 if (isinf(value)) {
2373 if (value < 0) {
2374 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2375 }
2376 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2377 }
2378 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2379 int f = FastD2I(f_number);
2380 RUNTIME_ASSERT(f >= 0);
2381 char* str = DoubleToFixedCString(value, f);
2382 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2383 DeleteArray(str);
2384 return res;
2385}
2386
2387
2388static Object* Runtime_NumberToExponential(Arguments args) {
2389 NoHandleAllocation ha;
2390 ASSERT(args.length() == 2);
2391
2392 CONVERT_DOUBLE_CHECKED(value, args[0]);
2393 if (isnan(value)) {
2394 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2395 }
2396 if (isinf(value)) {
2397 if (value < 0) {
2398 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2399 }
2400 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2401 }
2402 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2403 int f = FastD2I(f_number);
2404 RUNTIME_ASSERT(f >= -1 && f <= 20);
2405 char* str = DoubleToExponentialCString(value, f);
2406 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2407 DeleteArray(str);
2408 return res;
2409}
2410
2411
2412static Object* Runtime_NumberToPrecision(Arguments args) {
2413 NoHandleAllocation ha;
2414 ASSERT(args.length() == 2);
2415
2416 CONVERT_DOUBLE_CHECKED(value, args[0]);
2417 if (isnan(value)) {
2418 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2419 }
2420 if (isinf(value)) {
2421 if (value < 0) {
2422 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2423 }
2424 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2425 }
2426 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2427 int f = FastD2I(f_number);
2428 RUNTIME_ASSERT(f >= 1 && f <= 21);
2429 char* str = DoubleToPrecisionCString(value, f);
2430 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2431 DeleteArray(str);
2432 return res;
2433}
2434
2435
2436// Returns a single character string where first character equals
2437// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002438static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002439 if (index < static_cast<uint32_t>(string->length())) {
2440 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002441 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002442 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002443 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002444 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002445}
2446
2447
2448Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2449 // Handle [] indexing on Strings
2450 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002451 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2452 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002453 }
2454
2455 // Handle [] indexing on String objects
2456 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002457 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2458 Handle<Object> result =
2459 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2460 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002461 }
2462
2463 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002464 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002465 return prototype->GetElement(index);
2466 }
2467
2468 return object->GetElement(index);
2469}
2470
2471
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002472Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2473 HandleScope scope;
2474
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002475 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002476 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002477 Handle<Object> error =
2478 Factory::NewTypeError("non_object_property_load",
2479 HandleVector(args, 2));
2480 return Top::Throw(*error);
2481 }
2482
2483 // Check if the given key is an array index.
2484 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002485 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002486 return GetElementOrCharAt(object, index);
2487 }
2488
2489 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002490 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002491 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002492 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002493 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002494 bool has_pending_exception = false;
2495 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002496 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002497 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002498 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002499 }
2500
ager@chromium.org32912102009-01-16 10:38:43 +00002501 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002502 // the element if so.
2503 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002504 return GetElementOrCharAt(object, index);
2505 } else {
2506 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002507 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002508 }
2509}
2510
2511
2512static Object* Runtime_GetProperty(Arguments args) {
2513 NoHandleAllocation ha;
2514 ASSERT(args.length() == 2);
2515
2516 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002517 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002518
2519 return Runtime::GetObjectProperty(object, key);
2520}
2521
2522
ager@chromium.org7c537e22008-10-16 08:43:32 +00002523
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002524// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002525static Object* Runtime_KeyedGetProperty(Arguments args) {
2526 NoHandleAllocation ha;
2527 ASSERT(args.length() == 2);
2528
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002529 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002530 // itself.
2531 //
2532 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002533 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002534 // global proxy object never has properties. This is the case
2535 // because the global proxy object forwards everything to its hidden
2536 // prototype including local lookups.
2537 //
2538 // Additionally, we need to make sure that we do not cache results
2539 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002540 if (args[0]->IsJSObject() &&
2541 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002542 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002543 args[1]->IsString()) {
2544 JSObject* receiver = JSObject::cast(args[0]);
2545 String* key = String::cast(args[1]);
2546 if (receiver->HasFastProperties()) {
2547 // Attempt to use lookup cache.
2548 Object* obj = Heap::GetKeyedLookupCache();
2549 if (obj->IsFailure()) return obj;
2550 LookupCache* cache = LookupCache::cast(obj);
2551 Map* receiver_map = receiver->map();
2552 int offset = cache->Lookup(receiver_map, key);
2553 if (offset != LookupCache::kNotFound) {
2554 Object* value = receiver->FastPropertyAt(offset);
2555 return value->IsTheHole() ? Heap::undefined_value() : value;
2556 }
2557 // Lookup cache miss. Perform lookup and update the cache if
2558 // appropriate.
2559 LookupResult result;
2560 receiver->LocalLookup(key, &result);
2561 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2562 int offset = result.GetFieldIndex();
2563 Object* obj = cache->Put(receiver_map, key, offset);
2564 if (obj->IsFailure()) return obj;
2565 Heap::SetKeyedLookupCache(LookupCache::cast(obj));
2566 Object* value = receiver->FastPropertyAt(offset);
2567 return value->IsTheHole() ? Heap::undefined_value() : value;
2568 }
2569 } else {
2570 // Attempt dictionary lookup.
2571 Dictionary* dictionary = receiver->property_dictionary();
2572 int entry = dictionary->FindStringEntry(key);
2573 if ((entry != DescriptorArray::kNotFound) &&
2574 (dictionary->DetailsAt(entry).type() == NORMAL)) {
2575 return dictionary->ValueAt(entry);
2576 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002577 }
2578 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002579
2580 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002581 return Runtime::GetObjectProperty(args.at<Object>(0),
2582 args.at<Object>(1));
2583}
2584
2585
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002586Object* Runtime::SetObjectProperty(Handle<Object> object,
2587 Handle<Object> key,
2588 Handle<Object> value,
2589 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002590 HandleScope scope;
2591
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002592 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002593 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002594 Handle<Object> error =
2595 Factory::NewTypeError("non_object_property_store",
2596 HandleVector(args, 2));
2597 return Top::Throw(*error);
2598 }
2599
2600 // If the object isn't a JavaScript object, we ignore the store.
2601 if (!object->IsJSObject()) return *value;
2602
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002603 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2604
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002605 // Check if the given key is an array index.
2606 uint32_t index;
2607 if (Array::IndexFromObject(*key, &index)) {
2608 ASSERT(attr == NONE);
2609
2610 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2611 // of a string using [] notation. We need to support this too in
2612 // JavaScript.
2613 // In the case of a String object we just need to redirect the assignment to
2614 // the underlying string if the index is in range. Since the underlying
2615 // string does nothing with the assignment then we can ignore such
2616 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002617 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002618 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002619 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002620
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002621 Handle<Object> result = SetElement(js_object, index, value);
2622 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002623 return *value;
2624 }
2625
2626 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002627 Handle<Object> result;
2628 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002629 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002630 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002631 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002632 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002633 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002634 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002635 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002636 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002637 return *value;
2638 }
2639
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002640 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002641 bool has_pending_exception = false;
2642 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2643 if (has_pending_exception) return Failure::Exception();
2644 Handle<String> name = Handle<String>::cast(converted);
2645
2646 if (name->AsArrayIndex(&index)) {
2647 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002648 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002649 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002650 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002651 }
2652}
2653
2654
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002655static Object* Runtime_SetProperty(Arguments args) {
2656 NoHandleAllocation ha;
2657 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2658
2659 Handle<Object> object = args.at<Object>(0);
2660 Handle<Object> key = args.at<Object>(1);
2661 Handle<Object> value = args.at<Object>(2);
2662
2663 // Compute attributes.
2664 PropertyAttributes attributes = NONE;
2665 if (args.length() == 4) {
2666 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002667 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002668 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002669 RUNTIME_ASSERT(
2670 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2671 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002672 }
2673 return Runtime::SetObjectProperty(object, key, value, attributes);
2674}
2675
2676
2677// Set a local property, even if it is READ_ONLY. If the property does not
2678// exist, it will be added with attributes NONE.
2679static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2680 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002681 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002682 CONVERT_CHECKED(JSObject, object, args[0]);
2683 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002684 // Compute attributes.
2685 PropertyAttributes attributes = NONE;
2686 if (args.length() == 4) {
2687 CONVERT_CHECKED(Smi, value_obj, args[3]);
2688 int unchecked_value = value_obj->value();
2689 // Only attribute bits should be set.
2690 RUNTIME_ASSERT(
2691 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2692 attributes = static_cast<PropertyAttributes>(unchecked_value);
2693 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002694
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002695 return object->
2696 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002697}
2698
2699
2700static Object* Runtime_DeleteProperty(Arguments args) {
2701 NoHandleAllocation ha;
2702 ASSERT(args.length() == 2);
2703
2704 CONVERT_CHECKED(JSObject, object, args[0]);
2705 CONVERT_CHECKED(String, key, args[1]);
2706 return object->DeleteProperty(key);
2707}
2708
2709
2710static Object* Runtime_HasLocalProperty(Arguments args) {
2711 NoHandleAllocation ha;
2712 ASSERT(args.length() == 2);
2713 CONVERT_CHECKED(String, key, args[1]);
2714
2715 // Only JS objects can have properties.
2716 if (args[0]->IsJSObject()) {
2717 JSObject* object = JSObject::cast(args[0]);
2718 if (object->HasLocalProperty(key)) return Heap::true_value();
2719 } else if (args[0]->IsString()) {
2720 // Well, there is one exception: Handle [] on strings.
2721 uint32_t index;
2722 if (key->AsArrayIndex(&index)) {
2723 String* string = String::cast(args[0]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002724 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002725 return Heap::true_value();
2726 }
2727 }
2728 return Heap::false_value();
2729}
2730
2731
2732static Object* Runtime_HasProperty(Arguments args) {
2733 NoHandleAllocation na;
2734 ASSERT(args.length() == 2);
2735
2736 // Only JS objects can have properties.
2737 if (args[0]->IsJSObject()) {
2738 JSObject* object = JSObject::cast(args[0]);
2739 CONVERT_CHECKED(String, key, args[1]);
2740 if (object->HasProperty(key)) return Heap::true_value();
2741 }
2742 return Heap::false_value();
2743}
2744
2745
2746static Object* Runtime_HasElement(Arguments args) {
2747 NoHandleAllocation na;
2748 ASSERT(args.length() == 2);
2749
2750 // Only JS objects can have elements.
2751 if (args[0]->IsJSObject()) {
2752 JSObject* object = JSObject::cast(args[0]);
2753 CONVERT_CHECKED(Smi, index_obj, args[1]);
2754 uint32_t index = index_obj->value();
2755 if (object->HasElement(index)) return Heap::true_value();
2756 }
2757 return Heap::false_value();
2758}
2759
2760
2761static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2762 NoHandleAllocation ha;
2763 ASSERT(args.length() == 2);
2764
2765 CONVERT_CHECKED(JSObject, object, args[0]);
2766 CONVERT_CHECKED(String, key, args[1]);
2767
2768 uint32_t index;
2769 if (key->AsArrayIndex(&index)) {
2770 return Heap::ToBoolean(object->HasElement(index));
2771 }
2772
ager@chromium.org870a0b62008-11-04 11:43:05 +00002773 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2774 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002775}
2776
2777
2778static Object* Runtime_GetPropertyNames(Arguments args) {
2779 HandleScope scope;
2780 ASSERT(args.length() == 1);
2781
2782 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2783 Handle<JSObject> object(raw_object);
2784 return *GetKeysFor(object);
2785}
2786
2787
2788// Returns either a FixedArray as Runtime_GetPropertyNames,
2789// or, if the given object has an enum cache that contains
2790// all enumerable properties of the object and its prototypes
2791// have none, the map of the object. This is used to speed up
2792// the check for deletions during a for-in.
2793static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2794 ASSERT(args.length() == 1);
2795
2796 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2797
2798 if (raw_object->IsSimpleEnum()) return raw_object->map();
2799
2800 HandleScope scope;
2801 Handle<JSObject> object(raw_object);
2802 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2803
2804 // Test again, since cache may have been built by preceding call.
2805 if (object->IsSimpleEnum()) return object->map();
2806
2807 return *content;
2808}
2809
2810
2811static Object* Runtime_GetArgumentsProperty(Arguments args) {
2812 NoHandleAllocation ha;
2813 ASSERT(args.length() == 1);
2814
2815 // Compute the frame holding the arguments.
2816 JavaScriptFrameIterator it;
2817 it.AdvanceToArgumentsFrame();
2818 JavaScriptFrame* frame = it.frame();
2819
2820 // Get the actual number of provided arguments.
2821 const uint32_t n = frame->GetProvidedParametersCount();
2822
2823 // Try to convert the key to an index. If successful and within
2824 // index return the the argument from the frame.
2825 uint32_t index;
2826 if (Array::IndexFromObject(args[0], &index) && index < n) {
2827 return frame->GetParameter(index);
2828 }
2829
2830 // Convert the key to a string.
2831 HandleScope scope;
2832 bool exception = false;
2833 Handle<Object> converted =
2834 Execution::ToString(args.at<Object>(0), &exception);
2835 if (exception) return Failure::Exception();
2836 Handle<String> key = Handle<String>::cast(converted);
2837
2838 // Try to convert the string key into an array index.
2839 if (key->AsArrayIndex(&index)) {
2840 if (index < n) {
2841 return frame->GetParameter(index);
2842 } else {
2843 return Top::initial_object_prototype()->GetElement(index);
2844 }
2845 }
2846
2847 // Handle special arguments properties.
2848 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
2849 if (key->Equals(Heap::callee_symbol())) return frame->function();
2850
2851 // Lookup in the initial Object.prototype object.
2852 return Top::initial_object_prototype()->GetProperty(*key);
2853}
2854
2855
kasperl@chromium.org061ef742009-02-27 12:16:20 +00002856static Object* Runtime_ToFastProperties(Arguments args) {
2857 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00002858 Handle<Object> object = args.at<Object>(0);
2859 if (object->IsJSObject()) {
2860 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2861 js_object->TransformToFastProperties(0);
2862 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00002863 return *object;
2864}
2865
2866
2867static Object* Runtime_ToSlowProperties(Arguments args) {
2868 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00002869 Handle<Object> object = args.at<Object>(0);
2870 if (object->IsJSObject()) {
2871 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2872 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
2873 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00002874 return *object;
2875}
2876
2877
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002878static Object* Runtime_ToBool(Arguments args) {
2879 NoHandleAllocation ha;
2880 ASSERT(args.length() == 1);
2881
2882 return args[0]->ToBoolean();
2883}
2884
2885
2886// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
2887// Possible optimizations: put the type string into the oddballs.
2888static Object* Runtime_Typeof(Arguments args) {
2889 NoHandleAllocation ha;
2890
2891 Object* obj = args[0];
2892 if (obj->IsNumber()) return Heap::number_symbol();
2893 HeapObject* heap_obj = HeapObject::cast(obj);
2894
2895 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002896 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002897
2898 InstanceType instance_type = heap_obj->map()->instance_type();
2899 if (instance_type < FIRST_NONSTRING_TYPE) {
2900 return Heap::string_symbol();
2901 }
2902
2903 switch (instance_type) {
2904 case ODDBALL_TYPE:
2905 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
2906 return Heap::boolean_symbol();
2907 }
2908 if (heap_obj->IsNull()) {
2909 return Heap::object_symbol();
2910 }
2911 ASSERT(heap_obj->IsUndefined());
2912 return Heap::undefined_symbol();
2913 case JS_FUNCTION_TYPE:
2914 return Heap::function_symbol();
2915 default:
2916 // For any kind of object not handled above, the spec rule for
2917 // host objects gives that it is okay to return "object"
2918 return Heap::object_symbol();
2919 }
2920}
2921
2922
2923static Object* Runtime_StringToNumber(Arguments args) {
2924 NoHandleAllocation ha;
2925 ASSERT(args.length() == 1);
2926 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002927 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002928 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
2929}
2930
2931
2932static Object* Runtime_StringFromCharCodeArray(Arguments args) {
2933 NoHandleAllocation ha;
2934 ASSERT(args.length() == 1);
2935
2936 CONVERT_CHECKED(JSArray, codes, args[0]);
2937 int length = Smi::cast(codes->length())->value();
2938
2939 // Check if the string can be ASCII.
2940 int i;
2941 for (i = 0; i < length; i++) {
2942 Object* element = codes->GetElement(i);
2943 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
2944 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
2945 break;
2946 }
2947
2948 Object* object = NULL;
2949 if (i == length) { // The string is ASCII.
2950 object = Heap::AllocateRawAsciiString(length);
2951 } else { // The string is not ASCII.
2952 object = Heap::AllocateRawTwoByteString(length);
2953 }
2954
2955 if (object->IsFailure()) return object;
2956 String* result = String::cast(object);
2957 for (int i = 0; i < length; i++) {
2958 Object* element = codes->GetElement(i);
2959 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002960 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002961 }
2962 return result;
2963}
2964
2965
2966// kNotEscaped is generated by the following:
2967//
2968// #!/bin/perl
2969// for (my $i = 0; $i < 256; $i++) {
2970// print "\n" if $i % 16 == 0;
2971// my $c = chr($i);
2972// my $escaped = 1;
2973// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
2974// print $escaped ? "0, " : "1, ";
2975// }
2976
2977
2978static bool IsNotEscaped(uint16_t character) {
2979 // Only for 8 bit characters, the rest are always escaped (in a different way)
2980 ASSERT(character < 256);
2981 static const char kNotEscaped[256] = {
2982 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2983 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2984 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
2985 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
2986 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2987 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
2988 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2989 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
2990 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2991 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2992 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2993 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2994 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2995 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2996 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2997 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2998 };
2999 return kNotEscaped[character] != 0;
3000}
3001
3002
3003static Object* Runtime_URIEscape(Arguments args) {
3004 const char hex_chars[] = "0123456789ABCDEF";
3005 NoHandleAllocation ha;
3006 ASSERT(args.length() == 1);
3007 CONVERT_CHECKED(String, source, args[0]);
3008
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003009 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003010
3011 int escaped_length = 0;
3012 int length = source->length();
3013 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003014 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003015 buffer->Reset(source);
3016 while (buffer->has_more()) {
3017 uint16_t character = buffer->GetNext();
3018 if (character >= 256) {
3019 escaped_length += 6;
3020 } else if (IsNotEscaped(character)) {
3021 escaped_length++;
3022 } else {
3023 escaped_length += 3;
3024 }
3025 // We don't allow strings that are longer than Smi range.
3026 if (!Smi::IsValid(escaped_length)) {
3027 Top::context()->mark_out_of_memory();
3028 return Failure::OutOfMemoryException();
3029 }
3030 }
3031 }
3032 // No length change implies no change. Return original string if no change.
3033 if (escaped_length == length) {
3034 return source;
3035 }
3036 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3037 if (o->IsFailure()) return o;
3038 String* destination = String::cast(o);
3039 int dest_position = 0;
3040
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003041 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003042 buffer->Rewind();
3043 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003044 uint16_t chr = buffer->GetNext();
3045 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003046 destination->Set(dest_position, '%');
3047 destination->Set(dest_position+1, 'u');
3048 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3049 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3050 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3051 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003052 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003053 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003054 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003055 dest_position++;
3056 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003057 destination->Set(dest_position, '%');
3058 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3059 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003060 dest_position += 3;
3061 }
3062 }
3063 return destination;
3064}
3065
3066
3067static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3068 static const signed char kHexValue['g'] = {
3069 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3070 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3071 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3072 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3073 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3074 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3075 -1, 10, 11, 12, 13, 14, 15 };
3076
3077 if (character1 > 'f') return -1;
3078 int hi = kHexValue[character1];
3079 if (hi == -1) return -1;
3080 if (character2 > 'f') return -1;
3081 int lo = kHexValue[character2];
3082 if (lo == -1) return -1;
3083 return (hi << 4) + lo;
3084}
3085
3086
ager@chromium.org870a0b62008-11-04 11:43:05 +00003087static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003088 int i,
3089 int length,
3090 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003091 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003092 int32_t hi = 0;
3093 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003094 if (character == '%' &&
3095 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003096 source->Get(i + 1) == 'u' &&
3097 (hi = TwoDigitHex(source->Get(i + 2),
3098 source->Get(i + 3))) != -1 &&
3099 (lo = TwoDigitHex(source->Get(i + 4),
3100 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003101 *step = 6;
3102 return (hi << 8) + lo;
3103 } else if (character == '%' &&
3104 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003105 (lo = TwoDigitHex(source->Get(i + 1),
3106 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003107 *step = 3;
3108 return lo;
3109 } else {
3110 *step = 1;
3111 return character;
3112 }
3113}
3114
3115
3116static Object* Runtime_URIUnescape(Arguments args) {
3117 NoHandleAllocation ha;
3118 ASSERT(args.length() == 1);
3119 CONVERT_CHECKED(String, source, args[0]);
3120
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003121 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003122
3123 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003124 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003125
3126 int unescaped_length = 0;
3127 for (int i = 0; i < length; unescaped_length++) {
3128 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003129 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003130 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003131 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003132 i += step;
3133 }
3134
3135 // No length change implies no change. Return original string if no change.
3136 if (unescaped_length == length)
3137 return source;
3138
3139 Object* o = ascii ?
3140 Heap::AllocateRawAsciiString(unescaped_length) :
3141 Heap::AllocateRawTwoByteString(unescaped_length);
3142 if (o->IsFailure()) return o;
3143 String* destination = String::cast(o);
3144
3145 int dest_position = 0;
3146 for (int i = 0; i < length; dest_position++) {
3147 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003148 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003149 i += step;
3150 }
3151 return destination;
3152}
3153
3154
3155static Object* Runtime_StringParseInt(Arguments args) {
3156 NoHandleAllocation ha;
3157
3158 CONVERT_CHECKED(String, s, args[0]);
3159 CONVERT_DOUBLE_CHECKED(n, args[1]);
3160 int radix = FastD2I(n);
3161
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003162 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003163
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003164 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003165 int i;
3166
3167 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003168 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003169 if (i == len) return Heap::nan_value();
3170
3171 // Compute the sign (default to +).
3172 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003173 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003174 sign = -1;
3175 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003176 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003177 i++;
3178 }
3179
3180 // Compute the radix if 0.
3181 if (radix == 0) {
3182 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003183 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003184 radix = 8;
3185 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003186 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003187 if (c == 'x' || c == 'X') {
3188 radix = 16;
3189 i += 2;
3190 }
3191 }
3192 }
3193 } else if (radix == 16) {
3194 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003195 if (i + 1 < len && s->Get(i) == '0') {
3196 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003197 if (c == 'x' || c == 'X') i += 2;
3198 }
3199 }
3200
3201 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3202 double value;
3203 int end_index = StringToInt(s, i, radix, &value);
3204 if (end_index != i) {
3205 return Heap::NumberFromDouble(sign * value);
3206 }
3207 return Heap::nan_value();
3208}
3209
3210
3211static Object* Runtime_StringParseFloat(Arguments args) {
3212 NoHandleAllocation ha;
3213 CONVERT_CHECKED(String, str, args[0]);
3214
3215 // ECMA-262 section 15.1.2.3, empty string is NaN
3216 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3217
3218 // Create a number object from the value.
3219 return Heap::NumberFromDouble(value);
3220}
3221
3222
3223static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3224static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3225
3226
3227template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003228static Object* ConvertCaseHelper(String* s,
3229 int length,
3230 int input_string_length,
3231 unibrow::Mapping<Converter, 128>* mapping) {
3232 // We try this twice, once with the assumption that the result is no longer
3233 // than the input and, if that assumption breaks, again with the exact
3234 // length. This may not be pretty, but it is nicer than what was here before
3235 // and I hereby claim my vaffel-is.
3236 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003237 // Allocate the resulting string.
3238 //
3239 // NOTE: This assumes that the upper/lower case of an ascii
3240 // character is also ascii. This is currently the case, but it
3241 // might break in the future if we implement more context and locale
3242 // dependent upper/lower conversions.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003243 Object* o = StringShape(s).IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003244 ? Heap::AllocateRawAsciiString(length)
3245 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003246 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003247 String* result = String::cast(o);
3248 bool has_changed_character = false;
3249
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003250 // Convert all characters to upper case, assuming that they will fit
3251 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003252 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003253 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003254 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003255 // We can assume that the string is not empty
3256 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003257 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003258 bool has_next = buffer->has_more();
3259 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003260 int char_length = mapping->get(current, next, chars);
3261 if (char_length == 0) {
3262 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003263 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003264 i++;
3265 } else if (char_length == 1) {
3266 // Common case: converting the letter resulted in one character.
3267 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003268 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003269 has_changed_character = true;
3270 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003271 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003272 // We've assumed that the result would be as long as the
3273 // input but here is a character that converts to several
3274 // characters. No matter, we calculate the exact length
3275 // of the result and try the whole thing again.
3276 //
3277 // Note that this leaves room for optimization. We could just
3278 // memcpy what we already have to the result string. Also,
3279 // the result string is the last object allocated we could
3280 // "realloc" it and probably, in the vast majority of cases,
3281 // extend the existing string to be able to hold the full
3282 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003283 int next_length = 0;
3284 if (has_next) {
3285 next_length = mapping->get(next, 0, chars);
3286 if (next_length == 0) next_length = 1;
3287 }
3288 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003289 while (buffer->has_more()) {
3290 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003291 // NOTE: we use 0 as the next character here because, while
3292 // the next character may affect what a character converts to,
3293 // it does not in any case affect the length of what it convert
3294 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003295 int char_length = mapping->get(current, 0, chars);
3296 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003297 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003298 if (current_length > Smi::kMaxValue) {
3299 Top::context()->mark_out_of_memory();
3300 return Failure::OutOfMemoryException();
3301 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003302 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003303 // Try again with the real length.
3304 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003305 } else {
3306 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003307 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003308 i++;
3309 }
3310 has_changed_character = true;
3311 }
3312 current = next;
3313 }
3314 if (has_changed_character) {
3315 return result;
3316 } else {
3317 // If we didn't actually change anything in doing the conversion
3318 // we simple return the result and let the converted string
3319 // become garbage; there is no reason to keep two identical strings
3320 // alive.
3321 return s;
3322 }
3323}
3324
3325
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003326template <class Converter>
3327static Object* ConvertCase(Arguments args,
3328 unibrow::Mapping<Converter, 128>* mapping) {
3329 NoHandleAllocation ha;
3330
3331 CONVERT_CHECKED(String, s, args[0]);
3332 s->TryFlattenIfNotFlat();
3333
3334 int input_string_length = s->length();
3335 // Assume that the string is not empty; we need this assumption later
3336 if (input_string_length == 0) return s;
3337 int length = input_string_length;
3338
3339 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3340 if (answer->IsSmi()) {
3341 // Retry with correct length.
3342 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3343 }
3344 return answer; // This may be a failure.
3345}
3346
3347
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003348static Object* Runtime_StringToLowerCase(Arguments args) {
3349 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3350}
3351
3352
3353static Object* Runtime_StringToUpperCase(Arguments args) {
3354 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3355}
3356
3357
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003358static Object* Runtime_NumberToString(Arguments args) {
3359 NoHandleAllocation ha;
3360 ASSERT(args.length() == 1);
3361
3362 Object* number = args[0];
3363 RUNTIME_ASSERT(number->IsNumber());
3364
3365 Object* cached = Heap::GetNumberStringCache(number);
3366 if (cached != Heap::undefined_value()) {
3367 return cached;
3368 }
3369
3370 char arr[100];
3371 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3372 const char* str;
3373 if (number->IsSmi()) {
3374 int num = Smi::cast(number)->value();
3375 str = IntToCString(num, buffer);
3376 } else {
3377 double num = HeapNumber::cast(number)->value();
3378 str = DoubleToCString(num, buffer);
3379 }
3380 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3381
3382 if (!result->IsFailure()) {
3383 Heap::SetNumberStringCache(number, String::cast(result));
3384 }
3385 return result;
3386}
3387
3388
3389static Object* Runtime_NumberToInteger(Arguments args) {
3390 NoHandleAllocation ha;
3391 ASSERT(args.length() == 1);
3392
3393 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003394 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003395 CONVERT_DOUBLE_CHECKED(number, obj);
3396 return Heap::NumberFromDouble(DoubleToInteger(number));
3397}
3398
3399
3400static Object* Runtime_NumberToJSUint32(Arguments args) {
3401 NoHandleAllocation ha;
3402 ASSERT(args.length() == 1);
3403
3404 Object* obj = args[0];
3405 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3406 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3407 return Heap::NumberFromUint32(number);
3408}
3409
3410
3411static Object* Runtime_NumberToJSInt32(Arguments args) {
3412 NoHandleAllocation ha;
3413 ASSERT(args.length() == 1);
3414
3415 Object* obj = args[0];
3416 if (obj->IsSmi()) return obj;
3417 CONVERT_DOUBLE_CHECKED(number, obj);
3418 return Heap::NumberFromInt32(DoubleToInt32(number));
3419}
3420
3421
ager@chromium.org870a0b62008-11-04 11:43:05 +00003422// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3423// a small integer.
3424static Object* Runtime_NumberToSmi(Arguments args) {
3425 NoHandleAllocation ha;
3426 ASSERT(args.length() == 1);
3427
3428 Object* obj = args[0];
3429 if (obj->IsSmi()) {
3430 return obj;
3431 }
3432 if (obj->IsHeapNumber()) {
3433 double value = HeapNumber::cast(obj)->value();
3434 int int_value = FastD2I(value);
3435 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3436 return Smi::FromInt(int_value);
3437 }
3438 }
3439 return Heap::nan_value();
3440}
3441
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003442static Object* Runtime_NumberAdd(Arguments args) {
3443 NoHandleAllocation ha;
3444 ASSERT(args.length() == 2);
3445
3446 CONVERT_DOUBLE_CHECKED(x, args[0]);
3447 CONVERT_DOUBLE_CHECKED(y, args[1]);
3448 return Heap::AllocateHeapNumber(x + y);
3449}
3450
3451
3452static Object* Runtime_NumberSub(Arguments args) {
3453 NoHandleAllocation ha;
3454 ASSERT(args.length() == 2);
3455
3456 CONVERT_DOUBLE_CHECKED(x, args[0]);
3457 CONVERT_DOUBLE_CHECKED(y, args[1]);
3458 return Heap::AllocateHeapNumber(x - y);
3459}
3460
3461
3462static Object* Runtime_NumberMul(Arguments args) {
3463 NoHandleAllocation ha;
3464 ASSERT(args.length() == 2);
3465
3466 CONVERT_DOUBLE_CHECKED(x, args[0]);
3467 CONVERT_DOUBLE_CHECKED(y, args[1]);
3468 return Heap::AllocateHeapNumber(x * y);
3469}
3470
3471
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003472static Object* Runtime_NumberUnaryMinus(Arguments args) {
3473 NoHandleAllocation ha;
3474 ASSERT(args.length() == 1);
3475
3476 CONVERT_DOUBLE_CHECKED(x, args[0]);
3477 return Heap::AllocateHeapNumber(-x);
3478}
3479
3480
3481static Object* Runtime_NumberDiv(Arguments args) {
3482 NoHandleAllocation ha;
3483 ASSERT(args.length() == 2);
3484
3485 CONVERT_DOUBLE_CHECKED(x, args[0]);
3486 CONVERT_DOUBLE_CHECKED(y, args[1]);
3487 return Heap::NewNumberFromDouble(x / y);
3488}
3489
3490
3491static Object* Runtime_NumberMod(Arguments args) {
3492 NoHandleAllocation ha;
3493 ASSERT(args.length() == 2);
3494
3495 CONVERT_DOUBLE_CHECKED(x, args[0]);
3496 CONVERT_DOUBLE_CHECKED(y, args[1]);
3497
3498#ifdef WIN32
3499 // Workaround MS fmod bugs. ECMA-262 says:
3500 // dividend is finite and divisor is an infinity => result equals dividend
3501 // dividend is a zero and divisor is nonzero finite => result equals dividend
3502 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3503 !(x == 0 && (y != 0 && isfinite(y))))
3504#endif
3505 x = fmod(x, y);
3506 // NewNumberFromDouble may return a Smi instead of a Number object
3507 return Heap::NewNumberFromDouble(x);
3508}
3509
3510
3511static Object* Runtime_StringAdd(Arguments args) {
3512 NoHandleAllocation ha;
3513 ASSERT(args.length() == 2);
3514
3515 CONVERT_CHECKED(String, str1, args[0]);
3516 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003517 int len1 = str1->length();
3518 int len2 = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003519 if (len1 == 0) return str2;
3520 if (len2 == 0) return str1;
3521 int length_sum = len1 + len2;
3522 // Make sure that an out of memory exception is thrown if the length
3523 // of the new cons string is too large to fit in a Smi.
3524 if (length_sum > Smi::kMaxValue || length_sum < 0) {
3525 Top::context()->mark_out_of_memory();
3526 return Failure::OutOfMemoryException();
3527 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003528 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003529}
3530
3531
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003532template<typename sinkchar>
3533static inline void StringBuilderConcatHelper(String* special,
3534 sinkchar* sink,
3535 FixedArray* fixed_array,
3536 int array_length) {
3537 int position = 0;
3538 for (int i = 0; i < array_length; i++) {
3539 Object* element = fixed_array->get(i);
3540 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003541 int encoded_slice = Smi::cast(element)->value();
3542 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3543 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003544 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003545 sink + position,
3546 pos,
3547 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003548 position += len;
3549 } else {
3550 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003551 int element_length = string->length();
3552 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003553 position += element_length;
3554 }
3555 }
3556}
3557
3558
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003559static Object* Runtime_StringBuilderConcat(Arguments args) {
3560 NoHandleAllocation ha;
3561 ASSERT(args.length() == 2);
3562 CONVERT_CHECKED(JSArray, array, args[0]);
3563 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003564 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003565 Object* smi_array_length = array->length();
3566 if (!smi_array_length->IsSmi()) {
3567 Top::context()->mark_out_of_memory();
3568 return Failure::OutOfMemoryException();
3569 }
3570 int array_length = Smi::cast(smi_array_length)->value();
3571 if (!array->HasFastElements()) {
3572 return Top::Throw(Heap::illegal_argument_symbol());
3573 }
3574 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003575 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003576 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003577 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003578
3579 if (array_length == 0) {
3580 return Heap::empty_string();
3581 } else if (array_length == 1) {
3582 Object* first = fixed_array->get(0);
3583 if (first->IsString()) return first;
3584 }
3585
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003586 bool ascii = StringShape(special).IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003587 int position = 0;
3588 for (int i = 0; i < array_length; i++) {
3589 Object* elt = fixed_array->get(i);
3590 if (elt->IsSmi()) {
3591 int len = Smi::cast(elt)->value();
3592 int pos = len >> 11;
3593 len &= 0x7ff;
3594 if (pos + len > special_length) {
3595 return Top::Throw(Heap::illegal_argument_symbol());
3596 }
3597 position += len;
3598 } else if (elt->IsString()) {
3599 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003600 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003601 if (!Smi::IsValid(element_length + position)) {
3602 Top::context()->mark_out_of_memory();
3603 return Failure::OutOfMemoryException();
3604 }
3605 position += element_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003606 if (ascii && !StringShape(element).IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003607 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003608 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003609 } else {
3610 return Top::Throw(Heap::illegal_argument_symbol());
3611 }
3612 }
3613
3614 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003615 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003616
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003617 if (ascii) {
3618 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003619 if (object->IsFailure()) return object;
3620 SeqAsciiString* answer = SeqAsciiString::cast(object);
3621 StringBuilderConcatHelper(special,
3622 answer->GetChars(),
3623 fixed_array,
3624 array_length);
3625 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003626 } else {
3627 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003628 if (object->IsFailure()) return object;
3629 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3630 StringBuilderConcatHelper(special,
3631 answer->GetChars(),
3632 fixed_array,
3633 array_length);
3634 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003635 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003636}
3637
3638
3639static Object* Runtime_NumberOr(Arguments args) {
3640 NoHandleAllocation ha;
3641 ASSERT(args.length() == 2);
3642
3643 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3644 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3645 return Heap::NumberFromInt32(x | y);
3646}
3647
3648
3649static Object* Runtime_NumberAnd(Arguments args) {
3650 NoHandleAllocation ha;
3651 ASSERT(args.length() == 2);
3652
3653 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3654 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3655 return Heap::NumberFromInt32(x & y);
3656}
3657
3658
3659static Object* Runtime_NumberXor(Arguments args) {
3660 NoHandleAllocation ha;
3661 ASSERT(args.length() == 2);
3662
3663 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3664 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3665 return Heap::NumberFromInt32(x ^ y);
3666}
3667
3668
3669static Object* Runtime_NumberNot(Arguments args) {
3670 NoHandleAllocation ha;
3671 ASSERT(args.length() == 1);
3672
3673 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3674 return Heap::NumberFromInt32(~x);
3675}
3676
3677
3678static Object* Runtime_NumberShl(Arguments args) {
3679 NoHandleAllocation ha;
3680 ASSERT(args.length() == 2);
3681
3682 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3683 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3684 return Heap::NumberFromInt32(x << (y & 0x1f));
3685}
3686
3687
3688static Object* Runtime_NumberShr(Arguments args) {
3689 NoHandleAllocation ha;
3690 ASSERT(args.length() == 2);
3691
3692 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3693 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3694 return Heap::NumberFromUint32(x >> (y & 0x1f));
3695}
3696
3697
3698static Object* Runtime_NumberSar(Arguments args) {
3699 NoHandleAllocation ha;
3700 ASSERT(args.length() == 2);
3701
3702 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3703 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3704 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3705}
3706
3707
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003708static Object* Runtime_NumberEquals(Arguments args) {
3709 NoHandleAllocation ha;
3710 ASSERT(args.length() == 2);
3711
3712 CONVERT_DOUBLE_CHECKED(x, args[0]);
3713 CONVERT_DOUBLE_CHECKED(y, args[1]);
3714 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3715 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3716 if (x == y) return Smi::FromInt(EQUAL);
3717 Object* result;
3718 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3719 result = Smi::FromInt(EQUAL);
3720 } else {
3721 result = Smi::FromInt(NOT_EQUAL);
3722 }
3723 return result;
3724}
3725
3726
3727static Object* Runtime_StringEquals(Arguments args) {
3728 NoHandleAllocation ha;
3729 ASSERT(args.length() == 2);
3730
3731 CONVERT_CHECKED(String, x, args[0]);
3732 CONVERT_CHECKED(String, y, args[1]);
3733
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003734 bool not_equal = !x->Equals(y);
3735 // This is slightly convoluted because the value that signifies
3736 // equality is 0 and inequality is 1 so we have to negate the result
3737 // from String::Equals.
3738 ASSERT(not_equal == 0 || not_equal == 1);
3739 STATIC_CHECK(EQUAL == 0);
3740 STATIC_CHECK(NOT_EQUAL == 1);
3741 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003742}
3743
3744
3745static Object* Runtime_NumberCompare(Arguments args) {
3746 NoHandleAllocation ha;
3747 ASSERT(args.length() == 3);
3748
3749 CONVERT_DOUBLE_CHECKED(x, args[0]);
3750 CONVERT_DOUBLE_CHECKED(y, args[1]);
3751 if (isnan(x) || isnan(y)) return args[2];
3752 if (x == y) return Smi::FromInt(EQUAL);
3753 if (isless(x, y)) return Smi::FromInt(LESS);
3754 return Smi::FromInt(GREATER);
3755}
3756
3757
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003758// Compare two Smis as if they were converted to strings and then
3759// compared lexicographically.
3760static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3761 NoHandleAllocation ha;
3762 ASSERT(args.length() == 2);
3763
3764 // Arrays for the individual characters of the two Smis. Smis are
3765 // 31 bit integers and 10 decimal digits are therefore enough.
3766 static int x_elms[10];
3767 static int y_elms[10];
3768
3769 // Extract the integer values from the Smis.
3770 CONVERT_CHECKED(Smi, x, args[0]);
3771 CONVERT_CHECKED(Smi, y, args[1]);
3772 int x_value = x->value();
3773 int y_value = y->value();
3774
3775 // If the integers are equal so are the string representations.
3776 if (x_value == y_value) return Smi::FromInt(EQUAL);
3777
3778 // If one of the integers are zero the normal integer order is the
3779 // same as the lexicographic order of the string representations.
3780 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3781
ager@chromium.org32912102009-01-16 10:38:43 +00003782 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003783 // smallest because the char code of '-' is less than the char code
3784 // of any digit. Otherwise, we make both values positive.
3785 if (x_value < 0 || y_value < 0) {
3786 if (y_value >= 0) return Smi::FromInt(LESS);
3787 if (x_value >= 0) return Smi::FromInt(GREATER);
3788 x_value = -x_value;
3789 y_value = -y_value;
3790 }
3791
3792 // Convert the integers to arrays of their decimal digits.
3793 int x_index = 0;
3794 int y_index = 0;
3795 while (x_value > 0) {
3796 x_elms[x_index++] = x_value % 10;
3797 x_value /= 10;
3798 }
3799 while (y_value > 0) {
3800 y_elms[y_index++] = y_value % 10;
3801 y_value /= 10;
3802 }
3803
3804 // Loop through the arrays of decimal digits finding the first place
3805 // where they differ.
3806 while (--x_index >= 0 && --y_index >= 0) {
3807 int diff = x_elms[x_index] - y_elms[y_index];
3808 if (diff != 0) return Smi::FromInt(diff);
3809 }
3810
3811 // If one array is a suffix of the other array, the longest array is
3812 // the representation of the largest of the Smis in the
3813 // lexicographic ordering.
3814 return Smi::FromInt(x_index - y_index);
3815}
3816
3817
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003818static Object* Runtime_StringCompare(Arguments args) {
3819 NoHandleAllocation ha;
3820 ASSERT(args.length() == 2);
3821
3822 CONVERT_CHECKED(String, x, args[0]);
3823 CONVERT_CHECKED(String, y, args[1]);
3824
3825 // A few fast case tests before we flatten.
3826 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003827 if (y->length() == 0) {
3828 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003829 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003830 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003831 return Smi::FromInt(LESS);
3832 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003833
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003834 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003835 if (d < 0) return Smi::FromInt(LESS);
3836 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003837
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003838 x->TryFlattenIfNotFlat();
3839 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003840
3841 static StringInputBuffer bufx;
3842 static StringInputBuffer bufy;
3843 bufx.Reset(x);
3844 bufy.Reset(y);
3845 while (bufx.has_more() && bufy.has_more()) {
3846 int d = bufx.GetNext() - bufy.GetNext();
3847 if (d < 0) return Smi::FromInt(LESS);
3848 else if (d > 0) return Smi::FromInt(GREATER);
3849 }
3850
3851 // x is (non-trivial) prefix of y:
3852 if (bufy.has_more()) return Smi::FromInt(LESS);
3853 // y is prefix of x:
3854 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
3855}
3856
3857
3858static Object* Runtime_Math_abs(Arguments args) {
3859 NoHandleAllocation ha;
3860 ASSERT(args.length() == 1);
3861
3862 CONVERT_DOUBLE_CHECKED(x, args[0]);
3863 return Heap::AllocateHeapNumber(fabs(x));
3864}
3865
3866
3867static Object* Runtime_Math_acos(Arguments args) {
3868 NoHandleAllocation ha;
3869 ASSERT(args.length() == 1);
3870
3871 CONVERT_DOUBLE_CHECKED(x, args[0]);
3872 return Heap::AllocateHeapNumber(acos(x));
3873}
3874
3875
3876static Object* Runtime_Math_asin(Arguments args) {
3877 NoHandleAllocation ha;
3878 ASSERT(args.length() == 1);
3879
3880 CONVERT_DOUBLE_CHECKED(x, args[0]);
3881 return Heap::AllocateHeapNumber(asin(x));
3882}
3883
3884
3885static Object* Runtime_Math_atan(Arguments args) {
3886 NoHandleAllocation ha;
3887 ASSERT(args.length() == 1);
3888
3889 CONVERT_DOUBLE_CHECKED(x, args[0]);
3890 return Heap::AllocateHeapNumber(atan(x));
3891}
3892
3893
3894static Object* Runtime_Math_atan2(Arguments args) {
3895 NoHandleAllocation ha;
3896 ASSERT(args.length() == 2);
3897
3898 CONVERT_DOUBLE_CHECKED(x, args[0]);
3899 CONVERT_DOUBLE_CHECKED(y, args[1]);
3900 double result;
3901 if (isinf(x) && isinf(y)) {
3902 // Make sure that the result in case of two infinite arguments
3903 // is a multiple of Pi / 4. The sign of the result is determined
3904 // by the first argument (x) and the sign of the second argument
3905 // determines the multiplier: one or three.
3906 static double kPiDividedBy4 = 0.78539816339744830962;
3907 int multiplier = (x < 0) ? -1 : 1;
3908 if (y < 0) multiplier *= 3;
3909 result = multiplier * kPiDividedBy4;
3910 } else {
3911 result = atan2(x, y);
3912 }
3913 return Heap::AllocateHeapNumber(result);
3914}
3915
3916
3917static Object* Runtime_Math_ceil(Arguments args) {
3918 NoHandleAllocation ha;
3919 ASSERT(args.length() == 1);
3920
3921 CONVERT_DOUBLE_CHECKED(x, args[0]);
3922 return Heap::NumberFromDouble(ceiling(x));
3923}
3924
3925
3926static Object* Runtime_Math_cos(Arguments args) {
3927 NoHandleAllocation ha;
3928 ASSERT(args.length() == 1);
3929
3930 CONVERT_DOUBLE_CHECKED(x, args[0]);
3931 return Heap::AllocateHeapNumber(cos(x));
3932}
3933
3934
3935static Object* Runtime_Math_exp(Arguments args) {
3936 NoHandleAllocation ha;
3937 ASSERT(args.length() == 1);
3938
3939 CONVERT_DOUBLE_CHECKED(x, args[0]);
3940 return Heap::AllocateHeapNumber(exp(x));
3941}
3942
3943
3944static Object* Runtime_Math_floor(Arguments args) {
3945 NoHandleAllocation ha;
3946 ASSERT(args.length() == 1);
3947
3948 CONVERT_DOUBLE_CHECKED(x, args[0]);
3949 return Heap::NumberFromDouble(floor(x));
3950}
3951
3952
3953static Object* Runtime_Math_log(Arguments args) {
3954 NoHandleAllocation ha;
3955 ASSERT(args.length() == 1);
3956
3957 CONVERT_DOUBLE_CHECKED(x, args[0]);
3958 return Heap::AllocateHeapNumber(log(x));
3959}
3960
3961
3962static Object* Runtime_Math_pow(Arguments args) {
3963 NoHandleAllocation ha;
3964 ASSERT(args.length() == 2);
3965
3966 CONVERT_DOUBLE_CHECKED(x, args[0]);
3967 CONVERT_DOUBLE_CHECKED(y, args[1]);
3968 if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
3969 return Heap::nan_value();
3970 } else if (y == 0) {
3971 return Smi::FromInt(1);
3972 } else {
3973 return Heap::AllocateHeapNumber(pow(x, y));
3974 }
3975}
3976
3977// Returns a number value with positive sign, greater than or equal to
3978// 0 but less than 1, chosen randomly.
mads.s.ager31e71382008-08-13 09:32:07 +00003979static Object* Runtime_Math_random(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003980 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003981 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003982
3983 // To get much better precision, we combine the results of two
3984 // invocations of random(). The result is computed by normalizing a
3985 // double in the range [0, RAND_MAX + 1) obtained by adding the
3986 // high-order bits in the range [0, RAND_MAX] with the low-order
3987 // bits in the range [0, 1).
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003988 double lo = static_cast<double>(random()) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003989 double hi = static_cast<double>(random());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003990 double result = (hi + lo) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003991 ASSERT(result >= 0 && result < 1);
3992 return Heap::AllocateHeapNumber(result);
3993}
3994
3995
3996static Object* Runtime_Math_round(Arguments args) {
3997 NoHandleAllocation ha;
3998 ASSERT(args.length() == 1);
3999
4000 CONVERT_DOUBLE_CHECKED(x, args[0]);
4001 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4002 return Heap::NumberFromDouble(floor(x + 0.5));
4003}
4004
4005
4006static Object* Runtime_Math_sin(Arguments args) {
4007 NoHandleAllocation ha;
4008 ASSERT(args.length() == 1);
4009
4010 CONVERT_DOUBLE_CHECKED(x, args[0]);
4011 return Heap::AllocateHeapNumber(sin(x));
4012}
4013
4014
4015static Object* Runtime_Math_sqrt(Arguments args) {
4016 NoHandleAllocation ha;
4017 ASSERT(args.length() == 1);
4018
4019 CONVERT_DOUBLE_CHECKED(x, args[0]);
4020 return Heap::AllocateHeapNumber(sqrt(x));
4021}
4022
4023
4024static Object* Runtime_Math_tan(Arguments args) {
4025 NoHandleAllocation ha;
4026 ASSERT(args.length() == 1);
4027
4028 CONVERT_DOUBLE_CHECKED(x, args[0]);
4029 return Heap::AllocateHeapNumber(tan(x));
4030}
4031
4032
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004033// The NewArguments function is only used when constructing the
4034// arguments array when calling non-functions from JavaScript in
4035// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004036static Object* Runtime_NewArguments(Arguments args) {
4037 NoHandleAllocation ha;
4038 ASSERT(args.length() == 1);
4039
4040 // ECMA-262, 3rd., 10.1.8, p.39
4041 CONVERT_CHECKED(JSFunction, callee, args[0]);
4042
4043 // Compute the frame holding the arguments.
4044 JavaScriptFrameIterator it;
4045 it.AdvanceToArgumentsFrame();
4046 JavaScriptFrame* frame = it.frame();
4047
4048 const int length = frame->GetProvidedParametersCount();
4049 Object* result = Heap::AllocateArgumentsObject(callee, length);
4050 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004051 if (length > 0) {
4052 Object* obj = Heap::AllocateFixedArray(length);
4053 if (obj->IsFailure()) return obj;
4054 FixedArray* array = FixedArray::cast(obj);
4055 ASSERT(array->length() == length);
4056 WriteBarrierMode mode = array->GetWriteBarrierMode();
4057 for (int i = 0; i < length; i++) {
4058 array->set(i, frame->GetParameter(i), mode);
4059 }
4060 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004061 }
4062 return result;
4063}
4064
4065
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004066static Object* Runtime_NewArgumentsFast(Arguments args) {
4067 NoHandleAllocation ha;
4068 ASSERT(args.length() == 3);
4069
4070 JSFunction* callee = JSFunction::cast(args[0]);
4071 Object** parameters = reinterpret_cast<Object**>(args[1]);
4072 const int length = Smi::cast(args[2])->value();
4073
4074 Object* result = Heap::AllocateArgumentsObject(callee, length);
4075 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004076 ASSERT(Heap::InNewSpace(result));
4077
4078 // Allocate the elements if needed.
4079 if (length > 0) {
4080 // Allocate the fixed array.
4081 Object* obj = Heap::AllocateRawFixedArray(length);
4082 if (obj->IsFailure()) return obj;
4083 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4084 FixedArray* array = FixedArray::cast(obj);
4085 array->set_length(length);
4086 WriteBarrierMode mode = array->GetWriteBarrierMode();
4087 for (int i = 0; i < length; i++) {
4088 array->set(i, *--parameters, mode);
4089 }
4090 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4091 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004092 }
4093 return result;
4094}
4095
4096
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004097static Object* Runtime_NewClosure(Arguments args) {
4098 HandleScope scope;
4099 ASSERT(args.length() == 2);
4100 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4101 CONVERT_ARG_CHECKED(Context, context, 1);
4102
4103 Handle<JSFunction> result =
4104 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4105 return *result;
4106}
4107
4108
4109static Object* Runtime_NewObject(Arguments args) {
4110 NoHandleAllocation ha;
4111 ASSERT(args.length() == 1);
4112
4113 Object* constructor = args[0];
4114 if (constructor->IsJSFunction()) {
4115 JSFunction* function = JSFunction::cast(constructor);
4116
ager@chromium.org32912102009-01-16 10:38:43 +00004117 // Handle stepping into constructors if step into is active.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004118 if (Debug::StepInActive()) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004119 HandleScope scope;
4120 Debug::HandleStepIn(Handle<JSFunction>(function), 0, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004121 }
4122
4123 if (function->has_initial_map() &&
4124 function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
4125 // The 'Function' function ignores the receiver object when
4126 // called using 'new' and creates a new JSFunction object that
4127 // is returned. The receiver object is only used for error
4128 // reporting if an error occurs when constructing the new
4129 // JSFunction. AllocateJSObject should not be used to allocate
4130 // JSFunctions since it does not properly initialize the shared
4131 // part of the function. Since the receiver is ignored anyway,
4132 // we use the global object as the receiver instead of a new
4133 // JSFunction object. This way, errors are reported the same
4134 // way whether or not 'Function' is called using 'new'.
4135 return Top::context()->global();
4136 }
4137 return Heap::AllocateJSObject(function);
4138 }
4139
4140 HandleScope scope;
4141 Handle<Object> cons(constructor);
4142 // The constructor is not a function; throw a type error.
4143 Handle<Object> type_error =
4144 Factory::NewTypeError("not_constructor", HandleVector(&cons, 1));
4145 return Top::Throw(*type_error);
4146}
4147
4148
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004149static Object* Runtime_LazyCompile(Arguments args) {
4150 HandleScope scope;
4151 ASSERT(args.length() == 1);
4152
4153 Handle<JSFunction> function = args.at<JSFunction>(0);
4154#ifdef DEBUG
4155 if (FLAG_trace_lazy) {
4156 PrintF("[lazy: ");
4157 function->shared()->name()->Print();
4158 PrintF("]\n");
4159 }
4160#endif
4161
4162 // Compile the target function.
4163 ASSERT(!function->is_compiled());
4164 if (!CompileLazy(function, KEEP_EXCEPTION)) {
4165 return Failure::Exception();
4166 }
4167
4168 return function->code();
4169}
4170
4171
4172static Object* Runtime_GetCalledFunction(Arguments args) {
4173 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004174 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004175 StackFrameIterator it;
4176 // Get past the JS-to-C exit frame.
4177 ASSERT(it.frame()->is_exit());
4178 it.Advance();
4179 // Get past the CALL_NON_FUNCTION activation frame.
4180 ASSERT(it.frame()->is_java_script());
4181 it.Advance();
4182 // Argument adaptor frames do not copy the function; we have to skip
4183 // past them to get to the real calling frame.
4184 if (it.frame()->is_arguments_adaptor()) it.Advance();
4185 // Get the function from the top of the expression stack of the
4186 // calling frame.
4187 StandardFrame* frame = StandardFrame::cast(it.frame());
4188 int index = frame->ComputeExpressionsCount() - 1;
4189 Object* result = frame->GetExpression(index);
4190 return result;
4191}
4192
4193
4194static Object* Runtime_GetFunctionDelegate(Arguments args) {
4195 HandleScope scope;
4196 ASSERT(args.length() == 1);
4197 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4198 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4199}
4200
4201
4202static Object* Runtime_NewContext(Arguments args) {
4203 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004204 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004205
kasper.lund7276f142008-07-30 08:49:36 +00004206 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004207 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4208 Object* result = Heap::AllocateFunctionContext(length, function);
4209 if (result->IsFailure()) return result;
4210
4211 Top::set_context(Context::cast(result));
4212
kasper.lund7276f142008-07-30 08:49:36 +00004213 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004214}
4215
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004216static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004217 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004218 Object* js_object = object;
4219 if (!js_object->IsJSObject()) {
4220 js_object = js_object->ToObject();
4221 if (js_object->IsFailure()) {
4222 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004223 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004224 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004225 Handle<Object> result =
4226 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4227 return Top::Throw(*result);
4228 }
4229 }
4230
4231 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004232 Heap::AllocateWithContext(Top::context(),
4233 JSObject::cast(js_object),
4234 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004235 if (result->IsFailure()) return result;
4236
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004237 Context* context = Context::cast(result);
4238 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004239
kasper.lund7276f142008-07-30 08:49:36 +00004240 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004241}
4242
4243
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004244static Object* Runtime_PushContext(Arguments args) {
4245 NoHandleAllocation ha;
4246 ASSERT(args.length() == 1);
4247 return PushContextHelper(args[0], false);
4248}
4249
4250
4251static Object* Runtime_PushCatchContext(Arguments args) {
4252 NoHandleAllocation ha;
4253 ASSERT(args.length() == 1);
4254 return PushContextHelper(args[0], true);
4255}
4256
4257
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004258static Object* Runtime_LookupContext(Arguments args) {
4259 HandleScope scope;
4260 ASSERT(args.length() == 2);
4261
4262 CONVERT_ARG_CHECKED(Context, context, 0);
4263 CONVERT_ARG_CHECKED(String, name, 1);
4264
4265 int index;
4266 PropertyAttributes attributes;
4267 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004268 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004269 context->Lookup(name, flags, &index, &attributes);
4270
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004271 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004272 ASSERT(holder->IsJSObject());
4273 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004274 }
4275
4276 // No intermediate context found. Use global object by default.
4277 return Top::context()->global();
4278}
4279
4280
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004281// A mechanism to return pairs of Object*'s. This is somewhat
4282// compiler-dependent as it assumes that a 64-bit value (a long long)
4283// is returned via two registers (edx:eax on ia32). Both the ia32 and
4284// arm platform support this; it is mostly an issue of "coaxing" the
4285// compiler to do the right thing.
4286//
4287// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004288typedef uint64_t ObjectPair;
4289static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004290 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004291 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004292}
4293
4294
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004295static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004296 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4297 USE(attributes);
4298 return x->IsTheHole() ? Heap::undefined_value() : x;
4299}
4300
4301
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004302static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4303 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004304 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004305 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004306 JSFunction* context_extension_function =
4307 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004308 // If the holder isn't a context extension object, we just return it
4309 // as the receiver. This allows arguments objects to be used as
4310 // receivers, but only if they are put in the context scope chain
4311 // explicitly via a with-statement.
4312 Object* constructor = holder->map()->constructor();
4313 if (constructor != context_extension_function) return holder;
4314 // Fall back to using the global object as the receiver if the
4315 // property turns out to be a local variable allocated in a context
4316 // extension object - introduced via eval.
4317 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004318}
4319
4320
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004321static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004322 HandleScope scope;
4323 ASSERT(args.length() == 2);
4324
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004325 if (!args[0]->IsContext() || !args[1]->IsString()) {
4326 return MakePair(IllegalOperation(), NULL);
4327 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004328 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004329 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004330
4331 int index;
4332 PropertyAttributes attributes;
4333 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004334 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004335 context->Lookup(name, flags, &index, &attributes);
4336
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004337 // If the index is non-negative, the slot has been found in a local
4338 // variable or a parameter. Read it from the context object or the
4339 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004340 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004341 // If the "property" we were looking for is a local variable or an
4342 // argument in a context, the receiver is the global object; see
4343 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4344 JSObject* receiver = Top::context()->global()->global_receiver();
4345 Object* value = (holder->IsContext())
4346 ? Context::cast(*holder)->get(index)
4347 : JSObject::cast(*holder)->GetElement(index);
4348 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004349 }
4350
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004351 // If the holder is found, we read the property from it.
4352 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004353 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004354 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004355 JSObject* receiver;
4356 if (object->IsGlobalObject()) {
4357 receiver = GlobalObject::cast(object)->global_receiver();
4358 } else if (context->is_exception_holder(*holder)) {
4359 receiver = Top::context()->global()->global_receiver();
4360 } else {
4361 receiver = ComputeReceiverForNonGlobal(object);
4362 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004363 // No need to unhole the value here. This is taken care of by the
4364 // GetProperty function.
4365 Object* value = object->GetProperty(*name);
4366 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004367 }
4368
4369 if (throw_error) {
4370 // The property doesn't exist - throw exception.
4371 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004372 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004373 return MakePair(Top::Throw(*reference_error), NULL);
4374 } else {
4375 // The property doesn't exist - return undefined
4376 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4377 }
4378}
4379
4380
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004381static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004382 return LoadContextSlotHelper(args, true);
4383}
4384
4385
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004386static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004387 return LoadContextSlotHelper(args, false);
4388}
4389
4390
4391static Object* Runtime_StoreContextSlot(Arguments args) {
4392 HandleScope scope;
4393 ASSERT(args.length() == 3);
4394
4395 Handle<Object> value(args[0]);
4396 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004397 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004398
4399 int index;
4400 PropertyAttributes attributes;
4401 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004402 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004403 context->Lookup(name, flags, &index, &attributes);
4404
4405 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004406 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004407 // Ignore if read_only variable.
4408 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004409 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004410 }
4411 } else {
4412 ASSERT((attributes & READ_ONLY) == 0);
4413 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004414 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004415 USE(result);
4416 ASSERT(!result->IsFailure());
4417 }
4418 return *value;
4419 }
4420
4421 // Slow case: The property is not in a FixedArray context.
4422 // It is either in an JSObject extension context or it was not found.
4423 Handle<JSObject> context_ext;
4424
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004425 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004426 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004427 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004428 } else {
4429 // The property was not found. It needs to be stored in the global context.
4430 ASSERT(attributes == ABSENT);
4431 attributes = NONE;
4432 context_ext = Handle<JSObject>(Top::context()->global());
4433 }
4434
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004435 // Set the property, but ignore if read_only variable on the context
4436 // extension object itself.
4437 if ((attributes & READ_ONLY) == 0 ||
4438 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004439 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4440 if (set.is_null()) {
4441 // Failure::Exception is converted to a null handle in the
4442 // handle-based methods such as SetProperty. We therefore need
4443 // to convert null handles back to exceptions.
4444 ASSERT(Top::has_pending_exception());
4445 return Failure::Exception();
4446 }
4447 }
4448 return *value;
4449}
4450
4451
4452static Object* Runtime_Throw(Arguments args) {
4453 HandleScope scope;
4454 ASSERT(args.length() == 1);
4455
4456 return Top::Throw(args[0]);
4457}
4458
4459
4460static Object* Runtime_ReThrow(Arguments args) {
4461 HandleScope scope;
4462 ASSERT(args.length() == 1);
4463
4464 return Top::ReThrow(args[0]);
4465}
4466
4467
4468static Object* Runtime_ThrowReferenceError(Arguments args) {
4469 HandleScope scope;
4470 ASSERT(args.length() == 1);
4471
4472 Handle<Object> name(args[0]);
4473 Handle<Object> reference_error =
4474 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4475 return Top::Throw(*reference_error);
4476}
4477
4478
4479static Object* Runtime_StackOverflow(Arguments args) {
4480 NoHandleAllocation na;
4481 return Top::StackOverflow();
4482}
4483
4484
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004485static Object* Runtime_DebugBreak(Arguments args) {
4486 ASSERT(args.length() == 0);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004487 return Execution::DebugBreakHelper();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004488}
4489
4490
4491static Object* Runtime_StackGuard(Arguments args) {
4492 ASSERT(args.length() == 1);
4493
4494 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004495 if (StackGuard::IsStackOverflow()) {
4496 return Runtime_StackOverflow(args);
4497 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004498
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004499 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004500}
4501
4502
4503// NOTE: These PrintXXX functions are defined for all builds (not just
4504// DEBUG builds) because we may want to be able to trace function
4505// calls in all modes.
4506static void PrintString(String* str) {
4507 // not uncommon to have empty strings
4508 if (str->length() > 0) {
4509 SmartPointer<char> s =
4510 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4511 PrintF("%s", *s);
4512 }
4513}
4514
4515
4516static void PrintObject(Object* obj) {
4517 if (obj->IsSmi()) {
4518 PrintF("%d", Smi::cast(obj)->value());
4519 } else if (obj->IsString() || obj->IsSymbol()) {
4520 PrintString(String::cast(obj));
4521 } else if (obj->IsNumber()) {
4522 PrintF("%g", obj->Number());
4523 } else if (obj->IsFailure()) {
4524 PrintF("<failure>");
4525 } else if (obj->IsUndefined()) {
4526 PrintF("<undefined>");
4527 } else if (obj->IsNull()) {
4528 PrintF("<null>");
4529 } else if (obj->IsTrue()) {
4530 PrintF("<true>");
4531 } else if (obj->IsFalse()) {
4532 PrintF("<false>");
4533 } else {
4534 PrintF("%p", obj);
4535 }
4536}
4537
4538
4539static int StackSize() {
4540 int n = 0;
4541 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4542 return n;
4543}
4544
4545
4546static void PrintTransition(Object* result) {
4547 // indentation
4548 { const int nmax = 80;
4549 int n = StackSize();
4550 if (n <= nmax)
4551 PrintF("%4d:%*s", n, n, "");
4552 else
4553 PrintF("%4d:%*s", n, nmax, "...");
4554 }
4555
4556 if (result == NULL) {
4557 // constructor calls
4558 JavaScriptFrameIterator it;
4559 JavaScriptFrame* frame = it.frame();
4560 if (frame->IsConstructor()) PrintF("new ");
4561 // function name
4562 Object* fun = frame->function();
4563 if (fun->IsJSFunction()) {
4564 PrintObject(JSFunction::cast(fun)->shared()->name());
4565 } else {
4566 PrintObject(fun);
4567 }
4568 // function arguments
4569 // (we are intentionally only printing the actually
4570 // supplied parameters, not all parameters required)
4571 PrintF("(this=");
4572 PrintObject(frame->receiver());
4573 const int length = frame->GetProvidedParametersCount();
4574 for (int i = 0; i < length; i++) {
4575 PrintF(", ");
4576 PrintObject(frame->GetParameter(i));
4577 }
4578 PrintF(") {\n");
4579
4580 } else {
4581 // function result
4582 PrintF("} -> ");
4583 PrintObject(result);
4584 PrintF("\n");
4585 }
4586}
4587
4588
4589static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004590 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004591 NoHandleAllocation ha;
4592 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004593 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004594}
4595
4596
4597static Object* Runtime_TraceExit(Arguments args) {
4598 NoHandleAllocation ha;
4599 PrintTransition(args[0]);
4600 return args[0]; // return TOS
4601}
4602
4603
4604static Object* Runtime_DebugPrint(Arguments args) {
4605 NoHandleAllocation ha;
4606 ASSERT(args.length() == 1);
4607
4608#ifdef DEBUG
4609 if (args[0]->IsString()) {
4610 // If we have a string, assume it's a code "marker"
4611 // and print some interesting cpu debugging info.
4612 JavaScriptFrameIterator it;
4613 JavaScriptFrame* frame = it.frame();
4614 PrintF("fp = %p, sp = %p, pp = %p: ",
4615 frame->fp(), frame->sp(), frame->pp());
4616 } else {
4617 PrintF("DebugPrint: ");
4618 }
4619 args[0]->Print();
4620#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004621 // ShortPrint is available in release mode. Print is not.
4622 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004623#endif
4624 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004625 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004626
4627 return args[0]; // return TOS
4628}
4629
4630
4631static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004632 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004633 NoHandleAllocation ha;
4634 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004635 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004636}
4637
4638
mads.s.ager31e71382008-08-13 09:32:07 +00004639static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004640 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004641 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004642
4643 // According to ECMA-262, section 15.9.1, page 117, the precision of
4644 // the number in a Date object representing a particular instant in
4645 // time is milliseconds. Therefore, we floor the result of getting
4646 // the OS time.
4647 double millis = floor(OS::TimeCurrentMillis());
4648 return Heap::NumberFromDouble(millis);
4649}
4650
4651
4652static Object* Runtime_DateParseString(Arguments args) {
4653 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004654 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004655
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004656 CONVERT_ARG_CHECKED(String, str, 0);
4657 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004658
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004659 CONVERT_ARG_CHECKED(JSArray, output, 1);
4660 RUNTIME_ASSERT(output->HasFastElements());
4661
4662 AssertNoAllocation no_allocation;
4663
4664 FixedArray* output_array = output->elements();
4665 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4666 bool result;
4667 if (StringShape(*str).IsAsciiRepresentation()) {
4668 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004669 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004670 ASSERT(StringShape(*str).IsTwoByteRepresentation());
4671 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4672 }
4673
4674 if (result) {
4675 return *output;
4676 } else {
4677 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004678 }
4679}
4680
4681
4682static Object* Runtime_DateLocalTimezone(Arguments args) {
4683 NoHandleAllocation ha;
4684 ASSERT(args.length() == 1);
4685
4686 CONVERT_DOUBLE_CHECKED(x, args[0]);
4687 char* zone = OS::LocalTimezone(x);
4688 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4689}
4690
4691
4692static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4693 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004694 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004695
4696 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4697}
4698
4699
4700static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4701 NoHandleAllocation ha;
4702 ASSERT(args.length() == 1);
4703
4704 CONVERT_DOUBLE_CHECKED(x, args[0]);
4705 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4706}
4707
4708
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004709static Object* Runtime_NumberIsFinite(Arguments args) {
4710 NoHandleAllocation ha;
4711 ASSERT(args.length() == 1);
4712
4713 CONVERT_DOUBLE_CHECKED(value, args[0]);
4714 Object* result;
4715 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4716 result = Heap::false_value();
4717 } else {
4718 result = Heap::true_value();
4719 }
4720 return result;
4721}
4722
4723
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004724static Object* Runtime_GlobalReceiver(Arguments args) {
4725 ASSERT(args.length() == 1);
4726 Object* global = args[0];
4727 if (!global->IsJSGlobalObject()) return Heap::null_value();
4728 return JSGlobalObject::cast(global)->global_receiver();
4729}
4730
4731
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004732static Object* Runtime_CompileString(Arguments args) {
4733 HandleScope scope;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004734 ASSERT(args.length() == 2);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004735 CONVERT_ARG_CHECKED(String, source, 0);
ager@chromium.org236ad962008-09-25 09:45:57 +00004736 CONVERT_ARG_CHECKED(Smi, line_offset, 1);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004737
ager@chromium.org381abbb2009-02-25 13:23:22 +00004738 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004739 Handle<Context> context(Top::context()->global_context());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004740 Handle<JSFunction> boilerplate =
4741 Compiler::CompileEval(source, context, line_offset->value(), true);
4742 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004743 Handle<JSFunction> fun =
4744 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4745 return *fun;
4746}
4747
4748
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004749static Handle<JSFunction> GetBuiltinFunction(String* name) {
4750 LookupResult result;
4751 Top::global_context()->builtins()->LocalLookup(name, &result);
4752 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
4753}
4754
4755
4756static Object* CompileDirectEval(Handle<String> source) {
4757 // Compute the eval context.
4758 HandleScope scope;
4759 StackFrameLocator locator;
4760 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4761 Handle<Context> context(Context::cast(frame->context()));
4762 bool is_global = context->IsGlobalContext();
4763
ager@chromium.org381abbb2009-02-25 13:23:22 +00004764 // Compile source string in the current context.
4765 Handle<JSFunction> boilerplate =
4766 Compiler::CompileEval(source, context, 0, is_global);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004767 if (boilerplate.is_null()) return Failure::Exception();
4768 Handle<JSFunction> fun =
4769 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4770 return *fun;
4771}
4772
4773
4774static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
4775 ASSERT(args.length() == 2);
4776
4777 HandleScope scope;
4778
4779 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
4780
4781 Handle<Object> receiver;
4782
4783 // Find where the 'eval' symbol is bound. It is unaliased only if
4784 // it is bound in the global context.
4785 StackFrameLocator locator;
4786 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4787 Handle<Context> context(Context::cast(frame->context()));
4788 int index;
4789 PropertyAttributes attributes;
4790 while (!context.is_null()) {
4791 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
4792 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00004793 // Stop search when eval is found or when the global context is
4794 // reached.
4795 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004796 if (context->is_function_context()) {
4797 context = Handle<Context>(Context::cast(context->closure()->context()));
4798 } else {
4799 context = Handle<Context>(context->previous());
4800 }
4801 }
4802
iposva@chromium.org245aa852009-02-10 00:49:54 +00004803 // If eval could not be resolved, it has been deleted and we need to
4804 // throw a reference error.
4805 if (attributes == ABSENT) {
4806 Handle<Object> name = Factory::eval_symbol();
4807 Handle<Object> reference_error =
4808 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4809 return Top::Throw(*reference_error);
4810 }
4811
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004812 if (context->IsGlobalContext()) {
4813 // 'eval' is bound in the global context, but it may have been overwritten.
4814 // Compare it to the builtin 'GlobalEval' function to make sure.
4815 Handle<JSFunction> global_eval =
4816 GetBuiltinFunction(Heap::global_eval_symbol());
4817 if (global_eval.is_identical_to(callee)) {
4818 // A direct eval call.
4819 if (args[1]->IsString()) {
4820 CONVERT_ARG_CHECKED(String, source, 1);
4821 // A normal eval call on a string. Compile it and return the
4822 // compiled function bound in the local context.
4823 Object* compiled_source = CompileDirectEval(source);
4824 if (compiled_source->IsFailure()) return compiled_source;
4825 receiver = Handle<Object>(frame->receiver());
4826 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
4827 } else {
4828 // An eval call that is not called on a string. Global eval
4829 // deals better with this.
4830 receiver = Handle<Object>(Top::global_context()->global());
4831 }
4832 } else {
4833 // 'eval' is overwritten. Just call the function with the given arguments.
4834 receiver = Handle<Object>(Top::global_context()->global());
4835 }
4836 } else {
4837 // 'eval' is not bound in the global context. Just call the function
4838 // with the given arguments. This is not necessarily the global eval.
4839 if (receiver->IsContext()) {
4840 context = Handle<Context>::cast(receiver);
4841 receiver = Handle<Object>(context->get(index));
4842 }
4843 }
4844
4845 Handle<FixedArray> call = Factory::NewFixedArray(2);
4846 call->set(0, *callee);
4847 call->set(1, *receiver);
4848 return *call;
4849}
4850
4851
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004852static Object* Runtime_CompileScript(Arguments args) {
4853 HandleScope scope;
4854 ASSERT(args.length() == 4);
4855
4856 CONVERT_ARG_CHECKED(String, source, 0);
4857 CONVERT_ARG_CHECKED(String, script, 1);
4858 CONVERT_CHECKED(Smi, line_attrs, args[2]);
4859 int line = line_attrs->value();
4860 CONVERT_CHECKED(Smi, col_attrs, args[3]);
4861 int col = col_attrs->value();
4862 Handle<JSFunction> boilerplate =
4863 Compiler::Compile(source, script, line, col, NULL, NULL);
4864 if (boilerplate.is_null()) return Failure::Exception();
4865 Handle<JSFunction> fun =
4866 Factory::NewFunctionFromBoilerplate(boilerplate,
4867 Handle<Context>(Top::context()));
4868 return *fun;
4869}
4870
4871
4872static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
4873 // This utility adjusts the property attributes for newly created Function
4874 // object ("new Function(...)") by changing the map.
4875 // All it does is changing the prototype property to enumerable
4876 // as specified in ECMA262, 15.3.5.2.
4877 HandleScope scope;
4878 ASSERT(args.length() == 1);
4879 CONVERT_ARG_CHECKED(JSFunction, func, 0);
4880 ASSERT(func->map()->instance_type() ==
4881 Top::function_instance_map()->instance_type());
4882 ASSERT(func->map()->instance_size() ==
4883 Top::function_instance_map()->instance_size());
4884 func->set_map(*Top::function_instance_map());
4885 return *func;
4886}
4887
4888
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004889// Push an array unto an array of arrays if it is not already in the
4890// array. Returns true if the element was pushed on the stack and
4891// false otherwise.
4892static Object* Runtime_PushIfAbsent(Arguments args) {
4893 ASSERT(args.length() == 2);
4894 CONVERT_CHECKED(JSArray, array, args[0]);
4895 CONVERT_CHECKED(JSArray, element, args[1]);
4896 RUNTIME_ASSERT(array->HasFastElements());
4897 int length = Smi::cast(array->length())->value();
4898 FixedArray* elements = FixedArray::cast(array->elements());
4899 for (int i = 0; i < length; i++) {
4900 if (elements->get(i) == element) return Heap::false_value();
4901 }
4902 Object* obj = array->SetFastElement(length, element);
4903 if (obj->IsFailure()) return obj;
4904 return Heap::true_value();
4905}
4906
4907
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004908/**
4909 * A simple visitor visits every element of Array's.
4910 * The backend storage can be a fixed array for fast elements case,
4911 * or a dictionary for sparse array. Since Dictionary is a subtype
4912 * of FixedArray, the class can be used by both fast and slow cases.
4913 * The second parameter of the constructor, fast_elements, specifies
4914 * whether the storage is a FixedArray or Dictionary.
4915 *
4916 * An index limit is used to deal with the situation that a result array
4917 * length overflows 32-bit non-negative integer.
4918 */
4919class ArrayConcatVisitor {
4920 public:
4921 ArrayConcatVisitor(Handle<FixedArray> storage,
4922 uint32_t index_limit,
4923 bool fast_elements) :
4924 storage_(storage), index_limit_(index_limit),
4925 fast_elements_(fast_elements), index_offset_(0) { }
4926
4927 void visit(uint32_t i, Handle<Object> elm) {
4928 uint32_t index = i + index_offset_;
4929 if (index >= index_limit_) return;
4930
4931 if (fast_elements_) {
4932 ASSERT(index < static_cast<uint32_t>(storage_->length()));
4933 storage_->set(index, *elm);
4934
4935 } else {
4936 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
4937 Handle<Dictionary> result =
4938 Factory::DictionaryAtNumberPut(dict, index, elm);
4939 if (!result.is_identical_to(dict))
4940 storage_ = result;
4941 }
4942 }
4943
4944 void increase_index_offset(uint32_t delta) {
4945 index_offset_ += delta;
4946 }
4947
4948 private:
4949 Handle<FixedArray> storage_;
4950 uint32_t index_limit_;
4951 bool fast_elements_;
4952 uint32_t index_offset_;
4953};
4954
4955
4956/**
4957 * A helper function that visits elements of a JSObject. Only elements
4958 * whose index between 0 and range (exclusive) are visited.
4959 *
4960 * If the third parameter, visitor, is not NULL, the visitor is called
4961 * with parameters, 'visitor_index_offset + element index' and the element.
4962 *
4963 * It returns the number of visisted elements.
4964 */
4965static uint32_t IterateElements(Handle<JSObject> receiver,
4966 uint32_t range,
4967 ArrayConcatVisitor* visitor) {
4968 uint32_t num_of_elements = 0;
4969
4970 if (receiver->HasFastElements()) {
4971 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
4972 uint32_t len = elements->length();
4973 if (range < len) len = range;
4974
4975 for (uint32_t j = 0; j < len; j++) {
4976 Handle<Object> e(elements->get(j));
4977 if (!e->IsTheHole()) {
4978 num_of_elements++;
4979 if (visitor)
4980 visitor->visit(j, e);
4981 }
4982 }
4983
4984 } else {
4985 Handle<Dictionary> dict(receiver->element_dictionary());
4986 uint32_t capacity = dict->Capacity();
4987 for (uint32_t j = 0; j < capacity; j++) {
4988 Handle<Object> k(dict->KeyAt(j));
4989 if (dict->IsKey(*k)) {
4990 ASSERT(k->IsNumber());
4991 uint32_t index = static_cast<uint32_t>(k->Number());
4992 if (index < range) {
4993 num_of_elements++;
4994 if (visitor) {
4995 visitor->visit(index,
4996 Handle<Object>(dict->ValueAt(j)));
4997 }
4998 }
4999 }
5000 }
5001 }
5002
5003 return num_of_elements;
5004}
5005
5006
5007/**
5008 * A helper function that visits elements of an Array object, and elements
5009 * on its prototypes.
5010 *
5011 * Elements on prototypes are visited first, and only elements whose indices
5012 * less than Array length are visited.
5013 *
5014 * If a ArrayConcatVisitor object is given, the visitor is called with
5015 * parameters, element's index + visitor_index_offset and the element.
5016 */
5017static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5018 ArrayConcatVisitor* visitor) {
5019 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5020 Handle<Object> obj = array;
5021
5022 static const int kEstimatedPrototypes = 3;
5023 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5024
5025 // Visit prototype first. If an element on the prototype is shadowed by
5026 // the inheritor using the same index, the ArrayConcatVisitor visits
5027 // the prototype element before the shadowing element.
5028 // The visitor can simply overwrite the old value by new value using
5029 // the same index. This follows Array::concat semantics.
5030 while (!obj->IsNull()) {
5031 objects.Add(Handle<JSObject>::cast(obj));
5032 obj = Handle<Object>(obj->GetPrototype());
5033 }
5034
5035 uint32_t nof_elements = 0;
5036 for (int i = objects.length() - 1; i >= 0; i--) {
5037 Handle<JSObject> obj = objects[i];
5038 nof_elements +=
5039 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5040 }
5041
5042 return nof_elements;
5043}
5044
5045
5046/**
5047 * A helper function of Runtime_ArrayConcat.
5048 *
5049 * The first argument is an Array of arrays and objects. It is the
5050 * same as the arguments array of Array::concat JS function.
5051 *
5052 * If an argument is an Array object, the function visits array
5053 * elements. If an argument is not an Array object, the function
5054 * visits the object as if it is an one-element array.
5055 *
5056 * If the result array index overflows 32-bit integer, the rounded
5057 * non-negative number is used as new length. For example, if one
5058 * array length is 2^32 - 1, second array length is 1, the
5059 * concatenated array length is 0.
5060 */
5061static uint32_t IterateArguments(Handle<JSArray> arguments,
5062 ArrayConcatVisitor* visitor) {
5063 uint32_t visited_elements = 0;
5064 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5065
5066 for (uint32_t i = 0; i < num_of_args; i++) {
5067 Handle<Object> obj(arguments->GetElement(i));
5068 if (obj->IsJSArray()) {
5069 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5070 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5071 uint32_t nof_elements =
5072 IterateArrayAndPrototypeElements(array, visitor);
5073 // Total elements of array and its prototype chain can be more than
5074 // the array length, but ArrayConcat can only concatenate at most
5075 // the array length number of elements.
5076 visited_elements += (nof_elements > len) ? len : nof_elements;
5077 if (visitor) visitor->increase_index_offset(len);
5078
5079 } else {
5080 if (visitor) {
5081 visitor->visit(0, obj);
5082 visitor->increase_index_offset(1);
5083 }
5084 visited_elements++;
5085 }
5086 }
5087 return visited_elements;
5088}
5089
5090
5091/**
5092 * Array::concat implementation.
5093 * See ECMAScript 262, 15.4.4.4.
5094 */
5095static Object* Runtime_ArrayConcat(Arguments args) {
5096 ASSERT(args.length() == 1);
5097 HandleScope handle_scope;
5098
5099 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5100 Handle<JSArray> arguments(arg_arrays);
5101
5102 // Pass 1: estimate the number of elements of the result
5103 // (it could be more than real numbers if prototype has elements).
5104 uint32_t result_length = 0;
5105 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5106
5107 { AssertNoAllocation nogc;
5108 for (uint32_t i = 0; i < num_of_args; i++) {
5109 Object* obj = arguments->GetElement(i);
5110 if (obj->IsJSArray()) {
5111 result_length +=
5112 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5113 } else {
5114 result_length++;
5115 }
5116 }
5117 }
5118
5119 // Allocate an empty array, will set length and content later.
5120 Handle<JSArray> result = Factory::NewJSArray(0);
5121
5122 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5123 // If estimated number of elements is more than half of length, a
5124 // fixed array (fast case) is more time and space-efficient than a
5125 // dictionary.
5126 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5127
5128 Handle<FixedArray> storage;
5129 if (fast_case) {
5130 // The backing storage array must have non-existing elements to
5131 // preserve holes across concat operations.
5132 storage = Factory::NewFixedArrayWithHoles(result_length);
5133
5134 } else {
5135 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5136 uint32_t at_least_space_for = estimate_nof_elements +
5137 (estimate_nof_elements >> 2);
5138 storage = Handle<FixedArray>::cast(
5139 Factory::NewDictionary(at_least_space_for));
5140 }
5141
5142 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5143
5144 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5145
5146 IterateArguments(arguments, &visitor);
5147
5148 result->set_length(*len);
5149 result->set_elements(*storage);
5150
5151 return *result;
5152}
5153
5154
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005155// This will not allocate (flatten the string), but it may run
5156// very slowly for very deeply nested ConsStrings. For debugging use only.
5157static Object* Runtime_GlobalPrint(Arguments args) {
5158 NoHandleAllocation ha;
5159 ASSERT(args.length() == 1);
5160
5161 CONVERT_CHECKED(String, string, args[0]);
5162 StringInputBuffer buffer(string);
5163 while (buffer.has_more()) {
5164 uint16_t character = buffer.GetNext();
5165 PrintF("%c", character);
5166 }
5167 return string;
5168}
5169
5170
5171static Object* Runtime_RemoveArrayHoles(Arguments args) {
5172 ASSERT(args.length() == 1);
5173 // Ignore the case if this is not a JSArray.
5174 if (!args[0]->IsJSArray()) return args[0];
5175 return JSArray::cast(args[0])->RemoveHoles();
5176}
5177
5178
5179// Move contents of argument 0 (an array) to argument 1 (an array)
5180static Object* Runtime_MoveArrayContents(Arguments args) {
5181 ASSERT(args.length() == 2);
5182 CONVERT_CHECKED(JSArray, from, args[0]);
5183 CONVERT_CHECKED(JSArray, to, args[1]);
5184 to->SetContent(FixedArray::cast(from->elements()));
5185 to->set_length(from->length());
5186 from->SetContent(Heap::empty_fixed_array());
5187 from->set_length(0);
5188 return to;
5189}
5190
5191
5192// How many elements does this array have?
5193static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5194 ASSERT(args.length() == 1);
5195 CONVERT_CHECKED(JSArray, array, args[0]);
5196 HeapObject* elements = array->elements();
5197 if (elements->IsDictionary()) {
5198 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
5199 } else {
5200 return array->length();
5201 }
5202}
5203
5204
5205// Returns an array that tells you where in the [0, length) interval an array
5206// might have elements. Can either return keys or intervals. Keys can have
5207// gaps in (undefined). Intervals can also span over some undefined keys.
5208static Object* Runtime_GetArrayKeys(Arguments args) {
5209 ASSERT(args.length() == 2);
5210 HandleScope scope;
5211 CONVERT_CHECKED(JSArray, raw_array, args[0]);
5212 Handle<JSArray> array(raw_array);
5213 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005214 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005215 // Create an array and get all the keys into it, then remove all the
5216 // keys that are not integers in the range 0 to length-1.
5217 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5218 int keys_length = keys->length();
5219 for (int i = 0; i < keys_length; i++) {
5220 Object* key = keys->get(i);
5221 uint32_t index;
5222 if (!Array::IndexFromObject(key, &index) || index >= length) {
5223 // Zap invalid keys.
5224 keys->set_undefined(i);
5225 }
5226 }
5227 return *Factory::NewJSArrayWithElements(keys);
5228 } else {
5229 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5230 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005231 single_interval->set(0,
5232 Smi::FromInt(-1),
5233 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005234 Handle<Object> length_object =
5235 Factory::NewNumber(static_cast<double>(length));
5236 single_interval->set(1, *length_object);
5237 return *Factory::NewJSArrayWithElements(single_interval);
5238 }
5239}
5240
5241
5242// DefineAccessor takes an optional final argument which is the
5243// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5244// to the way accessors are implemented, it is set for both the getter
5245// and setter on the first call to DefineAccessor and ignored on
5246// subsequent calls.
5247static Object* Runtime_DefineAccessor(Arguments args) {
5248 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5249 // Compute attributes.
5250 PropertyAttributes attributes = NONE;
5251 if (args.length() == 5) {
5252 CONVERT_CHECKED(Smi, attrs, args[4]);
5253 int value = attrs->value();
5254 // Only attribute bits should be set.
5255 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5256 attributes = static_cast<PropertyAttributes>(value);
5257 }
5258
5259 CONVERT_CHECKED(JSObject, obj, args[0]);
5260 CONVERT_CHECKED(String, name, args[1]);
5261 CONVERT_CHECKED(Smi, flag, args[2]);
5262 CONVERT_CHECKED(JSFunction, fun, args[3]);
5263 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5264}
5265
5266
5267static Object* Runtime_LookupAccessor(Arguments args) {
5268 ASSERT(args.length() == 3);
5269 CONVERT_CHECKED(JSObject, obj, args[0]);
5270 CONVERT_CHECKED(String, name, args[1]);
5271 CONVERT_CHECKED(Smi, flag, args[2]);
5272 return obj->LookupAccessor(name, flag->value() == 0);
5273}
5274
5275
5276// Helper functions for wrapping and unwrapping stack frame ids.
5277static Smi* WrapFrameId(StackFrame::Id id) {
5278 ASSERT(IsAligned(OffsetFrom(id), 4));
5279 return Smi::FromInt(id >> 2);
5280}
5281
5282
5283static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5284 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5285}
5286
5287
5288// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005289// args[0]: debug event listener function to set or null or undefined for
5290// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005291// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005292static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005293 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005294 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5295 args[0]->IsUndefined() ||
5296 args[0]->IsNull());
5297 Handle<Object> callback = args.at<Object>(0);
5298 Handle<Object> data = args.at<Object>(1);
5299 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005300
5301 return Heap::undefined_value();
5302}
5303
5304
5305static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005306 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005307 StackGuard::DebugBreak();
5308 return Heap::undefined_value();
5309}
5310
5311
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005312// Find the length of the prototype chain that is to to handled as one. If a
5313// prototype object is hidden it is to be viewed as part of the the object it
5314// is prototype for.
5315static int LocalPrototypeChainLength(JSObject* obj) {
5316 int count = 1;
5317 Object* proto = obj->GetPrototype();
5318 while (proto->IsJSObject() &&
5319 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5320 count++;
5321 proto = JSObject::cast(proto)->GetPrototype();
5322 }
5323 return count;
5324}
5325
5326
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005327static Object* DebugLookupResultValue(Object* receiver, LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005328 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005329 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005330 switch (result->type()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005331 case NORMAL: {
5332 Dictionary* dict =
5333 JSObject::cast(result->holder())->property_dictionary();
5334 value = dict->ValueAt(result->GetDictionaryEntry());
5335 if (value->IsTheHole()) {
5336 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005337 }
5338 return value;
5339 }
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005340 case FIELD:
5341 value =
5342 JSObject::cast(
5343 result->holder())->FastPropertyAt(result->GetFieldIndex());
5344 if (value->IsTheHole()) {
5345 return Heap::undefined_value();
5346 }
5347 return value;
5348 case CONSTANT_FUNCTION:
5349 return result->GetConstantFunction();
5350 case CALLBACKS: {
5351 Object* structure = result->GetCallbackObject();
5352 if (structure->IsProxy()) {
5353 AccessorDescriptor* callback =
5354 reinterpret_cast<AccessorDescriptor*>(
5355 Proxy::cast(structure)->proxy());
5356 value = (callback->getter)(receiver, callback->data);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005357 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005358 value = Top::pending_exception();
5359 Top::clear_pending_exception();
5360 if (caught_exception != NULL) {
5361 *caught_exception = true;
5362 }
5363 }
5364 return value;
5365 } else {
5366 return Heap::undefined_value();
5367 }
5368 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005369 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005370 case MAP_TRANSITION:
5371 case CONSTANT_TRANSITION:
5372 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005373 return Heap::undefined_value();
5374 default:
5375 UNREACHABLE();
5376 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005377 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005378 return Heap::undefined_value();
5379}
5380
5381
ager@chromium.org32912102009-01-16 10:38:43 +00005382// Get debugger related details for an object property.
5383// args[0]: object holding property
5384// args[1]: name of the property
5385//
5386// The array returned contains the following information:
5387// 0: Property value
5388// 1: Property details
5389// 2: Property value is exception
5390// 3: Getter function if defined
5391// 4: Setter function if defined
5392// Items 2-4 are only filled if the property has either a getter or a setter
5393// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005394static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005395 HandleScope scope;
5396
5397 ASSERT(args.length() == 2);
5398
5399 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5400 CONVERT_ARG_CHECKED(String, name, 1);
5401
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005402 // Skip the global proxy as it has no properties and always delegates to the
5403 // real global object.
5404 if (obj->IsJSGlobalProxy()) {
5405 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5406 }
5407
5408
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005409 // Check if the name is trivially convertible to an index and get the element
5410 // if so.
5411 uint32_t index;
5412 if (name->AsArrayIndex(&index)) {
5413 Handle<FixedArray> details = Factory::NewFixedArray(2);
5414 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5415 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5416 return *Factory::NewJSArrayWithElements(details);
5417 }
5418
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005419 // Find the number of objects making up this.
5420 int length = LocalPrototypeChainLength(*obj);
5421
5422 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005423 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005424 Handle<JSObject> jsproto = obj;
5425 for (int i = 0; i < length; i++) {
5426 jsproto->LocalLookup(*name, &result);
5427 if (result.IsProperty()) {
5428 break;
5429 }
5430 if (i < length - 1) {
5431 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5432 }
5433 }
5434
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005435 if (result.IsProperty()) {
ager@chromium.org32912102009-01-16 10:38:43 +00005436 bool caught_exception = false;
ager@chromium.org381abbb2009-02-25 13:23:22 +00005437 Object* value = DebugLookupResultValue(*obj, &result,
5438 &caught_exception);
5439 if (value->IsFailure()) return value;
5440 Handle<Object> value_handle(value);
ager@chromium.org32912102009-01-16 10:38:43 +00005441 // If the callback object is a fixed array then it contains JavaScript
5442 // getter and/or setter.
5443 bool hasJavaScriptAccessors = result.type() == CALLBACKS &&
5444 result.GetCallbackObject()->IsFixedArray();
5445 Handle<FixedArray> details =
5446 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005447 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005448 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005449 if (hasJavaScriptAccessors) {
5450 details->set(2,
5451 caught_exception ? Heap::true_value() : Heap::false_value());
5452 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5453 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5454 }
5455
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005456 return *Factory::NewJSArrayWithElements(details);
5457 }
5458 return Heap::undefined_value();
5459}
5460
5461
5462static Object* Runtime_DebugGetProperty(Arguments args) {
5463 HandleScope scope;
5464
5465 ASSERT(args.length() == 2);
5466
5467 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5468 CONVERT_ARG_CHECKED(String, name, 1);
5469
5470 LookupResult result;
5471 obj->Lookup(*name, &result);
5472 if (result.IsProperty()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005473 return DebugLookupResultValue(*obj, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005474 }
5475 return Heap::undefined_value();
5476}
5477
5478
5479// Return the names of the local named properties.
5480// args[0]: object
5481static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5482 HandleScope scope;
5483 ASSERT(args.length() == 1);
5484 if (!args[0]->IsJSObject()) {
5485 return Heap::undefined_value();
5486 }
5487 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5488
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005489 // Skip the global proxy as it has no properties and always delegates to the
5490 // real global object.
5491 if (obj->IsJSGlobalProxy()) {
5492 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5493 }
5494
5495 // Find the number of objects making up this.
5496 int length = LocalPrototypeChainLength(*obj);
5497
5498 // Find the number of local properties for each of the objects.
5499 int* local_property_count = NewArray<int>(length);
5500 int total_property_count = 0;
5501 Handle<JSObject> jsproto = obj;
5502 for (int i = 0; i < length; i++) {
5503 int n;
5504 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5505 local_property_count[i] = n;
5506 total_property_count += n;
5507 if (i < length - 1) {
5508 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5509 }
5510 }
5511
5512 // Allocate an array with storage for all the property names.
5513 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5514
5515 // Get the property names.
5516 jsproto = obj;
5517 for (int i = 0; i < length; i++) {
5518 jsproto->GetLocalPropertyNames(*names,
5519 i == 0 ? 0 : local_property_count[i - 1]);
5520 if (i < length - 1) {
5521 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5522 }
5523 }
5524
5525 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005526 return *Factory::NewJSArrayWithElements(names);
5527}
5528
5529
5530// Return the names of the local indexed properties.
5531// args[0]: object
5532static Object* Runtime_DebugLocalElementNames(Arguments args) {
5533 HandleScope scope;
5534 ASSERT(args.length() == 1);
5535 if (!args[0]->IsJSObject()) {
5536 return Heap::undefined_value();
5537 }
5538 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5539
5540 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5541 Handle<FixedArray> names = Factory::NewFixedArray(n);
5542 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5543 return *Factory::NewJSArrayWithElements(names);
5544}
5545
5546
5547// Return the property type calculated from the property details.
5548// args[0]: smi with property details.
5549static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5550 ASSERT(args.length() == 1);
5551 CONVERT_CHECKED(Smi, details, args[0]);
5552 PropertyType type = PropertyDetails(details).type();
5553 return Smi::FromInt(static_cast<int>(type));
5554}
5555
5556
5557// Return the property attribute calculated from the property details.
5558// args[0]: smi with property details.
5559static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5560 ASSERT(args.length() == 1);
5561 CONVERT_CHECKED(Smi, details, args[0]);
5562 PropertyAttributes attributes = PropertyDetails(details).attributes();
5563 return Smi::FromInt(static_cast<int>(attributes));
5564}
5565
5566
5567// Return the property insertion index calculated from the property details.
5568// args[0]: smi with property details.
5569static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5570 ASSERT(args.length() == 1);
5571 CONVERT_CHECKED(Smi, details, args[0]);
5572 int index = PropertyDetails(details).index();
5573 return Smi::FromInt(index);
5574}
5575
5576
5577// Return information on whether an object has a named or indexed interceptor.
5578// args[0]: object
5579static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5580 HandleScope scope;
5581 ASSERT(args.length() == 1);
5582 if (!args[0]->IsJSObject()) {
5583 return Smi::FromInt(0);
5584 }
5585 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5586
5587 int result = 0;
5588 if (obj->HasNamedInterceptor()) result |= 2;
5589 if (obj->HasIndexedInterceptor()) result |= 1;
5590
5591 return Smi::FromInt(result);
5592}
5593
5594
5595// Return property names from named interceptor.
5596// args[0]: object
5597static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5598 HandleScope scope;
5599 ASSERT(args.length() == 1);
5600 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005601
ager@chromium.org32912102009-01-16 10:38:43 +00005602 if (obj->HasNamedInterceptor()) {
5603 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5604 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5605 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005606 return Heap::undefined_value();
5607}
5608
5609
5610// Return element names from indexed interceptor.
5611// args[0]: object
5612static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5613 HandleScope scope;
5614 ASSERT(args.length() == 1);
5615 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005616
ager@chromium.org32912102009-01-16 10:38:43 +00005617 if (obj->HasIndexedInterceptor()) {
5618 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5619 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5620 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005621 return Heap::undefined_value();
5622}
5623
5624
5625// Return property value from named interceptor.
5626// args[0]: object
5627// args[1]: property name
5628static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5629 HandleScope scope;
5630 ASSERT(args.length() == 2);
5631 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5632 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5633 CONVERT_ARG_CHECKED(String, name, 1);
5634
5635 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005636 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005637}
5638
5639
5640// Return element value from indexed interceptor.
5641// args[0]: object
5642// args[1]: index
5643static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5644 HandleScope scope;
5645 ASSERT(args.length() == 2);
5646 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5647 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5648 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5649
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005650 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005651}
5652
5653
5654static Object* Runtime_CheckExecutionState(Arguments args) {
5655 ASSERT(args.length() >= 1);
5656 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005657 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005658 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005659 return Top::Throw(Heap::illegal_execution_state_symbol());
5660 }
5661
5662 return Heap::true_value();
5663}
5664
5665
5666static Object* Runtime_GetFrameCount(Arguments args) {
5667 HandleScope scope;
5668 ASSERT(args.length() == 1);
5669
5670 // Check arguments.
5671 Object* result = Runtime_CheckExecutionState(args);
5672 if (result->IsFailure()) return result;
5673
5674 // Count all frames which are relevant to debugging stack trace.
5675 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005676 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005677 if (id == StackFrame::NO_ID) {
5678 // If there is no JavaScript stack frame count is 0.
5679 return Smi::FromInt(0);
5680 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005681 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5682 return Smi::FromInt(n);
5683}
5684
5685
5686static const int kFrameDetailsFrameIdIndex = 0;
5687static const int kFrameDetailsReceiverIndex = 1;
5688static const int kFrameDetailsFunctionIndex = 2;
5689static const int kFrameDetailsArgumentCountIndex = 3;
5690static const int kFrameDetailsLocalCountIndex = 4;
5691static const int kFrameDetailsSourcePositionIndex = 5;
5692static const int kFrameDetailsConstructCallIndex = 6;
5693static const int kFrameDetailsDebuggerFrameIndex = 7;
5694static const int kFrameDetailsFirstDynamicIndex = 8;
5695
5696// Return an array with frame details
5697// args[0]: number: break id
5698// args[1]: number: frame index
5699//
5700// The array returned contains the following information:
5701// 0: Frame id
5702// 1: Receiver
5703// 2: Function
5704// 3: Argument count
5705// 4: Local count
5706// 5: Source position
5707// 6: Constructor call
5708// 7: Debugger frame
5709// Arguments name, value
5710// Locals name, value
5711static Object* Runtime_GetFrameDetails(Arguments args) {
5712 HandleScope scope;
5713 ASSERT(args.length() == 2);
5714
5715 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005716 Object* check = Runtime_CheckExecutionState(args);
5717 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005718 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5719
5720 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005721 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005722 if (id == StackFrame::NO_ID) {
5723 // If there are no JavaScript stack frames return undefined.
5724 return Heap::undefined_value();
5725 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005726 int count = 0;
5727 JavaScriptFrameIterator it(id);
5728 for (; !it.done(); it.Advance()) {
5729 if (count == index) break;
5730 count++;
5731 }
5732 if (it.done()) return Heap::undefined_value();
5733
5734 // Traverse the saved contexts chain to find the active context for the
5735 // selected frame.
5736 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005737 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005738 save = save->prev();
5739 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005740 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005741
5742 // Get the frame id.
5743 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
5744
5745 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005746 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005747
5748 // Check for constructor frame.
5749 bool constructor = it.frame()->IsConstructor();
5750
5751 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005752 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005753 ScopeInfo<> info(*code);
5754
5755 // Get the context.
5756 Handle<Context> context(Context::cast(it.frame()->context()));
5757
5758 // Get the locals names and values into a temporary array.
5759 //
5760 // TODO(1240907): Hide compiler-introduced stack variables
5761 // (e.g. .result)? For users of the debugger, they will probably be
5762 // confusing.
5763 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
5764 for (int i = 0; i < info.NumberOfLocals(); i++) {
5765 // Name of the local.
5766 locals->set(i * 2, *info.LocalName(i));
5767
5768 // Fetch the value of the local - either from the stack or from a
5769 // heap-allocated context.
5770 if (i < info.number_of_stack_slots()) {
5771 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
5772 } else {
5773 Handle<String> name = info.LocalName(i);
5774 // Traverse the context chain to the function context as all local
5775 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005776 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005777 context = Handle<Context>(context->previous());
5778 }
5779 ASSERT(context->is_function_context());
5780 locals->set(i * 2 + 1,
5781 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
5782 NULL)));
5783 }
5784 }
5785
5786 // Now advance to the arguments adapter frame (if any). If contains all
5787 // the provided parameters and
5788
5789 // Now advance to the arguments adapter frame (if any). It contains all
5790 // the provided parameters whereas the function frame always have the number
5791 // of arguments matching the functions parameters. The rest of the
5792 // information (except for what is collected above) is the same.
5793 it.AdvanceToArgumentsFrame();
5794
5795 // Find the number of arguments to fill. At least fill the number of
5796 // parameters for the function and fill more if more parameters are provided.
5797 int argument_count = info.number_of_parameters();
5798 if (argument_count < it.frame()->GetProvidedParametersCount()) {
5799 argument_count = it.frame()->GetProvidedParametersCount();
5800 }
5801
5802 // Calculate the size of the result.
5803 int details_size = kFrameDetailsFirstDynamicIndex +
5804 2 * (argument_count + info.NumberOfLocals());
5805 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
5806
5807 // Add the frame id.
5808 details->set(kFrameDetailsFrameIdIndex, *frame_id);
5809
5810 // Add the function (same as in function frame).
5811 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
5812
5813 // Add the arguments count.
5814 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
5815
5816 // Add the locals count
5817 details->set(kFrameDetailsLocalCountIndex,
5818 Smi::FromInt(info.NumberOfLocals()));
5819
5820 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00005821 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005822 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
5823 } else {
5824 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
5825 }
5826
5827 // Add the constructor information.
5828 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
5829
5830 // Add information on whether this frame is invoked in the debugger context.
5831 details->set(kFrameDetailsDebuggerFrameIndex,
5832 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
5833
5834 // Fill the dynamic part.
5835 int details_index = kFrameDetailsFirstDynamicIndex;
5836
5837 // Add arguments name and value.
5838 for (int i = 0; i < argument_count; i++) {
5839 // Name of the argument.
5840 if (i < info.number_of_parameters()) {
5841 details->set(details_index++, *info.parameter_name(i));
5842 } else {
5843 details->set(details_index++, Heap::undefined_value());
5844 }
5845
5846 // Parameter value.
5847 if (i < it.frame()->GetProvidedParametersCount()) {
5848 details->set(details_index++, it.frame()->GetParameter(i));
5849 } else {
5850 details->set(details_index++, Heap::undefined_value());
5851 }
5852 }
5853
5854 // Add locals name and value from the temporary copy from the function frame.
5855 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
5856 details->set(details_index++, locals->get(i));
5857 }
5858
5859 // Add the receiver (same as in function frame).
5860 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
5861 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
5862 Handle<Object> receiver(it.frame()->receiver());
5863 if (!receiver->IsJSObject()) {
5864 // If the receiver is NOT a JSObject we have hit an optimization
5865 // where a value object is not converted into a wrapped JS objects.
5866 // To hide this optimization from the debugger, we wrap the receiver
5867 // by creating correct wrapper object based on the calling frame's
5868 // global context.
5869 it.Advance();
5870 Handle<Context> calling_frames_global_context(
5871 Context::cast(Context::cast(it.frame()->context())->global_context()));
5872 receiver = Factory::ToObject(receiver, calling_frames_global_context);
5873 }
5874 details->set(kFrameDetailsReceiverIndex, *receiver);
5875
5876 ASSERT_EQ(details_size, details_index);
5877 return *Factory::NewJSArrayWithElements(details);
5878}
5879
5880
5881static Object* Runtime_GetCFrames(Arguments args) {
5882 HandleScope scope;
5883 ASSERT(args.length() == 1);
5884 Object* result = Runtime_CheckExecutionState(args);
5885 if (result->IsFailure()) return result;
5886
5887 static const int kMaxCFramesSize = 200;
5888 OS::StackFrame frames[kMaxCFramesSize];
5889 int frames_count = OS::StackWalk(frames, kMaxCFramesSize);
5890 if (frames_count == OS::kStackWalkError) {
5891 return Heap::undefined_value();
5892 }
5893
5894 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
5895 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
5896 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
5897 for (int i = 0; i < frames_count; i++) {
5898 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
5899 frame_value->SetProperty(
5900 *address_str,
5901 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
5902 NONE);
5903
5904 // Get the stack walk text for this frame.
5905 Handle<String> frame_text;
5906 if (strlen(frames[i].text) > 0) {
5907 Vector<const char> str(frames[i].text, strlen(frames[i].text));
5908 frame_text = Factory::NewStringFromAscii(str);
5909 }
5910
5911 if (!frame_text.is_null()) {
5912 frame_value->SetProperty(*text_str, *frame_text, NONE);
5913 }
5914
5915 frames_array->set(i, *frame_value);
5916 }
5917 return *Factory::NewJSArrayWithElements(frames_array);
5918}
5919
5920
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005921static Object* Runtime_GetThreadCount(Arguments args) {
5922 HandleScope scope;
5923 ASSERT(args.length() == 1);
5924
5925 // Check arguments.
5926 Object* result = Runtime_CheckExecutionState(args);
5927 if (result->IsFailure()) return result;
5928
5929 // Count all archived V8 threads.
5930 int n = 0;
5931 for (ThreadState* thread = ThreadState::FirstInUse();
5932 thread != NULL;
5933 thread = thread->Next()) {
5934 n++;
5935 }
5936
5937 // Total number of threads is current thread and archived threads.
5938 return Smi::FromInt(n + 1);
5939}
5940
5941
5942static const int kThreadDetailsCurrentThreadIndex = 0;
5943static const int kThreadDetailsThreadIdIndex = 1;
5944static const int kThreadDetailsSize = 2;
5945
5946// Return an array with thread details
5947// args[0]: number: break id
5948// args[1]: number: thread index
5949//
5950// The array returned contains the following information:
5951// 0: Is current thread?
5952// 1: Thread id
5953static Object* Runtime_GetThreadDetails(Arguments args) {
5954 HandleScope scope;
5955 ASSERT(args.length() == 2);
5956
5957 // Check arguments.
5958 Object* check = Runtime_CheckExecutionState(args);
5959 if (check->IsFailure()) return check;
5960 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5961
5962 // Allocate array for result.
5963 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
5964
5965 // Thread index 0 is current thread.
5966 if (index == 0) {
5967 // Fill the details.
5968 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
5969 details->set(kThreadDetailsThreadIdIndex,
5970 Smi::FromInt(ThreadManager::CurrentId()));
5971 } else {
5972 // Find the thread with the requested index.
5973 int n = 1;
5974 ThreadState* thread = ThreadState::FirstInUse();
5975 while (index != n && thread != NULL) {
5976 thread = thread->Next();
5977 n++;
5978 }
5979 if (thread == NULL) {
5980 return Heap::undefined_value();
5981 }
5982
5983 // Fill the details.
5984 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
5985 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
5986 }
5987
5988 // Convert to JS array and return.
5989 return *Factory::NewJSArrayWithElements(details);
5990}
5991
5992
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005993static Object* Runtime_GetBreakLocations(Arguments args) {
5994 HandleScope scope;
5995 ASSERT(args.length() == 1);
5996
5997 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
5998 Handle<SharedFunctionInfo> shared(raw_fun->shared());
5999 // Find the number of break points
6000 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6001 if (break_locations->IsUndefined()) return Heap::undefined_value();
6002 // Return array as JS array
6003 return *Factory::NewJSArrayWithElements(
6004 Handle<FixedArray>::cast(break_locations));
6005}
6006
6007
6008// Set a break point in a function
6009// args[0]: function
6010// args[1]: number: break source position (within the function source)
6011// args[2]: number: break point object
6012static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6013 HandleScope scope;
6014 ASSERT(args.length() == 3);
6015 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6016 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6017 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6018 RUNTIME_ASSERT(source_position >= 0);
6019 Handle<Object> break_point_object_arg = args.at<Object>(2);
6020
6021 // Set break point.
6022 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6023
6024 return Heap::undefined_value();
6025}
6026
6027
6028static Object* FindSharedFunctionInfoInScript(Handle<Script> script,
6029 int position) {
6030 // Iterate the heap looking for SharedFunctionInfo generated from the
6031 // script. The inner most SharedFunctionInfo containing the source position
6032 // for the requested break point is found.
6033 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6034 // which is found is not compiled it is compiled and the heap is iterated
6035 // again as the compilation might create inner functions from the newly
6036 // compiled function and the actual requested break point might be in one of
6037 // these functions.
6038 bool done = false;
6039 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006040 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006041 Handle<SharedFunctionInfo> target;
6042 // The current candidate for the last function in script:
6043 Handle<SharedFunctionInfo> last;
6044 while (!done) {
6045 HeapIterator iterator;
6046 while (iterator.has_next()) {
6047 HeapObject* obj = iterator.next();
6048 ASSERT(obj != NULL);
6049 if (obj->IsSharedFunctionInfo()) {
6050 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6051 if (shared->script() == *script) {
6052 // If the SharedFunctionInfo found has the requested script data and
6053 // contains the source position it is a candidate.
6054 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006055 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006056 start_position = shared->start_position();
6057 }
6058 if (start_position <= position &&
6059 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006060 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006061 // candidate this is the new candidate.
6062 if (target.is_null()) {
6063 target_start_position = start_position;
6064 target = shared;
6065 } else {
6066 if (target_start_position < start_position &&
6067 shared->end_position() < target->end_position()) {
6068 target_start_position = start_position;
6069 target = shared;
6070 }
6071 }
6072 }
6073
6074 // Keep track of the last function in the script.
6075 if (last.is_null() ||
6076 shared->end_position() > last->start_position()) {
6077 last = shared;
6078 }
6079 }
6080 }
6081 }
6082
6083 // Make sure some candidate is selected.
6084 if (target.is_null()) {
6085 if (!last.is_null()) {
6086 // Position after the last function - use last.
6087 target = last;
6088 } else {
6089 // Unable to find function - possibly script without any function.
6090 return Heap::undefined_value();
6091 }
6092 }
6093
6094 // If the candidate found is compiled we are done. NOTE: when lazy
6095 // compilation of inner functions is introduced some additional checking
6096 // needs to be done here to compile inner functions.
6097 done = target->is_compiled();
6098 if (!done) {
6099 // If the candidate is not compiled compile it to reveal any inner
6100 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006101 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006102 }
6103 }
6104
6105 return *target;
6106}
6107
6108
6109// Change the state of a break point in a script. NOTE: Regarding performance
6110// see the NOTE for GetScriptFromScriptData.
6111// args[0]: script to set break point in
6112// args[1]: number: break source position (within the script source)
6113// args[2]: number: break point object
6114static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6115 HandleScope scope;
6116 ASSERT(args.length() == 3);
6117 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6118 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6119 RUNTIME_ASSERT(source_position >= 0);
6120 Handle<Object> break_point_object_arg = args.at<Object>(2);
6121
6122 // Get the script from the script wrapper.
6123 RUNTIME_ASSERT(wrapper->value()->IsScript());
6124 Handle<Script> script(Script::cast(wrapper->value()));
6125
6126 Object* result = FindSharedFunctionInfoInScript(script, source_position);
6127 if (!result->IsUndefined()) {
6128 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6129 // Find position within function. The script position might be before the
6130 // source position of the first function.
6131 int position;
6132 if (shared->start_position() > source_position) {
6133 position = 0;
6134 } else {
6135 position = source_position - shared->start_position();
6136 }
6137 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6138 }
6139 return Heap::undefined_value();
6140}
6141
6142
6143// Clear a break point
6144// args[0]: number: break point object
6145static Object* Runtime_ClearBreakPoint(Arguments args) {
6146 HandleScope scope;
6147 ASSERT(args.length() == 1);
6148 Handle<Object> break_point_object_arg = args.at<Object>(0);
6149
6150 // Clear break point.
6151 Debug::ClearBreakPoint(break_point_object_arg);
6152
6153 return Heap::undefined_value();
6154}
6155
6156
6157// Change the state of break on exceptions
6158// args[0]: boolean indicating uncaught exceptions
6159// args[1]: boolean indicating on/off
6160static Object* Runtime_ChangeBreakOnException(Arguments args) {
6161 HandleScope scope;
6162 ASSERT(args.length() == 2);
6163 ASSERT(args[0]->IsNumber());
6164 ASSERT(args[1]->IsBoolean());
6165
6166 // Update break point state
6167 ExceptionBreakType type =
6168 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6169 bool enable = args[1]->ToBoolean()->IsTrue();
6170 Debug::ChangeBreakOnException(type, enable);
6171 return Heap::undefined_value();
6172}
6173
6174
6175// Prepare for stepping
6176// args[0]: break id for checking execution state
6177// args[1]: step action from the enumeration StepAction
6178// args[2]: number of times to perform the step
6179static Object* Runtime_PrepareStep(Arguments args) {
6180 HandleScope scope;
6181 ASSERT(args.length() == 3);
6182 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006183 Object* check = Runtime_CheckExecutionState(args);
6184 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006185 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6186 return Top::Throw(Heap::illegal_argument_symbol());
6187 }
6188
6189 // Get the step action and check validity.
6190 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6191 if (step_action != StepIn &&
6192 step_action != StepNext &&
6193 step_action != StepOut &&
6194 step_action != StepInMin &&
6195 step_action != StepMin) {
6196 return Top::Throw(Heap::illegal_argument_symbol());
6197 }
6198
6199 // Get the number of steps.
6200 int step_count = NumberToInt32(args[2]);
6201 if (step_count < 1) {
6202 return Top::Throw(Heap::illegal_argument_symbol());
6203 }
6204
6205 // Prepare step.
6206 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6207 return Heap::undefined_value();
6208}
6209
6210
6211// Clear all stepping set by PrepareStep.
6212static Object* Runtime_ClearStepping(Arguments args) {
6213 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006214 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006215 Debug::ClearStepping();
6216 return Heap::undefined_value();
6217}
6218
6219
6220// Creates a copy of the with context chain. The copy of the context chain is
6221// is linked to the function context supplied.
6222static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6223 Handle<Context> function_context) {
6224 // At the bottom of the chain. Return the function context to link to.
6225 if (context_chain->is_function_context()) {
6226 return function_context;
6227 }
6228
6229 // Recursively copy the with contexts.
6230 Handle<Context> previous(context_chain->previous());
6231 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6232 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006233 CopyWithContextChain(function_context, previous),
6234 extension,
6235 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006236}
6237
6238
6239// Helper function to find or create the arguments object for
6240// Runtime_DebugEvaluate.
6241static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6242 Handle<JSFunction> function,
6243 Handle<Code> code,
6244 const ScopeInfo<>* sinfo,
6245 Handle<Context> function_context) {
6246 // Try to find the value of 'arguments' to pass as parameter. If it is not
6247 // found (that is the debugged function does not reference 'arguments' and
6248 // does not support eval) then create an 'arguments' object.
6249 int index;
6250 if (sinfo->number_of_stack_slots() > 0) {
6251 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6252 if (index != -1) {
6253 return Handle<Object>(frame->GetExpression(index));
6254 }
6255 }
6256
6257 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6258 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6259 NULL);
6260 if (index != -1) {
6261 return Handle<Object>(function_context->get(index));
6262 }
6263 }
6264
6265 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006266 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6267 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006268 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006269 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006270 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006271 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006272 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006273 return arguments;
6274}
6275
6276
6277// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006278// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006279// extension part has all the parameters and locals of the function on the
6280// stack frame. A function which calls eval with the code to evaluate is then
6281// compiled in this context and called in this context. As this context
6282// replaces the context of the function on the stack frame a new (empty)
6283// function is created as well to be used as the closure for the context.
6284// This function and the context acts as replacements for the function on the
6285// stack frame presenting the same view of the values of parameters and
6286// local variables as if the piece of JavaScript was evaluated at the point
6287// where the function on the stack frame is currently stopped.
6288static Object* Runtime_DebugEvaluate(Arguments args) {
6289 HandleScope scope;
6290
6291 // Check the execution state and decode arguments frame and source to be
6292 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006293 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006294 Object* check_result = Runtime_CheckExecutionState(args);
6295 if (check_result->IsFailure()) return check_result;
6296 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6297 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006298 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6299
6300 // Handle the processing of break.
6301 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006302
6303 // Get the frame where the debugging is performed.
6304 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6305 JavaScriptFrameIterator it(id);
6306 JavaScriptFrame* frame = it.frame();
6307 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6308 Handle<Code> code(function->code());
6309 ScopeInfo<> sinfo(*code);
6310
6311 // Traverse the saved contexts chain to find the active context for the
6312 // selected frame.
6313 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006314 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006315 save = save->prev();
6316 }
6317 ASSERT(save != NULL);
6318 SaveContext savex;
6319 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006320
6321 // Create the (empty) function replacing the function on the stack frame for
6322 // the purpose of evaluating in the context created below. It is important
6323 // that this function does not describe any parameters and local variables
6324 // in the context. If it does then this will cause problems with the lookup
6325 // in Context::Lookup, where context slots for parameters and local variables
6326 // are looked at before the extension object.
6327 Handle<JSFunction> go_between =
6328 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
6329 go_between->set_context(function->context());
6330#ifdef DEBUG
6331 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
6332 ASSERT(go_between_sinfo.number_of_parameters() == 0);
6333 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
6334#endif
6335
6336 // Allocate and initialize a context extension object with all the
6337 // arguments, stack locals heap locals and extension properties of the
6338 // debugged function.
6339 Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function());
6340 // First fill all parameters to the context extension.
6341 for (int i = 0; i < sinfo.number_of_parameters(); ++i) {
6342 SetProperty(context_ext,
6343 sinfo.parameter_name(i),
6344 Handle<Object>(frame->GetParameter(i)), NONE);
6345 }
6346 // Second fill all stack locals to the context extension.
6347 for (int i = 0; i < sinfo.number_of_stack_slots(); i++) {
6348 SetProperty(context_ext,
6349 sinfo.stack_slot_name(i),
6350 Handle<Object>(frame->GetExpression(i)), NONE);
6351 }
6352 // Third fill all context locals to the context extension.
6353 Handle<Context> frame_context(Context::cast(frame->context()));
6354 Handle<Context> function_context(frame_context->fcontext());
6355 for (int i = Context::MIN_CONTEXT_SLOTS;
6356 i < sinfo.number_of_context_slots();
6357 ++i) {
6358 int context_index =
6359 ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL);
6360 SetProperty(context_ext,
6361 sinfo.context_slot_name(i),
6362 Handle<Object>(function_context->get(context_index)), NONE);
6363 }
6364 // Finally copy any properties from the function context extension. This will
6365 // be variables introduced by eval.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006366 if (function_context->has_extension() &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006367 !function_context->IsGlobalContext()) {
6368 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6369 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6370 for (int i = 0; i < keys->length(); i++) {
6371 // Names of variables introduced by eval are strings.
6372 ASSERT(keys->get(i)->IsString());
6373 Handle<String> key(String::cast(keys->get(i)));
6374 SetProperty(context_ext, key, GetProperty(ext, key), NONE);
6375 }
6376 }
6377
6378 // Allocate a new context for the debug evaluation and set the extension
6379 // object build.
6380 Handle<Context> context =
6381 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
6382 context->set_extension(*context_ext);
6383 // Copy any with contexts present and chain them in front of this context.
6384 context = CopyWithContextChain(frame_context, context);
6385
6386 // Wrap the evaluation statement in a new function compiled in the newly
6387 // created context. The function has one parameter which has to be called
6388 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00006389 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006390 // function(arguments,__source__) {return eval(__source__);}
6391 static const char* source_str =
6392 "function(arguments,__source__){return eval(__source__);}";
6393 static const int source_str_length = strlen(source_str);
6394 Handle<String> function_source =
6395 Factory::NewStringFromAscii(Vector<const char>(source_str,
6396 source_str_length));
6397 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00006398 Compiler::CompileEval(function_source,
6399 context,
6400 0,
6401 context->IsGlobalContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006402 if (boilerplate.is_null()) return Failure::Exception();
6403 Handle<JSFunction> compiled_function =
6404 Factory::NewFunctionFromBoilerplate(boilerplate, context);
6405
6406 // Invoke the result of the compilation to get the evaluation function.
6407 bool has_pending_exception;
6408 Handle<Object> receiver(frame->receiver());
6409 Handle<Object> evaluation_function =
6410 Execution::Call(compiled_function, receiver, 0, NULL,
6411 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006412 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006413
6414 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
6415 function_context);
6416
6417 // Invoke the evaluation function and return the result.
6418 const int argc = 2;
6419 Object** argv[argc] = { arguments.location(),
6420 Handle<Object>::cast(source).location() };
6421 Handle<Object> result =
6422 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
6423 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006424 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006425 return *result;
6426}
6427
6428
6429static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
6430 HandleScope scope;
6431
6432 // Check the execution state and decode arguments frame and source to be
6433 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006434 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006435 Object* check_result = Runtime_CheckExecutionState(args);
6436 if (check_result->IsFailure()) return check_result;
6437 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006438 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
6439
6440 // Handle the processing of break.
6441 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006442
6443 // Enter the top context from before the debugger was invoked.
6444 SaveContext save;
6445 SaveContext* top = &save;
6446 while (top != NULL && *top->context() == *Debug::debug_context()) {
6447 top = top->prev();
6448 }
6449 if (top != NULL) {
6450 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006451 }
6452
6453 // Get the global context now set to the top context from before the
6454 // debugger was invoked.
6455 Handle<Context> context = Top::global_context();
6456
6457 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00006458 Handle<JSFunction> boilerplate =
6459 Handle<JSFunction>(Compiler::CompileEval(source, context, 0, true));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006460 if (boilerplate.is_null()) return Failure::Exception();
6461 Handle<JSFunction> compiled_function =
6462 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
6463 context));
6464
6465 // Invoke the result of the compilation to get the evaluation function.
6466 bool has_pending_exception;
6467 Handle<Object> receiver = Top::global();
6468 Handle<Object> result =
6469 Execution::Call(compiled_function, receiver, 0, NULL,
6470 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006471 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006472 return *result;
6473}
6474
6475
6476// Helper function used by Runtime_DebugGetLoadedScripts below.
6477static int DebugGetLoadedScripts(FixedArray* instances, int instances_size) {
6478 NoHandleAllocation ha;
6479 AssertNoAllocation no_alloc;
6480
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006481 // Scan heap for Script objects.
6482 int count = 0;
6483 HeapIterator iterator;
6484 while (iterator.has_next()) {
6485 HeapObject* obj = iterator.next();
6486 ASSERT(obj != NULL);
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006487 if (obj->IsScript()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006488 if (instances != NULL && count < instances_size) {
6489 instances->set(count, obj);
6490 }
6491 count++;
6492 }
6493 }
6494
6495 return count;
6496}
6497
6498
6499static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
6500 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006501 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006502
6503 // Perform two GCs to get rid of all unreferenced scripts. The first GC gets
ager@chromium.org32912102009-01-16 10:38:43 +00006504 // rid of all the cached script wrappers and the second gets rid of the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006505 // scripts which is no longer referenced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006506 Heap::CollectAllGarbage();
6507 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006508
6509 // Get the number of scripts.
6510 int count;
6511 count = DebugGetLoadedScripts(NULL, 0);
6512
6513 // Allocate an array to hold the result.
6514 Handle<FixedArray> instances = Factory::NewFixedArray(count);
6515
6516 // Fill the script objects.
6517 count = DebugGetLoadedScripts(*instances, count);
6518
6519 // Convert the script objects to proper JS objects.
6520 for (int i = 0; i < count; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006521 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
6522 // Get the script wrapper in a local handle before calling GetScriptWrapper,
6523 // because using
6524 // instances->set(i, *GetScriptWrapper(script))
6525 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
6526 // already have deferenced the instances handle.
6527 Handle<JSValue> wrapper = GetScriptWrapper(script);
6528 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006529 }
6530
6531 // Return result as a JS array.
6532 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
6533 Handle<JSArray>::cast(result)->SetContent(*instances);
6534 return *result;
6535}
6536
6537
6538// Helper function used by Runtime_DebugReferencedBy below.
6539static int DebugReferencedBy(JSObject* target,
6540 Object* instance_filter, int max_references,
6541 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006542 JSFunction* arguments_function) {
6543 NoHandleAllocation ha;
6544 AssertNoAllocation no_alloc;
6545
6546 // Iterate the heap.
6547 int count = 0;
6548 JSObject* last = NULL;
6549 HeapIterator iterator;
6550 while (iterator.has_next() &&
6551 (max_references == 0 || count < max_references)) {
6552 // Only look at all JSObjects.
6553 HeapObject* heap_obj = iterator.next();
6554 if (heap_obj->IsJSObject()) {
6555 // Skip context extension objects and argument arrays as these are
6556 // checked in the context of functions using them.
6557 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006558 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006559 obj->map()->constructor() == arguments_function) {
6560 continue;
6561 }
6562
6563 // Check if the JS object has a reference to the object looked for.
6564 if (obj->ReferencesObject(target)) {
6565 // Check instance filter if supplied. This is normally used to avoid
6566 // references from mirror objects (see Runtime_IsInPrototypeChain).
6567 if (!instance_filter->IsUndefined()) {
6568 Object* V = obj;
6569 while (true) {
6570 Object* prototype = V->GetPrototype();
6571 if (prototype->IsNull()) {
6572 break;
6573 }
6574 if (instance_filter == prototype) {
6575 obj = NULL; // Don't add this object.
6576 break;
6577 }
6578 V = prototype;
6579 }
6580 }
6581
6582 if (obj != NULL) {
6583 // Valid reference found add to instance array if supplied an update
6584 // count.
6585 if (instances != NULL && count < instances_size) {
6586 instances->set(count, obj);
6587 }
6588 last = obj;
6589 count++;
6590 }
6591 }
6592 }
6593 }
6594
6595 // Check for circular reference only. This can happen when the object is only
6596 // referenced from mirrors and has a circular reference in which case the
6597 // object is not really alive and would have been garbage collected if not
6598 // referenced from the mirror.
6599 if (count == 1 && last == target) {
6600 count = 0;
6601 }
6602
6603 // Return the number of referencing objects found.
6604 return count;
6605}
6606
6607
6608// Scan the heap for objects with direct references to an object
6609// args[0]: the object to find references to
6610// args[1]: constructor function for instances to exclude (Mirror)
6611// args[2]: the the maximum number of objects to return
6612static Object* Runtime_DebugReferencedBy(Arguments args) {
6613 ASSERT(args.length() == 3);
6614
6615 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006616 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006617
6618 // Check parameters.
6619 CONVERT_CHECKED(JSObject, target, args[0]);
6620 Object* instance_filter = args[1];
6621 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
6622 instance_filter->IsJSObject());
6623 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
6624 RUNTIME_ASSERT(max_references >= 0);
6625
6626 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006627 JSObject* arguments_boilerplate =
6628 Top::context()->global_context()->arguments_boilerplate();
6629 JSFunction* arguments_function =
6630 JSFunction::cast(arguments_boilerplate->map()->constructor());
6631
6632 // Get the number of referencing objects.
6633 int count;
6634 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006635 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006636
6637 // Allocate an array to hold the result.
6638 Object* object = Heap::AllocateFixedArray(count);
6639 if (object->IsFailure()) return object;
6640 FixedArray* instances = FixedArray::cast(object);
6641
6642 // Fill the referencing objects.
6643 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006644 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006645
6646 // Return result as JS array.
6647 Object* result =
6648 Heap::AllocateJSObject(
6649 Top::context()->global_context()->array_function());
6650 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6651 return result;
6652}
6653
6654
6655// Helper function used by Runtime_DebugConstructedBy below.
6656static int DebugConstructedBy(JSFunction* constructor, int max_references,
6657 FixedArray* instances, int instances_size) {
6658 AssertNoAllocation no_alloc;
6659
6660 // Iterate the heap.
6661 int count = 0;
6662 HeapIterator iterator;
6663 while (iterator.has_next() &&
6664 (max_references == 0 || count < max_references)) {
6665 // Only look at all JSObjects.
6666 HeapObject* heap_obj = iterator.next();
6667 if (heap_obj->IsJSObject()) {
6668 JSObject* obj = JSObject::cast(heap_obj);
6669 if (obj->map()->constructor() == constructor) {
6670 // Valid reference found add to instance array if supplied an update
6671 // count.
6672 if (instances != NULL && count < instances_size) {
6673 instances->set(count, obj);
6674 }
6675 count++;
6676 }
6677 }
6678 }
6679
6680 // Return the number of referencing objects found.
6681 return count;
6682}
6683
6684
6685// Scan the heap for objects constructed by a specific function.
6686// args[0]: the constructor to find instances of
6687// args[1]: the the maximum number of objects to return
6688static Object* Runtime_DebugConstructedBy(Arguments args) {
6689 ASSERT(args.length() == 2);
6690
6691 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006692 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006693
6694 // Check parameters.
6695 CONVERT_CHECKED(JSFunction, constructor, args[0]);
6696 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
6697 RUNTIME_ASSERT(max_references >= 0);
6698
6699 // Get the number of referencing objects.
6700 int count;
6701 count = DebugConstructedBy(constructor, max_references, NULL, 0);
6702
6703 // Allocate an array to hold the result.
6704 Object* object = Heap::AllocateFixedArray(count);
6705 if (object->IsFailure()) return object;
6706 FixedArray* instances = FixedArray::cast(object);
6707
6708 // Fill the referencing objects.
6709 count = DebugConstructedBy(constructor, max_references, instances, count);
6710
6711 // Return result as JS array.
6712 Object* result =
6713 Heap::AllocateJSObject(
6714 Top::context()->global_context()->array_function());
6715 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6716 return result;
6717}
6718
6719
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006720// Find the effective prototype object as returned by __proto__.
6721// args[0]: the object to find the prototype for.
6722static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006723 ASSERT(args.length() == 1);
6724
6725 CONVERT_CHECKED(JSObject, obj, args[0]);
6726
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006727 // Use the __proto__ accessor.
6728 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006729}
6730
6731
6732static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006733 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006734 CPU::DebugBreak();
6735 return Heap::undefined_value();
6736}
6737
6738
6739// Finds the script object from the script data. NOTE: This operation uses
6740// heap traversal to find the function generated for the source position
6741// for the requested break point. For lazily compiled functions several heap
6742// traversals might be required rendering this operation as a rather slow
6743// operation. However for setting break points which is normally done through
6744// some kind of user interaction the performance is not crucial.
6745static Handle<Object> Runtime_GetScriptFromScriptName(
6746 Handle<String> script_name) {
6747 // Scan the heap for Script objects to find the script with the requested
6748 // script data.
6749 Handle<Script> script;
6750 HeapIterator iterator;
6751 while (script.is_null() && iterator.has_next()) {
6752 HeapObject* obj = iterator.next();
6753 // If a script is found check if it has the script data requested.
6754 if (obj->IsScript()) {
6755 if (Script::cast(obj)->name()->IsString()) {
6756 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
6757 script = Handle<Script>(Script::cast(obj));
6758 }
6759 }
6760 }
6761 }
6762
6763 // If no script with the requested script data is found return undefined.
6764 if (script.is_null()) return Factory::undefined_value();
6765
6766 // Return the script found.
6767 return GetScriptWrapper(script);
6768}
6769
6770
6771// Get the script object from script data. NOTE: Regarding performance
6772// see the NOTE for GetScriptFromScriptData.
6773// args[0]: script data for the script to find the source for
6774static Object* Runtime_GetScript(Arguments args) {
6775 HandleScope scope;
6776
6777 ASSERT(args.length() == 1);
6778
6779 CONVERT_CHECKED(String, script_name, args[0]);
6780
6781 // Find the requested script.
6782 Handle<Object> result =
6783 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
6784 return *result;
6785}
6786
6787
6788static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
6789#ifdef DEBUG
6790 HandleScope scope;
6791 ASSERT(args.length() == 1);
6792 // Get the function and make sure it is compiled.
6793 CONVERT_ARG_CHECKED(JSFunction, func, 0);
6794 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
6795 return Failure::Exception();
6796 }
6797 func->code()->PrintLn();
6798#endif // DEBUG
6799 return Heap::undefined_value();
6800}
6801
6802
6803static Object* Runtime_Abort(Arguments args) {
6804 ASSERT(args.length() == 2);
6805 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
6806 Smi::cast(args[1])->value());
6807 Top::PrintStack();
6808 OS::Abort();
6809 UNREACHABLE();
6810 return NULL;
6811}
6812
6813
kasper.lund44510672008-07-25 07:37:58 +00006814#ifdef DEBUG
6815// ListNatives is ONLY used by the fuzz-natives.js in debug mode
6816// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006817static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006818 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006819 HandleScope scope;
6820 Handle<JSArray> result = Factory::NewJSArray(0);
6821 int index = 0;
6822#define ADD_ENTRY(Name, argc) \
6823 { \
6824 HandleScope inner; \
6825 Handle<String> name = \
6826 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
6827 Handle<JSArray> pair = Factory::NewJSArray(0); \
6828 SetElement(pair, 0, name); \
6829 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
6830 SetElement(result, index++, pair); \
6831 }
6832 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
6833#undef ADD_ENTRY
6834 return *result;
6835}
kasper.lund44510672008-07-25 07:37:58 +00006836#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006837
6838
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006839static Object* Runtime_Log(Arguments args) {
6840 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00006841 CONVERT_CHECKED(String, format, args[0]);
6842 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006843 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006844 Logger::LogRuntime(chars, elms);
6845 return Heap::undefined_value();
6846}
6847
6848
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006849static Object* Runtime_IS_VAR(Arguments args) {
6850 UNREACHABLE(); // implemented as macro in the parser
6851 return NULL;
6852}
6853
6854
6855// ----------------------------------------------------------------------------
6856// Implementation of Runtime
6857
6858#define F(name, nargs) \
6859 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
6860 static_cast<int>(Runtime::k##name) },
6861
6862static Runtime::Function Runtime_functions[] = {
6863 RUNTIME_FUNCTION_LIST(F)
6864 { NULL, NULL, NULL, 0, -1 }
6865};
6866
6867#undef F
6868
6869
6870Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
6871 ASSERT(0 <= fid && fid < kNofFunctions);
6872 return &Runtime_functions[fid];
6873}
6874
6875
6876Runtime::Function* Runtime::FunctionForName(const char* name) {
6877 for (Function* f = Runtime_functions; f->name != NULL; f++) {
6878 if (strcmp(f->name, name) == 0) {
6879 return f;
6880 }
6881 }
6882 return NULL;
6883}
6884
6885
6886void Runtime::PerformGC(Object* result) {
6887 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00006888 if (failure->IsRetryAfterGC()) {
6889 // Try to do a garbage collection; ignore it if it fails. The C
6890 // entry stub will throw an out-of-memory exception in that case.
6891 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
6892 } else {
6893 // Handle last resort GC and make sure to allow future allocations
6894 // to grow the heap without causing GCs (if possible).
6895 Counters::gc_last_resort_from_js.Increment();
6896 Heap::CollectAllGarbage();
6897 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006898}
6899
6900
6901} } // namespace v8::internal