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