blob: 3a738dfc521667a41970f6762cfec8118cd420ae [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),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001285 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001286 // 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);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001329 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001330 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());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001586 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001587 AssertNoAllocation no_alloc;
1588 ParseReplacementPattern(&parts_,
1589 replacement->ToAsciiVector(),
1590 capture_count,
1591 subject_length);
1592 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001593 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001594 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.org5ec48922009-05-05 07:25:34 +00002168 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002169 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.org5ec48922009-05-05 07:25:34 +00002193 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002194 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002195 if (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.org5ec48922009-05-05 07:25:34 +00002201 if (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.org5ec48922009-05-05 07:25:34 +00003332 Object* o = 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.org5ec48922009-05-05 07:25:34 +00003683 bool ascii = 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.org5ec48922009-05-05 07:25:34 +00003703 if (ascii && !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;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004760 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004761 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004762 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004763 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004764 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4765 }
4766
4767 if (result) {
4768 return *output;
4769 } else {
4770 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004771 }
4772}
4773
4774
4775static Object* Runtime_DateLocalTimezone(Arguments args) {
4776 NoHandleAllocation ha;
4777 ASSERT(args.length() == 1);
4778
4779 CONVERT_DOUBLE_CHECKED(x, args[0]);
4780 char* zone = OS::LocalTimezone(x);
4781 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4782}
4783
4784
4785static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4786 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004787 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004788
4789 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4790}
4791
4792
4793static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4794 NoHandleAllocation ha;
4795 ASSERT(args.length() == 1);
4796
4797 CONVERT_DOUBLE_CHECKED(x, args[0]);
4798 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4799}
4800
4801
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004802static Object* Runtime_NumberIsFinite(Arguments args) {
4803 NoHandleAllocation ha;
4804 ASSERT(args.length() == 1);
4805
4806 CONVERT_DOUBLE_CHECKED(value, args[0]);
4807 Object* result;
4808 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4809 result = Heap::false_value();
4810 } else {
4811 result = Heap::true_value();
4812 }
4813 return result;
4814}
4815
4816
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004817static Object* Runtime_GlobalReceiver(Arguments args) {
4818 ASSERT(args.length() == 1);
4819 Object* global = args[0];
4820 if (!global->IsJSGlobalObject()) return Heap::null_value();
4821 return JSGlobalObject::cast(global)->global_receiver();
4822}
4823
4824
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004825static Object* Runtime_CompileString(Arguments args) {
4826 HandleScope scope;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004827 ASSERT_EQ(3, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004828 CONVERT_ARG_CHECKED(String, source, 0);
ager@chromium.org236ad962008-09-25 09:45:57 +00004829 CONVERT_ARG_CHECKED(Smi, line_offset, 1);
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004830 CONVERT_ARG_CHECKED(Oddball, is_json, 2)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004831
ager@chromium.org381abbb2009-02-25 13:23:22 +00004832 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004833 Handle<Context> context(Top::context()->global_context());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004834 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
4835 context,
4836 line_offset->value(),
4837 true,
4838 is_json->IsTrue());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004839 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004840 Handle<JSFunction> fun =
4841 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4842 return *fun;
4843}
4844
4845
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004846static Handle<JSFunction> GetBuiltinFunction(String* name) {
4847 LookupResult result;
4848 Top::global_context()->builtins()->LocalLookup(name, &result);
4849 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
4850}
4851
4852
4853static Object* CompileDirectEval(Handle<String> source) {
4854 // Compute the eval context.
4855 HandleScope scope;
4856 StackFrameLocator locator;
4857 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4858 Handle<Context> context(Context::cast(frame->context()));
4859 bool is_global = context->IsGlobalContext();
4860
ager@chromium.org381abbb2009-02-25 13:23:22 +00004861 // Compile source string in the current context.
4862 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004863 Compiler::CompileEval(source, context, 0, is_global, false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004864 if (boilerplate.is_null()) return Failure::Exception();
4865 Handle<JSFunction> fun =
4866 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4867 return *fun;
4868}
4869
4870
4871static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
4872 ASSERT(args.length() == 2);
4873
4874 HandleScope scope;
4875
4876 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
4877
4878 Handle<Object> receiver;
4879
4880 // Find where the 'eval' symbol is bound. It is unaliased only if
4881 // it is bound in the global context.
4882 StackFrameLocator locator;
4883 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4884 Handle<Context> context(Context::cast(frame->context()));
4885 int index;
4886 PropertyAttributes attributes;
4887 while (!context.is_null()) {
4888 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
4889 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00004890 // Stop search when eval is found or when the global context is
4891 // reached.
4892 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004893 if (context->is_function_context()) {
4894 context = Handle<Context>(Context::cast(context->closure()->context()));
4895 } else {
4896 context = Handle<Context>(context->previous());
4897 }
4898 }
4899
iposva@chromium.org245aa852009-02-10 00:49:54 +00004900 // If eval could not be resolved, it has been deleted and we need to
4901 // throw a reference error.
4902 if (attributes == ABSENT) {
4903 Handle<Object> name = Factory::eval_symbol();
4904 Handle<Object> reference_error =
4905 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4906 return Top::Throw(*reference_error);
4907 }
4908
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004909 if (context->IsGlobalContext()) {
4910 // 'eval' is bound in the global context, but it may have been overwritten.
4911 // Compare it to the builtin 'GlobalEval' function to make sure.
4912 Handle<JSFunction> global_eval =
4913 GetBuiltinFunction(Heap::global_eval_symbol());
4914 if (global_eval.is_identical_to(callee)) {
4915 // A direct eval call.
4916 if (args[1]->IsString()) {
4917 CONVERT_ARG_CHECKED(String, source, 1);
4918 // A normal eval call on a string. Compile it and return the
4919 // compiled function bound in the local context.
4920 Object* compiled_source = CompileDirectEval(source);
4921 if (compiled_source->IsFailure()) return compiled_source;
4922 receiver = Handle<Object>(frame->receiver());
4923 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
4924 } else {
4925 // An eval call that is not called on a string. Global eval
4926 // deals better with this.
4927 receiver = Handle<Object>(Top::global_context()->global());
4928 }
4929 } else {
4930 // 'eval' is overwritten. Just call the function with the given arguments.
4931 receiver = Handle<Object>(Top::global_context()->global());
4932 }
4933 } else {
4934 // 'eval' is not bound in the global context. Just call the function
4935 // with the given arguments. This is not necessarily the global eval.
4936 if (receiver->IsContext()) {
4937 context = Handle<Context>::cast(receiver);
4938 receiver = Handle<Object>(context->get(index));
4939 }
4940 }
4941
4942 Handle<FixedArray> call = Factory::NewFixedArray(2);
4943 call->set(0, *callee);
4944 call->set(1, *receiver);
4945 return *call;
4946}
4947
4948
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004949static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
4950 // This utility adjusts the property attributes for newly created Function
4951 // object ("new Function(...)") by changing the map.
4952 // All it does is changing the prototype property to enumerable
4953 // as specified in ECMA262, 15.3.5.2.
4954 HandleScope scope;
4955 ASSERT(args.length() == 1);
4956 CONVERT_ARG_CHECKED(JSFunction, func, 0);
4957 ASSERT(func->map()->instance_type() ==
4958 Top::function_instance_map()->instance_type());
4959 ASSERT(func->map()->instance_size() ==
4960 Top::function_instance_map()->instance_size());
4961 func->set_map(*Top::function_instance_map());
4962 return *func;
4963}
4964
4965
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004966// Push an array unto an array of arrays if it is not already in the
4967// array. Returns true if the element was pushed on the stack and
4968// false otherwise.
4969static Object* Runtime_PushIfAbsent(Arguments args) {
4970 ASSERT(args.length() == 2);
4971 CONVERT_CHECKED(JSArray, array, args[0]);
4972 CONVERT_CHECKED(JSArray, element, args[1]);
4973 RUNTIME_ASSERT(array->HasFastElements());
4974 int length = Smi::cast(array->length())->value();
4975 FixedArray* elements = FixedArray::cast(array->elements());
4976 for (int i = 0; i < length; i++) {
4977 if (elements->get(i) == element) return Heap::false_value();
4978 }
4979 Object* obj = array->SetFastElement(length, element);
4980 if (obj->IsFailure()) return obj;
4981 return Heap::true_value();
4982}
4983
4984
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004985/**
4986 * A simple visitor visits every element of Array's.
4987 * The backend storage can be a fixed array for fast elements case,
4988 * or a dictionary for sparse array. Since Dictionary is a subtype
4989 * of FixedArray, the class can be used by both fast and slow cases.
4990 * The second parameter of the constructor, fast_elements, specifies
4991 * whether the storage is a FixedArray or Dictionary.
4992 *
4993 * An index limit is used to deal with the situation that a result array
4994 * length overflows 32-bit non-negative integer.
4995 */
4996class ArrayConcatVisitor {
4997 public:
4998 ArrayConcatVisitor(Handle<FixedArray> storage,
4999 uint32_t index_limit,
5000 bool fast_elements) :
5001 storage_(storage), index_limit_(index_limit),
5002 fast_elements_(fast_elements), index_offset_(0) { }
5003
5004 void visit(uint32_t i, Handle<Object> elm) {
5005 uint32_t index = i + index_offset_;
5006 if (index >= index_limit_) return;
5007
5008 if (fast_elements_) {
5009 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5010 storage_->set(index, *elm);
5011
5012 } else {
5013 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
5014 Handle<Dictionary> result =
5015 Factory::DictionaryAtNumberPut(dict, index, elm);
5016 if (!result.is_identical_to(dict))
5017 storage_ = result;
5018 }
5019 }
5020
5021 void increase_index_offset(uint32_t delta) {
5022 index_offset_ += delta;
5023 }
5024
5025 private:
5026 Handle<FixedArray> storage_;
5027 uint32_t index_limit_;
5028 bool fast_elements_;
5029 uint32_t index_offset_;
5030};
5031
5032
5033/**
5034 * A helper function that visits elements of a JSObject. Only elements
5035 * whose index between 0 and range (exclusive) are visited.
5036 *
5037 * If the third parameter, visitor, is not NULL, the visitor is called
5038 * with parameters, 'visitor_index_offset + element index' and the element.
5039 *
5040 * It returns the number of visisted elements.
5041 */
5042static uint32_t IterateElements(Handle<JSObject> receiver,
5043 uint32_t range,
5044 ArrayConcatVisitor* visitor) {
5045 uint32_t num_of_elements = 0;
5046
5047 if (receiver->HasFastElements()) {
5048 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5049 uint32_t len = elements->length();
5050 if (range < len) len = range;
5051
5052 for (uint32_t j = 0; j < len; j++) {
5053 Handle<Object> e(elements->get(j));
5054 if (!e->IsTheHole()) {
5055 num_of_elements++;
5056 if (visitor)
5057 visitor->visit(j, e);
5058 }
5059 }
5060
5061 } else {
5062 Handle<Dictionary> dict(receiver->element_dictionary());
5063 uint32_t capacity = dict->Capacity();
5064 for (uint32_t j = 0; j < capacity; j++) {
5065 Handle<Object> k(dict->KeyAt(j));
5066 if (dict->IsKey(*k)) {
5067 ASSERT(k->IsNumber());
5068 uint32_t index = static_cast<uint32_t>(k->Number());
5069 if (index < range) {
5070 num_of_elements++;
5071 if (visitor) {
5072 visitor->visit(index,
5073 Handle<Object>(dict->ValueAt(j)));
5074 }
5075 }
5076 }
5077 }
5078 }
5079
5080 return num_of_elements;
5081}
5082
5083
5084/**
5085 * A helper function that visits elements of an Array object, and elements
5086 * on its prototypes.
5087 *
5088 * Elements on prototypes are visited first, and only elements whose indices
5089 * less than Array length are visited.
5090 *
5091 * If a ArrayConcatVisitor object is given, the visitor is called with
5092 * parameters, element's index + visitor_index_offset and the element.
5093 */
5094static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5095 ArrayConcatVisitor* visitor) {
5096 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5097 Handle<Object> obj = array;
5098
5099 static const int kEstimatedPrototypes = 3;
5100 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5101
5102 // Visit prototype first. If an element on the prototype is shadowed by
5103 // the inheritor using the same index, the ArrayConcatVisitor visits
5104 // the prototype element before the shadowing element.
5105 // The visitor can simply overwrite the old value by new value using
5106 // the same index. This follows Array::concat semantics.
5107 while (!obj->IsNull()) {
5108 objects.Add(Handle<JSObject>::cast(obj));
5109 obj = Handle<Object>(obj->GetPrototype());
5110 }
5111
5112 uint32_t nof_elements = 0;
5113 for (int i = objects.length() - 1; i >= 0; i--) {
5114 Handle<JSObject> obj = objects[i];
5115 nof_elements +=
5116 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5117 }
5118
5119 return nof_elements;
5120}
5121
5122
5123/**
5124 * A helper function of Runtime_ArrayConcat.
5125 *
5126 * The first argument is an Array of arrays and objects. It is the
5127 * same as the arguments array of Array::concat JS function.
5128 *
5129 * If an argument is an Array object, the function visits array
5130 * elements. If an argument is not an Array object, the function
5131 * visits the object as if it is an one-element array.
5132 *
5133 * If the result array index overflows 32-bit integer, the rounded
5134 * non-negative number is used as new length. For example, if one
5135 * array length is 2^32 - 1, second array length is 1, the
5136 * concatenated array length is 0.
5137 */
5138static uint32_t IterateArguments(Handle<JSArray> arguments,
5139 ArrayConcatVisitor* visitor) {
5140 uint32_t visited_elements = 0;
5141 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5142
5143 for (uint32_t i = 0; i < num_of_args; i++) {
5144 Handle<Object> obj(arguments->GetElement(i));
5145 if (obj->IsJSArray()) {
5146 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5147 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5148 uint32_t nof_elements =
5149 IterateArrayAndPrototypeElements(array, visitor);
5150 // Total elements of array and its prototype chain can be more than
5151 // the array length, but ArrayConcat can only concatenate at most
5152 // the array length number of elements.
5153 visited_elements += (nof_elements > len) ? len : nof_elements;
5154 if (visitor) visitor->increase_index_offset(len);
5155
5156 } else {
5157 if (visitor) {
5158 visitor->visit(0, obj);
5159 visitor->increase_index_offset(1);
5160 }
5161 visited_elements++;
5162 }
5163 }
5164 return visited_elements;
5165}
5166
5167
5168/**
5169 * Array::concat implementation.
5170 * See ECMAScript 262, 15.4.4.4.
5171 */
5172static Object* Runtime_ArrayConcat(Arguments args) {
5173 ASSERT(args.length() == 1);
5174 HandleScope handle_scope;
5175
5176 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5177 Handle<JSArray> arguments(arg_arrays);
5178
5179 // Pass 1: estimate the number of elements of the result
5180 // (it could be more than real numbers if prototype has elements).
5181 uint32_t result_length = 0;
5182 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5183
5184 { AssertNoAllocation nogc;
5185 for (uint32_t i = 0; i < num_of_args; i++) {
5186 Object* obj = arguments->GetElement(i);
5187 if (obj->IsJSArray()) {
5188 result_length +=
5189 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5190 } else {
5191 result_length++;
5192 }
5193 }
5194 }
5195
5196 // Allocate an empty array, will set length and content later.
5197 Handle<JSArray> result = Factory::NewJSArray(0);
5198
5199 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5200 // If estimated number of elements is more than half of length, a
5201 // fixed array (fast case) is more time and space-efficient than a
5202 // dictionary.
5203 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5204
5205 Handle<FixedArray> storage;
5206 if (fast_case) {
5207 // The backing storage array must have non-existing elements to
5208 // preserve holes across concat operations.
5209 storage = Factory::NewFixedArrayWithHoles(result_length);
5210
5211 } else {
5212 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5213 uint32_t at_least_space_for = estimate_nof_elements +
5214 (estimate_nof_elements >> 2);
5215 storage = Handle<FixedArray>::cast(
5216 Factory::NewDictionary(at_least_space_for));
5217 }
5218
5219 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5220
5221 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5222
5223 IterateArguments(arguments, &visitor);
5224
5225 result->set_length(*len);
5226 result->set_elements(*storage);
5227
5228 return *result;
5229}
5230
5231
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005232// This will not allocate (flatten the string), but it may run
5233// very slowly for very deeply nested ConsStrings. For debugging use only.
5234static Object* Runtime_GlobalPrint(Arguments args) {
5235 NoHandleAllocation ha;
5236 ASSERT(args.length() == 1);
5237
5238 CONVERT_CHECKED(String, string, args[0]);
5239 StringInputBuffer buffer(string);
5240 while (buffer.has_more()) {
5241 uint16_t character = buffer.GetNext();
5242 PrintF("%c", character);
5243 }
5244 return string;
5245}
5246
ager@chromium.org5ec48922009-05-05 07:25:34 +00005247// Moves all own elements of an object, that are below a limit, to positions
5248// starting at zero. All undefined values are placed after non-undefined values,
5249// and are followed by non-existing element. Does not change the length
5250// property.
5251// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005252static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005253 ASSERT(args.length() == 2);
5254 CONVERT_CHECKED(JSObject, object, args[0]);
5255 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5256 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005257}
5258
5259
5260// Move contents of argument 0 (an array) to argument 1 (an array)
5261static Object* Runtime_MoveArrayContents(Arguments args) {
5262 ASSERT(args.length() == 2);
5263 CONVERT_CHECKED(JSArray, from, args[0]);
5264 CONVERT_CHECKED(JSArray, to, args[1]);
5265 to->SetContent(FixedArray::cast(from->elements()));
5266 to->set_length(from->length());
5267 from->SetContent(Heap::empty_fixed_array());
5268 from->set_length(0);
5269 return to;
5270}
5271
5272
5273// How many elements does this array have?
5274static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5275 ASSERT(args.length() == 1);
5276 CONVERT_CHECKED(JSArray, array, args[0]);
5277 HeapObject* elements = array->elements();
5278 if (elements->IsDictionary()) {
5279 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
5280 } else {
5281 return array->length();
5282 }
5283}
5284
5285
5286// Returns an array that tells you where in the [0, length) interval an array
5287// might have elements. Can either return keys or intervals. Keys can have
5288// gaps in (undefined). Intervals can also span over some undefined keys.
5289static Object* Runtime_GetArrayKeys(Arguments args) {
5290 ASSERT(args.length() == 2);
5291 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005292 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005293 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005294 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005295 // Create an array and get all the keys into it, then remove all the
5296 // keys that are not integers in the range 0 to length-1.
5297 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5298 int keys_length = keys->length();
5299 for (int i = 0; i < keys_length; i++) {
5300 Object* key = keys->get(i);
5301 uint32_t index;
5302 if (!Array::IndexFromObject(key, &index) || index >= length) {
5303 // Zap invalid keys.
5304 keys->set_undefined(i);
5305 }
5306 }
5307 return *Factory::NewJSArrayWithElements(keys);
5308 } else {
5309 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5310 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005311 single_interval->set(0,
5312 Smi::FromInt(-1),
5313 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005314 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5315 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005316 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005317 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005318 single_interval->set(1, *length_object);
5319 return *Factory::NewJSArrayWithElements(single_interval);
5320 }
5321}
5322
5323
5324// DefineAccessor takes an optional final argument which is the
5325// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5326// to the way accessors are implemented, it is set for both the getter
5327// and setter on the first call to DefineAccessor and ignored on
5328// subsequent calls.
5329static Object* Runtime_DefineAccessor(Arguments args) {
5330 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5331 // Compute attributes.
5332 PropertyAttributes attributes = NONE;
5333 if (args.length() == 5) {
5334 CONVERT_CHECKED(Smi, attrs, args[4]);
5335 int value = attrs->value();
5336 // Only attribute bits should be set.
5337 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5338 attributes = static_cast<PropertyAttributes>(value);
5339 }
5340
5341 CONVERT_CHECKED(JSObject, obj, args[0]);
5342 CONVERT_CHECKED(String, name, args[1]);
5343 CONVERT_CHECKED(Smi, flag, args[2]);
5344 CONVERT_CHECKED(JSFunction, fun, args[3]);
5345 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5346}
5347
5348
5349static Object* Runtime_LookupAccessor(Arguments args) {
5350 ASSERT(args.length() == 3);
5351 CONVERT_CHECKED(JSObject, obj, args[0]);
5352 CONVERT_CHECKED(String, name, args[1]);
5353 CONVERT_CHECKED(Smi, flag, args[2]);
5354 return obj->LookupAccessor(name, flag->value() == 0);
5355}
5356
5357
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005358#ifdef ENABLE_DEBUGGER_SUPPORT
5359static Object* Runtime_DebugBreak(Arguments args) {
5360 ASSERT(args.length() == 0);
5361 return Execution::DebugBreakHelper();
5362}
5363
5364
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005365// Helper functions for wrapping and unwrapping stack frame ids.
5366static Smi* WrapFrameId(StackFrame::Id id) {
5367 ASSERT(IsAligned(OffsetFrom(id), 4));
5368 return Smi::FromInt(id >> 2);
5369}
5370
5371
5372static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5373 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5374}
5375
5376
5377// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005378// args[0]: debug event listener function to set or null or undefined for
5379// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005380// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005381static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005382 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005383 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5384 args[0]->IsUndefined() ||
5385 args[0]->IsNull());
5386 Handle<Object> callback = args.at<Object>(0);
5387 Handle<Object> data = args.at<Object>(1);
5388 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005389
5390 return Heap::undefined_value();
5391}
5392
5393
5394static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005395 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005396 StackGuard::DebugBreak();
5397 return Heap::undefined_value();
5398}
5399
5400
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005401// Find the length of the prototype chain that is to to handled as one. If a
5402// prototype object is hidden it is to be viewed as part of the the object it
5403// is prototype for.
5404static int LocalPrototypeChainLength(JSObject* obj) {
5405 int count = 1;
5406 Object* proto = obj->GetPrototype();
5407 while (proto->IsJSObject() &&
5408 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5409 count++;
5410 proto = JSObject::cast(proto)->GetPrototype();
5411 }
5412 return count;
5413}
5414
5415
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005416static Object* DebugLookupResultValue(Object* receiver, LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005417 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005418 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005419 switch (result->type()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005420 case NORMAL: {
5421 Dictionary* dict =
5422 JSObject::cast(result->holder())->property_dictionary();
5423 value = dict->ValueAt(result->GetDictionaryEntry());
5424 if (value->IsTheHole()) {
5425 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005426 }
5427 return value;
5428 }
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005429 case FIELD:
5430 value =
5431 JSObject::cast(
5432 result->holder())->FastPropertyAt(result->GetFieldIndex());
5433 if (value->IsTheHole()) {
5434 return Heap::undefined_value();
5435 }
5436 return value;
5437 case CONSTANT_FUNCTION:
5438 return result->GetConstantFunction();
5439 case CALLBACKS: {
5440 Object* structure = result->GetCallbackObject();
5441 if (structure->IsProxy()) {
5442 AccessorDescriptor* callback =
5443 reinterpret_cast<AccessorDescriptor*>(
5444 Proxy::cast(structure)->proxy());
5445 value = (callback->getter)(receiver, callback->data);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005446 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005447 value = Top::pending_exception();
5448 Top::clear_pending_exception();
5449 if (caught_exception != NULL) {
5450 *caught_exception = true;
5451 }
5452 }
5453 return value;
5454 } else {
5455 return Heap::undefined_value();
5456 }
5457 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005458 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005459 case MAP_TRANSITION:
5460 case CONSTANT_TRANSITION:
5461 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005462 return Heap::undefined_value();
5463 default:
5464 UNREACHABLE();
5465 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005466 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005467 return Heap::undefined_value();
5468}
5469
5470
ager@chromium.org32912102009-01-16 10:38:43 +00005471// Get debugger related details for an object property.
5472// args[0]: object holding property
5473// args[1]: name of the property
5474//
5475// The array returned contains the following information:
5476// 0: Property value
5477// 1: Property details
5478// 2: Property value is exception
5479// 3: Getter function if defined
5480// 4: Setter function if defined
5481// Items 2-4 are only filled if the property has either a getter or a setter
5482// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005483static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005484 HandleScope scope;
5485
5486 ASSERT(args.length() == 2);
5487
5488 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5489 CONVERT_ARG_CHECKED(String, name, 1);
5490
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005491 // Skip the global proxy as it has no properties and always delegates to the
5492 // real global object.
5493 if (obj->IsJSGlobalProxy()) {
5494 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5495 }
5496
5497
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005498 // Check if the name is trivially convertible to an index and get the element
5499 // if so.
5500 uint32_t index;
5501 if (name->AsArrayIndex(&index)) {
5502 Handle<FixedArray> details = Factory::NewFixedArray(2);
5503 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5504 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5505 return *Factory::NewJSArrayWithElements(details);
5506 }
5507
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005508 // Find the number of objects making up this.
5509 int length = LocalPrototypeChainLength(*obj);
5510
5511 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005512 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005513 Handle<JSObject> jsproto = obj;
5514 for (int i = 0; i < length; i++) {
5515 jsproto->LocalLookup(*name, &result);
5516 if (result.IsProperty()) {
5517 break;
5518 }
5519 if (i < length - 1) {
5520 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5521 }
5522 }
5523
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005524 if (result.IsProperty()) {
ager@chromium.org32912102009-01-16 10:38:43 +00005525 bool caught_exception = false;
ager@chromium.org381abbb2009-02-25 13:23:22 +00005526 Object* value = DebugLookupResultValue(*obj, &result,
5527 &caught_exception);
5528 if (value->IsFailure()) return value;
5529 Handle<Object> value_handle(value);
ager@chromium.org32912102009-01-16 10:38:43 +00005530 // If the callback object is a fixed array then it contains JavaScript
5531 // getter and/or setter.
5532 bool hasJavaScriptAccessors = result.type() == CALLBACKS &&
5533 result.GetCallbackObject()->IsFixedArray();
5534 Handle<FixedArray> details =
5535 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005536 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005537 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005538 if (hasJavaScriptAccessors) {
5539 details->set(2,
5540 caught_exception ? Heap::true_value() : Heap::false_value());
5541 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5542 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5543 }
5544
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005545 return *Factory::NewJSArrayWithElements(details);
5546 }
5547 return Heap::undefined_value();
5548}
5549
5550
5551static Object* Runtime_DebugGetProperty(Arguments args) {
5552 HandleScope scope;
5553
5554 ASSERT(args.length() == 2);
5555
5556 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5557 CONVERT_ARG_CHECKED(String, name, 1);
5558
5559 LookupResult result;
5560 obj->Lookup(*name, &result);
5561 if (result.IsProperty()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005562 return DebugLookupResultValue(*obj, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005563 }
5564 return Heap::undefined_value();
5565}
5566
5567
5568// Return the names of the local named properties.
5569// args[0]: object
5570static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5571 HandleScope scope;
5572 ASSERT(args.length() == 1);
5573 if (!args[0]->IsJSObject()) {
5574 return Heap::undefined_value();
5575 }
5576 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5577
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005578 // Skip the global proxy as it has no properties and always delegates to the
5579 // real global object.
5580 if (obj->IsJSGlobalProxy()) {
5581 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5582 }
5583
5584 // Find the number of objects making up this.
5585 int length = LocalPrototypeChainLength(*obj);
5586
5587 // Find the number of local properties for each of the objects.
5588 int* local_property_count = NewArray<int>(length);
5589 int total_property_count = 0;
5590 Handle<JSObject> jsproto = obj;
5591 for (int i = 0; i < length; i++) {
5592 int n;
5593 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5594 local_property_count[i] = n;
5595 total_property_count += n;
5596 if (i < length - 1) {
5597 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5598 }
5599 }
5600
5601 // Allocate an array with storage for all the property names.
5602 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5603
5604 // Get the property names.
5605 jsproto = obj;
5606 for (int i = 0; i < length; i++) {
5607 jsproto->GetLocalPropertyNames(*names,
5608 i == 0 ? 0 : local_property_count[i - 1]);
5609 if (i < length - 1) {
5610 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5611 }
5612 }
5613
5614 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005615 return *Factory::NewJSArrayWithElements(names);
5616}
5617
5618
5619// Return the names of the local indexed properties.
5620// args[0]: object
5621static Object* Runtime_DebugLocalElementNames(Arguments args) {
5622 HandleScope scope;
5623 ASSERT(args.length() == 1);
5624 if (!args[0]->IsJSObject()) {
5625 return Heap::undefined_value();
5626 }
5627 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5628
5629 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5630 Handle<FixedArray> names = Factory::NewFixedArray(n);
5631 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5632 return *Factory::NewJSArrayWithElements(names);
5633}
5634
5635
5636// Return the property type calculated from the property details.
5637// args[0]: smi with property details.
5638static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5639 ASSERT(args.length() == 1);
5640 CONVERT_CHECKED(Smi, details, args[0]);
5641 PropertyType type = PropertyDetails(details).type();
5642 return Smi::FromInt(static_cast<int>(type));
5643}
5644
5645
5646// Return the property attribute calculated from the property details.
5647// args[0]: smi with property details.
5648static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5649 ASSERT(args.length() == 1);
5650 CONVERT_CHECKED(Smi, details, args[0]);
5651 PropertyAttributes attributes = PropertyDetails(details).attributes();
5652 return Smi::FromInt(static_cast<int>(attributes));
5653}
5654
5655
5656// Return the property insertion index calculated from the property details.
5657// args[0]: smi with property details.
5658static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5659 ASSERT(args.length() == 1);
5660 CONVERT_CHECKED(Smi, details, args[0]);
5661 int index = PropertyDetails(details).index();
5662 return Smi::FromInt(index);
5663}
5664
5665
5666// Return information on whether an object has a named or indexed interceptor.
5667// args[0]: object
5668static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5669 HandleScope scope;
5670 ASSERT(args.length() == 1);
5671 if (!args[0]->IsJSObject()) {
5672 return Smi::FromInt(0);
5673 }
5674 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5675
5676 int result = 0;
5677 if (obj->HasNamedInterceptor()) result |= 2;
5678 if (obj->HasIndexedInterceptor()) result |= 1;
5679
5680 return Smi::FromInt(result);
5681}
5682
5683
5684// Return property names from named interceptor.
5685// args[0]: object
5686static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5687 HandleScope scope;
5688 ASSERT(args.length() == 1);
5689 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005690
ager@chromium.org32912102009-01-16 10:38:43 +00005691 if (obj->HasNamedInterceptor()) {
5692 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5693 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5694 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005695 return Heap::undefined_value();
5696}
5697
5698
5699// Return element names from indexed interceptor.
5700// args[0]: object
5701static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5702 HandleScope scope;
5703 ASSERT(args.length() == 1);
5704 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005705
ager@chromium.org32912102009-01-16 10:38:43 +00005706 if (obj->HasIndexedInterceptor()) {
5707 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5708 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5709 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005710 return Heap::undefined_value();
5711}
5712
5713
5714// Return property value from named interceptor.
5715// args[0]: object
5716// args[1]: property name
5717static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5718 HandleScope scope;
5719 ASSERT(args.length() == 2);
5720 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5721 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5722 CONVERT_ARG_CHECKED(String, name, 1);
5723
5724 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005725 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005726}
5727
5728
5729// Return element value from indexed interceptor.
5730// args[0]: object
5731// args[1]: index
5732static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5733 HandleScope scope;
5734 ASSERT(args.length() == 2);
5735 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5736 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5737 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5738
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005739 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005740}
5741
5742
5743static Object* Runtime_CheckExecutionState(Arguments args) {
5744 ASSERT(args.length() >= 1);
5745 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005746 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005747 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005748 return Top::Throw(Heap::illegal_execution_state_symbol());
5749 }
5750
5751 return Heap::true_value();
5752}
5753
5754
5755static Object* Runtime_GetFrameCount(Arguments args) {
5756 HandleScope scope;
5757 ASSERT(args.length() == 1);
5758
5759 // Check arguments.
5760 Object* result = Runtime_CheckExecutionState(args);
5761 if (result->IsFailure()) return result;
5762
5763 // Count all frames which are relevant to debugging stack trace.
5764 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005765 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005766 if (id == StackFrame::NO_ID) {
5767 // If there is no JavaScript stack frame count is 0.
5768 return Smi::FromInt(0);
5769 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005770 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5771 return Smi::FromInt(n);
5772}
5773
5774
5775static const int kFrameDetailsFrameIdIndex = 0;
5776static const int kFrameDetailsReceiverIndex = 1;
5777static const int kFrameDetailsFunctionIndex = 2;
5778static const int kFrameDetailsArgumentCountIndex = 3;
5779static const int kFrameDetailsLocalCountIndex = 4;
5780static const int kFrameDetailsSourcePositionIndex = 5;
5781static const int kFrameDetailsConstructCallIndex = 6;
5782static const int kFrameDetailsDebuggerFrameIndex = 7;
5783static const int kFrameDetailsFirstDynamicIndex = 8;
5784
5785// Return an array with frame details
5786// args[0]: number: break id
5787// args[1]: number: frame index
5788//
5789// The array returned contains the following information:
5790// 0: Frame id
5791// 1: Receiver
5792// 2: Function
5793// 3: Argument count
5794// 4: Local count
5795// 5: Source position
5796// 6: Constructor call
5797// 7: Debugger frame
5798// Arguments name, value
5799// Locals name, value
5800static Object* Runtime_GetFrameDetails(Arguments args) {
5801 HandleScope scope;
5802 ASSERT(args.length() == 2);
5803
5804 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005805 Object* check = Runtime_CheckExecutionState(args);
5806 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005807 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5808
5809 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005810 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005811 if (id == StackFrame::NO_ID) {
5812 // If there are no JavaScript stack frames return undefined.
5813 return Heap::undefined_value();
5814 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005815 int count = 0;
5816 JavaScriptFrameIterator it(id);
5817 for (; !it.done(); it.Advance()) {
5818 if (count == index) break;
5819 count++;
5820 }
5821 if (it.done()) return Heap::undefined_value();
5822
5823 // Traverse the saved contexts chain to find the active context for the
5824 // selected frame.
5825 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005826 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005827 save = save->prev();
5828 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005829 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005830
5831 // Get the frame id.
5832 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
5833
5834 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005835 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005836
5837 // Check for constructor frame.
5838 bool constructor = it.frame()->IsConstructor();
5839
5840 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005841 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005842 ScopeInfo<> info(*code);
5843
5844 // Get the context.
5845 Handle<Context> context(Context::cast(it.frame()->context()));
5846
5847 // Get the locals names and values into a temporary array.
5848 //
5849 // TODO(1240907): Hide compiler-introduced stack variables
5850 // (e.g. .result)? For users of the debugger, they will probably be
5851 // confusing.
5852 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
5853 for (int i = 0; i < info.NumberOfLocals(); i++) {
5854 // Name of the local.
5855 locals->set(i * 2, *info.LocalName(i));
5856
5857 // Fetch the value of the local - either from the stack or from a
5858 // heap-allocated context.
5859 if (i < info.number_of_stack_slots()) {
5860 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
5861 } else {
5862 Handle<String> name = info.LocalName(i);
5863 // Traverse the context chain to the function context as all local
5864 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005865 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005866 context = Handle<Context>(context->previous());
5867 }
5868 ASSERT(context->is_function_context());
5869 locals->set(i * 2 + 1,
5870 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
5871 NULL)));
5872 }
5873 }
5874
5875 // Now advance to the arguments adapter frame (if any). If contains all
5876 // the provided parameters and
5877
5878 // Now advance to the arguments adapter frame (if any). It contains all
5879 // the provided parameters whereas the function frame always have the number
5880 // of arguments matching the functions parameters. The rest of the
5881 // information (except for what is collected above) is the same.
5882 it.AdvanceToArgumentsFrame();
5883
5884 // Find the number of arguments to fill. At least fill the number of
5885 // parameters for the function and fill more if more parameters are provided.
5886 int argument_count = info.number_of_parameters();
5887 if (argument_count < it.frame()->GetProvidedParametersCount()) {
5888 argument_count = it.frame()->GetProvidedParametersCount();
5889 }
5890
5891 // Calculate the size of the result.
5892 int details_size = kFrameDetailsFirstDynamicIndex +
5893 2 * (argument_count + info.NumberOfLocals());
5894 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
5895
5896 // Add the frame id.
5897 details->set(kFrameDetailsFrameIdIndex, *frame_id);
5898
5899 // Add the function (same as in function frame).
5900 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
5901
5902 // Add the arguments count.
5903 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
5904
5905 // Add the locals count
5906 details->set(kFrameDetailsLocalCountIndex,
5907 Smi::FromInt(info.NumberOfLocals()));
5908
5909 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00005910 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005911 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
5912 } else {
5913 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
5914 }
5915
5916 // Add the constructor information.
5917 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
5918
5919 // Add information on whether this frame is invoked in the debugger context.
5920 details->set(kFrameDetailsDebuggerFrameIndex,
5921 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
5922
5923 // Fill the dynamic part.
5924 int details_index = kFrameDetailsFirstDynamicIndex;
5925
5926 // Add arguments name and value.
5927 for (int i = 0; i < argument_count; i++) {
5928 // Name of the argument.
5929 if (i < info.number_of_parameters()) {
5930 details->set(details_index++, *info.parameter_name(i));
5931 } else {
5932 details->set(details_index++, Heap::undefined_value());
5933 }
5934
5935 // Parameter value.
5936 if (i < it.frame()->GetProvidedParametersCount()) {
5937 details->set(details_index++, it.frame()->GetParameter(i));
5938 } else {
5939 details->set(details_index++, Heap::undefined_value());
5940 }
5941 }
5942
5943 // Add locals name and value from the temporary copy from the function frame.
5944 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
5945 details->set(details_index++, locals->get(i));
5946 }
5947
5948 // Add the receiver (same as in function frame).
5949 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
5950 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
5951 Handle<Object> receiver(it.frame()->receiver());
5952 if (!receiver->IsJSObject()) {
5953 // If the receiver is NOT a JSObject we have hit an optimization
5954 // where a value object is not converted into a wrapped JS objects.
5955 // To hide this optimization from the debugger, we wrap the receiver
5956 // by creating correct wrapper object based on the calling frame's
5957 // global context.
5958 it.Advance();
5959 Handle<Context> calling_frames_global_context(
5960 Context::cast(Context::cast(it.frame()->context())->global_context()));
5961 receiver = Factory::ToObject(receiver, calling_frames_global_context);
5962 }
5963 details->set(kFrameDetailsReceiverIndex, *receiver);
5964
5965 ASSERT_EQ(details_size, details_index);
5966 return *Factory::NewJSArrayWithElements(details);
5967}
5968
5969
5970static Object* Runtime_GetCFrames(Arguments args) {
5971 HandleScope scope;
5972 ASSERT(args.length() == 1);
5973 Object* result = Runtime_CheckExecutionState(args);
5974 if (result->IsFailure()) return result;
5975
5976 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005977 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
5978 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005979 if (frames_count == OS::kStackWalkError) {
5980 return Heap::undefined_value();
5981 }
5982
5983 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
5984 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
5985 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
5986 for (int i = 0; i < frames_count; i++) {
5987 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
5988 frame_value->SetProperty(
5989 *address_str,
5990 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
5991 NONE);
5992
5993 // Get the stack walk text for this frame.
5994 Handle<String> frame_text;
5995 if (strlen(frames[i].text) > 0) {
5996 Vector<const char> str(frames[i].text, strlen(frames[i].text));
5997 frame_text = Factory::NewStringFromAscii(str);
5998 }
5999
6000 if (!frame_text.is_null()) {
6001 frame_value->SetProperty(*text_str, *frame_text, NONE);
6002 }
6003
6004 frames_array->set(i, *frame_value);
6005 }
6006 return *Factory::NewJSArrayWithElements(frames_array);
6007}
6008
6009
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006010static Object* Runtime_GetThreadCount(Arguments args) {
6011 HandleScope scope;
6012 ASSERT(args.length() == 1);
6013
6014 // Check arguments.
6015 Object* result = Runtime_CheckExecutionState(args);
6016 if (result->IsFailure()) return result;
6017
6018 // Count all archived V8 threads.
6019 int n = 0;
6020 for (ThreadState* thread = ThreadState::FirstInUse();
6021 thread != NULL;
6022 thread = thread->Next()) {
6023 n++;
6024 }
6025
6026 // Total number of threads is current thread and archived threads.
6027 return Smi::FromInt(n + 1);
6028}
6029
6030
6031static const int kThreadDetailsCurrentThreadIndex = 0;
6032static const int kThreadDetailsThreadIdIndex = 1;
6033static const int kThreadDetailsSize = 2;
6034
6035// Return an array with thread details
6036// args[0]: number: break id
6037// args[1]: number: thread index
6038//
6039// The array returned contains the following information:
6040// 0: Is current thread?
6041// 1: Thread id
6042static Object* Runtime_GetThreadDetails(Arguments args) {
6043 HandleScope scope;
6044 ASSERT(args.length() == 2);
6045
6046 // Check arguments.
6047 Object* check = Runtime_CheckExecutionState(args);
6048 if (check->IsFailure()) return check;
6049 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6050
6051 // Allocate array for result.
6052 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6053
6054 // Thread index 0 is current thread.
6055 if (index == 0) {
6056 // Fill the details.
6057 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6058 details->set(kThreadDetailsThreadIdIndex,
6059 Smi::FromInt(ThreadManager::CurrentId()));
6060 } else {
6061 // Find the thread with the requested index.
6062 int n = 1;
6063 ThreadState* thread = ThreadState::FirstInUse();
6064 while (index != n && thread != NULL) {
6065 thread = thread->Next();
6066 n++;
6067 }
6068 if (thread == NULL) {
6069 return Heap::undefined_value();
6070 }
6071
6072 // Fill the details.
6073 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6074 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6075 }
6076
6077 // Convert to JS array and return.
6078 return *Factory::NewJSArrayWithElements(details);
6079}
6080
6081
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006082static Object* Runtime_GetBreakLocations(Arguments args) {
6083 HandleScope scope;
6084 ASSERT(args.length() == 1);
6085
6086 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6087 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6088 // Find the number of break points
6089 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6090 if (break_locations->IsUndefined()) return Heap::undefined_value();
6091 // Return array as JS array
6092 return *Factory::NewJSArrayWithElements(
6093 Handle<FixedArray>::cast(break_locations));
6094}
6095
6096
6097// Set a break point in a function
6098// args[0]: function
6099// args[1]: number: break source position (within the function source)
6100// args[2]: number: break point object
6101static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6102 HandleScope scope;
6103 ASSERT(args.length() == 3);
6104 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
6105 Handle<SharedFunctionInfo> shared(raw_fun->shared());
6106 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6107 RUNTIME_ASSERT(source_position >= 0);
6108 Handle<Object> break_point_object_arg = args.at<Object>(2);
6109
6110 // Set break point.
6111 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6112
6113 return Heap::undefined_value();
6114}
6115
6116
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006117Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6118 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006119 // Iterate the heap looking for SharedFunctionInfo generated from the
6120 // script. The inner most SharedFunctionInfo containing the source position
6121 // for the requested break point is found.
6122 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6123 // which is found is not compiled it is compiled and the heap is iterated
6124 // again as the compilation might create inner functions from the newly
6125 // compiled function and the actual requested break point might be in one of
6126 // these functions.
6127 bool done = false;
6128 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006129 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006130 Handle<SharedFunctionInfo> target;
6131 // The current candidate for the last function in script:
6132 Handle<SharedFunctionInfo> last;
6133 while (!done) {
6134 HeapIterator iterator;
6135 while (iterator.has_next()) {
6136 HeapObject* obj = iterator.next();
6137 ASSERT(obj != NULL);
6138 if (obj->IsSharedFunctionInfo()) {
6139 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6140 if (shared->script() == *script) {
6141 // If the SharedFunctionInfo found has the requested script data and
6142 // contains the source position it is a candidate.
6143 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006144 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006145 start_position = shared->start_position();
6146 }
6147 if (start_position <= position &&
6148 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006149 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006150 // candidate this is the new candidate.
6151 if (target.is_null()) {
6152 target_start_position = start_position;
6153 target = shared;
6154 } else {
6155 if (target_start_position < start_position &&
6156 shared->end_position() < target->end_position()) {
6157 target_start_position = start_position;
6158 target = shared;
6159 }
6160 }
6161 }
6162
6163 // Keep track of the last function in the script.
6164 if (last.is_null() ||
6165 shared->end_position() > last->start_position()) {
6166 last = shared;
6167 }
6168 }
6169 }
6170 }
6171
6172 // Make sure some candidate is selected.
6173 if (target.is_null()) {
6174 if (!last.is_null()) {
6175 // Position after the last function - use last.
6176 target = last;
6177 } else {
6178 // Unable to find function - possibly script without any function.
6179 return Heap::undefined_value();
6180 }
6181 }
6182
6183 // If the candidate found is compiled we are done. NOTE: when lazy
6184 // compilation of inner functions is introduced some additional checking
6185 // needs to be done here to compile inner functions.
6186 done = target->is_compiled();
6187 if (!done) {
6188 // If the candidate is not compiled compile it to reveal any inner
6189 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006190 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006191 }
6192 }
6193
6194 return *target;
6195}
6196
6197
6198// Change the state of a break point in a script. NOTE: Regarding performance
6199// see the NOTE for GetScriptFromScriptData.
6200// args[0]: script to set break point in
6201// args[1]: number: break source position (within the script source)
6202// args[2]: number: break point object
6203static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6204 HandleScope scope;
6205 ASSERT(args.length() == 3);
6206 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6207 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6208 RUNTIME_ASSERT(source_position >= 0);
6209 Handle<Object> break_point_object_arg = args.at<Object>(2);
6210
6211 // Get the script from the script wrapper.
6212 RUNTIME_ASSERT(wrapper->value()->IsScript());
6213 Handle<Script> script(Script::cast(wrapper->value()));
6214
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006215 Object* result = Runtime::FindSharedFunctionInfoInScript(
6216 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006217 if (!result->IsUndefined()) {
6218 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6219 // Find position within function. The script position might be before the
6220 // source position of the first function.
6221 int position;
6222 if (shared->start_position() > source_position) {
6223 position = 0;
6224 } else {
6225 position = source_position - shared->start_position();
6226 }
6227 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6228 }
6229 return Heap::undefined_value();
6230}
6231
6232
6233// Clear a break point
6234// args[0]: number: break point object
6235static Object* Runtime_ClearBreakPoint(Arguments args) {
6236 HandleScope scope;
6237 ASSERT(args.length() == 1);
6238 Handle<Object> break_point_object_arg = args.at<Object>(0);
6239
6240 // Clear break point.
6241 Debug::ClearBreakPoint(break_point_object_arg);
6242
6243 return Heap::undefined_value();
6244}
6245
6246
6247// Change the state of break on exceptions
6248// args[0]: boolean indicating uncaught exceptions
6249// args[1]: boolean indicating on/off
6250static Object* Runtime_ChangeBreakOnException(Arguments args) {
6251 HandleScope scope;
6252 ASSERT(args.length() == 2);
6253 ASSERT(args[0]->IsNumber());
6254 ASSERT(args[1]->IsBoolean());
6255
6256 // Update break point state
6257 ExceptionBreakType type =
6258 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6259 bool enable = args[1]->ToBoolean()->IsTrue();
6260 Debug::ChangeBreakOnException(type, enable);
6261 return Heap::undefined_value();
6262}
6263
6264
6265// Prepare for stepping
6266// args[0]: break id for checking execution state
6267// args[1]: step action from the enumeration StepAction
6268// args[2]: number of times to perform the step
6269static Object* Runtime_PrepareStep(Arguments args) {
6270 HandleScope scope;
6271 ASSERT(args.length() == 3);
6272 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006273 Object* check = Runtime_CheckExecutionState(args);
6274 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006275 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6276 return Top::Throw(Heap::illegal_argument_symbol());
6277 }
6278
6279 // Get the step action and check validity.
6280 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6281 if (step_action != StepIn &&
6282 step_action != StepNext &&
6283 step_action != StepOut &&
6284 step_action != StepInMin &&
6285 step_action != StepMin) {
6286 return Top::Throw(Heap::illegal_argument_symbol());
6287 }
6288
6289 // Get the number of steps.
6290 int step_count = NumberToInt32(args[2]);
6291 if (step_count < 1) {
6292 return Top::Throw(Heap::illegal_argument_symbol());
6293 }
6294
6295 // Prepare step.
6296 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6297 return Heap::undefined_value();
6298}
6299
6300
6301// Clear all stepping set by PrepareStep.
6302static Object* Runtime_ClearStepping(Arguments args) {
6303 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006304 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006305 Debug::ClearStepping();
6306 return Heap::undefined_value();
6307}
6308
6309
6310// Creates a copy of the with context chain. The copy of the context chain is
6311// is linked to the function context supplied.
6312static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6313 Handle<Context> function_context) {
6314 // At the bottom of the chain. Return the function context to link to.
6315 if (context_chain->is_function_context()) {
6316 return function_context;
6317 }
6318
6319 // Recursively copy the with contexts.
6320 Handle<Context> previous(context_chain->previous());
6321 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6322 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006323 CopyWithContextChain(function_context, previous),
6324 extension,
6325 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006326}
6327
6328
6329// Helper function to find or create the arguments object for
6330// Runtime_DebugEvaluate.
6331static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6332 Handle<JSFunction> function,
6333 Handle<Code> code,
6334 const ScopeInfo<>* sinfo,
6335 Handle<Context> function_context) {
6336 // Try to find the value of 'arguments' to pass as parameter. If it is not
6337 // found (that is the debugged function does not reference 'arguments' and
6338 // does not support eval) then create an 'arguments' object.
6339 int index;
6340 if (sinfo->number_of_stack_slots() > 0) {
6341 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6342 if (index != -1) {
6343 return Handle<Object>(frame->GetExpression(index));
6344 }
6345 }
6346
6347 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6348 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6349 NULL);
6350 if (index != -1) {
6351 return Handle<Object>(function_context->get(index));
6352 }
6353 }
6354
6355 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006356 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6357 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006358 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006359 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006360 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006361 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006362 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006363 return arguments;
6364}
6365
6366
6367// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006368// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006369// extension part has all the parameters and locals of the function on the
6370// stack frame. A function which calls eval with the code to evaluate is then
6371// compiled in this context and called in this context. As this context
6372// replaces the context of the function on the stack frame a new (empty)
6373// function is created as well to be used as the closure for the context.
6374// This function and the context acts as replacements for the function on the
6375// stack frame presenting the same view of the values of parameters and
6376// local variables as if the piece of JavaScript was evaluated at the point
6377// where the function on the stack frame is currently stopped.
6378static Object* Runtime_DebugEvaluate(Arguments args) {
6379 HandleScope scope;
6380
6381 // Check the execution state and decode arguments frame and source to be
6382 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006383 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006384 Object* check_result = Runtime_CheckExecutionState(args);
6385 if (check_result->IsFailure()) return check_result;
6386 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6387 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006388 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6389
6390 // Handle the processing of break.
6391 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006392
6393 // Get the frame where the debugging is performed.
6394 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6395 JavaScriptFrameIterator it(id);
6396 JavaScriptFrame* frame = it.frame();
6397 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6398 Handle<Code> code(function->code());
6399 ScopeInfo<> sinfo(*code);
6400
6401 // Traverse the saved contexts chain to find the active context for the
6402 // selected frame.
6403 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006404 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006405 save = save->prev();
6406 }
6407 ASSERT(save != NULL);
6408 SaveContext savex;
6409 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006410
6411 // Create the (empty) function replacing the function on the stack frame for
6412 // the purpose of evaluating in the context created below. It is important
6413 // that this function does not describe any parameters and local variables
6414 // in the context. If it does then this will cause problems with the lookup
6415 // in Context::Lookup, where context slots for parameters and local variables
6416 // are looked at before the extension object.
6417 Handle<JSFunction> go_between =
6418 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
6419 go_between->set_context(function->context());
6420#ifdef DEBUG
6421 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
6422 ASSERT(go_between_sinfo.number_of_parameters() == 0);
6423 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
6424#endif
6425
6426 // Allocate and initialize a context extension object with all the
6427 // arguments, stack locals heap locals and extension properties of the
6428 // debugged function.
6429 Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function());
6430 // First fill all parameters to the context extension.
6431 for (int i = 0; i < sinfo.number_of_parameters(); ++i) {
6432 SetProperty(context_ext,
6433 sinfo.parameter_name(i),
6434 Handle<Object>(frame->GetParameter(i)), NONE);
6435 }
6436 // Second fill all stack locals to the context extension.
6437 for (int i = 0; i < sinfo.number_of_stack_slots(); i++) {
6438 SetProperty(context_ext,
6439 sinfo.stack_slot_name(i),
6440 Handle<Object>(frame->GetExpression(i)), NONE);
6441 }
6442 // Third fill all context locals to the context extension.
6443 Handle<Context> frame_context(Context::cast(frame->context()));
6444 Handle<Context> function_context(frame_context->fcontext());
6445 for (int i = Context::MIN_CONTEXT_SLOTS;
6446 i < sinfo.number_of_context_slots();
6447 ++i) {
6448 int context_index =
6449 ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL);
6450 SetProperty(context_ext,
6451 sinfo.context_slot_name(i),
6452 Handle<Object>(function_context->get(context_index)), NONE);
6453 }
6454 // Finally copy any properties from the function context extension. This will
6455 // be variables introduced by eval.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006456 if (function_context->has_extension() &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006457 !function_context->IsGlobalContext()) {
6458 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6459 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6460 for (int i = 0; i < keys->length(); i++) {
6461 // Names of variables introduced by eval are strings.
6462 ASSERT(keys->get(i)->IsString());
6463 Handle<String> key(String::cast(keys->get(i)));
6464 SetProperty(context_ext, key, GetProperty(ext, key), NONE);
6465 }
6466 }
6467
6468 // Allocate a new context for the debug evaluation and set the extension
6469 // object build.
6470 Handle<Context> context =
6471 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
6472 context->set_extension(*context_ext);
6473 // Copy any with contexts present and chain them in front of this context.
6474 context = CopyWithContextChain(frame_context, context);
6475
6476 // Wrap the evaluation statement in a new function compiled in the newly
6477 // created context. The function has one parameter which has to be called
6478 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00006479 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006480 // function(arguments,__source__) {return eval(__source__);}
6481 static const char* source_str =
6482 "function(arguments,__source__){return eval(__source__);}";
6483 static const int source_str_length = strlen(source_str);
6484 Handle<String> function_source =
6485 Factory::NewStringFromAscii(Vector<const char>(source_str,
6486 source_str_length));
6487 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00006488 Compiler::CompileEval(function_source,
6489 context,
6490 0,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006491 context->IsGlobalContext(),
6492 false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006493 if (boilerplate.is_null()) return Failure::Exception();
6494 Handle<JSFunction> compiled_function =
6495 Factory::NewFunctionFromBoilerplate(boilerplate, context);
6496
6497 // Invoke the result of the compilation to get the evaluation function.
6498 bool has_pending_exception;
6499 Handle<Object> receiver(frame->receiver());
6500 Handle<Object> evaluation_function =
6501 Execution::Call(compiled_function, receiver, 0, NULL,
6502 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006503 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006504
6505 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
6506 function_context);
6507
6508 // Invoke the evaluation function and return the result.
6509 const int argc = 2;
6510 Object** argv[argc] = { arguments.location(),
6511 Handle<Object>::cast(source).location() };
6512 Handle<Object> result =
6513 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
6514 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006515 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006516 return *result;
6517}
6518
6519
6520static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
6521 HandleScope scope;
6522
6523 // Check the execution state and decode arguments frame and source to be
6524 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006525 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006526 Object* check_result = Runtime_CheckExecutionState(args);
6527 if (check_result->IsFailure()) return check_result;
6528 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006529 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
6530
6531 // Handle the processing of break.
6532 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006533
6534 // Enter the top context from before the debugger was invoked.
6535 SaveContext save;
6536 SaveContext* top = &save;
6537 while (top != NULL && *top->context() == *Debug::debug_context()) {
6538 top = top->prev();
6539 }
6540 if (top != NULL) {
6541 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006542 }
6543
6544 // Get the global context now set to the top context from before the
6545 // debugger was invoked.
6546 Handle<Context> context = Top::global_context();
6547
6548 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00006549 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00006550 Handle<JSFunction>(Compiler::CompileEval(source,
6551 context,
6552 0,
6553 true,
6554 false));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006555 if (boilerplate.is_null()) return Failure::Exception();
6556 Handle<JSFunction> compiled_function =
6557 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
6558 context));
6559
6560 // Invoke the result of the compilation to get the evaluation function.
6561 bool has_pending_exception;
6562 Handle<Object> receiver = Top::global();
6563 Handle<Object> result =
6564 Execution::Call(compiled_function, receiver, 0, NULL,
6565 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006566 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006567 return *result;
6568}
6569
6570
ager@chromium.org41826e72009-03-30 13:30:57 +00006571// If an object given is an external string, check that the underlying
6572// resource is accessible. For other kinds of objects, always return true.
6573static bool IsExternalStringValid(Object* str) {
6574 if (!str->IsString() || !StringShape(String::cast(str)).IsExternal()) {
6575 return true;
6576 }
ager@chromium.org5ec48922009-05-05 07:25:34 +00006577 if (String::cast(str)->IsAsciiRepresentation()) {
ager@chromium.org80787b72009-04-17 10:24:24 +00006578 return ExternalAsciiString::cast(str)->resource() != NULL;
ager@chromium.org5ec48922009-05-05 07:25:34 +00006579 } else if (String::cast(str)->IsTwoByteRepresentation()) {
ager@chromium.org80787b72009-04-17 10:24:24 +00006580 return ExternalTwoByteString::cast(str)->resource() != NULL;
ager@chromium.org41826e72009-03-30 13:30:57 +00006581 } else {
6582 return true;
6583 }
6584}
6585
6586
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006587// Helper function used by Runtime_DebugGetLoadedScripts below.
6588static int DebugGetLoadedScripts(FixedArray* instances, int instances_size) {
6589 NoHandleAllocation ha;
6590 AssertNoAllocation no_alloc;
6591
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006592 // Scan heap for Script objects.
6593 int count = 0;
6594 HeapIterator iterator;
6595 while (iterator.has_next()) {
6596 HeapObject* obj = iterator.next();
6597 ASSERT(obj != NULL);
ager@chromium.org41826e72009-03-30 13:30:57 +00006598 if (obj->IsScript() && IsExternalStringValid(Script::cast(obj)->source())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006599 if (instances != NULL && count < instances_size) {
6600 instances->set(count, obj);
6601 }
6602 count++;
6603 }
6604 }
6605
6606 return count;
6607}
6608
6609
6610static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
6611 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006612 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006613
6614 // Perform two GCs to get rid of all unreferenced scripts. The first GC gets
ager@chromium.org32912102009-01-16 10:38:43 +00006615 // rid of all the cached script wrappers and the second gets rid of the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006616 // scripts which is no longer referenced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006617 Heap::CollectAllGarbage();
6618 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006619
6620 // Get the number of scripts.
6621 int count;
6622 count = DebugGetLoadedScripts(NULL, 0);
6623
6624 // Allocate an array to hold the result.
6625 Handle<FixedArray> instances = Factory::NewFixedArray(count);
6626
6627 // Fill the script objects.
6628 count = DebugGetLoadedScripts(*instances, count);
6629
6630 // Convert the script objects to proper JS objects.
6631 for (int i = 0; i < count; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00006632 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
6633 // Get the script wrapper in a local handle before calling GetScriptWrapper,
6634 // because using
6635 // instances->set(i, *GetScriptWrapper(script))
6636 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
6637 // already have deferenced the instances handle.
6638 Handle<JSValue> wrapper = GetScriptWrapper(script);
6639 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006640 }
6641
6642 // Return result as a JS array.
6643 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
6644 Handle<JSArray>::cast(result)->SetContent(*instances);
6645 return *result;
6646}
6647
6648
6649// Helper function used by Runtime_DebugReferencedBy below.
6650static int DebugReferencedBy(JSObject* target,
6651 Object* instance_filter, int max_references,
6652 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006653 JSFunction* arguments_function) {
6654 NoHandleAllocation ha;
6655 AssertNoAllocation no_alloc;
6656
6657 // Iterate the heap.
6658 int count = 0;
6659 JSObject* last = NULL;
6660 HeapIterator iterator;
6661 while (iterator.has_next() &&
6662 (max_references == 0 || count < max_references)) {
6663 // Only look at all JSObjects.
6664 HeapObject* heap_obj = iterator.next();
6665 if (heap_obj->IsJSObject()) {
6666 // Skip context extension objects and argument arrays as these are
6667 // checked in the context of functions using them.
6668 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00006669 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006670 obj->map()->constructor() == arguments_function) {
6671 continue;
6672 }
6673
6674 // Check if the JS object has a reference to the object looked for.
6675 if (obj->ReferencesObject(target)) {
6676 // Check instance filter if supplied. This is normally used to avoid
6677 // references from mirror objects (see Runtime_IsInPrototypeChain).
6678 if (!instance_filter->IsUndefined()) {
6679 Object* V = obj;
6680 while (true) {
6681 Object* prototype = V->GetPrototype();
6682 if (prototype->IsNull()) {
6683 break;
6684 }
6685 if (instance_filter == prototype) {
6686 obj = NULL; // Don't add this object.
6687 break;
6688 }
6689 V = prototype;
6690 }
6691 }
6692
6693 if (obj != NULL) {
6694 // Valid reference found add to instance array if supplied an update
6695 // count.
6696 if (instances != NULL && count < instances_size) {
6697 instances->set(count, obj);
6698 }
6699 last = obj;
6700 count++;
6701 }
6702 }
6703 }
6704 }
6705
6706 // Check for circular reference only. This can happen when the object is only
6707 // referenced from mirrors and has a circular reference in which case the
6708 // object is not really alive and would have been garbage collected if not
6709 // referenced from the mirror.
6710 if (count == 1 && last == target) {
6711 count = 0;
6712 }
6713
6714 // Return the number of referencing objects found.
6715 return count;
6716}
6717
6718
6719// Scan the heap for objects with direct references to an object
6720// args[0]: the object to find references to
6721// args[1]: constructor function for instances to exclude (Mirror)
6722// args[2]: the the maximum number of objects to return
6723static Object* Runtime_DebugReferencedBy(Arguments args) {
6724 ASSERT(args.length() == 3);
6725
6726 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006727 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006728
6729 // Check parameters.
6730 CONVERT_CHECKED(JSObject, target, args[0]);
6731 Object* instance_filter = args[1];
6732 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
6733 instance_filter->IsJSObject());
6734 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
6735 RUNTIME_ASSERT(max_references >= 0);
6736
6737 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006738 JSObject* arguments_boilerplate =
6739 Top::context()->global_context()->arguments_boilerplate();
6740 JSFunction* arguments_function =
6741 JSFunction::cast(arguments_boilerplate->map()->constructor());
6742
6743 // Get the number of referencing objects.
6744 int count;
6745 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006746 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006747
6748 // Allocate an array to hold the result.
6749 Object* object = Heap::AllocateFixedArray(count);
6750 if (object->IsFailure()) return object;
6751 FixedArray* instances = FixedArray::cast(object);
6752
6753 // Fill the referencing objects.
6754 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00006755 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006756
6757 // Return result as JS array.
6758 Object* result =
6759 Heap::AllocateJSObject(
6760 Top::context()->global_context()->array_function());
6761 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6762 return result;
6763}
6764
6765
6766// Helper function used by Runtime_DebugConstructedBy below.
6767static int DebugConstructedBy(JSFunction* constructor, int max_references,
6768 FixedArray* instances, int instances_size) {
6769 AssertNoAllocation no_alloc;
6770
6771 // Iterate the heap.
6772 int count = 0;
6773 HeapIterator iterator;
6774 while (iterator.has_next() &&
6775 (max_references == 0 || count < max_references)) {
6776 // Only look at all JSObjects.
6777 HeapObject* heap_obj = iterator.next();
6778 if (heap_obj->IsJSObject()) {
6779 JSObject* obj = JSObject::cast(heap_obj);
6780 if (obj->map()->constructor() == constructor) {
6781 // Valid reference found add to instance array if supplied an update
6782 // count.
6783 if (instances != NULL && count < instances_size) {
6784 instances->set(count, obj);
6785 }
6786 count++;
6787 }
6788 }
6789 }
6790
6791 // Return the number of referencing objects found.
6792 return count;
6793}
6794
6795
6796// Scan the heap for objects constructed by a specific function.
6797// args[0]: the constructor to find instances of
6798// args[1]: the the maximum number of objects to return
6799static Object* Runtime_DebugConstructedBy(Arguments args) {
6800 ASSERT(args.length() == 2);
6801
6802 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00006803 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006804
6805 // Check parameters.
6806 CONVERT_CHECKED(JSFunction, constructor, args[0]);
6807 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
6808 RUNTIME_ASSERT(max_references >= 0);
6809
6810 // Get the number of referencing objects.
6811 int count;
6812 count = DebugConstructedBy(constructor, max_references, NULL, 0);
6813
6814 // Allocate an array to hold the result.
6815 Object* object = Heap::AllocateFixedArray(count);
6816 if (object->IsFailure()) return object;
6817 FixedArray* instances = FixedArray::cast(object);
6818
6819 // Fill the referencing objects.
6820 count = DebugConstructedBy(constructor, max_references, instances, count);
6821
6822 // Return result as JS array.
6823 Object* result =
6824 Heap::AllocateJSObject(
6825 Top::context()->global_context()->array_function());
6826 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
6827 return result;
6828}
6829
6830
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006831// Find the effective prototype object as returned by __proto__.
6832// args[0]: the object to find the prototype for.
6833static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006834 ASSERT(args.length() == 1);
6835
6836 CONVERT_CHECKED(JSObject, obj, args[0]);
6837
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006838 // Use the __proto__ accessor.
6839 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006840}
6841
6842
6843static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006844 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006845 CPU::DebugBreak();
6846 return Heap::undefined_value();
6847}
6848
6849
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006850static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
6851#ifdef DEBUG
6852 HandleScope scope;
6853 ASSERT(args.length() == 1);
6854 // Get the function and make sure it is compiled.
6855 CONVERT_ARG_CHECKED(JSFunction, func, 0);
6856 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
6857 return Failure::Exception();
6858 }
6859 func->code()->PrintLn();
6860#endif // DEBUG
6861 return Heap::undefined_value();
6862}
6863#endif // ENABLE_DEBUGGER_SUPPORT
6864
6865
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006866// Finds the script object from the script data. NOTE: This operation uses
6867// heap traversal to find the function generated for the source position
6868// for the requested break point. For lazily compiled functions several heap
6869// traversals might be required rendering this operation as a rather slow
6870// operation. However for setting break points which is normally done through
6871// some kind of user interaction the performance is not crucial.
6872static Handle<Object> Runtime_GetScriptFromScriptName(
6873 Handle<String> script_name) {
6874 // Scan the heap for Script objects to find the script with the requested
6875 // script data.
6876 Handle<Script> script;
6877 HeapIterator iterator;
6878 while (script.is_null() && iterator.has_next()) {
6879 HeapObject* obj = iterator.next();
6880 // If a script is found check if it has the script data requested.
6881 if (obj->IsScript()) {
6882 if (Script::cast(obj)->name()->IsString()) {
6883 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
6884 script = Handle<Script>(Script::cast(obj));
6885 }
6886 }
6887 }
6888 }
6889
6890 // If no script with the requested script data is found return undefined.
6891 if (script.is_null()) return Factory::undefined_value();
6892
6893 // Return the script found.
6894 return GetScriptWrapper(script);
6895}
6896
6897
6898// Get the script object from script data. NOTE: Regarding performance
6899// see the NOTE for GetScriptFromScriptData.
6900// args[0]: script data for the script to find the source for
6901static Object* Runtime_GetScript(Arguments args) {
6902 HandleScope scope;
6903
6904 ASSERT(args.length() == 1);
6905
6906 CONVERT_CHECKED(String, script_name, args[0]);
6907
6908 // Find the requested script.
6909 Handle<Object> result =
6910 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
6911 return *result;
6912}
6913
6914
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006915static Object* Runtime_Abort(Arguments args) {
6916 ASSERT(args.length() == 2);
6917 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
6918 Smi::cast(args[1])->value());
6919 Top::PrintStack();
6920 OS::Abort();
6921 UNREACHABLE();
6922 return NULL;
6923}
6924
6925
kasper.lund44510672008-07-25 07:37:58 +00006926#ifdef DEBUG
6927// ListNatives is ONLY used by the fuzz-natives.js in debug mode
6928// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006929static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00006930 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006931 HandleScope scope;
6932 Handle<JSArray> result = Factory::NewJSArray(0);
6933 int index = 0;
6934#define ADD_ENTRY(Name, argc) \
6935 { \
6936 HandleScope inner; \
6937 Handle<String> name = \
6938 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
6939 Handle<JSArray> pair = Factory::NewJSArray(0); \
6940 SetElement(pair, 0, name); \
6941 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
6942 SetElement(result, index++, pair); \
6943 }
6944 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
6945#undef ADD_ENTRY
6946 return *result;
6947}
kasper.lund44510672008-07-25 07:37:58 +00006948#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006949
6950
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006951static Object* Runtime_Log(Arguments args) {
6952 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00006953 CONVERT_CHECKED(String, format, args[0]);
6954 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006955 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006956 Logger::LogRuntime(chars, elms);
6957 return Heap::undefined_value();
6958}
6959
6960
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006961static Object* Runtime_IS_VAR(Arguments args) {
6962 UNREACHABLE(); // implemented as macro in the parser
6963 return NULL;
6964}
6965
6966
6967// ----------------------------------------------------------------------------
6968// Implementation of Runtime
6969
6970#define F(name, nargs) \
6971 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
6972 static_cast<int>(Runtime::k##name) },
6973
6974static Runtime::Function Runtime_functions[] = {
6975 RUNTIME_FUNCTION_LIST(F)
6976 { NULL, NULL, NULL, 0, -1 }
6977};
6978
6979#undef F
6980
6981
6982Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
6983 ASSERT(0 <= fid && fid < kNofFunctions);
6984 return &Runtime_functions[fid];
6985}
6986
6987
6988Runtime::Function* Runtime::FunctionForName(const char* name) {
6989 for (Function* f = Runtime_functions; f->name != NULL; f++) {
6990 if (strcmp(f->name, name) == 0) {
6991 return f;
6992 }
6993 }
6994 return NULL;
6995}
6996
6997
6998void Runtime::PerformGC(Object* result) {
6999 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007000 if (failure->IsRetryAfterGC()) {
7001 // Try to do a garbage collection; ignore it if it fails. The C
7002 // entry stub will throw an out-of-memory exception in that case.
7003 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7004 } else {
7005 // Handle last resort GC and make sure to allow future allocations
7006 // to grow the heap without causing GCs (if possible).
7007 Counters::gc_last_resort_from_js.Increment();
7008 Heap::CollectAllGarbage();
7009 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007010}
7011
7012
7013} } // namespace v8::internal