blob: ea2690e7773d85f531da2c04c87e3da28ebf3fd2 [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
kasperl@chromium.org71affb52009-05-26 05:44:31 +000049namespace v8 {
50namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000051
52
ager@chromium.org3e875802009-06-29 08:26:34 +000053#define RUNTIME_ASSERT(value) \
54 if (!(value)) return Top::ThrowIllegalOperation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000055
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
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000099static Object* DeepCopyBoilerplate(JSObject* boilerplate) {
100 StackLimitCheck check;
101 if (check.HasOverflowed()) return Top::StackOverflow();
102
103 Object* result = Heap::CopyJSObject(boilerplate);
104 if (result->IsFailure()) return result;
105 JSObject* copy = JSObject::cast(result);
106
107 // Deep copy local properties.
108 if (copy->HasFastProperties()) {
109 FixedArray* properties = copy->properties();
110 WriteBarrierMode mode = properties->GetWriteBarrierMode();
111 for (int i = 0; i < properties->length(); i++) {
112 Object* value = properties->get(i);
113 if (value->IsJSObject()) {
114 JSObject* jsObject = JSObject::cast(value);
115 result = DeepCopyBoilerplate(jsObject);
116 if (result->IsFailure()) return result;
117 properties->set(i, result, mode);
118 }
119 }
120 mode = copy->GetWriteBarrierMode();
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000121 int nof = copy->map()->inobject_properties();
122 for (int i = 0; i < nof; i++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000123 Object* value = copy->InObjectPropertyAt(i);
124 if (value->IsJSObject()) {
125 JSObject* jsObject = JSObject::cast(value);
126 result = DeepCopyBoilerplate(jsObject);
127 if (result->IsFailure()) return result;
128 copy->InObjectPropertyAtPut(i, result, mode);
129 }
130 }
131 } else {
132 result = Heap::AllocateFixedArray(copy->NumberOfLocalProperties(NONE));
133 if (result->IsFailure()) return result;
134 FixedArray* names = FixedArray::cast(result);
135 copy->GetLocalPropertyNames(names, 0);
136 for (int i = 0; i < names->length(); i++) {
137 ASSERT(names->get(i)->IsString());
138 String* keyString = String::cast(names->get(i));
139 PropertyAttributes attributes =
140 copy->GetLocalPropertyAttribute(keyString);
141 // Only deep copy fields from the object literal expression.
142 // In particular, don't try to copy the length attribute of
143 // an array.
144 if (attributes != NONE) continue;
145 Object* value = copy->GetProperty(keyString, &attributes);
146 ASSERT(!value->IsFailure());
147 if (value->IsJSObject()) {
148 JSObject* jsObject = JSObject::cast(value);
149 result = DeepCopyBoilerplate(jsObject);
150 if (result->IsFailure()) return result;
151 result = copy->SetProperty(keyString, result, NONE);
152 if (result->IsFailure()) return result;
153 }
154 }
155 }
156
157 // Deep copy local elements.
158 if (copy->HasFastElements()) {
159 FixedArray* elements = copy->elements();
160 WriteBarrierMode mode = elements->GetWriteBarrierMode();
161 for (int i = 0; i < elements->length(); i++) {
162 Object* value = elements->get(i);
163 if (value->IsJSObject()) {
164 JSObject* jsObject = JSObject::cast(value);
165 result = DeepCopyBoilerplate(jsObject);
166 if (result->IsFailure()) return result;
167 elements->set(i, result, mode);
168 }
169 }
170 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +0000171 NumberDictionary* element_dictionary = copy->element_dictionary();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000172 int capacity = element_dictionary->Capacity();
173 for (int i = 0; i < capacity; i++) {
174 Object* k = element_dictionary->KeyAt(i);
175 if (element_dictionary->IsKey(k)) {
176 Object* value = element_dictionary->ValueAt(i);
177 if (value->IsJSObject()) {
178 JSObject* jsObject = JSObject::cast(value);
179 result = DeepCopyBoilerplate(jsObject);
180 if (result->IsFailure()) return result;
181 element_dictionary->ValueAtPut(i, result);
182 }
183 }
184 }
185 }
186 return copy;
187}
188
189
190static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
191 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
192 return DeepCopyBoilerplate(boilerplate);
193}
194
195
196static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000197 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000198 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000199}
200
201
ager@chromium.org236ad962008-09-25 09:45:57 +0000202static Handle<Map> ComputeObjectLiteralMap(
203 Handle<Context> context,
204 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000205 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000206 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000207 if (FLAG_canonicalize_object_literal_maps) {
208 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000209 int number_of_symbol_keys = 0;
210 while ((number_of_symbol_keys < number_of_properties) &&
211 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
212 number_of_symbol_keys++;
213 }
214 // Based on the number of prefix symbols key we decide whether
215 // to use the map cache in the global context.
216 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000217 if ((number_of_symbol_keys == number_of_properties) &&
218 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000219 // Create the fixed array with the key.
220 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
221 for (int i = 0; i < number_of_symbol_keys; i++) {
222 keys->set(i, constant_properties->get(i*2));
223 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000224 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000225 return Factory::ObjectLiteralMapFromCache(context, keys);
226 }
227 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000228 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000229 return Factory::CopyMap(
230 Handle<Map>(context->object_function()->initial_map()),
231 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000232}
233
234
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000235static Handle<Object> CreateLiteralBoilerplate(
236 Handle<FixedArray> literals,
237 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000238
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000239
240static Handle<Object> CreateObjectLiteralBoilerplate(
241 Handle<FixedArray> literals,
242 Handle<FixedArray> constant_properties) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000243 // Get the global context from the literals array. This is the
244 // context in which the function was created and we use the object
245 // function from this context to create the object literal. We do
246 // not use the object function from the current global context
247 // because this might be the object function from another context
248 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000249 Handle<Context> context =
250 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
251
252 bool is_result_from_cache;
253 Handle<Map> map = ComputeObjectLiteralMap(context,
254 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000255 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000256
ager@chromium.org236ad962008-09-25 09:45:57 +0000257 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
ager@chromium.org32912102009-01-16 10:38:43 +0000258 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000259 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000260 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
261 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000262 for (int index = 0; index < length; index +=2) {
263 Handle<Object> key(constant_properties->get(index+0));
264 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000265 if (value->IsFixedArray()) {
266 // The value contains the constant_properties of a
267 // simple object literal.
268 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
269 value = CreateLiteralBoilerplate(literals, array);
270 if (value.is_null()) return value;
271 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000272 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 uint32_t element_index = 0;
274 if (key->IsSymbol()) {
275 // If key is a symbol it is not an array element.
276 Handle<String> name(String::cast(*key));
277 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000278 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000279 } else if (Array::IndexFromObject(*key, &element_index)) {
280 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000281 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000282 } else {
283 // Non-uint32 number.
284 ASSERT(key->IsNumber());
285 double num = key->Number();
286 char arr[100];
287 Vector<char> buffer(arr, ARRAY_SIZE(arr));
288 const char* str = DoubleToCString(num, buffer);
289 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000290 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000291 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000292 // If setting the property on the boilerplate throws an
293 // exception, the exception is converted to an empty handle in
294 // the handle based operations. In that case, we need to
295 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000296 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000297 }
298 }
299
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000300 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000301}
302
303
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000304static Handle<Object> CreateArrayLiteralBoilerplate(
305 Handle<FixedArray> literals,
306 Handle<FixedArray> elements) {
307 // Create the JSArray.
308 Handle<JSFunction> constructor(
309 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
310 Handle<Object> object = Factory::NewJSObject(constructor);
311
312 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
313
314 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
315 for (int i = 0; i < content->length(); i++) {
316 if (content->get(i)->IsFixedArray()) {
317 // The value contains the constant_properties of a
318 // simple object literal.
319 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
320 Handle<Object> result =
321 CreateLiteralBoilerplate(literals, fa);
322 if (result.is_null()) return result;
323 content->set(i, *result);
324 }
325 }
326
327 // Set the elements.
328 Handle<JSArray>::cast(object)->SetContent(*content);
329 return object;
330}
331
332
333static Handle<Object> CreateLiteralBoilerplate(
334 Handle<FixedArray> literals,
335 Handle<FixedArray> array) {
336 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
337 switch (CompileTimeValue::GetType(array)) {
338 case CompileTimeValue::OBJECT_LITERAL:
339 return CreateObjectLiteralBoilerplate(literals, elements);
340 case CompileTimeValue::ARRAY_LITERAL:
341 return CreateArrayLiteralBoilerplate(literals, elements);
342 default:
343 UNREACHABLE();
344 return Handle<Object>::null();
345 }
346}
347
348
349static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
350 HandleScope scope;
351 ASSERT(args.length() == 3);
352 // Copy the arguments.
353 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
354 CONVERT_SMI_CHECKED(literals_index, args[1]);
355 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
356
357 Handle<Object> result =
358 CreateObjectLiteralBoilerplate(literals, constant_properties);
359
360 if (result.is_null()) return Failure::Exception();
361
362 // Update the functions literal and return the boilerplate.
363 literals->set(literals_index, *result);
364
365 return *result;
366}
367
368
369static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000370 // Takes a FixedArray of elements containing the literal elements of
371 // the array literal and produces JSArray with those elements.
372 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000373 // which contains the context from which to get the Array function
374 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000375 HandleScope scope;
376 ASSERT(args.length() == 3);
377 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
378 CONVERT_SMI_CHECKED(literals_index, args[1]);
379 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000381 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
382 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000383
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000384 // Update the functions literal and return the boilerplate.
385 literals->set(literals_index, *object);
386 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000387}
388
389
ager@chromium.org32912102009-01-16 10:38:43 +0000390static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
391 ASSERT(args.length() == 2);
392 CONVERT_CHECKED(String, key, args[0]);
393 Object* value = args[1];
394 // Create a catch context extension object.
395 JSFunction* constructor =
396 Top::context()->global_context()->context_extension_function();
397 Object* object = Heap::AllocateJSObject(constructor);
398 if (object->IsFailure()) return object;
399 // Assign the exception value to the catch variable and make sure
400 // that the catch variable is DontDelete.
401 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
402 if (value->IsFailure()) return value;
403 return object;
404}
405
406
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000407static Object* Runtime_ClassOf(Arguments args) {
408 NoHandleAllocation ha;
409 ASSERT(args.length() == 1);
410 Object* obj = args[0];
411 if (!obj->IsJSObject()) return Heap::null_value();
412 return JSObject::cast(obj)->class_name();
413}
414
ager@chromium.org7c537e22008-10-16 08:43:32 +0000415
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000416static Object* Runtime_IsInPrototypeChain(Arguments args) {
417 NoHandleAllocation ha;
418 ASSERT(args.length() == 2);
419 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
420 Object* O = args[0];
421 Object* V = args[1];
422 while (true) {
423 Object* prototype = V->GetPrototype();
424 if (prototype->IsNull()) return Heap::false_value();
425 if (O == prototype) return Heap::true_value();
426 V = prototype;
427 }
428}
429
430
ager@chromium.org9085a012009-05-11 19:22:57 +0000431// Inserts an object as the hidden prototype of another object.
432static Object* Runtime_SetHiddenPrototype(Arguments args) {
433 NoHandleAllocation ha;
434 ASSERT(args.length() == 2);
435 CONVERT_CHECKED(JSObject, jsobject, args[0]);
436 CONVERT_CHECKED(JSObject, proto, args[1]);
437
438 // Sanity checks. The old prototype (that we are replacing) could
439 // theoretically be null, but if it is not null then check that we
440 // didn't already install a hidden prototype here.
441 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
442 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
443 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
444
445 // Allocate up front before we start altering state in case we get a GC.
446 Object* map_or_failure = proto->map()->CopyDropTransitions();
447 if (map_or_failure->IsFailure()) return map_or_failure;
448 Map* new_proto_map = Map::cast(map_or_failure);
449
450 map_or_failure = jsobject->map()->CopyDropTransitions();
451 if (map_or_failure->IsFailure()) return map_or_failure;
452 Map* new_map = Map::cast(map_or_failure);
453
454 // Set proto's prototype to be the old prototype of the object.
455 new_proto_map->set_prototype(jsobject->GetPrototype());
456 proto->set_map(new_proto_map);
457 new_proto_map->set_is_hidden_prototype();
458
459 // Set the object's prototype to proto.
460 new_map->set_prototype(proto);
461 jsobject->set_map(new_map);
462
463 return Heap::undefined_value();
464}
465
466
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000467static Object* Runtime_IsConstructCall(Arguments args) {
468 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000469 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000470 JavaScriptFrameIterator it;
471 return Heap::ToBoolean(it.frame()->IsConstructor());
472}
473
474
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000475static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000476 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000477 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000478 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
479 CONVERT_ARG_CHECKED(String, pattern, 1);
480 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000481 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
482 if (result.is_null()) return Failure::Exception();
483 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000484}
485
486
487static Object* Runtime_CreateApiFunction(Arguments args) {
488 HandleScope scope;
489 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000490 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000491 return *Factory::CreateApiFunction(data);
492}
493
494
495static Object* Runtime_IsTemplate(Arguments args) {
496 ASSERT(args.length() == 1);
497 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000498 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000499 return Heap::ToBoolean(result);
500}
501
502
503static Object* Runtime_GetTemplateField(Arguments args) {
504 ASSERT(args.length() == 2);
505 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000506 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000507 int index = field->value();
508 int offset = index * kPointerSize + HeapObject::kHeaderSize;
509 InstanceType type = templ->map()->instance_type();
510 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
511 type == OBJECT_TEMPLATE_INFO_TYPE);
512 RUNTIME_ASSERT(offset > 0);
513 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
514 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
515 } else {
516 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
517 }
518 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000519}
520
521
ager@chromium.org870a0b62008-11-04 11:43:05 +0000522static Object* Runtime_DisableAccessChecks(Arguments args) {
523 ASSERT(args.length() == 1);
524 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000525 Map* old_map = object->map();
526 bool needs_access_checks = old_map->is_access_check_needed();
527 if (needs_access_checks) {
528 // Copy map so it won't interfere constructor's initial map.
529 Object* new_map = old_map->CopyDropTransitions();
530 if (new_map->IsFailure()) return new_map;
531
532 Map::cast(new_map)->set_is_access_check_needed(false);
533 object->set_map(Map::cast(new_map));
534 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000535 return needs_access_checks ? Heap::true_value() : Heap::false_value();
536}
537
538
539static Object* Runtime_EnableAccessChecks(Arguments args) {
540 ASSERT(args.length() == 1);
541 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000542 Map* old_map = object->map();
543 if (!old_map->is_access_check_needed()) {
544 // Copy map so it won't interfere constructor's initial map.
545 Object* new_map = old_map->CopyDropTransitions();
546 if (new_map->IsFailure()) return new_map;
547
548 Map::cast(new_map)->set_is_access_check_needed(true);
549 object->set_map(Map::cast(new_map));
550 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000551 return Heap::undefined_value();
552}
553
554
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000555static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
556 HandleScope scope;
557 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
558 Handle<Object> args[2] = { type_handle, name };
559 Handle<Object> error =
560 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
561 return Top::Throw(*error);
562}
563
564
565static Object* Runtime_DeclareGlobals(Arguments args) {
566 HandleScope scope;
567 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
568
569 CONVERT_ARG_CHECKED(FixedArray, pairs, 0);
570 Handle<Context> context = args.at<Context>(1);
571 bool is_eval = Smi::cast(args[2])->value() == 1;
572
573 // Compute the property attributes. According to ECMA-262, section
574 // 13, page 71, the property must be read-only and
575 // non-deletable. However, neither SpiderMonkey nor KJS creates the
576 // property as read-only, so we don't either.
577 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
578
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000579 // Traverse the name/value pairs and set the properties.
580 int length = pairs->length();
581 for (int i = 0; i < length; i += 2) {
582 HandleScope scope;
583 Handle<String> name(String::cast(pairs->get(i)));
584 Handle<Object> value(pairs->get(i + 1));
585
586 // We have to declare a global const property. To capture we only
587 // assign to it when evaluating the assignment for "const x =
588 // <expr>" the initial value is the hole.
589 bool is_const_property = value->IsTheHole();
590
591 if (value->IsUndefined() || is_const_property) {
592 // Lookup the property in the global object, and don't set the
593 // value of the variable if the property is already there.
594 LookupResult lookup;
595 global->Lookup(*name, &lookup);
596 if (lookup.IsProperty()) {
597 // Determine if the property is local by comparing the holder
598 // against the global object. The information will be used to
599 // avoid throwing re-declaration errors when declaring
600 // variables or constants that exist in the prototype chain.
601 bool is_local = (*global == lookup.holder());
602 // Get the property attributes and determine if the property is
603 // read-only.
604 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
605 bool is_read_only = (attributes & READ_ONLY) != 0;
606 if (lookup.type() == INTERCEPTOR) {
607 // If the interceptor says the property is there, we
608 // just return undefined without overwriting the property.
609 // Otherwise, we continue to setting the property.
610 if (attributes != ABSENT) {
611 // Check if the existing property conflicts with regards to const.
612 if (is_local && (is_read_only || is_const_property)) {
613 const char* type = (is_read_only) ? "const" : "var";
614 return ThrowRedeclarationError(type, name);
615 };
616 // The property already exists without conflicting: Go to
617 // the next declaration.
618 continue;
619 }
620 // Fall-through and introduce the absent property by using
621 // SetProperty.
622 } else {
623 if (is_local && (is_read_only || is_const_property)) {
624 const char* type = (is_read_only) ? "const" : "var";
625 return ThrowRedeclarationError(type, name);
626 }
627 // The property already exists without conflicting: Go to
628 // the next declaration.
629 continue;
630 }
631 }
632 } else {
633 // Copy the function and update its context. Use it as value.
634 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
635 Handle<JSFunction> function =
636 Factory::NewFunctionFromBoilerplate(boilerplate, context);
637 value = function;
638 }
639
640 LookupResult lookup;
641 global->LocalLookup(*name, &lookup);
642
643 PropertyAttributes attributes = is_const_property
644 ? static_cast<PropertyAttributes>(base | READ_ONLY)
645 : base;
646
647 if (lookup.IsProperty()) {
648 // There's a local property that we need to overwrite because
649 // we're either declaring a function or there's an interceptor
650 // that claims the property is absent.
651
652 // Check for conflicting re-declarations. We cannot have
653 // conflicting types in case of intercepted properties because
654 // they are absent.
655 if (lookup.type() != INTERCEPTOR &&
656 (lookup.IsReadOnly() || is_const_property)) {
657 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
658 return ThrowRedeclarationError(type, name);
659 }
660 SetProperty(global, name, value, attributes);
661 } else {
662 // If a property with this name does not already exist on the
663 // global object add the property locally. We take special
664 // precautions to always add it as a local property even in case
665 // of callbacks in the prototype chain (this rules out using
666 // SetProperty). Also, we must use the handle-based version to
667 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000668 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000669 }
670 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000671
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000672 return Heap::undefined_value();
673}
674
675
676static Object* Runtime_DeclareContextSlot(Arguments args) {
677 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000678 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000679
ager@chromium.org7c537e22008-10-16 08:43:32 +0000680 CONVERT_ARG_CHECKED(Context, context, 0);
681 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000682 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000683 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000684 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000685 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000686
687 // Declarations are always done in the function context.
688 context = Handle<Context>(context->fcontext());
689
690 int index;
691 PropertyAttributes attributes;
692 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000693 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000694 context->Lookup(name, flags, &index, &attributes);
695
696 if (attributes != ABSENT) {
697 // The name was declared before; check for conflicting
698 // re-declarations: This is similar to the code in parser.cc in
699 // the AstBuildingParser::Declare function.
700 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
701 // Functions are not read-only.
702 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
703 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
704 return ThrowRedeclarationError(type, name);
705 }
706
707 // Initialize it if necessary.
708 if (*initial_value != NULL) {
709 if (index >= 0) {
710 // The variable or constant context slot should always be in
711 // the function context; not in any outer context nor in the
712 // arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000713 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000714 if (((attributes & READ_ONLY) == 0) ||
715 context->get(index)->IsTheHole()) {
716 context->set(index, *initial_value);
717 }
718 } else {
719 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000720 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000721 SetProperty(context_ext, name, initial_value, mode);
722 }
723 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000724
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000725 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000726 // The property is not in the function context. It needs to be
727 // "declared" in the function context's extension context, or in the
728 // global context.
729 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000730 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000731 // The function context's extension context exists - use it.
732 context_ext = Handle<JSObject>(context->extension());
733 } else {
734 // The function context's extension context does not exists - allocate
735 // it.
736 context_ext = Factory::NewJSObject(Top::context_extension_function());
737 // And store it in the extension slot.
738 context->set_extension(*context_ext);
739 }
740 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000741
ager@chromium.org7c537e22008-10-16 08:43:32 +0000742 // Declare the property by setting it to the initial value if provided,
743 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
744 // constant declarations).
745 ASSERT(!context_ext->HasLocalProperty(*name));
746 Handle<Object> value(Heap::undefined_value());
747 if (*initial_value != NULL) value = initial_value;
748 SetProperty(context_ext, name, value, mode);
749 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
750 }
751
752 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000753}
754
755
756static Object* Runtime_InitializeVarGlobal(Arguments args) {
757 NoHandleAllocation nha;
758
759 // Determine if we need to assign to the variable if it already
760 // exists (based on the number of arguments).
761 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
762 bool assign = args.length() == 2;
763
764 CONVERT_ARG_CHECKED(String, name, 0);
765 GlobalObject* global = Top::context()->global();
766
767 // According to ECMA-262, section 12.2, page 62, the property must
768 // not be deletable.
769 PropertyAttributes attributes = DONT_DELETE;
770
771 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000772 // there, there is a property with this name in the prototype chain.
773 // We follow Safari and Firefox behavior and only set the property
774 // locally if there is an explicit initialization value that we have
775 // to assign to the property. When adding the property we take
776 // special precautions to always add it as a local property even in
777 // case of callbacks in the prototype chain (this rules out using
778 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
779 // this.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000780 LookupResult lookup;
781 global->LocalLookup(*name, &lookup);
782 if (!lookup.IsProperty()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000783 if (assign) {
784 return global->IgnoreAttributesAndSetLocalProperty(*name,
785 args[1],
786 attributes);
787 }
788 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000789 }
790
791 // Determine if this is a redeclaration of something read-only.
792 if (lookup.IsReadOnly()) {
793 return ThrowRedeclarationError("const", name);
794 }
795
796 // Determine if this is a redeclaration of an intercepted read-only
797 // property and figure out if the property exists at all.
798 bool found = true;
799 PropertyType type = lookup.type();
800 if (type == INTERCEPTOR) {
801 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
802 if (intercepted == ABSENT) {
803 // The interceptor claims the property isn't there. We need to
804 // make sure to introduce it.
805 found = false;
806 } else if ((intercepted & READ_ONLY) != 0) {
807 // The property is present, but read-only. Since we're trying to
808 // overwrite it with a variable declaration we must throw a
809 // re-declaration error.
810 return ThrowRedeclarationError("const", name);
811 }
812 // Restore global object from context (in case of GC).
813 global = Top::context()->global();
814 }
815
816 if (found && !assign) {
817 // The global property is there and we're not assigning any value
818 // to it. Just return.
819 return Heap::undefined_value();
820 }
821
822 // Assign the value (or undefined) to the property.
823 Object* value = (assign) ? args[1] : Heap::undefined_value();
824 return global->SetProperty(&lookup, *name, value, attributes);
825}
826
827
828static Object* Runtime_InitializeConstGlobal(Arguments args) {
829 // All constants are declared with an initial value. The name
830 // of the constant is the first argument and the initial value
831 // is the second.
832 RUNTIME_ASSERT(args.length() == 2);
833 CONVERT_ARG_CHECKED(String, name, 0);
834 Handle<Object> value = args.at<Object>(1);
835
836 // Get the current global object from top.
837 GlobalObject* global = Top::context()->global();
838
839 // According to ECMA-262, section 12.2, page 62, the property must
840 // not be deletable. Since it's a const, it must be READ_ONLY too.
841 PropertyAttributes attributes =
842 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
843
844 // Lookup the property locally in the global object. If it isn't
845 // there, we add the property and take special precautions to always
846 // add it as a local property even in case of callbacks in the
847 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000848 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000849 LookupResult lookup;
850 global->LocalLookup(*name, &lookup);
851 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000852 return global->IgnoreAttributesAndSetLocalProperty(*name,
853 *value,
854 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000855 }
856
857 // Determine if this is a redeclaration of something not
858 // read-only. In case the result is hidden behind an interceptor we
859 // need to ask it for the property attributes.
860 if (!lookup.IsReadOnly()) {
861 if (lookup.type() != INTERCEPTOR) {
862 return ThrowRedeclarationError("var", name);
863 }
864
865 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
866
867 // Throw re-declaration error if the intercepted property is present
868 // but not read-only.
869 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
870 return ThrowRedeclarationError("var", name);
871 }
872
873 // Restore global object from context (in case of GC) and continue
874 // with setting the value because the property is either absent or
875 // read-only. We also have to do redo the lookup.
876 global = Top::context()->global();
877
878 // BUG 1213579: Handle the case where we have to set a read-only
879 // property through an interceptor and only do it if it's
880 // uninitialized, e.g. the hole. Nirk...
881 global->SetProperty(*name, *value, attributes);
882 return *value;
883 }
884
885 // Set the value, but only we're assigning the initial value to a
886 // constant. For now, we determine this by checking if the
887 // current value is the hole.
888 PropertyType type = lookup.type();
889 if (type == FIELD) {
890 FixedArray* properties = global->properties();
891 int index = lookup.GetFieldIndex();
892 if (properties->get(index)->IsTheHole()) {
893 properties->set(index, *value);
894 }
895 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000896 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
897 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000898 }
899 } else {
900 // Ignore re-initialization of constants that have already been
901 // assigned a function value.
902 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
903 }
904
905 // Use the set value as the result of the operation.
906 return *value;
907}
908
909
910static Object* Runtime_InitializeConstContextSlot(Arguments args) {
911 HandleScope scope;
912 ASSERT(args.length() == 3);
913
914 Handle<Object> value(args[0]);
915 ASSERT(!value->IsTheHole());
916 CONVERT_ARG_CHECKED(Context, context, 1);
917 Handle<String> name(String::cast(args[2]));
918
919 // Initializations are always done in the function context.
920 context = Handle<Context>(context->fcontext());
921
922 int index;
923 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000924 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000925 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000926 context->Lookup(name, flags, &index, &attributes);
927
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000928 // In most situations, the property introduced by the const
929 // declaration should be present in the context extension object.
930 // However, because declaration and initialization are separate, the
931 // property might have been deleted (if it was introduced by eval)
932 // before we reach the initialization point.
933 //
934 // Example:
935 //
936 // function f() { eval("delete x; const x;"); }
937 //
938 // In that case, the initialization behaves like a normal assignment
939 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000940 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000941 // Property was found in a context.
942 if (holder->IsContext()) {
943 // The holder cannot be the function context. If it is, there
944 // should have been a const redeclaration error when declaring
945 // the const property.
946 ASSERT(!holder.is_identical_to(context));
947 if ((attributes & READ_ONLY) == 0) {
948 Handle<Context>::cast(holder)->set(index, *value);
949 }
950 } else {
951 // The holder is an arguments object.
952 ASSERT((attributes & READ_ONLY) == 0);
953 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000954 }
955 return *value;
956 }
957
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000958 // The property could not be found, we introduce it in the global
959 // context.
960 if (attributes == ABSENT) {
961 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
962 SetProperty(global, name, value, NONE);
963 return *value;
964 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000965
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000966 // The property was present in a context extension object.
967 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000968
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000969 if (*context_ext == context->extension()) {
970 // This is the property that was introduced by the const
971 // declaration. Set it if it hasn't been set before. NOTE: We
972 // cannot use GetProperty() to get the current value as it
973 // 'unholes' the value.
974 LookupResult lookup;
975 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
976 ASSERT(lookup.IsProperty()); // the property was declared
977 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
978
979 PropertyType type = lookup.type();
980 if (type == FIELD) {
981 FixedArray* properties = context_ext->properties();
982 int index = lookup.GetFieldIndex();
983 if (properties->get(index)->IsTheHole()) {
984 properties->set(index, *value);
985 }
986 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000987 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
988 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000989 }
990 } else {
991 // We should not reach here. Any real, named property should be
992 // either a field or a dictionary slot.
993 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000994 }
995 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000996 // The property was found in a different context extension object.
997 // Set it if it is not a read-only property.
998 if ((attributes & READ_ONLY) == 0) {
999 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1000 // Setting a property might throw an exception. Exceptions
1001 // are converted to empty handles in handle operations. We
1002 // need to convert back to exceptions here.
1003 if (set.is_null()) {
1004 ASSERT(Top::has_pending_exception());
1005 return Failure::Exception();
1006 }
1007 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001008 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001009
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001010 return *value;
1011}
1012
1013
1014static Object* Runtime_RegExpExec(Arguments args) {
1015 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001016 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001017 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1018 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001019 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001020 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001021 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001022 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001023 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001024 RUNTIME_ASSERT(index >= 0);
1025 RUNTIME_ASSERT(index <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001026 Handle<Object> result = RegExpImpl::Exec(regexp,
1027 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001028 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001029 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001030 if (result.is_null()) return Failure::Exception();
1031 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001032}
1033
1034
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001035static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1036 HandleScope scope;
1037 ASSERT(args.length() == 4);
1038 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1039 int index = Smi::cast(args[1])->value();
1040 Handle<String> pattern = args.at<String>(2);
1041 Handle<String> flags = args.at<String>(3);
1042
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001043 // Get the RegExp function from the context in the literals array.
1044 // This is the RegExp function from the context in which the
1045 // function was created. We do not use the RegExp function from the
1046 // current global context because this might be the RegExp function
1047 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001048 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001049 Handle<JSFunction>(
1050 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001051 // Compute the regular expression literal.
1052 bool has_pending_exception;
1053 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001054 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1055 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001056 if (has_pending_exception) {
1057 ASSERT(Top::has_pending_exception());
1058 return Failure::Exception();
1059 }
1060 literals->set(index, *regexp);
1061 return *regexp;
1062}
1063
1064
1065static Object* Runtime_FunctionGetName(Arguments args) {
1066 NoHandleAllocation ha;
1067 ASSERT(args.length() == 1);
1068
1069 CONVERT_CHECKED(JSFunction, f, args[0]);
1070 return f->shared()->name();
1071}
1072
1073
ager@chromium.org236ad962008-09-25 09:45:57 +00001074static Object* Runtime_FunctionSetName(Arguments args) {
1075 NoHandleAllocation ha;
1076 ASSERT(args.length() == 2);
1077
1078 CONVERT_CHECKED(JSFunction, f, args[0]);
1079 CONVERT_CHECKED(String, name, args[1]);
1080 f->shared()->set_name(name);
1081 return Heap::undefined_value();
1082}
1083
1084
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001085static Object* Runtime_FunctionGetScript(Arguments args) {
1086 HandleScope scope;
1087 ASSERT(args.length() == 1);
1088
1089 CONVERT_CHECKED(JSFunction, fun, args[0]);
1090 Handle<Object> script = Handle<Object>(fun->shared()->script());
1091 if (!script->IsScript()) return Heap::undefined_value();
1092
1093 return *GetScriptWrapper(Handle<Script>::cast(script));
1094}
1095
1096
1097static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1098 NoHandleAllocation ha;
1099 ASSERT(args.length() == 1);
1100
1101 CONVERT_CHECKED(JSFunction, f, args[0]);
1102 return f->shared()->GetSourceCode();
1103}
1104
1105
1106static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1107 NoHandleAllocation ha;
1108 ASSERT(args.length() == 1);
1109
1110 CONVERT_CHECKED(JSFunction, fun, args[0]);
1111 int pos = fun->shared()->start_position();
1112 return Smi::FromInt(pos);
1113}
1114
1115
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001116static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1117 ASSERT(args.length() == 2);
1118
1119 CONVERT_CHECKED(JSFunction, fun, args[0]);
1120 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1121
1122 Code* code = fun->code();
1123 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1124
1125 Address pc = code->address() + offset;
1126 return Smi::FromInt(fun->code()->SourcePosition(pc));
1127}
1128
1129
1130
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001131static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1132 NoHandleAllocation ha;
1133 ASSERT(args.length() == 2);
1134
1135 CONVERT_CHECKED(JSFunction, fun, args[0]);
1136 CONVERT_CHECKED(String, name, args[1]);
1137 fun->SetInstanceClassName(name);
1138 return Heap::undefined_value();
1139}
1140
1141
1142static Object* Runtime_FunctionSetLength(Arguments args) {
1143 NoHandleAllocation ha;
1144 ASSERT(args.length() == 2);
1145
1146 CONVERT_CHECKED(JSFunction, fun, args[0]);
1147 CONVERT_CHECKED(Smi, length, args[1]);
1148 fun->shared()->set_length(length->value());
1149 return length;
1150}
1151
1152
1153static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001154 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001155 ASSERT(args.length() == 2);
1156
1157 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001158 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1159 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001160 return args[0]; // return TOS
1161}
1162
1163
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001164static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1165 NoHandleAllocation ha;
1166 ASSERT(args.length() == 1);
1167
1168 CONVERT_CHECKED(JSFunction, f, args[0]);
1169 // The function_data field of the shared function info is used exclusively by
1170 // the API.
1171 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1172 : Heap::false_value();
1173}
1174
1175
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001176static Object* Runtime_SetCode(Arguments args) {
1177 HandleScope scope;
1178 ASSERT(args.length() == 2);
1179
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001180 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001181 Handle<Object> code = args.at<Object>(1);
1182
1183 Handle<Context> context(target->context());
1184
1185 if (!code->IsNull()) {
1186 RUNTIME_ASSERT(code->IsJSFunction());
1187 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1188 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1189 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1190 return Failure::Exception();
1191 }
1192 // Set the code, formal parameter count, and the length of the target
1193 // function.
1194 target->set_code(fun->code());
1195 target->shared()->set_length(fun->shared()->length());
1196 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001197 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001198 // Set the source code of the target function to undefined.
1199 // SetCode is only used for built-in constructors like String,
1200 // Array, and Object, and some web code
1201 // doesn't like seeing source code for constructors.
1202 target->shared()->set_script(Heap::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001203 context = Handle<Context>(fun->context());
1204
1205 // Make sure we get a fresh copy of the literal vector to avoid
1206 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001207 int number_of_literals = fun->NumberOfLiterals();
1208 Handle<FixedArray> literals =
1209 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001210 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001211 // Insert the object, regexp and array functions in the literals
1212 // array prefix. These are the functions that will be used when
1213 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001214 literals->set(JSFunction::kLiteralGlobalContextIndex,
1215 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001216 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001217 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001218 }
1219
1220 target->set_context(*context);
1221 return *target;
1222}
1223
1224
1225static Object* CharCodeAt(String* subject, Object* index) {
1226 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001227 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001228 // Flatten the string. If someone wants to get a char at an index
1229 // in a cons string, it is likely that more indices will be
1230 // accessed.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001231 subject->TryFlattenIfNotFlat();
1232 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001233 return Heap::nan_value();
1234 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001235 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001236}
1237
1238
1239static Object* Runtime_StringCharCodeAt(Arguments args) {
1240 NoHandleAllocation ha;
1241 ASSERT(args.length() == 2);
1242
1243 CONVERT_CHECKED(String, subject, args[0]);
1244 Object* index = args[1];
1245 return CharCodeAt(subject, index);
1246}
1247
1248
1249static Object* Runtime_CharFromCode(Arguments args) {
1250 NoHandleAllocation ha;
1251 ASSERT(args.length() == 1);
1252 uint32_t code;
1253 if (Array::IndexFromObject(args[0], &code)) {
1254 if (code <= 0xffff) {
1255 return Heap::LookupSingleCharacterStringFromCode(code);
1256 }
1257 }
1258 return Heap::empty_string();
1259}
1260
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001261// Forward declarations.
1262static const int kStringBuilderConcatHelperLengthBits = 11;
1263static const int kStringBuilderConcatHelperPositionBits = 19;
1264
1265template <typename schar>
1266static inline void StringBuilderConcatHelper(String*,
1267 schar*,
1268 FixedArray*,
1269 int);
1270
1271typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1272typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1273
1274class ReplacementStringBuilder {
1275 public:
1276 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1277 : subject_(subject),
1278 parts_(Factory::NewFixedArray(estimated_part_count)),
1279 part_count_(0),
1280 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001281 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001282 // Require a non-zero initial size. Ensures that doubling the size to
1283 // extend the array will work.
1284 ASSERT(estimated_part_count > 0);
1285 }
1286
1287 void EnsureCapacity(int elements) {
1288 int length = parts_->length();
1289 int required_length = part_count_ + elements;
1290 if (length < required_length) {
1291 int new_length = length;
1292 do {
1293 new_length *= 2;
1294 } while (new_length < required_length);
1295 Handle<FixedArray> extended_array =
1296 Factory::NewFixedArray(new_length);
1297 parts_->CopyTo(0, *extended_array, 0, part_count_);
1298 parts_ = extended_array;
1299 }
1300 }
1301
1302 void AddSubjectSlice(int from, int to) {
1303 ASSERT(from >= 0);
1304 int length = to - from;
1305 ASSERT(length > 0);
1306 // Can we encode the slice in 11 bits for length and 19 bits for
1307 // start position - as used by StringBuilderConcatHelper?
1308 if (StringBuilderSubstringLength::is_valid(length) &&
1309 StringBuilderSubstringPosition::is_valid(from)) {
1310 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1311 StringBuilderSubstringPosition::encode(from);
1312 AddElement(Smi::FromInt(encoded_slice));
1313 } else {
1314 Handle<String> slice = Factory::NewStringSlice(subject_, from, to);
1315 AddElement(*slice);
1316 }
1317 IncrementCharacterCount(length);
1318 }
1319
1320
1321 void AddString(Handle<String> string) {
1322 int length = string->length();
1323 ASSERT(length > 0);
1324 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001325 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001326 is_ascii_ = false;
1327 }
1328 IncrementCharacterCount(length);
1329 }
1330
1331
1332 Handle<String> ToString() {
1333 if (part_count_ == 0) {
1334 return Factory::empty_string();
1335 }
1336
1337 Handle<String> joined_string;
1338 if (is_ascii_) {
1339 joined_string = NewRawAsciiString(character_count_);
1340 AssertNoAllocation no_alloc;
1341 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1342 char* char_buffer = seq->GetChars();
1343 StringBuilderConcatHelper(*subject_,
1344 char_buffer,
1345 *parts_,
1346 part_count_);
1347 } else {
1348 // Non-ASCII.
1349 joined_string = NewRawTwoByteString(character_count_);
1350 AssertNoAllocation no_alloc;
1351 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1352 uc16* char_buffer = seq->GetChars();
1353 StringBuilderConcatHelper(*subject_,
1354 char_buffer,
1355 *parts_,
1356 part_count_);
1357 }
1358 return joined_string;
1359 }
1360
1361
1362 void IncrementCharacterCount(int by) {
1363 if (character_count_ > Smi::kMaxValue - by) {
1364 V8::FatalProcessOutOfMemory("String.replace result too large.");
1365 }
1366 character_count_ += by;
1367 }
1368
1369 private:
1370
1371 Handle<String> NewRawAsciiString(int size) {
1372 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1373 }
1374
1375
1376 Handle<String> NewRawTwoByteString(int size) {
1377 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1378 }
1379
1380
1381 void AddElement(Object* element) {
1382 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001383 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001384 parts_->set(part_count_, element);
1385 part_count_++;
1386 }
1387
1388 Handle<String> subject_;
1389 Handle<FixedArray> parts_;
1390 int part_count_;
1391 int character_count_;
1392 bool is_ascii_;
1393};
1394
1395
1396class CompiledReplacement {
1397 public:
1398 CompiledReplacement()
1399 : parts_(1), replacement_substrings_(0) {}
1400
1401 void Compile(Handle<String> replacement,
1402 int capture_count,
1403 int subject_length);
1404
1405 void Apply(ReplacementStringBuilder* builder,
1406 int match_from,
1407 int match_to,
1408 Handle<JSArray> last_match_info);
1409
1410 // Number of distinct parts of the replacement pattern.
1411 int parts() {
1412 return parts_.length();
1413 }
1414 private:
1415 enum PartType {
1416 SUBJECT_PREFIX = 1,
1417 SUBJECT_SUFFIX,
1418 SUBJECT_CAPTURE,
1419 REPLACEMENT_SUBSTRING,
1420 REPLACEMENT_STRING,
1421
1422 NUMBER_OF_PART_TYPES
1423 };
1424
1425 struct ReplacementPart {
1426 static inline ReplacementPart SubjectMatch() {
1427 return ReplacementPart(SUBJECT_CAPTURE, 0);
1428 }
1429 static inline ReplacementPart SubjectCapture(int capture_index) {
1430 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1431 }
1432 static inline ReplacementPart SubjectPrefix() {
1433 return ReplacementPart(SUBJECT_PREFIX, 0);
1434 }
1435 static inline ReplacementPart SubjectSuffix(int subject_length) {
1436 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1437 }
1438 static inline ReplacementPart ReplacementString() {
1439 return ReplacementPart(REPLACEMENT_STRING, 0);
1440 }
1441 static inline ReplacementPart ReplacementSubString(int from, int to) {
1442 ASSERT(from >= 0);
1443 ASSERT(to > from);
1444 return ReplacementPart(-from, to);
1445 }
1446
1447 // If tag <= 0 then it is the negation of a start index of a substring of
1448 // the replacement pattern, otherwise it's a value from PartType.
1449 ReplacementPart(int tag, int data)
1450 : tag(tag), data(data) {
1451 // Must be non-positive or a PartType value.
1452 ASSERT(tag < NUMBER_OF_PART_TYPES);
1453 }
1454 // Either a value of PartType or a non-positive number that is
1455 // the negation of an index into the replacement string.
1456 int tag;
1457 // The data value's interpretation depends on the value of tag:
1458 // tag == SUBJECT_PREFIX ||
1459 // tag == SUBJECT_SUFFIX: data is unused.
1460 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1461 // tag == REPLACEMENT_SUBSTRING ||
1462 // tag == REPLACEMENT_STRING: data is index into array of substrings
1463 // of the replacement string.
1464 // tag <= 0: Temporary representation of the substring of the replacement
1465 // string ranging over -tag .. data.
1466 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1467 // substring objects.
1468 int data;
1469 };
1470
1471 template<typename Char>
1472 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1473 Vector<Char> characters,
1474 int capture_count,
1475 int subject_length) {
1476 int length = characters.length();
1477 int last = 0;
1478 for (int i = 0; i < length; i++) {
1479 Char c = characters[i];
1480 if (c == '$') {
1481 int next_index = i + 1;
1482 if (next_index == length) { // No next character!
1483 break;
1484 }
1485 Char c2 = characters[next_index];
1486 switch (c2) {
1487 case '$':
1488 if (i > last) {
1489 // There is a substring before. Include the first "$".
1490 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1491 last = next_index + 1; // Continue after the second "$".
1492 } else {
1493 // Let the next substring start with the second "$".
1494 last = next_index;
1495 }
1496 i = next_index;
1497 break;
1498 case '`':
1499 if (i > last) {
1500 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1501 }
1502 parts->Add(ReplacementPart::SubjectPrefix());
1503 i = next_index;
1504 last = i + 1;
1505 break;
1506 case '\'':
1507 if (i > last) {
1508 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1509 }
1510 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1511 i = next_index;
1512 last = i + 1;
1513 break;
1514 case '&':
1515 if (i > last) {
1516 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1517 }
1518 parts->Add(ReplacementPart::SubjectMatch());
1519 i = next_index;
1520 last = i + 1;
1521 break;
1522 case '0':
1523 case '1':
1524 case '2':
1525 case '3':
1526 case '4':
1527 case '5':
1528 case '6':
1529 case '7':
1530 case '8':
1531 case '9': {
1532 int capture_ref = c2 - '0';
1533 if (capture_ref > capture_count) {
1534 i = next_index;
1535 continue;
1536 }
1537 int second_digit_index = next_index + 1;
1538 if (second_digit_index < length) {
1539 // Peek ahead to see if we have two digits.
1540 Char c3 = characters[second_digit_index];
1541 if ('0' <= c3 && c3 <= '9') { // Double digits.
1542 int double_digit_ref = capture_ref * 10 + c3 - '0';
1543 if (double_digit_ref <= capture_count) {
1544 next_index = second_digit_index;
1545 capture_ref = double_digit_ref;
1546 }
1547 }
1548 }
1549 if (capture_ref > 0) {
1550 if (i > last) {
1551 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1552 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001553 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001554 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1555 last = next_index + 1;
1556 }
1557 i = next_index;
1558 break;
1559 }
1560 default:
1561 i = next_index;
1562 break;
1563 }
1564 }
1565 }
1566 if (length > last) {
1567 if (last == 0) {
1568 parts->Add(ReplacementPart::ReplacementString());
1569 } else {
1570 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1571 }
1572 }
1573 }
1574
1575 ZoneList<ReplacementPart> parts_;
1576 ZoneList<Handle<String> > replacement_substrings_;
1577};
1578
1579
1580void CompiledReplacement::Compile(Handle<String> replacement,
1581 int capture_count,
1582 int subject_length) {
1583 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001584 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001585 AssertNoAllocation no_alloc;
1586 ParseReplacementPattern(&parts_,
1587 replacement->ToAsciiVector(),
1588 capture_count,
1589 subject_length);
1590 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001591 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001592 AssertNoAllocation no_alloc;
1593
1594 ParseReplacementPattern(&parts_,
1595 replacement->ToUC16Vector(),
1596 capture_count,
1597 subject_length);
1598 }
1599 // Find substrings of replacement string and create them as String objects..
1600 int substring_index = 0;
1601 for (int i = 0, n = parts_.length(); i < n; i++) {
1602 int tag = parts_[i].tag;
1603 if (tag <= 0) { // A replacement string slice.
1604 int from = -tag;
1605 int to = parts_[i].data;
1606 replacement_substrings_.Add(Factory::NewStringSlice(replacement,
1607 from,
1608 to));
1609 parts_[i].tag = REPLACEMENT_SUBSTRING;
1610 parts_[i].data = substring_index;
1611 substring_index++;
1612 } else if (tag == REPLACEMENT_STRING) {
1613 replacement_substrings_.Add(replacement);
1614 parts_[i].data = substring_index;
1615 substring_index++;
1616 }
1617 }
1618}
1619
1620
1621void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1622 int match_from,
1623 int match_to,
1624 Handle<JSArray> last_match_info) {
1625 for (int i = 0, n = parts_.length(); i < n; i++) {
1626 ReplacementPart part = parts_[i];
1627 switch (part.tag) {
1628 case SUBJECT_PREFIX:
1629 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1630 break;
1631 case SUBJECT_SUFFIX: {
1632 int subject_length = part.data;
1633 if (match_to < subject_length) {
1634 builder->AddSubjectSlice(match_to, subject_length);
1635 }
1636 break;
1637 }
1638 case SUBJECT_CAPTURE: {
1639 int capture = part.data;
1640 FixedArray* match_info = last_match_info->elements();
1641 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1642 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1643 if (from >= 0 && to > from) {
1644 builder->AddSubjectSlice(from, to);
1645 }
1646 break;
1647 }
1648 case REPLACEMENT_SUBSTRING:
1649 case REPLACEMENT_STRING:
1650 builder->AddString(replacement_substrings_[part.data]);
1651 break;
1652 default:
1653 UNREACHABLE();
1654 }
1655 }
1656}
1657
1658
1659
1660static Object* StringReplaceRegExpWithString(String* subject,
1661 JSRegExp* regexp,
1662 String* replacement,
1663 JSArray* last_match_info) {
1664 ASSERT(subject->IsFlat());
1665 ASSERT(replacement->IsFlat());
1666
1667 HandleScope handles;
1668
1669 int length = subject->length();
1670 Handle<String> subject_handle(subject);
1671 Handle<JSRegExp> regexp_handle(regexp);
1672 Handle<String> replacement_handle(replacement);
1673 Handle<JSArray> last_match_info_handle(last_match_info);
1674 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1675 subject_handle,
1676 0,
1677 last_match_info_handle);
1678 if (match.is_null()) {
1679 return Failure::Exception();
1680 }
1681 if (match->IsNull()) {
1682 return *subject_handle;
1683 }
1684
1685 int capture_count = regexp_handle->CaptureCount();
1686
1687 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001688 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001689 CompiledReplacement compiled_replacement;
1690 compiled_replacement.Compile(replacement_handle,
1691 capture_count,
1692 length);
1693
1694 bool is_global = regexp_handle->GetFlags().is_global();
1695
1696 // Guessing the number of parts that the final result string is built
1697 // from. Global regexps can match any number of times, so we guess
1698 // conservatively.
1699 int expected_parts =
1700 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1701 ReplacementStringBuilder builder(subject_handle, expected_parts);
1702
1703 // Index of end of last match.
1704 int prev = 0;
1705
1706 // Number of parts added by compiled replacement plus preceeding string
1707 // and possibly suffix after last match.
1708 const int parts_added_per_loop = compiled_replacement.parts() + 2;
1709 bool matched = true;
1710 do {
1711 ASSERT(last_match_info_handle->HasFastElements());
1712 // Increase the capacity of the builder before entering local handle-scope,
1713 // so its internal buffer can safely allocate a new handle if it grows.
1714 builder.EnsureCapacity(parts_added_per_loop);
1715
1716 HandleScope loop_scope;
1717 int start, end;
1718 {
1719 AssertNoAllocation match_info_array_is_not_in_a_handle;
1720 FixedArray* match_info_array = last_match_info_handle->elements();
1721
1722 ASSERT_EQ(capture_count * 2 + 2,
1723 RegExpImpl::GetLastCaptureCount(match_info_array));
1724 start = RegExpImpl::GetCapture(match_info_array, 0);
1725 end = RegExpImpl::GetCapture(match_info_array, 1);
1726 }
1727
1728 if (prev < start) {
1729 builder.AddSubjectSlice(prev, start);
1730 }
1731 compiled_replacement.Apply(&builder,
1732 start,
1733 end,
1734 last_match_info_handle);
1735 prev = end;
1736
1737 // Only continue checking for global regexps.
1738 if (!is_global) break;
1739
1740 // Continue from where the match ended, unless it was an empty match.
1741 int next = end;
1742 if (start == end) {
1743 next = end + 1;
1744 if (next > length) break;
1745 }
1746
1747 match = RegExpImpl::Exec(regexp_handle,
1748 subject_handle,
1749 next,
1750 last_match_info_handle);
1751 if (match.is_null()) {
1752 return Failure::Exception();
1753 }
1754 matched = !match->IsNull();
1755 } while (matched);
1756
1757 if (prev < length) {
1758 builder.AddSubjectSlice(prev, length);
1759 }
1760
1761 return *(builder.ToString());
1762}
1763
1764
1765static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1766 ASSERT(args.length() == 4);
1767
1768 CONVERT_CHECKED(String, subject, args[0]);
1769 if (!subject->IsFlat()) {
1770 Object* flat_subject = subject->TryFlatten();
1771 if (flat_subject->IsFailure()) {
1772 return flat_subject;
1773 }
1774 subject = String::cast(flat_subject);
1775 }
1776
1777 CONVERT_CHECKED(String, replacement, args[2]);
1778 if (!replacement->IsFlat()) {
1779 Object* flat_replacement = replacement->TryFlatten();
1780 if (flat_replacement->IsFailure()) {
1781 return flat_replacement;
1782 }
1783 replacement = String::cast(flat_replacement);
1784 }
1785
1786 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1787 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1788
1789 ASSERT(last_match_info->HasFastElements());
1790
1791 return StringReplaceRegExpWithString(subject,
1792 regexp,
1793 replacement,
1794 last_match_info);
1795}
1796
1797
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001798
ager@chromium.org7c537e22008-10-16 08:43:32 +00001799// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1800// limit, we can fix the size of tables.
1801static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001802// Reduce alphabet to this size.
1803static const int kBMAlphabetSize = 0x100;
1804// For patterns below this length, the skip length of Boyer-Moore is too short
1805// to compensate for the algorithmic overhead compared to simple brute force.
1806static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001807
ager@chromium.org7c537e22008-10-16 08:43:32 +00001808// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1809// shift. Only allows the last kBMMaxShift characters of the needle
1810// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001811class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001812 public:
1813 BMGoodSuffixBuffers() {}
1814 inline void init(int needle_length) {
1815 ASSERT(needle_length > 1);
1816 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1817 int len = needle_length - start;
1818 biased_suffixes_ = suffixes_ - start;
1819 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1820 for (int i = 0; i <= len; i++) {
1821 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001822 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001823 }
1824 inline int& suffix(int index) {
1825 ASSERT(biased_suffixes_ + index >= suffixes_);
1826 return biased_suffixes_[index];
1827 }
1828 inline int& shift(int index) {
1829 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1830 return biased_good_suffix_shift_[index];
1831 }
1832 private:
1833 int suffixes_[kBMMaxShift + 1];
1834 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001835 int* biased_suffixes_;
1836 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001837 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1838};
1839
1840// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001841static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001842static BMGoodSuffixBuffers bmgs_buffers;
1843
1844// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001845template <typename pchar>
1846static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1847 int start) {
1848 // Run forwards to populate bad_char_table, so that *last* instance
1849 // of character equivalence class is the one registered.
1850 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001851 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1852 : kBMAlphabetSize;
1853 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001854 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001855 } else {
1856 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001857 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001858 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001859 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001860 for (int i = start; i < pattern.length() - 1; i++) {
1861 pchar c = pattern[i];
1862 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001863 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001864 }
1865}
1866
1867template <typename pchar>
1868static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001869 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001870 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001871 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001872 // Compute Good Suffix tables.
1873 bmgs_buffers.init(m);
1874
1875 bmgs_buffers.shift(m-1) = 1;
1876 bmgs_buffers.suffix(m) = m + 1;
1877 pchar last_char = pattern[m - 1];
1878 int suffix = m + 1;
1879 for (int i = m; i > start;) {
1880 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1881 if (bmgs_buffers.shift(suffix) == len) {
1882 bmgs_buffers.shift(suffix) = suffix - i;
1883 }
1884 suffix = bmgs_buffers.suffix(suffix);
1885 }
1886 i--;
1887 suffix--;
1888 bmgs_buffers.suffix(i) = suffix;
1889 if (suffix == m) {
1890 // No suffix to extend, so we check against last_char only.
1891 while (i > start && pattern[i - 1] != last_char) {
1892 if (bmgs_buffers.shift(m) == len) {
1893 bmgs_buffers.shift(m) = m - i;
1894 }
1895 i--;
1896 bmgs_buffers.suffix(i) = m;
1897 }
1898 if (i > start) {
1899 i--;
1900 suffix--;
1901 bmgs_buffers.suffix(i) = suffix;
1902 }
1903 }
1904 }
1905 if (suffix < m) {
1906 for (int i = start; i <= m; i++) {
1907 if (bmgs_buffers.shift(i) == len) {
1908 bmgs_buffers.shift(i) = suffix - start;
1909 }
1910 if (i == suffix) {
1911 suffix = bmgs_buffers.suffix(suffix);
1912 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001913 }
1914 }
1915}
1916
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001917template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001918static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001919 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001920 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001921 }
1922 if (sizeof(pchar) == 1) {
1923 if (char_code > String::kMaxAsciiCharCode) {
1924 return -1;
1925 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001926 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001927 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001928 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001929}
1930
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001931// Restricted simplified Boyer-Moore string matching.
1932// Uses only the bad-shift table of Boyer-Moore and only uses it
1933// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001934template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001935static int BoyerMooreHorspool(Vector<const schar> subject,
1936 Vector<const pchar> pattern,
1937 int start_index,
1938 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001939 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001940 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001941 // Only preprocess at most kBMMaxShift last characters of pattern.
1942 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001943
ager@chromium.org7c537e22008-10-16 08:43:32 +00001944 BoyerMoorePopulateBadCharTable(pattern, start);
1945
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001946 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001947 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001948 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001949 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001950 // Perform search
1951 for (idx = start_index; idx <= n - m;) {
1952 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001953 int c;
1954 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001955 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001956 int shift = j - bc_occ;
1957 idx += shift;
1958 badness += 1 - shift; // at most zero, so badness cannot increase.
1959 if (idx > n - m) {
1960 *complete = true;
1961 return -1;
1962 }
1963 }
1964 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001965 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001966 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001967 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001968 return idx;
1969 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001970 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001971 // Badness increases by the number of characters we have
1972 // checked, and decreases by the number of characters we
1973 // can skip by shifting. It's a measure of how we are doing
1974 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001975 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001976 if (badness > 0) {
1977 *complete = false;
1978 return idx;
1979 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001980 }
1981 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001982 *complete = true;
1983 return -1;
1984}
ager@chromium.org7c537e22008-10-16 08:43:32 +00001985
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001986
1987template <typename schar, typename pchar>
1988static int BoyerMooreIndexOf(Vector<const schar> subject,
1989 Vector<const pchar> pattern,
1990 int idx) {
1991 int n = subject.length();
1992 int m = pattern.length();
1993 // Only preprocess at most kBMMaxShift last characters of pattern.
1994 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
1995
1996 // Build the Good Suffix table and continue searching.
1997 BoyerMoorePopulateGoodSuffixTable(pattern, start);
1998 pchar last_char = pattern[m - 1];
1999 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002000 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002001 int j = m - 1;
2002 schar c;
2003 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002004 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002005 idx += shift;
2006 if (idx > n - m) {
2007 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002008 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002009 }
2010 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2011 if (j < 0) {
2012 return idx;
2013 } else if (j < start) {
2014 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002015 // Fall back on BMH shift.
2016 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002017 } else {
2018 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002019 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002020 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002021 if (gs_shift > shift) {
2022 shift = gs_shift;
2023 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002024 idx += shift;
2025 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002026 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002027
2028 return -1;
2029}
2030
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002031
2032template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002033static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002034 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002035 int start_index) {
2036 for (int i = start_index, n = string.length(); i < n; i++) {
2037 if (pattern_char == string[i]) {
2038 return i;
2039 }
2040 }
2041 return -1;
2042}
2043
2044// Trivial string search for shorter strings.
2045// On return, if "complete" is set to true, the return value is the
2046// final result of searching for the patter in the subject.
2047// If "complete" is set to false, the return value is the index where
2048// further checking should start, i.e., it's guaranteed that the pattern
2049// does not occur at a position prior to the returned index.
2050template <typename pchar, typename schar>
2051static int SimpleIndexOf(Vector<const schar> subject,
2052 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002053 int idx,
2054 bool* complete) {
2055 // Badness is a count of how much work we have done. When we have
2056 // done enough work we decide it's probably worth switching to a better
2057 // algorithm.
2058 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002059 // We know our pattern is at least 2 characters, we cache the first so
2060 // the common case of the first character not matching is faster.
2061 pchar pattern_first_char = pattern[0];
2062
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002063 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2064 badness++;
2065 if (badness > 0) {
2066 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002067 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002068 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002069 if (subject[i] != pattern_first_char) continue;
2070 int j = 1;
2071 do {
2072 if (pattern[j] != subject[i+j]) {
2073 break;
2074 }
2075 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002076 } while (j < pattern.length());
2077 if (j == pattern.length()) {
2078 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002079 return i;
2080 }
2081 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002082 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002083 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002084 return -1;
2085}
2086
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002087// Simple indexOf that never bails out. For short patterns only.
2088template <typename pchar, typename schar>
2089static int SimpleIndexOf(Vector<const schar> subject,
2090 Vector<const pchar> pattern,
2091 int idx) {
2092 pchar pattern_first_char = pattern[0];
2093 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2094 if (subject[i] != pattern_first_char) continue;
2095 int j = 1;
2096 do {
2097 if (pattern[j] != subject[i+j]) {
2098 break;
2099 }
2100 j++;
2101 } while (j < pattern.length());
2102 if (j == pattern.length()) {
2103 return i;
2104 }
2105 }
2106 return -1;
2107}
2108
2109
2110// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002111template <typename schar, typename pchar>
2112static int StringMatchStrategy(Vector<const schar> sub,
2113 Vector<const pchar> pat,
2114 int start_index) {
2115 ASSERT(pat.length() > 1);
2116
2117 // We have an ASCII haystack and a non-ASCII needle. Check if there
2118 // really is a non-ASCII character in the needle and bail out if there
2119 // is.
2120 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2121 for (int i = 0; i < pat.length(); i++) {
2122 uc16 c = pat[i];
2123 if (c > String::kMaxAsciiCharCode) {
2124 return -1;
2125 }
2126 }
2127 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002128 if (pat.length() < kBMMinPatternLength) {
2129 // We don't believe fancy searching can ever be more efficient.
2130 // The max shift of Boyer-Moore on a pattern of this length does
2131 // not compensate for the overhead.
2132 return SimpleIndexOf(sub, pat, start_index);
2133 }
2134 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002135 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002136 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2137 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002138 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002139 if (complete) return idx;
2140 return BoyerMooreIndexOf(sub, pat, idx);
2141}
2142
2143// Perform string match of pattern on subject, starting at start index.
2144// Caller must ensure that 0 <= start_index <= sub->length(),
2145// and should check that pat->length() + start_index <= sub->length()
2146int Runtime::StringMatch(Handle<String> sub,
2147 Handle<String> pat,
2148 int start_index) {
2149 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002150 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002151
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002152 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002153 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002154
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002155 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002156 if (start_index + pattern_length > subject_length) return -1;
2157
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002158 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002159 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002160 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002161 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002162 // character patterns linear search is necessary, so any smart
2163 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002164 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002165 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002166 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002167 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002168 if (pchar > String::kMaxAsciiCharCode) {
2169 return -1;
2170 }
2171 Vector<const char> ascii_vector =
2172 sub->ToAsciiVector().SubVector(start_index, subject_length);
2173 const void* pos = memchr(ascii_vector.start(),
2174 static_cast<const char>(pchar),
2175 static_cast<size_t>(ascii_vector.length()));
2176 if (pos == NULL) {
2177 return -1;
2178 }
2179 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
2180 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002181 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002182 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002183 }
2184
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002185 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002186 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002187 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002188
ager@chromium.org7c537e22008-10-16 08:43:32 +00002189 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2190 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002191 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002192 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002193 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002194 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002195 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002196 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002197 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002198 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002199 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002200 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002201 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002202 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002203}
2204
2205
2206static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002207 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002208 ASSERT(args.length() == 3);
2209
ager@chromium.org7c537e22008-10-16 08:43:32 +00002210 CONVERT_ARG_CHECKED(String, sub, 0);
2211 CONVERT_ARG_CHECKED(String, pat, 1);
2212
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002213 Object* index = args[2];
2214 uint32_t start_index;
2215 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2216
ager@chromium.org870a0b62008-11-04 11:43:05 +00002217 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002218 int position = Runtime::StringMatch(sub, pat, start_index);
2219 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002220}
2221
2222
2223static Object* Runtime_StringLastIndexOf(Arguments args) {
2224 NoHandleAllocation ha;
2225 ASSERT(args.length() == 3);
2226
2227 CONVERT_CHECKED(String, sub, args[0]);
2228 CONVERT_CHECKED(String, pat, args[1]);
2229 Object* index = args[2];
2230
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002231 sub->TryFlattenIfNotFlat();
2232 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002233
2234 uint32_t start_index;
2235 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2236
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002237 uint32_t pattern_length = pat->length();
2238 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002239
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002240 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002241 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002242 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002243
2244 for (int i = start_index; i >= 0; i--) {
2245 bool found = true;
2246 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002247 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002248 found = false;
2249 break;
2250 }
2251 }
2252 if (found) return Smi::FromInt(i);
2253 }
2254
2255 return Smi::FromInt(-1);
2256}
2257
2258
2259static Object* Runtime_StringLocaleCompare(Arguments args) {
2260 NoHandleAllocation ha;
2261 ASSERT(args.length() == 2);
2262
2263 CONVERT_CHECKED(String, str1, args[0]);
2264 CONVERT_CHECKED(String, str2, args[1]);
2265
2266 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002267 int str1_length = str1->length();
2268 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002269
2270 // Decide trivial cases without flattening.
2271 if (str1_length == 0) {
2272 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2273 return Smi::FromInt(-str2_length);
2274 } else {
2275 if (str2_length == 0) return Smi::FromInt(str1_length);
2276 }
2277
2278 int end = str1_length < str2_length ? str1_length : str2_length;
2279
2280 // No need to flatten if we are going to find the answer on the first
2281 // character. At this point we know there is at least one character
2282 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002283 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002284 if (d != 0) return Smi::FromInt(d);
2285
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002286 str1->TryFlattenIfNotFlat();
2287 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002288
2289 static StringInputBuffer buf1;
2290 static StringInputBuffer buf2;
2291
2292 buf1.Reset(str1);
2293 buf2.Reset(str2);
2294
2295 for (int i = 0; i < end; i++) {
2296 uint16_t char1 = buf1.GetNext();
2297 uint16_t char2 = buf2.GetNext();
2298 if (char1 != char2) return Smi::FromInt(char1 - char2);
2299 }
2300
2301 return Smi::FromInt(str1_length - str2_length);
2302}
2303
2304
2305static Object* Runtime_StringSlice(Arguments args) {
2306 NoHandleAllocation ha;
2307 ASSERT(args.length() == 3);
2308
2309 CONVERT_CHECKED(String, value, args[0]);
2310 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
2311 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
2312
2313 int start = FastD2I(from_number);
2314 int end = FastD2I(to_number);
2315
2316 RUNTIME_ASSERT(end >= start);
2317 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002318 RUNTIME_ASSERT(end <= value->length());
2319 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002320}
2321
2322
ager@chromium.org41826e72009-03-30 13:30:57 +00002323static Object* Runtime_StringMatch(Arguments args) {
2324 ASSERT_EQ(3, args.length());
2325
2326 CONVERT_ARG_CHECKED(String, subject, 0);
2327 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2328 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2329 HandleScope handles;
2330
2331 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2332
2333 if (match.is_null()) {
2334 return Failure::Exception();
2335 }
2336 if (match->IsNull()) {
2337 return Heap::null_value();
2338 }
2339 int length = subject->length();
2340
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002341 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002342 ZoneList<int> offsets(8);
2343 do {
2344 int start;
2345 int end;
2346 {
2347 AssertNoAllocation no_alloc;
2348 FixedArray* elements = regexp_info->elements();
2349 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2350 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2351 }
2352 offsets.Add(start);
2353 offsets.Add(end);
2354 int index = start < end ? end : end + 1;
2355 if (index > length) break;
2356 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2357 if (match.is_null()) {
2358 return Failure::Exception();
2359 }
2360 } while (!match->IsNull());
2361 int matches = offsets.length() / 2;
2362 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2363 for (int i = 0; i < matches ; i++) {
2364 int from = offsets.at(i * 2);
2365 int to = offsets.at(i * 2 + 1);
2366 elements->set(i, *Factory::NewStringSlice(subject, from, to));
2367 }
2368 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2369 result->set_length(Smi::FromInt(matches));
2370 return *result;
2371}
2372
2373
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002374static Object* Runtime_NumberToRadixString(Arguments args) {
2375 NoHandleAllocation ha;
2376 ASSERT(args.length() == 2);
2377
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002378 // Fast case where the result is a one character string.
2379 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2380 int value = Smi::cast(args[0])->value();
2381 int radix = Smi::cast(args[1])->value();
2382 if (value >= 0 && value < radix) {
2383 RUNTIME_ASSERT(radix <= 36);
2384 // Character array used for conversion.
2385 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2386 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2387 }
2388 }
2389
2390 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002391 CONVERT_DOUBLE_CHECKED(value, args[0]);
2392 if (isnan(value)) {
2393 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2394 }
2395 if (isinf(value)) {
2396 if (value < 0) {
2397 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2398 }
2399 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2400 }
2401 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2402 int radix = FastD2I(radix_number);
2403 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2404 char* str = DoubleToRadixCString(value, radix);
2405 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2406 DeleteArray(str);
2407 return result;
2408}
2409
2410
2411static Object* Runtime_NumberToFixed(Arguments args) {
2412 NoHandleAllocation ha;
2413 ASSERT(args.length() == 2);
2414
2415 CONVERT_DOUBLE_CHECKED(value, args[0]);
2416 if (isnan(value)) {
2417 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2418 }
2419 if (isinf(value)) {
2420 if (value < 0) {
2421 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2422 }
2423 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2424 }
2425 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2426 int f = FastD2I(f_number);
2427 RUNTIME_ASSERT(f >= 0);
2428 char* str = DoubleToFixedCString(value, f);
2429 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2430 DeleteArray(str);
2431 return res;
2432}
2433
2434
2435static Object* Runtime_NumberToExponential(Arguments args) {
2436 NoHandleAllocation ha;
2437 ASSERT(args.length() == 2);
2438
2439 CONVERT_DOUBLE_CHECKED(value, args[0]);
2440 if (isnan(value)) {
2441 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2442 }
2443 if (isinf(value)) {
2444 if (value < 0) {
2445 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2446 }
2447 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2448 }
2449 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2450 int f = FastD2I(f_number);
2451 RUNTIME_ASSERT(f >= -1 && f <= 20);
2452 char* str = DoubleToExponentialCString(value, f);
2453 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2454 DeleteArray(str);
2455 return res;
2456}
2457
2458
2459static Object* Runtime_NumberToPrecision(Arguments args) {
2460 NoHandleAllocation ha;
2461 ASSERT(args.length() == 2);
2462
2463 CONVERT_DOUBLE_CHECKED(value, args[0]);
2464 if (isnan(value)) {
2465 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2466 }
2467 if (isinf(value)) {
2468 if (value < 0) {
2469 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2470 }
2471 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2472 }
2473 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2474 int f = FastD2I(f_number);
2475 RUNTIME_ASSERT(f >= 1 && f <= 21);
2476 char* str = DoubleToPrecisionCString(value, f);
2477 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2478 DeleteArray(str);
2479 return res;
2480}
2481
2482
2483// Returns a single character string where first character equals
2484// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002485static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002486 if (index < static_cast<uint32_t>(string->length())) {
2487 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002488 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002489 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002490 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002491 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002492}
2493
2494
2495Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2496 // Handle [] indexing on Strings
2497 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002498 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2499 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002500 }
2501
2502 // Handle [] indexing on String objects
2503 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002504 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2505 Handle<Object> result =
2506 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2507 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002508 }
2509
2510 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002511 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002512 return prototype->GetElement(index);
2513 }
2514
2515 return object->GetElement(index);
2516}
2517
2518
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002519Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2520 HandleScope scope;
2521
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002522 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002523 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002524 Handle<Object> error =
2525 Factory::NewTypeError("non_object_property_load",
2526 HandleVector(args, 2));
2527 return Top::Throw(*error);
2528 }
2529
2530 // Check if the given key is an array index.
2531 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002532 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002533 return GetElementOrCharAt(object, index);
2534 }
2535
2536 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002537 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002538 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002539 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002540 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002541 bool has_pending_exception = false;
2542 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002543 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002544 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002545 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002546 }
2547
ager@chromium.org32912102009-01-16 10:38:43 +00002548 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002549 // the element if so.
2550 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002551 return GetElementOrCharAt(object, index);
2552 } else {
2553 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002554 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002555 }
2556}
2557
2558
2559static Object* Runtime_GetProperty(Arguments args) {
2560 NoHandleAllocation ha;
2561 ASSERT(args.length() == 2);
2562
2563 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002564 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002565
2566 return Runtime::GetObjectProperty(object, key);
2567}
2568
2569
ager@chromium.org7c537e22008-10-16 08:43:32 +00002570
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002571// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002572static Object* Runtime_KeyedGetProperty(Arguments args) {
2573 NoHandleAllocation ha;
2574 ASSERT(args.length() == 2);
2575
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002576 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002577 // itself.
2578 //
2579 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002580 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002581 // global proxy object never has properties. This is the case
2582 // because the global proxy object forwards everything to its hidden
2583 // prototype including local lookups.
2584 //
2585 // Additionally, we need to make sure that we do not cache results
2586 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002587 if (args[0]->IsJSObject() &&
2588 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002589 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002590 args[1]->IsString()) {
2591 JSObject* receiver = JSObject::cast(args[0]);
2592 String* key = String::cast(args[1]);
2593 if (receiver->HasFastProperties()) {
2594 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002595 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002596 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2597 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002598 Object* value = receiver->FastPropertyAt(offset);
2599 return value->IsTheHole() ? Heap::undefined_value() : value;
2600 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002601 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002602 LookupResult result;
2603 receiver->LocalLookup(key, &result);
2604 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2605 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002606 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002607 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002608 }
2609 } else {
2610 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002611 StringDictionary* dictionary = receiver->property_dictionary();
2612 int entry = dictionary->FindEntry(key);
2613 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002614 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002615 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002616 if (!receiver->IsGlobalObject()) return value;
2617 value = JSGlobalPropertyCell::cast(value)->value();
2618 if (!value->IsTheHole()) return value;
2619 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002620 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002621 }
2622 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002623
2624 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002625 return Runtime::GetObjectProperty(args.at<Object>(0),
2626 args.at<Object>(1));
2627}
2628
2629
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002630Object* Runtime::SetObjectProperty(Handle<Object> object,
2631 Handle<Object> key,
2632 Handle<Object> value,
2633 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002634 HandleScope scope;
2635
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002636 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002637 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002638 Handle<Object> error =
2639 Factory::NewTypeError("non_object_property_store",
2640 HandleVector(args, 2));
2641 return Top::Throw(*error);
2642 }
2643
2644 // If the object isn't a JavaScript object, we ignore the store.
2645 if (!object->IsJSObject()) return *value;
2646
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002647 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2648
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002649 // Check if the given key is an array index.
2650 uint32_t index;
2651 if (Array::IndexFromObject(*key, &index)) {
2652 ASSERT(attr == NONE);
2653
2654 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2655 // of a string using [] notation. We need to support this too in
2656 // JavaScript.
2657 // In the case of a String object we just need to redirect the assignment to
2658 // the underlying string if the index is in range. Since the underlying
2659 // string does nothing with the assignment then we can ignore such
2660 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002661 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002662 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002663 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002664
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002665 Handle<Object> result = SetElement(js_object, index, value);
2666 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002667 return *value;
2668 }
2669
2670 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002671 Handle<Object> result;
2672 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002673 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002674 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002675 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002676 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002677 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002678 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002679 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002680 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002681 return *value;
2682 }
2683
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002684 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002685 bool has_pending_exception = false;
2686 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2687 if (has_pending_exception) return Failure::Exception();
2688 Handle<String> name = Handle<String>::cast(converted);
2689
2690 if (name->AsArrayIndex(&index)) {
2691 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002692 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002693 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002694 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002695 }
2696}
2697
2698
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002699Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2700 Handle<Object> key,
2701 Handle<Object> value,
2702 PropertyAttributes attr) {
2703 HandleScope scope;
2704
2705 // Check if the given key is an array index.
2706 uint32_t index;
2707 if (Array::IndexFromObject(*key, &index)) {
2708 ASSERT(attr == NONE);
2709
2710 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2711 // of a string using [] notation. We need to support this too in
2712 // JavaScript.
2713 // In the case of a String object we just need to redirect the assignment to
2714 // the underlying string if the index is in range. Since the underlying
2715 // string does nothing with the assignment then we can ignore such
2716 // assignments.
2717 if (js_object->IsStringObjectWithCharacterAt(index)) {
2718 return *value;
2719 }
2720
2721 return js_object->SetElement(index, *value);
2722 }
2723
2724 if (key->IsString()) {
2725 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2726 ASSERT(attr == NONE);
2727 return js_object->SetElement(index, *value);
2728 } else {
2729 Handle<String> key_string = Handle<String>::cast(key);
2730 key_string->TryFlattenIfNotFlat();
2731 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2732 *value,
2733 attr);
2734 }
2735 }
2736
2737 // Call-back into JavaScript to convert the key to a string.
2738 bool has_pending_exception = false;
2739 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2740 if (has_pending_exception) return Failure::Exception();
2741 Handle<String> name = Handle<String>::cast(converted);
2742
2743 if (name->AsArrayIndex(&index)) {
2744 ASSERT(attr == NONE);
2745 return js_object->SetElement(index, *value);
2746 } else {
2747 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2748 }
2749}
2750
2751
ager@chromium.orge2902be2009-06-08 12:21:35 +00002752Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2753 Handle<Object> key) {
2754 HandleScope scope;
2755
2756 // Check if the given key is an array index.
2757 uint32_t index;
2758 if (Array::IndexFromObject(*key, &index)) {
2759 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2760 // characters of a string using [] notation. In the case of a
2761 // String object we just need to redirect the deletion to the
2762 // underlying string if the index is in range. Since the
2763 // underlying string does nothing with the deletion, we can ignore
2764 // such deletions.
2765 if (js_object->IsStringObjectWithCharacterAt(index)) {
2766 return Heap::true_value();
2767 }
2768
2769 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2770 }
2771
2772 Handle<String> key_string;
2773 if (key->IsString()) {
2774 key_string = Handle<String>::cast(key);
2775 } else {
2776 // Call-back into JavaScript to convert the key to a string.
2777 bool has_pending_exception = false;
2778 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2779 if (has_pending_exception) return Failure::Exception();
2780 key_string = Handle<String>::cast(converted);
2781 }
2782
2783 key_string->TryFlattenIfNotFlat();
2784 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2785}
2786
2787
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002788static Object* Runtime_SetProperty(Arguments args) {
2789 NoHandleAllocation ha;
2790 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2791
2792 Handle<Object> object = args.at<Object>(0);
2793 Handle<Object> key = args.at<Object>(1);
2794 Handle<Object> value = args.at<Object>(2);
2795
2796 // Compute attributes.
2797 PropertyAttributes attributes = NONE;
2798 if (args.length() == 4) {
2799 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002800 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002801 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002802 RUNTIME_ASSERT(
2803 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2804 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002805 }
2806 return Runtime::SetObjectProperty(object, key, value, attributes);
2807}
2808
2809
2810// Set a local property, even if it is READ_ONLY. If the property does not
2811// exist, it will be added with attributes NONE.
2812static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2813 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002814 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002815 CONVERT_CHECKED(JSObject, object, args[0]);
2816 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002817 // Compute attributes.
2818 PropertyAttributes attributes = NONE;
2819 if (args.length() == 4) {
2820 CONVERT_CHECKED(Smi, value_obj, args[3]);
2821 int unchecked_value = value_obj->value();
2822 // Only attribute bits should be set.
2823 RUNTIME_ASSERT(
2824 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2825 attributes = static_cast<PropertyAttributes>(unchecked_value);
2826 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002827
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002828 return object->
2829 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002830}
2831
2832
2833static Object* Runtime_DeleteProperty(Arguments args) {
2834 NoHandleAllocation ha;
2835 ASSERT(args.length() == 2);
2836
2837 CONVERT_CHECKED(JSObject, object, args[0]);
2838 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002839 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002840}
2841
2842
ager@chromium.org9085a012009-05-11 19:22:57 +00002843static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2844 Handle<String> key) {
2845 if (object->HasLocalProperty(*key)) return Heap::true_value();
2846 // Handle hidden prototypes. If there's a hidden prototype above this thing
2847 // then we have to check it for properties, because they are supposed to
2848 // look like they are on this object.
2849 Handle<Object> proto(object->GetPrototype());
2850 if (proto->IsJSObject() &&
2851 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2852 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2853 }
2854 return Heap::false_value();
2855}
2856
2857
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002858static Object* Runtime_HasLocalProperty(Arguments args) {
2859 NoHandleAllocation ha;
2860 ASSERT(args.length() == 2);
2861 CONVERT_CHECKED(String, key, args[1]);
2862
ager@chromium.org9085a012009-05-11 19:22:57 +00002863 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002864 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002865 if (obj->IsJSObject()) {
2866 JSObject* object = JSObject::cast(obj);
2867 // Fast case - no interceptors.
2868 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2869 // Slow case. Either it's not there or we have an interceptor. We should
2870 // have handles for this kind of deal.
2871 HandleScope scope;
2872 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2873 Handle<String>(key));
2874 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002875 // Well, there is one exception: Handle [] on strings.
2876 uint32_t index;
2877 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002878 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002879 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002880 return Heap::true_value();
2881 }
2882 }
2883 return Heap::false_value();
2884}
2885
2886
2887static Object* Runtime_HasProperty(Arguments args) {
2888 NoHandleAllocation na;
2889 ASSERT(args.length() == 2);
2890
2891 // Only JS objects can have properties.
2892 if (args[0]->IsJSObject()) {
2893 JSObject* object = JSObject::cast(args[0]);
2894 CONVERT_CHECKED(String, key, args[1]);
2895 if (object->HasProperty(key)) return Heap::true_value();
2896 }
2897 return Heap::false_value();
2898}
2899
2900
2901static Object* Runtime_HasElement(Arguments args) {
2902 NoHandleAllocation na;
2903 ASSERT(args.length() == 2);
2904
2905 // Only JS objects can have elements.
2906 if (args[0]->IsJSObject()) {
2907 JSObject* object = JSObject::cast(args[0]);
2908 CONVERT_CHECKED(Smi, index_obj, args[1]);
2909 uint32_t index = index_obj->value();
2910 if (object->HasElement(index)) return Heap::true_value();
2911 }
2912 return Heap::false_value();
2913}
2914
2915
2916static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2917 NoHandleAllocation ha;
2918 ASSERT(args.length() == 2);
2919
2920 CONVERT_CHECKED(JSObject, object, args[0]);
2921 CONVERT_CHECKED(String, key, args[1]);
2922
2923 uint32_t index;
2924 if (key->AsArrayIndex(&index)) {
2925 return Heap::ToBoolean(object->HasElement(index));
2926 }
2927
ager@chromium.org870a0b62008-11-04 11:43:05 +00002928 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2929 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002930}
2931
2932
2933static Object* Runtime_GetPropertyNames(Arguments args) {
2934 HandleScope scope;
2935 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002936 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002937 return *GetKeysFor(object);
2938}
2939
2940
2941// Returns either a FixedArray as Runtime_GetPropertyNames,
2942// or, if the given object has an enum cache that contains
2943// all enumerable properties of the object and its prototypes
2944// have none, the map of the object. This is used to speed up
2945// the check for deletions during a for-in.
2946static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2947 ASSERT(args.length() == 1);
2948
2949 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2950
2951 if (raw_object->IsSimpleEnum()) return raw_object->map();
2952
2953 HandleScope scope;
2954 Handle<JSObject> object(raw_object);
2955 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2956
2957 // Test again, since cache may have been built by preceding call.
2958 if (object->IsSimpleEnum()) return object->map();
2959
2960 return *content;
2961}
2962
2963
2964static Object* Runtime_GetArgumentsProperty(Arguments args) {
2965 NoHandleAllocation ha;
2966 ASSERT(args.length() == 1);
2967
2968 // Compute the frame holding the arguments.
2969 JavaScriptFrameIterator it;
2970 it.AdvanceToArgumentsFrame();
2971 JavaScriptFrame* frame = it.frame();
2972
2973 // Get the actual number of provided arguments.
2974 const uint32_t n = frame->GetProvidedParametersCount();
2975
2976 // Try to convert the key to an index. If successful and within
2977 // index return the the argument from the frame.
2978 uint32_t index;
2979 if (Array::IndexFromObject(args[0], &index) && index < n) {
2980 return frame->GetParameter(index);
2981 }
2982
2983 // Convert the key to a string.
2984 HandleScope scope;
2985 bool exception = false;
2986 Handle<Object> converted =
2987 Execution::ToString(args.at<Object>(0), &exception);
2988 if (exception) return Failure::Exception();
2989 Handle<String> key = Handle<String>::cast(converted);
2990
2991 // Try to convert the string key into an array index.
2992 if (key->AsArrayIndex(&index)) {
2993 if (index < n) {
2994 return frame->GetParameter(index);
2995 } else {
2996 return Top::initial_object_prototype()->GetElement(index);
2997 }
2998 }
2999
3000 // Handle special arguments properties.
3001 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3002 if (key->Equals(Heap::callee_symbol())) return frame->function();
3003
3004 // Lookup in the initial Object.prototype object.
3005 return Top::initial_object_prototype()->GetProperty(*key);
3006}
3007
3008
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003009static Object* Runtime_ToFastProperties(Arguments args) {
3010 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003011 Handle<Object> object = args.at<Object>(0);
3012 if (object->IsJSObject()) {
3013 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3014 js_object->TransformToFastProperties(0);
3015 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003016 return *object;
3017}
3018
3019
3020static Object* Runtime_ToSlowProperties(Arguments args) {
3021 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003022 Handle<Object> object = args.at<Object>(0);
3023 if (object->IsJSObject()) {
3024 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3025 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
3026 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003027 return *object;
3028}
3029
3030
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003031static Object* Runtime_ToBool(Arguments args) {
3032 NoHandleAllocation ha;
3033 ASSERT(args.length() == 1);
3034
3035 return args[0]->ToBoolean();
3036}
3037
3038
3039// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3040// Possible optimizations: put the type string into the oddballs.
3041static Object* Runtime_Typeof(Arguments args) {
3042 NoHandleAllocation ha;
3043
3044 Object* obj = args[0];
3045 if (obj->IsNumber()) return Heap::number_symbol();
3046 HeapObject* heap_obj = HeapObject::cast(obj);
3047
3048 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003049 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003050
3051 InstanceType instance_type = heap_obj->map()->instance_type();
3052 if (instance_type < FIRST_NONSTRING_TYPE) {
3053 return Heap::string_symbol();
3054 }
3055
3056 switch (instance_type) {
3057 case ODDBALL_TYPE:
3058 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3059 return Heap::boolean_symbol();
3060 }
3061 if (heap_obj->IsNull()) {
3062 return Heap::object_symbol();
3063 }
3064 ASSERT(heap_obj->IsUndefined());
3065 return Heap::undefined_symbol();
3066 case JS_FUNCTION_TYPE:
3067 return Heap::function_symbol();
3068 default:
3069 // For any kind of object not handled above, the spec rule for
3070 // host objects gives that it is okay to return "object"
3071 return Heap::object_symbol();
3072 }
3073}
3074
3075
3076static Object* Runtime_StringToNumber(Arguments args) {
3077 NoHandleAllocation ha;
3078 ASSERT(args.length() == 1);
3079 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003080 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003081 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3082}
3083
3084
3085static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3086 NoHandleAllocation ha;
3087 ASSERT(args.length() == 1);
3088
3089 CONVERT_CHECKED(JSArray, codes, args[0]);
3090 int length = Smi::cast(codes->length())->value();
3091
3092 // Check if the string can be ASCII.
3093 int i;
3094 for (i = 0; i < length; i++) {
3095 Object* element = codes->GetElement(i);
3096 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3097 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3098 break;
3099 }
3100
3101 Object* object = NULL;
3102 if (i == length) { // The string is ASCII.
3103 object = Heap::AllocateRawAsciiString(length);
3104 } else { // The string is not ASCII.
3105 object = Heap::AllocateRawTwoByteString(length);
3106 }
3107
3108 if (object->IsFailure()) return object;
3109 String* result = String::cast(object);
3110 for (int i = 0; i < length; i++) {
3111 Object* element = codes->GetElement(i);
3112 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003113 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003114 }
3115 return result;
3116}
3117
3118
3119// kNotEscaped is generated by the following:
3120//
3121// #!/bin/perl
3122// for (my $i = 0; $i < 256; $i++) {
3123// print "\n" if $i % 16 == 0;
3124// my $c = chr($i);
3125// my $escaped = 1;
3126// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3127// print $escaped ? "0, " : "1, ";
3128// }
3129
3130
3131static bool IsNotEscaped(uint16_t character) {
3132 // Only for 8 bit characters, the rest are always escaped (in a different way)
3133 ASSERT(character < 256);
3134 static const char kNotEscaped[256] = {
3135 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3136 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3137 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3138 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3139 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3140 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3141 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3142 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3143 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3144 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3145 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3146 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3147 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3148 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3149 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3150 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3151 };
3152 return kNotEscaped[character] != 0;
3153}
3154
3155
3156static Object* Runtime_URIEscape(Arguments args) {
3157 const char hex_chars[] = "0123456789ABCDEF";
3158 NoHandleAllocation ha;
3159 ASSERT(args.length() == 1);
3160 CONVERT_CHECKED(String, source, args[0]);
3161
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003162 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003163
3164 int escaped_length = 0;
3165 int length = source->length();
3166 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003167 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003168 buffer->Reset(source);
3169 while (buffer->has_more()) {
3170 uint16_t character = buffer->GetNext();
3171 if (character >= 256) {
3172 escaped_length += 6;
3173 } else if (IsNotEscaped(character)) {
3174 escaped_length++;
3175 } else {
3176 escaped_length += 3;
3177 }
3178 // We don't allow strings that are longer than Smi range.
3179 if (!Smi::IsValid(escaped_length)) {
3180 Top::context()->mark_out_of_memory();
3181 return Failure::OutOfMemoryException();
3182 }
3183 }
3184 }
3185 // No length change implies no change. Return original string if no change.
3186 if (escaped_length == length) {
3187 return source;
3188 }
3189 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3190 if (o->IsFailure()) return o;
3191 String* destination = String::cast(o);
3192 int dest_position = 0;
3193
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003194 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003195 buffer->Rewind();
3196 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003197 uint16_t chr = buffer->GetNext();
3198 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003199 destination->Set(dest_position, '%');
3200 destination->Set(dest_position+1, 'u');
3201 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3202 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3203 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3204 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003205 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003206 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003207 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003208 dest_position++;
3209 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003210 destination->Set(dest_position, '%');
3211 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3212 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003213 dest_position += 3;
3214 }
3215 }
3216 return destination;
3217}
3218
3219
3220static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3221 static const signed char kHexValue['g'] = {
3222 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3223 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3224 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3225 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3226 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3227 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3228 -1, 10, 11, 12, 13, 14, 15 };
3229
3230 if (character1 > 'f') return -1;
3231 int hi = kHexValue[character1];
3232 if (hi == -1) return -1;
3233 if (character2 > 'f') return -1;
3234 int lo = kHexValue[character2];
3235 if (lo == -1) return -1;
3236 return (hi << 4) + lo;
3237}
3238
3239
ager@chromium.org870a0b62008-11-04 11:43:05 +00003240static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003241 int i,
3242 int length,
3243 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003244 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003245 int32_t hi = 0;
3246 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003247 if (character == '%' &&
3248 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003249 source->Get(i + 1) == 'u' &&
3250 (hi = TwoDigitHex(source->Get(i + 2),
3251 source->Get(i + 3))) != -1 &&
3252 (lo = TwoDigitHex(source->Get(i + 4),
3253 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003254 *step = 6;
3255 return (hi << 8) + lo;
3256 } else if (character == '%' &&
3257 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003258 (lo = TwoDigitHex(source->Get(i + 1),
3259 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003260 *step = 3;
3261 return lo;
3262 } else {
3263 *step = 1;
3264 return character;
3265 }
3266}
3267
3268
3269static Object* Runtime_URIUnescape(Arguments args) {
3270 NoHandleAllocation ha;
3271 ASSERT(args.length() == 1);
3272 CONVERT_CHECKED(String, source, args[0]);
3273
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003274 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003275
3276 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003277 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003278
3279 int unescaped_length = 0;
3280 for (int i = 0; i < length; unescaped_length++) {
3281 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003282 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003283 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003284 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003285 i += step;
3286 }
3287
3288 // No length change implies no change. Return original string if no change.
3289 if (unescaped_length == length)
3290 return source;
3291
3292 Object* o = ascii ?
3293 Heap::AllocateRawAsciiString(unescaped_length) :
3294 Heap::AllocateRawTwoByteString(unescaped_length);
3295 if (o->IsFailure()) return o;
3296 String* destination = String::cast(o);
3297
3298 int dest_position = 0;
3299 for (int i = 0; i < length; dest_position++) {
3300 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003301 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003302 i += step;
3303 }
3304 return destination;
3305}
3306
3307
3308static Object* Runtime_StringParseInt(Arguments args) {
3309 NoHandleAllocation ha;
3310
3311 CONVERT_CHECKED(String, s, args[0]);
3312 CONVERT_DOUBLE_CHECKED(n, args[1]);
3313 int radix = FastD2I(n);
3314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003315 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003316
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003317 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003318 int i;
3319
3320 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003321 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003322 if (i == len) return Heap::nan_value();
3323
3324 // Compute the sign (default to +).
3325 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003326 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003327 sign = -1;
3328 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003329 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003330 i++;
3331 }
3332
3333 // Compute the radix if 0.
3334 if (radix == 0) {
3335 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003336 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003337 radix = 8;
3338 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003339 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003340 if (c == 'x' || c == 'X') {
3341 radix = 16;
3342 i += 2;
3343 }
3344 }
3345 }
3346 } else if (radix == 16) {
3347 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003348 if (i + 1 < len && s->Get(i) == '0') {
3349 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003350 if (c == 'x' || c == 'X') i += 2;
3351 }
3352 }
3353
3354 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3355 double value;
3356 int end_index = StringToInt(s, i, radix, &value);
3357 if (end_index != i) {
3358 return Heap::NumberFromDouble(sign * value);
3359 }
3360 return Heap::nan_value();
3361}
3362
3363
3364static Object* Runtime_StringParseFloat(Arguments args) {
3365 NoHandleAllocation ha;
3366 CONVERT_CHECKED(String, str, args[0]);
3367
3368 // ECMA-262 section 15.1.2.3, empty string is NaN
3369 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3370
3371 // Create a number object from the value.
3372 return Heap::NumberFromDouble(value);
3373}
3374
3375
3376static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3377static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3378
3379
3380template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003381static Object* ConvertCaseHelper(String* s,
3382 int length,
3383 int input_string_length,
3384 unibrow::Mapping<Converter, 128>* mapping) {
3385 // We try this twice, once with the assumption that the result is no longer
3386 // than the input and, if that assumption breaks, again with the exact
3387 // length. This may not be pretty, but it is nicer than what was here before
3388 // and I hereby claim my vaffel-is.
3389 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003390 // Allocate the resulting string.
3391 //
3392 // NOTE: This assumes that the upper/lower case of an ascii
3393 // character is also ascii. This is currently the case, but it
3394 // might break in the future if we implement more context and locale
3395 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003396 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003397 ? Heap::AllocateRawAsciiString(length)
3398 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003399 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003400 String* result = String::cast(o);
3401 bool has_changed_character = false;
3402
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003403 // Convert all characters to upper case, assuming that they will fit
3404 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003405 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003406 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003407 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003408 // We can assume that the string is not empty
3409 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003410 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003411 bool has_next = buffer->has_more();
3412 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003413 int char_length = mapping->get(current, next, chars);
3414 if (char_length == 0) {
3415 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003416 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003417 i++;
3418 } else if (char_length == 1) {
3419 // Common case: converting the letter resulted in one character.
3420 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003421 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003422 has_changed_character = true;
3423 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003424 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003425 // We've assumed that the result would be as long as the
3426 // input but here is a character that converts to several
3427 // characters. No matter, we calculate the exact length
3428 // of the result and try the whole thing again.
3429 //
3430 // Note that this leaves room for optimization. We could just
3431 // memcpy what we already have to the result string. Also,
3432 // the result string is the last object allocated we could
3433 // "realloc" it and probably, in the vast majority of cases,
3434 // extend the existing string to be able to hold the full
3435 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003436 int next_length = 0;
3437 if (has_next) {
3438 next_length = mapping->get(next, 0, chars);
3439 if (next_length == 0) next_length = 1;
3440 }
3441 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003442 while (buffer->has_more()) {
3443 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003444 // NOTE: we use 0 as the next character here because, while
3445 // the next character may affect what a character converts to,
3446 // it does not in any case affect the length of what it convert
3447 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003448 int char_length = mapping->get(current, 0, chars);
3449 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003450 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003451 if (current_length > Smi::kMaxValue) {
3452 Top::context()->mark_out_of_memory();
3453 return Failure::OutOfMemoryException();
3454 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003455 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003456 // Try again with the real length.
3457 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003458 } else {
3459 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003460 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003461 i++;
3462 }
3463 has_changed_character = true;
3464 }
3465 current = next;
3466 }
3467 if (has_changed_character) {
3468 return result;
3469 } else {
3470 // If we didn't actually change anything in doing the conversion
3471 // we simple return the result and let the converted string
3472 // become garbage; there is no reason to keep two identical strings
3473 // alive.
3474 return s;
3475 }
3476}
3477
3478
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003479template <class Converter>
3480static Object* ConvertCase(Arguments args,
3481 unibrow::Mapping<Converter, 128>* mapping) {
3482 NoHandleAllocation ha;
3483
3484 CONVERT_CHECKED(String, s, args[0]);
3485 s->TryFlattenIfNotFlat();
3486
3487 int input_string_length = s->length();
3488 // Assume that the string is not empty; we need this assumption later
3489 if (input_string_length == 0) return s;
3490 int length = input_string_length;
3491
3492 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3493 if (answer->IsSmi()) {
3494 // Retry with correct length.
3495 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3496 }
3497 return answer; // This may be a failure.
3498}
3499
3500
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003501static Object* Runtime_StringToLowerCase(Arguments args) {
3502 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3503}
3504
3505
3506static Object* Runtime_StringToUpperCase(Arguments args) {
3507 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3508}
3509
3510
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003511bool Runtime::IsUpperCaseChar(uint16_t ch) {
3512 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3513 int char_length = to_upper_mapping.get(ch, 0, chars);
3514 return char_length == 0;
3515}
3516
3517
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003518static Object* Runtime_NumberToString(Arguments args) {
3519 NoHandleAllocation ha;
3520 ASSERT(args.length() == 1);
3521
3522 Object* number = args[0];
3523 RUNTIME_ASSERT(number->IsNumber());
3524
3525 Object* cached = Heap::GetNumberStringCache(number);
3526 if (cached != Heap::undefined_value()) {
3527 return cached;
3528 }
3529
3530 char arr[100];
3531 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3532 const char* str;
3533 if (number->IsSmi()) {
3534 int num = Smi::cast(number)->value();
3535 str = IntToCString(num, buffer);
3536 } else {
3537 double num = HeapNumber::cast(number)->value();
3538 str = DoubleToCString(num, buffer);
3539 }
3540 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3541
3542 if (!result->IsFailure()) {
3543 Heap::SetNumberStringCache(number, String::cast(result));
3544 }
3545 return result;
3546}
3547
3548
3549static Object* Runtime_NumberToInteger(Arguments args) {
3550 NoHandleAllocation ha;
3551 ASSERT(args.length() == 1);
3552
3553 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003554 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003555 CONVERT_DOUBLE_CHECKED(number, obj);
3556 return Heap::NumberFromDouble(DoubleToInteger(number));
3557}
3558
3559
3560static Object* Runtime_NumberToJSUint32(Arguments args) {
3561 NoHandleAllocation ha;
3562 ASSERT(args.length() == 1);
3563
3564 Object* obj = args[0];
3565 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3566 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3567 return Heap::NumberFromUint32(number);
3568}
3569
3570
3571static Object* Runtime_NumberToJSInt32(Arguments args) {
3572 NoHandleAllocation ha;
3573 ASSERT(args.length() == 1);
3574
3575 Object* obj = args[0];
3576 if (obj->IsSmi()) return obj;
3577 CONVERT_DOUBLE_CHECKED(number, obj);
3578 return Heap::NumberFromInt32(DoubleToInt32(number));
3579}
3580
3581
ager@chromium.org870a0b62008-11-04 11:43:05 +00003582// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3583// a small integer.
3584static Object* Runtime_NumberToSmi(Arguments args) {
3585 NoHandleAllocation ha;
3586 ASSERT(args.length() == 1);
3587
3588 Object* obj = args[0];
3589 if (obj->IsSmi()) {
3590 return obj;
3591 }
3592 if (obj->IsHeapNumber()) {
3593 double value = HeapNumber::cast(obj)->value();
3594 int int_value = FastD2I(value);
3595 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3596 return Smi::FromInt(int_value);
3597 }
3598 }
3599 return Heap::nan_value();
3600}
3601
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003602
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003603static Object* Runtime_NumberAdd(Arguments args) {
3604 NoHandleAllocation ha;
3605 ASSERT(args.length() == 2);
3606
3607 CONVERT_DOUBLE_CHECKED(x, args[0]);
3608 CONVERT_DOUBLE_CHECKED(y, args[1]);
3609 return Heap::AllocateHeapNumber(x + y);
3610}
3611
3612
3613static Object* Runtime_NumberSub(Arguments args) {
3614 NoHandleAllocation ha;
3615 ASSERT(args.length() == 2);
3616
3617 CONVERT_DOUBLE_CHECKED(x, args[0]);
3618 CONVERT_DOUBLE_CHECKED(y, args[1]);
3619 return Heap::AllocateHeapNumber(x - y);
3620}
3621
3622
3623static Object* Runtime_NumberMul(Arguments args) {
3624 NoHandleAllocation ha;
3625 ASSERT(args.length() == 2);
3626
3627 CONVERT_DOUBLE_CHECKED(x, args[0]);
3628 CONVERT_DOUBLE_CHECKED(y, args[1]);
3629 return Heap::AllocateHeapNumber(x * y);
3630}
3631
3632
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003633static Object* Runtime_NumberUnaryMinus(Arguments args) {
3634 NoHandleAllocation ha;
3635 ASSERT(args.length() == 1);
3636
3637 CONVERT_DOUBLE_CHECKED(x, args[0]);
3638 return Heap::AllocateHeapNumber(-x);
3639}
3640
3641
3642static Object* Runtime_NumberDiv(Arguments args) {
3643 NoHandleAllocation ha;
3644 ASSERT(args.length() == 2);
3645
3646 CONVERT_DOUBLE_CHECKED(x, args[0]);
3647 CONVERT_DOUBLE_CHECKED(y, args[1]);
3648 return Heap::NewNumberFromDouble(x / y);
3649}
3650
3651
3652static Object* Runtime_NumberMod(Arguments args) {
3653 NoHandleAllocation ha;
3654 ASSERT(args.length() == 2);
3655
3656 CONVERT_DOUBLE_CHECKED(x, args[0]);
3657 CONVERT_DOUBLE_CHECKED(y, args[1]);
3658
3659#ifdef WIN32
3660 // Workaround MS fmod bugs. ECMA-262 says:
3661 // dividend is finite and divisor is an infinity => result equals dividend
3662 // dividend is a zero and divisor is nonzero finite => result equals dividend
3663 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3664 !(x == 0 && (y != 0 && isfinite(y))))
3665#endif
3666 x = fmod(x, y);
3667 // NewNumberFromDouble may return a Smi instead of a Number object
3668 return Heap::NewNumberFromDouble(x);
3669}
3670
3671
3672static Object* Runtime_StringAdd(Arguments args) {
3673 NoHandleAllocation ha;
3674 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003675 CONVERT_CHECKED(String, str1, args[0]);
3676 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003677 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003678}
3679
3680
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003681template<typename sinkchar>
3682static inline void StringBuilderConcatHelper(String* special,
3683 sinkchar* sink,
3684 FixedArray* fixed_array,
3685 int array_length) {
3686 int position = 0;
3687 for (int i = 0; i < array_length; i++) {
3688 Object* element = fixed_array->get(i);
3689 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003690 int encoded_slice = Smi::cast(element)->value();
3691 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3692 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003693 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003694 sink + position,
3695 pos,
3696 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003697 position += len;
3698 } else {
3699 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003700 int element_length = string->length();
3701 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003702 position += element_length;
3703 }
3704 }
3705}
3706
3707
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003708static Object* Runtime_StringBuilderConcat(Arguments args) {
3709 NoHandleAllocation ha;
3710 ASSERT(args.length() == 2);
3711 CONVERT_CHECKED(JSArray, array, args[0]);
3712 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003713 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003714 Object* smi_array_length = array->length();
3715 if (!smi_array_length->IsSmi()) {
3716 Top::context()->mark_out_of_memory();
3717 return Failure::OutOfMemoryException();
3718 }
3719 int array_length = Smi::cast(smi_array_length)->value();
3720 if (!array->HasFastElements()) {
3721 return Top::Throw(Heap::illegal_argument_symbol());
3722 }
3723 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003724 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003725 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003726 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003727
3728 if (array_length == 0) {
3729 return Heap::empty_string();
3730 } else if (array_length == 1) {
3731 Object* first = fixed_array->get(0);
3732 if (first->IsString()) return first;
3733 }
3734
ager@chromium.org5ec48922009-05-05 07:25:34 +00003735 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003736 int position = 0;
3737 for (int i = 0; i < array_length; i++) {
3738 Object* elt = fixed_array->get(i);
3739 if (elt->IsSmi()) {
3740 int len = Smi::cast(elt)->value();
3741 int pos = len >> 11;
3742 len &= 0x7ff;
3743 if (pos + len > special_length) {
3744 return Top::Throw(Heap::illegal_argument_symbol());
3745 }
3746 position += len;
3747 } else if (elt->IsString()) {
3748 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003749 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003750 if (!Smi::IsValid(element_length + position)) {
3751 Top::context()->mark_out_of_memory();
3752 return Failure::OutOfMemoryException();
3753 }
3754 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003755 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003756 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003757 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003758 } else {
3759 return Top::Throw(Heap::illegal_argument_symbol());
3760 }
3761 }
3762
3763 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003764 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003765
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003766 if (ascii) {
3767 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003768 if (object->IsFailure()) return object;
3769 SeqAsciiString* answer = SeqAsciiString::cast(object);
3770 StringBuilderConcatHelper(special,
3771 answer->GetChars(),
3772 fixed_array,
3773 array_length);
3774 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003775 } else {
3776 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003777 if (object->IsFailure()) return object;
3778 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3779 StringBuilderConcatHelper(special,
3780 answer->GetChars(),
3781 fixed_array,
3782 array_length);
3783 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003784 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003785}
3786
3787
3788static Object* Runtime_NumberOr(Arguments args) {
3789 NoHandleAllocation ha;
3790 ASSERT(args.length() == 2);
3791
3792 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3793 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3794 return Heap::NumberFromInt32(x | y);
3795}
3796
3797
3798static Object* Runtime_NumberAnd(Arguments args) {
3799 NoHandleAllocation ha;
3800 ASSERT(args.length() == 2);
3801
3802 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3803 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3804 return Heap::NumberFromInt32(x & y);
3805}
3806
3807
3808static Object* Runtime_NumberXor(Arguments args) {
3809 NoHandleAllocation ha;
3810 ASSERT(args.length() == 2);
3811
3812 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3813 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3814 return Heap::NumberFromInt32(x ^ y);
3815}
3816
3817
3818static Object* Runtime_NumberNot(Arguments args) {
3819 NoHandleAllocation ha;
3820 ASSERT(args.length() == 1);
3821
3822 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3823 return Heap::NumberFromInt32(~x);
3824}
3825
3826
3827static Object* Runtime_NumberShl(Arguments args) {
3828 NoHandleAllocation ha;
3829 ASSERT(args.length() == 2);
3830
3831 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3832 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3833 return Heap::NumberFromInt32(x << (y & 0x1f));
3834}
3835
3836
3837static Object* Runtime_NumberShr(Arguments args) {
3838 NoHandleAllocation ha;
3839 ASSERT(args.length() == 2);
3840
3841 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3842 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3843 return Heap::NumberFromUint32(x >> (y & 0x1f));
3844}
3845
3846
3847static Object* Runtime_NumberSar(Arguments args) {
3848 NoHandleAllocation ha;
3849 ASSERT(args.length() == 2);
3850
3851 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3852 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3853 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3854}
3855
3856
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003857static Object* Runtime_NumberEquals(Arguments args) {
3858 NoHandleAllocation ha;
3859 ASSERT(args.length() == 2);
3860
3861 CONVERT_DOUBLE_CHECKED(x, args[0]);
3862 CONVERT_DOUBLE_CHECKED(y, args[1]);
3863 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3864 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3865 if (x == y) return Smi::FromInt(EQUAL);
3866 Object* result;
3867 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3868 result = Smi::FromInt(EQUAL);
3869 } else {
3870 result = Smi::FromInt(NOT_EQUAL);
3871 }
3872 return result;
3873}
3874
3875
3876static Object* Runtime_StringEquals(Arguments args) {
3877 NoHandleAllocation ha;
3878 ASSERT(args.length() == 2);
3879
3880 CONVERT_CHECKED(String, x, args[0]);
3881 CONVERT_CHECKED(String, y, args[1]);
3882
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003883 bool not_equal = !x->Equals(y);
3884 // This is slightly convoluted because the value that signifies
3885 // equality is 0 and inequality is 1 so we have to negate the result
3886 // from String::Equals.
3887 ASSERT(not_equal == 0 || not_equal == 1);
3888 STATIC_CHECK(EQUAL == 0);
3889 STATIC_CHECK(NOT_EQUAL == 1);
3890 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003891}
3892
3893
3894static Object* Runtime_NumberCompare(Arguments args) {
3895 NoHandleAllocation ha;
3896 ASSERT(args.length() == 3);
3897
3898 CONVERT_DOUBLE_CHECKED(x, args[0]);
3899 CONVERT_DOUBLE_CHECKED(y, args[1]);
3900 if (isnan(x) || isnan(y)) return args[2];
3901 if (x == y) return Smi::FromInt(EQUAL);
3902 if (isless(x, y)) return Smi::FromInt(LESS);
3903 return Smi::FromInt(GREATER);
3904}
3905
3906
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003907// Compare two Smis as if they were converted to strings and then
3908// compared lexicographically.
3909static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3910 NoHandleAllocation ha;
3911 ASSERT(args.length() == 2);
3912
3913 // Arrays for the individual characters of the two Smis. Smis are
3914 // 31 bit integers and 10 decimal digits are therefore enough.
3915 static int x_elms[10];
3916 static int y_elms[10];
3917
3918 // Extract the integer values from the Smis.
3919 CONVERT_CHECKED(Smi, x, args[0]);
3920 CONVERT_CHECKED(Smi, y, args[1]);
3921 int x_value = x->value();
3922 int y_value = y->value();
3923
3924 // If the integers are equal so are the string representations.
3925 if (x_value == y_value) return Smi::FromInt(EQUAL);
3926
3927 // If one of the integers are zero the normal integer order is the
3928 // same as the lexicographic order of the string representations.
3929 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3930
ager@chromium.org32912102009-01-16 10:38:43 +00003931 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003932 // smallest because the char code of '-' is less than the char code
3933 // of any digit. Otherwise, we make both values positive.
3934 if (x_value < 0 || y_value < 0) {
3935 if (y_value >= 0) return Smi::FromInt(LESS);
3936 if (x_value >= 0) return Smi::FromInt(GREATER);
3937 x_value = -x_value;
3938 y_value = -y_value;
3939 }
3940
3941 // Convert the integers to arrays of their decimal digits.
3942 int x_index = 0;
3943 int y_index = 0;
3944 while (x_value > 0) {
3945 x_elms[x_index++] = x_value % 10;
3946 x_value /= 10;
3947 }
3948 while (y_value > 0) {
3949 y_elms[y_index++] = y_value % 10;
3950 y_value /= 10;
3951 }
3952
3953 // Loop through the arrays of decimal digits finding the first place
3954 // where they differ.
3955 while (--x_index >= 0 && --y_index >= 0) {
3956 int diff = x_elms[x_index] - y_elms[y_index];
3957 if (diff != 0) return Smi::FromInt(diff);
3958 }
3959
3960 // If one array is a suffix of the other array, the longest array is
3961 // the representation of the largest of the Smis in the
3962 // lexicographic ordering.
3963 return Smi::FromInt(x_index - y_index);
3964}
3965
3966
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003967static Object* Runtime_StringCompare(Arguments args) {
3968 NoHandleAllocation ha;
3969 ASSERT(args.length() == 2);
3970
3971 CONVERT_CHECKED(String, x, args[0]);
3972 CONVERT_CHECKED(String, y, args[1]);
3973
3974 // A few fast case tests before we flatten.
3975 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003976 if (y->length() == 0) {
3977 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003978 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003979 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003980 return Smi::FromInt(LESS);
3981 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003982
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003983 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003984 if (d < 0) return Smi::FromInt(LESS);
3985 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003986
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003987 x->TryFlattenIfNotFlat();
3988 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003989
3990 static StringInputBuffer bufx;
3991 static StringInputBuffer bufy;
3992 bufx.Reset(x);
3993 bufy.Reset(y);
3994 while (bufx.has_more() && bufy.has_more()) {
3995 int d = bufx.GetNext() - bufy.GetNext();
3996 if (d < 0) return Smi::FromInt(LESS);
3997 else if (d > 0) return Smi::FromInt(GREATER);
3998 }
3999
4000 // x is (non-trivial) prefix of y:
4001 if (bufy.has_more()) return Smi::FromInt(LESS);
4002 // y is prefix of x:
4003 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4004}
4005
4006
4007static Object* Runtime_Math_abs(Arguments args) {
4008 NoHandleAllocation ha;
4009 ASSERT(args.length() == 1);
4010
4011 CONVERT_DOUBLE_CHECKED(x, args[0]);
4012 return Heap::AllocateHeapNumber(fabs(x));
4013}
4014
4015
4016static Object* Runtime_Math_acos(Arguments args) {
4017 NoHandleAllocation ha;
4018 ASSERT(args.length() == 1);
4019
4020 CONVERT_DOUBLE_CHECKED(x, args[0]);
4021 return Heap::AllocateHeapNumber(acos(x));
4022}
4023
4024
4025static Object* Runtime_Math_asin(Arguments args) {
4026 NoHandleAllocation ha;
4027 ASSERT(args.length() == 1);
4028
4029 CONVERT_DOUBLE_CHECKED(x, args[0]);
4030 return Heap::AllocateHeapNumber(asin(x));
4031}
4032
4033
4034static Object* Runtime_Math_atan(Arguments args) {
4035 NoHandleAllocation ha;
4036 ASSERT(args.length() == 1);
4037
4038 CONVERT_DOUBLE_CHECKED(x, args[0]);
4039 return Heap::AllocateHeapNumber(atan(x));
4040}
4041
4042
4043static Object* Runtime_Math_atan2(Arguments args) {
4044 NoHandleAllocation ha;
4045 ASSERT(args.length() == 2);
4046
4047 CONVERT_DOUBLE_CHECKED(x, args[0]);
4048 CONVERT_DOUBLE_CHECKED(y, args[1]);
4049 double result;
4050 if (isinf(x) && isinf(y)) {
4051 // Make sure that the result in case of two infinite arguments
4052 // is a multiple of Pi / 4. The sign of the result is determined
4053 // by the first argument (x) and the sign of the second argument
4054 // determines the multiplier: one or three.
4055 static double kPiDividedBy4 = 0.78539816339744830962;
4056 int multiplier = (x < 0) ? -1 : 1;
4057 if (y < 0) multiplier *= 3;
4058 result = multiplier * kPiDividedBy4;
4059 } else {
4060 result = atan2(x, y);
4061 }
4062 return Heap::AllocateHeapNumber(result);
4063}
4064
4065
4066static Object* Runtime_Math_ceil(Arguments args) {
4067 NoHandleAllocation ha;
4068 ASSERT(args.length() == 1);
4069
4070 CONVERT_DOUBLE_CHECKED(x, args[0]);
4071 return Heap::NumberFromDouble(ceiling(x));
4072}
4073
4074
4075static Object* Runtime_Math_cos(Arguments args) {
4076 NoHandleAllocation ha;
4077 ASSERT(args.length() == 1);
4078
4079 CONVERT_DOUBLE_CHECKED(x, args[0]);
4080 return Heap::AllocateHeapNumber(cos(x));
4081}
4082
4083
4084static Object* Runtime_Math_exp(Arguments args) {
4085 NoHandleAllocation ha;
4086 ASSERT(args.length() == 1);
4087
4088 CONVERT_DOUBLE_CHECKED(x, args[0]);
4089 return Heap::AllocateHeapNumber(exp(x));
4090}
4091
4092
4093static Object* Runtime_Math_floor(Arguments args) {
4094 NoHandleAllocation ha;
4095 ASSERT(args.length() == 1);
4096
4097 CONVERT_DOUBLE_CHECKED(x, args[0]);
4098 return Heap::NumberFromDouble(floor(x));
4099}
4100
4101
4102static Object* Runtime_Math_log(Arguments args) {
4103 NoHandleAllocation ha;
4104 ASSERT(args.length() == 1);
4105
4106 CONVERT_DOUBLE_CHECKED(x, args[0]);
4107 return Heap::AllocateHeapNumber(log(x));
4108}
4109
4110
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004111// Helper function to compute x^y, where y is known to be an
4112// integer. Uses binary decomposition to limit the number of
4113// multiplications; see the discussion in "Hacker's Delight" by Henry
4114// S. Warren, Jr., figure 11-6, page 213.
4115static double powi(double x, int y) {
4116 ASSERT(y != kMinInt);
4117 unsigned n = (y < 0) ? -y : y;
4118 double m = x;
4119 double p = 1;
4120 while (true) {
4121 if ((n & 1) != 0) p *= m;
4122 n >>= 1;
4123 if (n == 0) {
4124 if (y < 0) {
4125 // Unfortunately, we have to be careful when p has reached
4126 // infinity in the computation, because sometimes the higher
4127 // internal precision in the pow() implementation would have
4128 // given us a finite p. This happens very rarely.
4129 double result = 1.0 / p;
4130 return (result == 0 && isinf(p))
4131 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4132 : result;
4133 } else {
4134 return p;
4135 }
4136 }
4137 m *= m;
4138 }
4139}
4140
4141
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004142static Object* Runtime_Math_pow(Arguments args) {
4143 NoHandleAllocation ha;
4144 ASSERT(args.length() == 2);
4145
4146 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004147
4148 // If the second argument is a smi, it is much faster to call the
4149 // custom powi() function than the generic pow().
4150 if (args[1]->IsSmi()) {
4151 int y = Smi::cast(args[1])->value();
4152 return Heap::AllocateHeapNumber(powi(x, y));
4153 }
4154
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004155 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004156
4157 if (!isinf(x)) {
4158 if (y == 0.5) {
4159 // It's not uncommon to use Math.pow(x, 0.5) to compute the
4160 // square root of a number. To speed up such computations, we
4161 // explictly check for this case and use the sqrt() function
4162 // which is faster than pow().
4163 return Heap::AllocateHeapNumber(sqrt(x));
4164 } else if (y == -0.5) {
4165 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4166 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
4167 }
4168 }
4169
4170 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004171 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004172 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4173 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004174 } else {
4175 return Heap::AllocateHeapNumber(pow(x, y));
4176 }
4177}
4178
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004179
4180static Object* Runtime_Math_round(Arguments args) {
4181 NoHandleAllocation ha;
4182 ASSERT(args.length() == 1);
4183
4184 CONVERT_DOUBLE_CHECKED(x, args[0]);
4185 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4186 return Heap::NumberFromDouble(floor(x + 0.5));
4187}
4188
4189
4190static Object* Runtime_Math_sin(Arguments args) {
4191 NoHandleAllocation ha;
4192 ASSERT(args.length() == 1);
4193
4194 CONVERT_DOUBLE_CHECKED(x, args[0]);
4195 return Heap::AllocateHeapNumber(sin(x));
4196}
4197
4198
4199static Object* Runtime_Math_sqrt(Arguments args) {
4200 NoHandleAllocation ha;
4201 ASSERT(args.length() == 1);
4202
4203 CONVERT_DOUBLE_CHECKED(x, args[0]);
4204 return Heap::AllocateHeapNumber(sqrt(x));
4205}
4206
4207
4208static Object* Runtime_Math_tan(Arguments args) {
4209 NoHandleAllocation ha;
4210 ASSERT(args.length() == 1);
4211
4212 CONVERT_DOUBLE_CHECKED(x, args[0]);
4213 return Heap::AllocateHeapNumber(tan(x));
4214}
4215
4216
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004217// The NewArguments function is only used when constructing the
4218// arguments array when calling non-functions from JavaScript in
4219// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004220static Object* Runtime_NewArguments(Arguments args) {
4221 NoHandleAllocation ha;
4222 ASSERT(args.length() == 1);
4223
4224 // ECMA-262, 3rd., 10.1.8, p.39
4225 CONVERT_CHECKED(JSFunction, callee, args[0]);
4226
4227 // Compute the frame holding the arguments.
4228 JavaScriptFrameIterator it;
4229 it.AdvanceToArgumentsFrame();
4230 JavaScriptFrame* frame = it.frame();
4231
4232 const int length = frame->GetProvidedParametersCount();
4233 Object* result = Heap::AllocateArgumentsObject(callee, length);
4234 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004235 if (length > 0) {
4236 Object* obj = Heap::AllocateFixedArray(length);
4237 if (obj->IsFailure()) return obj;
4238 FixedArray* array = FixedArray::cast(obj);
4239 ASSERT(array->length() == length);
4240 WriteBarrierMode mode = array->GetWriteBarrierMode();
4241 for (int i = 0; i < length; i++) {
4242 array->set(i, frame->GetParameter(i), mode);
4243 }
4244 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004245 }
4246 return result;
4247}
4248
4249
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004250static Object* Runtime_NewArgumentsFast(Arguments args) {
4251 NoHandleAllocation ha;
4252 ASSERT(args.length() == 3);
4253
4254 JSFunction* callee = JSFunction::cast(args[0]);
4255 Object** parameters = reinterpret_cast<Object**>(args[1]);
4256 const int length = Smi::cast(args[2])->value();
4257
4258 Object* result = Heap::AllocateArgumentsObject(callee, length);
4259 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004260 ASSERT(Heap::InNewSpace(result));
4261
4262 // Allocate the elements if needed.
4263 if (length > 0) {
4264 // Allocate the fixed array.
4265 Object* obj = Heap::AllocateRawFixedArray(length);
4266 if (obj->IsFailure()) return obj;
4267 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4268 FixedArray* array = FixedArray::cast(obj);
4269 array->set_length(length);
4270 WriteBarrierMode mode = array->GetWriteBarrierMode();
4271 for (int i = 0; i < length; i++) {
4272 array->set(i, *--parameters, mode);
4273 }
4274 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4275 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004276 }
4277 return result;
4278}
4279
4280
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004281static Object* Runtime_NewClosure(Arguments args) {
4282 HandleScope scope;
4283 ASSERT(args.length() == 2);
4284 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4285 CONVERT_ARG_CHECKED(Context, context, 1);
4286
4287 Handle<JSFunction> result =
4288 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4289 return *result;
4290}
4291
4292
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004293static Handle<Code> ComputeConstructStub(Handle<Map> map) {
4294 // TODO(385): Change this to create a construct stub specialized for
4295 // the given map to make allocation of simple objects - and maybe
4296 // arrays - much faster.
4297 return Handle<Code>(Builtins::builtin(Builtins::JSConstructStubGeneric));
4298}
4299
4300
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004301static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004302 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004303 ASSERT(args.length() == 1);
4304
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004305 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004306
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004307 // If the constructor isn't a proper function we throw a type error.
4308 if (!constructor->IsJSFunction()) {
4309 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4310 Handle<Object> type_error =
4311 Factory::NewTypeError("not_constructor", arguments);
4312 return Top::Throw(*type_error);
4313 }
4314
4315 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004316#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004317 // Handle stepping into constructors if step into is active.
4318 if (Debug::StepInActive()) {
4319 Debug::HandleStepIn(function, 0, true);
4320 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004321#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004322
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004323 if (function->has_initial_map()) {
4324 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004325 // The 'Function' function ignores the receiver object when
4326 // called using 'new' and creates a new JSFunction object that
4327 // is returned. The receiver object is only used for error
4328 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004329 // JSFunction. Factory::NewJSObject() should not be used to
4330 // allocate JSFunctions since it does not properly initialize
4331 // the shared part of the function. Since the receiver is
4332 // ignored anyway, we use the global object as the receiver
4333 // instead of a new JSFunction object. This way, errors are
4334 // reported the same way whether or not 'Function' is called
4335 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004336 return Top::context()->global();
4337 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004338 }
4339
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004340 bool first_allocation = !function->has_initial_map();
4341 Handle<JSObject> result = Factory::NewJSObject(function);
4342 if (first_allocation) {
4343 Handle<Map> map = Handle<Map>(function->initial_map());
4344 Handle<Code> stub = ComputeConstructStub(map);
4345 function->shared()->set_construct_stub(*stub);
4346 }
4347 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004348}
4349
4350
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004351static Object* Runtime_LazyCompile(Arguments args) {
4352 HandleScope scope;
4353 ASSERT(args.length() == 1);
4354
4355 Handle<JSFunction> function = args.at<JSFunction>(0);
4356#ifdef DEBUG
4357 if (FLAG_trace_lazy) {
4358 PrintF("[lazy: ");
4359 function->shared()->name()->Print();
4360 PrintF("]\n");
4361 }
4362#endif
4363
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004364 // Compile the target function. Here we compile using CompileLazyInLoop in
4365 // order to get the optimized version. This helps code like delta-blue
4366 // that calls performance-critical routines through constructors. A
4367 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4368 // direct call. Since the in-loop tracking takes place through CallICs
4369 // this means that things called through constructors are never known to
4370 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004371 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004372 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004373 return Failure::Exception();
4374 }
4375
4376 return function->code();
4377}
4378
4379
4380static Object* Runtime_GetCalledFunction(Arguments args) {
4381 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004382 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004383 StackFrameIterator it;
4384 // Get past the JS-to-C exit frame.
4385 ASSERT(it.frame()->is_exit());
4386 it.Advance();
4387 // Get past the CALL_NON_FUNCTION activation frame.
4388 ASSERT(it.frame()->is_java_script());
4389 it.Advance();
4390 // Argument adaptor frames do not copy the function; we have to skip
4391 // past them to get to the real calling frame.
4392 if (it.frame()->is_arguments_adaptor()) it.Advance();
4393 // Get the function from the top of the expression stack of the
4394 // calling frame.
4395 StandardFrame* frame = StandardFrame::cast(it.frame());
4396 int index = frame->ComputeExpressionsCount() - 1;
4397 Object* result = frame->GetExpression(index);
4398 return result;
4399}
4400
4401
4402static Object* Runtime_GetFunctionDelegate(Arguments args) {
4403 HandleScope scope;
4404 ASSERT(args.length() == 1);
4405 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4406 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4407}
4408
4409
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004410static Object* Runtime_GetConstructorDelegate(Arguments args) {
4411 HandleScope scope;
4412 ASSERT(args.length() == 1);
4413 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4414 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4415}
4416
4417
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004418static Object* Runtime_NewContext(Arguments args) {
4419 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004420 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004421
kasper.lund7276f142008-07-30 08:49:36 +00004422 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004423 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4424 Object* result = Heap::AllocateFunctionContext(length, function);
4425 if (result->IsFailure()) return result;
4426
4427 Top::set_context(Context::cast(result));
4428
kasper.lund7276f142008-07-30 08:49:36 +00004429 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004430}
4431
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004432static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004433 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004434 Object* js_object = object;
4435 if (!js_object->IsJSObject()) {
4436 js_object = js_object->ToObject();
4437 if (js_object->IsFailure()) {
4438 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004439 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004440 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004441 Handle<Object> result =
4442 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4443 return Top::Throw(*result);
4444 }
4445 }
4446
4447 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004448 Heap::AllocateWithContext(Top::context(),
4449 JSObject::cast(js_object),
4450 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004451 if (result->IsFailure()) return result;
4452
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004453 Context* context = Context::cast(result);
4454 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004455
kasper.lund7276f142008-07-30 08:49:36 +00004456 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004457}
4458
4459
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004460static Object* Runtime_PushContext(Arguments args) {
4461 NoHandleAllocation ha;
4462 ASSERT(args.length() == 1);
4463 return PushContextHelper(args[0], false);
4464}
4465
4466
4467static Object* Runtime_PushCatchContext(Arguments args) {
4468 NoHandleAllocation ha;
4469 ASSERT(args.length() == 1);
4470 return PushContextHelper(args[0], true);
4471}
4472
4473
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004474static Object* Runtime_LookupContext(Arguments args) {
4475 HandleScope scope;
4476 ASSERT(args.length() == 2);
4477
4478 CONVERT_ARG_CHECKED(Context, context, 0);
4479 CONVERT_ARG_CHECKED(String, name, 1);
4480
4481 int index;
4482 PropertyAttributes attributes;
4483 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004484 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004485 context->Lookup(name, flags, &index, &attributes);
4486
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004487 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004488 ASSERT(holder->IsJSObject());
4489 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004490 }
4491
4492 // No intermediate context found. Use global object by default.
4493 return Top::context()->global();
4494}
4495
4496
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004497// A mechanism to return pairs of Object*'s. This is somewhat
4498// compiler-dependent as it assumes that a 64-bit value (a long long)
4499// is returned via two registers (edx:eax on ia32). Both the ia32 and
4500// arm platform support this; it is mostly an issue of "coaxing" the
4501// compiler to do the right thing.
4502//
4503// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004504#ifdef V8_HOST_ARCH_64_BIT
4505// Tested with GCC, not with MSVC.
4506struct ObjectPair {
4507 Object* x;
4508 Object* y;
4509};
4510static inline ObjectPair MakePair(Object* x, Object* y) {
4511 ObjectPair result = {x, y};
4512 return result; // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
4513}
4514#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004515typedef uint64_t ObjectPair;
4516static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004517 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004518 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004519}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004520#endif
4521
4522
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004523
4524
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004525static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004526 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4527 USE(attributes);
4528 return x->IsTheHole() ? Heap::undefined_value() : x;
4529}
4530
4531
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004532static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4533 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004534 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004535 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004536 JSFunction* context_extension_function =
4537 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004538 // If the holder isn't a context extension object, we just return it
4539 // as the receiver. This allows arguments objects to be used as
4540 // receivers, but only if they are put in the context scope chain
4541 // explicitly via a with-statement.
4542 Object* constructor = holder->map()->constructor();
4543 if (constructor != context_extension_function) return holder;
4544 // Fall back to using the global object as the receiver if the
4545 // property turns out to be a local variable allocated in a context
4546 // extension object - introduced via eval.
4547 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004548}
4549
4550
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004551static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004552 HandleScope scope;
4553 ASSERT(args.length() == 2);
4554
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004555 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004556 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004557 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004558 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004559 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004560
4561 int index;
4562 PropertyAttributes attributes;
4563 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004564 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004565 context->Lookup(name, flags, &index, &attributes);
4566
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004567 // If the index is non-negative, the slot has been found in a local
4568 // variable or a parameter. Read it from the context object or the
4569 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004570 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004571 // If the "property" we were looking for is a local variable or an
4572 // argument in a context, the receiver is the global object; see
4573 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4574 JSObject* receiver = Top::context()->global()->global_receiver();
4575 Object* value = (holder->IsContext())
4576 ? Context::cast(*holder)->get(index)
4577 : JSObject::cast(*holder)->GetElement(index);
4578 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004579 }
4580
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004581 // If the holder is found, we read the property from it.
4582 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004583 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004584 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004585 JSObject* receiver;
4586 if (object->IsGlobalObject()) {
4587 receiver = GlobalObject::cast(object)->global_receiver();
4588 } else if (context->is_exception_holder(*holder)) {
4589 receiver = Top::context()->global()->global_receiver();
4590 } else {
4591 receiver = ComputeReceiverForNonGlobal(object);
4592 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004593 // No need to unhole the value here. This is taken care of by the
4594 // GetProperty function.
4595 Object* value = object->GetProperty(*name);
4596 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004597 }
4598
4599 if (throw_error) {
4600 // The property doesn't exist - throw exception.
4601 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004602 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004603 return MakePair(Top::Throw(*reference_error), NULL);
4604 } else {
4605 // The property doesn't exist - return undefined
4606 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4607 }
4608}
4609
4610
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004611static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004612 return LoadContextSlotHelper(args, true);
4613}
4614
4615
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004616static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004617 return LoadContextSlotHelper(args, false);
4618}
4619
4620
4621static Object* Runtime_StoreContextSlot(Arguments args) {
4622 HandleScope scope;
4623 ASSERT(args.length() == 3);
4624
4625 Handle<Object> value(args[0]);
4626 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004627 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004628
4629 int index;
4630 PropertyAttributes attributes;
4631 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004632 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004633 context->Lookup(name, flags, &index, &attributes);
4634
4635 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004636 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004637 // Ignore if read_only variable.
4638 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004639 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004640 }
4641 } else {
4642 ASSERT((attributes & READ_ONLY) == 0);
4643 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004644 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004645 USE(result);
4646 ASSERT(!result->IsFailure());
4647 }
4648 return *value;
4649 }
4650
4651 // Slow case: The property is not in a FixedArray context.
4652 // It is either in an JSObject extension context or it was not found.
4653 Handle<JSObject> context_ext;
4654
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004655 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004656 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004657 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004658 } else {
4659 // The property was not found. It needs to be stored in the global context.
4660 ASSERT(attributes == ABSENT);
4661 attributes = NONE;
4662 context_ext = Handle<JSObject>(Top::context()->global());
4663 }
4664
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004665 // Set the property, but ignore if read_only variable on the context
4666 // extension object itself.
4667 if ((attributes & READ_ONLY) == 0 ||
4668 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004669 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4670 if (set.is_null()) {
4671 // Failure::Exception is converted to a null handle in the
4672 // handle-based methods such as SetProperty. We therefore need
4673 // to convert null handles back to exceptions.
4674 ASSERT(Top::has_pending_exception());
4675 return Failure::Exception();
4676 }
4677 }
4678 return *value;
4679}
4680
4681
4682static Object* Runtime_Throw(Arguments args) {
4683 HandleScope scope;
4684 ASSERT(args.length() == 1);
4685
4686 return Top::Throw(args[0]);
4687}
4688
4689
4690static Object* Runtime_ReThrow(Arguments args) {
4691 HandleScope scope;
4692 ASSERT(args.length() == 1);
4693
4694 return Top::ReThrow(args[0]);
4695}
4696
4697
4698static Object* Runtime_ThrowReferenceError(Arguments args) {
4699 HandleScope scope;
4700 ASSERT(args.length() == 1);
4701
4702 Handle<Object> name(args[0]);
4703 Handle<Object> reference_error =
4704 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4705 return Top::Throw(*reference_error);
4706}
4707
4708
4709static Object* Runtime_StackOverflow(Arguments args) {
4710 NoHandleAllocation na;
4711 return Top::StackOverflow();
4712}
4713
4714
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004715static Object* Runtime_StackGuard(Arguments args) {
4716 ASSERT(args.length() == 1);
4717
4718 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004719 if (StackGuard::IsStackOverflow()) {
4720 return Runtime_StackOverflow(args);
4721 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004722
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004723 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004724}
4725
4726
4727// NOTE: These PrintXXX functions are defined for all builds (not just
4728// DEBUG builds) because we may want to be able to trace function
4729// calls in all modes.
4730static void PrintString(String* str) {
4731 // not uncommon to have empty strings
4732 if (str->length() > 0) {
4733 SmartPointer<char> s =
4734 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4735 PrintF("%s", *s);
4736 }
4737}
4738
4739
4740static void PrintObject(Object* obj) {
4741 if (obj->IsSmi()) {
4742 PrintF("%d", Smi::cast(obj)->value());
4743 } else if (obj->IsString() || obj->IsSymbol()) {
4744 PrintString(String::cast(obj));
4745 } else if (obj->IsNumber()) {
4746 PrintF("%g", obj->Number());
4747 } else if (obj->IsFailure()) {
4748 PrintF("<failure>");
4749 } else if (obj->IsUndefined()) {
4750 PrintF("<undefined>");
4751 } else if (obj->IsNull()) {
4752 PrintF("<null>");
4753 } else if (obj->IsTrue()) {
4754 PrintF("<true>");
4755 } else if (obj->IsFalse()) {
4756 PrintF("<false>");
4757 } else {
4758 PrintF("%p", obj);
4759 }
4760}
4761
4762
4763static int StackSize() {
4764 int n = 0;
4765 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4766 return n;
4767}
4768
4769
4770static void PrintTransition(Object* result) {
4771 // indentation
4772 { const int nmax = 80;
4773 int n = StackSize();
4774 if (n <= nmax)
4775 PrintF("%4d:%*s", n, n, "");
4776 else
4777 PrintF("%4d:%*s", n, nmax, "...");
4778 }
4779
4780 if (result == NULL) {
4781 // constructor calls
4782 JavaScriptFrameIterator it;
4783 JavaScriptFrame* frame = it.frame();
4784 if (frame->IsConstructor()) PrintF("new ");
4785 // function name
4786 Object* fun = frame->function();
4787 if (fun->IsJSFunction()) {
4788 PrintObject(JSFunction::cast(fun)->shared()->name());
4789 } else {
4790 PrintObject(fun);
4791 }
4792 // function arguments
4793 // (we are intentionally only printing the actually
4794 // supplied parameters, not all parameters required)
4795 PrintF("(this=");
4796 PrintObject(frame->receiver());
4797 const int length = frame->GetProvidedParametersCount();
4798 for (int i = 0; i < length; i++) {
4799 PrintF(", ");
4800 PrintObject(frame->GetParameter(i));
4801 }
4802 PrintF(") {\n");
4803
4804 } else {
4805 // function result
4806 PrintF("} -> ");
4807 PrintObject(result);
4808 PrintF("\n");
4809 }
4810}
4811
4812
4813static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004814 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004815 NoHandleAllocation ha;
4816 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004817 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004818}
4819
4820
4821static Object* Runtime_TraceExit(Arguments args) {
4822 NoHandleAllocation ha;
4823 PrintTransition(args[0]);
4824 return args[0]; // return TOS
4825}
4826
4827
4828static Object* Runtime_DebugPrint(Arguments args) {
4829 NoHandleAllocation ha;
4830 ASSERT(args.length() == 1);
4831
4832#ifdef DEBUG
4833 if (args[0]->IsString()) {
4834 // If we have a string, assume it's a code "marker"
4835 // and print some interesting cpu debugging info.
4836 JavaScriptFrameIterator it;
4837 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004838 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
4839 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004840 } else {
4841 PrintF("DebugPrint: ");
4842 }
4843 args[0]->Print();
4844#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004845 // ShortPrint is available in release mode. Print is not.
4846 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004847#endif
4848 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004849 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004850
4851 return args[0]; // return TOS
4852}
4853
4854
4855static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004856 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004857 NoHandleAllocation ha;
4858 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004859 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004860}
4861
4862
mads.s.ager31e71382008-08-13 09:32:07 +00004863static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004864 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004865 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004866
4867 // According to ECMA-262, section 15.9.1, page 117, the precision of
4868 // the number in a Date object representing a particular instant in
4869 // time is milliseconds. Therefore, we floor the result of getting
4870 // the OS time.
4871 double millis = floor(OS::TimeCurrentMillis());
4872 return Heap::NumberFromDouble(millis);
4873}
4874
4875
4876static Object* Runtime_DateParseString(Arguments args) {
4877 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004878 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004879
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004880 CONVERT_ARG_CHECKED(String, str, 0);
4881 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004882
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004883 CONVERT_ARG_CHECKED(JSArray, output, 1);
4884 RUNTIME_ASSERT(output->HasFastElements());
4885
4886 AssertNoAllocation no_allocation;
4887
4888 FixedArray* output_array = output->elements();
4889 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4890 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004891 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004892 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004893 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004894 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004895 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4896 }
4897
4898 if (result) {
4899 return *output;
4900 } else {
4901 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004902 }
4903}
4904
4905
4906static Object* Runtime_DateLocalTimezone(Arguments args) {
4907 NoHandleAllocation ha;
4908 ASSERT(args.length() == 1);
4909
4910 CONVERT_DOUBLE_CHECKED(x, args[0]);
4911 char* zone = OS::LocalTimezone(x);
4912 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4913}
4914
4915
4916static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4917 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004918 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004919
4920 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4921}
4922
4923
4924static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4925 NoHandleAllocation ha;
4926 ASSERT(args.length() == 1);
4927
4928 CONVERT_DOUBLE_CHECKED(x, args[0]);
4929 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4930}
4931
4932
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004933static Object* Runtime_NumberIsFinite(Arguments args) {
4934 NoHandleAllocation ha;
4935 ASSERT(args.length() == 1);
4936
4937 CONVERT_DOUBLE_CHECKED(value, args[0]);
4938 Object* result;
4939 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4940 result = Heap::false_value();
4941 } else {
4942 result = Heap::true_value();
4943 }
4944 return result;
4945}
4946
4947
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004948static Object* Runtime_GlobalReceiver(Arguments args) {
4949 ASSERT(args.length() == 1);
4950 Object* global = args[0];
4951 if (!global->IsJSGlobalObject()) return Heap::null_value();
4952 return JSGlobalObject::cast(global)->global_receiver();
4953}
4954
4955
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004956static Object* Runtime_CompileString(Arguments args) {
4957 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004958 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004959 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004960 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004961
ager@chromium.org381abbb2009-02-25 13:23:22 +00004962 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004963 Handle<Context> context(Top::context()->global_context());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004964 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
4965 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004966 true,
4967 is_json->IsTrue());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004968 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004969 Handle<JSFunction> fun =
4970 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4971 return *fun;
4972}
4973
4974
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004975static Handle<JSFunction> GetBuiltinFunction(String* name) {
4976 LookupResult result;
4977 Top::global_context()->builtins()->LocalLookup(name, &result);
4978 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
4979}
4980
4981
4982static Object* CompileDirectEval(Handle<String> source) {
4983 // Compute the eval context.
4984 HandleScope scope;
4985 StackFrameLocator locator;
4986 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4987 Handle<Context> context(Context::cast(frame->context()));
4988 bool is_global = context->IsGlobalContext();
4989
ager@chromium.org381abbb2009-02-25 13:23:22 +00004990 // Compile source string in the current context.
4991 Handle<JSFunction> boilerplate =
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004992 Compiler::CompileEval(source, context, is_global, false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004993 if (boilerplate.is_null()) return Failure::Exception();
4994 Handle<JSFunction> fun =
4995 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4996 return *fun;
4997}
4998
4999
5000static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
5001 ASSERT(args.length() == 2);
5002
5003 HandleScope scope;
5004
5005 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5006
5007 Handle<Object> receiver;
5008
5009 // Find where the 'eval' symbol is bound. It is unaliased only if
5010 // it is bound in the global context.
5011 StackFrameLocator locator;
5012 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5013 Handle<Context> context(Context::cast(frame->context()));
5014 int index;
5015 PropertyAttributes attributes;
5016 while (!context.is_null()) {
5017 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5018 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005019 // Stop search when eval is found or when the global context is
5020 // reached.
5021 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005022 if (context->is_function_context()) {
5023 context = Handle<Context>(Context::cast(context->closure()->context()));
5024 } else {
5025 context = Handle<Context>(context->previous());
5026 }
5027 }
5028
iposva@chromium.org245aa852009-02-10 00:49:54 +00005029 // If eval could not be resolved, it has been deleted and we need to
5030 // throw a reference error.
5031 if (attributes == ABSENT) {
5032 Handle<Object> name = Factory::eval_symbol();
5033 Handle<Object> reference_error =
5034 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5035 return Top::Throw(*reference_error);
5036 }
5037
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005038 if (context->IsGlobalContext()) {
5039 // 'eval' is bound in the global context, but it may have been overwritten.
5040 // Compare it to the builtin 'GlobalEval' function to make sure.
5041 Handle<JSFunction> global_eval =
5042 GetBuiltinFunction(Heap::global_eval_symbol());
5043 if (global_eval.is_identical_to(callee)) {
5044 // A direct eval call.
5045 if (args[1]->IsString()) {
5046 CONVERT_ARG_CHECKED(String, source, 1);
5047 // A normal eval call on a string. Compile it and return the
5048 // compiled function bound in the local context.
5049 Object* compiled_source = CompileDirectEval(source);
5050 if (compiled_source->IsFailure()) return compiled_source;
5051 receiver = Handle<Object>(frame->receiver());
5052 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5053 } else {
5054 // An eval call that is not called on a string. Global eval
5055 // deals better with this.
5056 receiver = Handle<Object>(Top::global_context()->global());
5057 }
5058 } else {
5059 // 'eval' is overwritten. Just call the function with the given arguments.
5060 receiver = Handle<Object>(Top::global_context()->global());
5061 }
5062 } else {
5063 // 'eval' is not bound in the global context. Just call the function
5064 // with the given arguments. This is not necessarily the global eval.
5065 if (receiver->IsContext()) {
5066 context = Handle<Context>::cast(receiver);
5067 receiver = Handle<Object>(context->get(index));
5068 }
5069 }
5070
5071 Handle<FixedArray> call = Factory::NewFixedArray(2);
5072 call->set(0, *callee);
5073 call->set(1, *receiver);
5074 return *call;
5075}
5076
5077
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005078static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5079 // This utility adjusts the property attributes for newly created Function
5080 // object ("new Function(...)") by changing the map.
5081 // All it does is changing the prototype property to enumerable
5082 // as specified in ECMA262, 15.3.5.2.
5083 HandleScope scope;
5084 ASSERT(args.length() == 1);
5085 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5086 ASSERT(func->map()->instance_type() ==
5087 Top::function_instance_map()->instance_type());
5088 ASSERT(func->map()->instance_size() ==
5089 Top::function_instance_map()->instance_size());
5090 func->set_map(*Top::function_instance_map());
5091 return *func;
5092}
5093
5094
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005095// Push an array unto an array of arrays if it is not already in the
5096// array. Returns true if the element was pushed on the stack and
5097// false otherwise.
5098static Object* Runtime_PushIfAbsent(Arguments args) {
5099 ASSERT(args.length() == 2);
5100 CONVERT_CHECKED(JSArray, array, args[0]);
5101 CONVERT_CHECKED(JSArray, element, args[1]);
5102 RUNTIME_ASSERT(array->HasFastElements());
5103 int length = Smi::cast(array->length())->value();
5104 FixedArray* elements = FixedArray::cast(array->elements());
5105 for (int i = 0; i < length; i++) {
5106 if (elements->get(i) == element) return Heap::false_value();
5107 }
5108 Object* obj = array->SetFastElement(length, element);
5109 if (obj->IsFailure()) return obj;
5110 return Heap::true_value();
5111}
5112
5113
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005114/**
5115 * A simple visitor visits every element of Array's.
5116 * The backend storage can be a fixed array for fast elements case,
5117 * or a dictionary for sparse array. Since Dictionary is a subtype
5118 * of FixedArray, the class can be used by both fast and slow cases.
5119 * The second parameter of the constructor, fast_elements, specifies
5120 * whether the storage is a FixedArray or Dictionary.
5121 *
5122 * An index limit is used to deal with the situation that a result array
5123 * length overflows 32-bit non-negative integer.
5124 */
5125class ArrayConcatVisitor {
5126 public:
5127 ArrayConcatVisitor(Handle<FixedArray> storage,
5128 uint32_t index_limit,
5129 bool fast_elements) :
5130 storage_(storage), index_limit_(index_limit),
5131 fast_elements_(fast_elements), index_offset_(0) { }
5132
5133 void visit(uint32_t i, Handle<Object> elm) {
5134 uint32_t index = i + index_offset_;
5135 if (index >= index_limit_) return;
5136
5137 if (fast_elements_) {
5138 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5139 storage_->set(index, *elm);
5140
5141 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005142 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5143 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005144 Factory::DictionaryAtNumberPut(dict, index, elm);
5145 if (!result.is_identical_to(dict))
5146 storage_ = result;
5147 }
5148 }
5149
5150 void increase_index_offset(uint32_t delta) {
5151 index_offset_ += delta;
5152 }
5153
5154 private:
5155 Handle<FixedArray> storage_;
5156 uint32_t index_limit_;
5157 bool fast_elements_;
5158 uint32_t index_offset_;
5159};
5160
5161
5162/**
5163 * A helper function that visits elements of a JSObject. Only elements
5164 * whose index between 0 and range (exclusive) are visited.
5165 *
5166 * If the third parameter, visitor, is not NULL, the visitor is called
5167 * with parameters, 'visitor_index_offset + element index' and the element.
5168 *
5169 * It returns the number of visisted elements.
5170 */
5171static uint32_t IterateElements(Handle<JSObject> receiver,
5172 uint32_t range,
5173 ArrayConcatVisitor* visitor) {
5174 uint32_t num_of_elements = 0;
5175
5176 if (receiver->HasFastElements()) {
5177 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5178 uint32_t len = elements->length();
5179 if (range < len) len = range;
5180
5181 for (uint32_t j = 0; j < len; j++) {
5182 Handle<Object> e(elements->get(j));
5183 if (!e->IsTheHole()) {
5184 num_of_elements++;
5185 if (visitor)
5186 visitor->visit(j, e);
5187 }
5188 }
5189
5190 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005191 Handle<NumberDictionary> dict(receiver->element_dictionary());
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005192 uint32_t capacity = dict->Capacity();
5193 for (uint32_t j = 0; j < capacity; j++) {
5194 Handle<Object> k(dict->KeyAt(j));
5195 if (dict->IsKey(*k)) {
5196 ASSERT(k->IsNumber());
5197 uint32_t index = static_cast<uint32_t>(k->Number());
5198 if (index < range) {
5199 num_of_elements++;
5200 if (visitor) {
5201 visitor->visit(index,
5202 Handle<Object>(dict->ValueAt(j)));
5203 }
5204 }
5205 }
5206 }
5207 }
5208
5209 return num_of_elements;
5210}
5211
5212
5213/**
5214 * A helper function that visits elements of an Array object, and elements
5215 * on its prototypes.
5216 *
5217 * Elements on prototypes are visited first, and only elements whose indices
5218 * less than Array length are visited.
5219 *
5220 * If a ArrayConcatVisitor object is given, the visitor is called with
5221 * parameters, element's index + visitor_index_offset and the element.
5222 */
5223static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5224 ArrayConcatVisitor* visitor) {
5225 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5226 Handle<Object> obj = array;
5227
5228 static const int kEstimatedPrototypes = 3;
5229 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5230
5231 // Visit prototype first. If an element on the prototype is shadowed by
5232 // the inheritor using the same index, the ArrayConcatVisitor visits
5233 // the prototype element before the shadowing element.
5234 // The visitor can simply overwrite the old value by new value using
5235 // the same index. This follows Array::concat semantics.
5236 while (!obj->IsNull()) {
5237 objects.Add(Handle<JSObject>::cast(obj));
5238 obj = Handle<Object>(obj->GetPrototype());
5239 }
5240
5241 uint32_t nof_elements = 0;
5242 for (int i = objects.length() - 1; i >= 0; i--) {
5243 Handle<JSObject> obj = objects[i];
5244 nof_elements +=
5245 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5246 }
5247
5248 return nof_elements;
5249}
5250
5251
5252/**
5253 * A helper function of Runtime_ArrayConcat.
5254 *
5255 * The first argument is an Array of arrays and objects. It is the
5256 * same as the arguments array of Array::concat JS function.
5257 *
5258 * If an argument is an Array object, the function visits array
5259 * elements. If an argument is not an Array object, the function
5260 * visits the object as if it is an one-element array.
5261 *
5262 * If the result array index overflows 32-bit integer, the rounded
5263 * non-negative number is used as new length. For example, if one
5264 * array length is 2^32 - 1, second array length is 1, the
5265 * concatenated array length is 0.
5266 */
5267static uint32_t IterateArguments(Handle<JSArray> arguments,
5268 ArrayConcatVisitor* visitor) {
5269 uint32_t visited_elements = 0;
5270 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5271
5272 for (uint32_t i = 0; i < num_of_args; i++) {
5273 Handle<Object> obj(arguments->GetElement(i));
5274 if (obj->IsJSArray()) {
5275 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5276 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5277 uint32_t nof_elements =
5278 IterateArrayAndPrototypeElements(array, visitor);
5279 // Total elements of array and its prototype chain can be more than
5280 // the array length, but ArrayConcat can only concatenate at most
5281 // the array length number of elements.
5282 visited_elements += (nof_elements > len) ? len : nof_elements;
5283 if (visitor) visitor->increase_index_offset(len);
5284
5285 } else {
5286 if (visitor) {
5287 visitor->visit(0, obj);
5288 visitor->increase_index_offset(1);
5289 }
5290 visited_elements++;
5291 }
5292 }
5293 return visited_elements;
5294}
5295
5296
5297/**
5298 * Array::concat implementation.
5299 * See ECMAScript 262, 15.4.4.4.
5300 */
5301static Object* Runtime_ArrayConcat(Arguments args) {
5302 ASSERT(args.length() == 1);
5303 HandleScope handle_scope;
5304
5305 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5306 Handle<JSArray> arguments(arg_arrays);
5307
5308 // Pass 1: estimate the number of elements of the result
5309 // (it could be more than real numbers if prototype has elements).
5310 uint32_t result_length = 0;
5311 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5312
5313 { AssertNoAllocation nogc;
5314 for (uint32_t i = 0; i < num_of_args; i++) {
5315 Object* obj = arguments->GetElement(i);
5316 if (obj->IsJSArray()) {
5317 result_length +=
5318 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5319 } else {
5320 result_length++;
5321 }
5322 }
5323 }
5324
5325 // Allocate an empty array, will set length and content later.
5326 Handle<JSArray> result = Factory::NewJSArray(0);
5327
5328 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5329 // If estimated number of elements is more than half of length, a
5330 // fixed array (fast case) is more time and space-efficient than a
5331 // dictionary.
5332 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5333
5334 Handle<FixedArray> storage;
5335 if (fast_case) {
5336 // The backing storage array must have non-existing elements to
5337 // preserve holes across concat operations.
5338 storage = Factory::NewFixedArrayWithHoles(result_length);
5339
5340 } else {
5341 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5342 uint32_t at_least_space_for = estimate_nof_elements +
5343 (estimate_nof_elements >> 2);
5344 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005345 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005346 }
5347
5348 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5349
5350 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5351
5352 IterateArguments(arguments, &visitor);
5353
5354 result->set_length(*len);
5355 result->set_elements(*storage);
5356
5357 return *result;
5358}
5359
5360
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005361// This will not allocate (flatten the string), but it may run
5362// very slowly for very deeply nested ConsStrings. For debugging use only.
5363static Object* Runtime_GlobalPrint(Arguments args) {
5364 NoHandleAllocation ha;
5365 ASSERT(args.length() == 1);
5366
5367 CONVERT_CHECKED(String, string, args[0]);
5368 StringInputBuffer buffer(string);
5369 while (buffer.has_more()) {
5370 uint16_t character = buffer.GetNext();
5371 PrintF("%c", character);
5372 }
5373 return string;
5374}
5375
ager@chromium.org5ec48922009-05-05 07:25:34 +00005376// Moves all own elements of an object, that are below a limit, to positions
5377// starting at zero. All undefined values are placed after non-undefined values,
5378// and are followed by non-existing element. Does not change the length
5379// property.
5380// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005381static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005382 ASSERT(args.length() == 2);
5383 CONVERT_CHECKED(JSObject, object, args[0]);
5384 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5385 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005386}
5387
5388
5389// Move contents of argument 0 (an array) to argument 1 (an array)
5390static Object* Runtime_MoveArrayContents(Arguments args) {
5391 ASSERT(args.length() == 2);
5392 CONVERT_CHECKED(JSArray, from, args[0]);
5393 CONVERT_CHECKED(JSArray, to, args[1]);
5394 to->SetContent(FixedArray::cast(from->elements()));
5395 to->set_length(from->length());
5396 from->SetContent(Heap::empty_fixed_array());
5397 from->set_length(0);
5398 return to;
5399}
5400
5401
5402// How many elements does this array have?
5403static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5404 ASSERT(args.length() == 1);
5405 CONVERT_CHECKED(JSArray, array, args[0]);
5406 HeapObject* elements = array->elements();
5407 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005408 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005409 } else {
5410 return array->length();
5411 }
5412}
5413
5414
5415// Returns an array that tells you where in the [0, length) interval an array
5416// might have elements. Can either return keys or intervals. Keys can have
5417// gaps in (undefined). Intervals can also span over some undefined keys.
5418static Object* Runtime_GetArrayKeys(Arguments args) {
5419 ASSERT(args.length() == 2);
5420 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005421 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005422 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005423 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005424 // Create an array and get all the keys into it, then remove all the
5425 // keys that are not integers in the range 0 to length-1.
5426 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5427 int keys_length = keys->length();
5428 for (int i = 0; i < keys_length; i++) {
5429 Object* key = keys->get(i);
5430 uint32_t index;
5431 if (!Array::IndexFromObject(key, &index) || index >= length) {
5432 // Zap invalid keys.
5433 keys->set_undefined(i);
5434 }
5435 }
5436 return *Factory::NewJSArrayWithElements(keys);
5437 } else {
5438 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5439 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005440 single_interval->set(0,
5441 Smi::FromInt(-1),
5442 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005443 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5444 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005445 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005446 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005447 single_interval->set(1, *length_object);
5448 return *Factory::NewJSArrayWithElements(single_interval);
5449 }
5450}
5451
5452
5453// DefineAccessor takes an optional final argument which is the
5454// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5455// to the way accessors are implemented, it is set for both the getter
5456// and setter on the first call to DefineAccessor and ignored on
5457// subsequent calls.
5458static Object* Runtime_DefineAccessor(Arguments args) {
5459 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5460 // Compute attributes.
5461 PropertyAttributes attributes = NONE;
5462 if (args.length() == 5) {
5463 CONVERT_CHECKED(Smi, attrs, args[4]);
5464 int value = attrs->value();
5465 // Only attribute bits should be set.
5466 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5467 attributes = static_cast<PropertyAttributes>(value);
5468 }
5469
5470 CONVERT_CHECKED(JSObject, obj, args[0]);
5471 CONVERT_CHECKED(String, name, args[1]);
5472 CONVERT_CHECKED(Smi, flag, args[2]);
5473 CONVERT_CHECKED(JSFunction, fun, args[3]);
5474 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5475}
5476
5477
5478static Object* Runtime_LookupAccessor(Arguments args) {
5479 ASSERT(args.length() == 3);
5480 CONVERT_CHECKED(JSObject, obj, args[0]);
5481 CONVERT_CHECKED(String, name, args[1]);
5482 CONVERT_CHECKED(Smi, flag, args[2]);
5483 return obj->LookupAccessor(name, flag->value() == 0);
5484}
5485
5486
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005487#ifdef ENABLE_DEBUGGER_SUPPORT
5488static Object* Runtime_DebugBreak(Arguments args) {
5489 ASSERT(args.length() == 0);
5490 return Execution::DebugBreakHelper();
5491}
5492
5493
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005494// Helper functions for wrapping and unwrapping stack frame ids.
5495static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005496 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005497 return Smi::FromInt(id >> 2);
5498}
5499
5500
5501static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5502 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5503}
5504
5505
5506// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005507// args[0]: debug event listener function to set or null or undefined for
5508// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005509// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005510static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005511 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005512 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5513 args[0]->IsUndefined() ||
5514 args[0]->IsNull());
5515 Handle<Object> callback = args.at<Object>(0);
5516 Handle<Object> data = args.at<Object>(1);
5517 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005518
5519 return Heap::undefined_value();
5520}
5521
5522
5523static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005524 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005525 StackGuard::DebugBreak();
5526 return Heap::undefined_value();
5527}
5528
5529
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005530// Find the length of the prototype chain that is to to handled as one. If a
5531// prototype object is hidden it is to be viewed as part of the the object it
5532// is prototype for.
5533static int LocalPrototypeChainLength(JSObject* obj) {
5534 int count = 1;
5535 Object* proto = obj->GetPrototype();
5536 while (proto->IsJSObject() &&
5537 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5538 count++;
5539 proto = JSObject::cast(proto)->GetPrototype();
5540 }
5541 return count;
5542}
5543
5544
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005545static Object* DebugLookupResultValue(Object* receiver, String* name,
5546 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005547 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005548 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005549 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005550 case NORMAL:
5551 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005552 if (value->IsTheHole()) {
5553 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005554 }
5555 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005556 case FIELD:
5557 value =
5558 JSObject::cast(
5559 result->holder())->FastPropertyAt(result->GetFieldIndex());
5560 if (value->IsTheHole()) {
5561 return Heap::undefined_value();
5562 }
5563 return value;
5564 case CONSTANT_FUNCTION:
5565 return result->GetConstantFunction();
5566 case CALLBACKS: {
5567 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005568 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005569 value = receiver->GetPropertyWithCallback(
5570 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005571 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005572 value = Top::pending_exception();
5573 Top::clear_pending_exception();
5574 if (caught_exception != NULL) {
5575 *caught_exception = true;
5576 }
5577 }
5578 return value;
5579 } else {
5580 return Heap::undefined_value();
5581 }
5582 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005583 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005584 case MAP_TRANSITION:
5585 case CONSTANT_TRANSITION:
5586 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005587 return Heap::undefined_value();
5588 default:
5589 UNREACHABLE();
5590 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005591 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005592 return Heap::undefined_value();
5593}
5594
5595
ager@chromium.org32912102009-01-16 10:38:43 +00005596// Get debugger related details for an object property.
5597// args[0]: object holding property
5598// args[1]: name of the property
5599//
5600// The array returned contains the following information:
5601// 0: Property value
5602// 1: Property details
5603// 2: Property value is exception
5604// 3: Getter function if defined
5605// 4: Setter function if defined
5606// Items 2-4 are only filled if the property has either a getter or a setter
5607// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005608static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005609 HandleScope scope;
5610
5611 ASSERT(args.length() == 2);
5612
5613 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5614 CONVERT_ARG_CHECKED(String, name, 1);
5615
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005616 // Make sure to set the current context to the context before the debugger was
5617 // entered (if the debugger is entered). The reason for switching context here
5618 // is that for some property lookups (accessors and interceptors) callbacks
5619 // into the embedding application can occour, and the embedding application
5620 // could have the assumption that its own global context is the current
5621 // context and not some internal debugger context.
5622 SaveContext save;
5623 if (Debug::InDebugger()) {
5624 Top::set_context(*Debug::debugger_entry()->GetContext());
5625 }
5626
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005627 // Skip the global proxy as it has no properties and always delegates to the
5628 // real global object.
5629 if (obj->IsJSGlobalProxy()) {
5630 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5631 }
5632
5633
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005634 // Check if the name is trivially convertible to an index and get the element
5635 // if so.
5636 uint32_t index;
5637 if (name->AsArrayIndex(&index)) {
5638 Handle<FixedArray> details = Factory::NewFixedArray(2);
5639 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5640 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5641 return *Factory::NewJSArrayWithElements(details);
5642 }
5643
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005644 // Find the number of objects making up this.
5645 int length = LocalPrototypeChainLength(*obj);
5646
5647 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005648 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005649 Handle<JSObject> jsproto = obj;
5650 for (int i = 0; i < length; i++) {
5651 jsproto->LocalLookup(*name, &result);
5652 if (result.IsProperty()) {
5653 break;
5654 }
5655 if (i < length - 1) {
5656 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5657 }
5658 }
5659
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005660 if (result.IsProperty()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005661 // LookupResult is not GC safe as all its members are raw object pointers.
5662 // When calling DebugLookupResultValue GC can happen as this might invoke
5663 // callbacks. After the call to DebugLookupResultValue the callback object
5664 // in the LookupResult might still be needed. Put it into a handle for later
5665 // use.
5666 PropertyType result_type = result.type();
5667 Handle<Object> result_callback_obj;
5668 if (result_type == CALLBACKS) {
5669 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5670 }
5671
5672 // Find the actual value. Don't use result after this call as it's content
5673 // can be invalid.
ager@chromium.org32912102009-01-16 10:38:43 +00005674 bool caught_exception = false;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005675 Object* value = DebugLookupResultValue(*obj, *name, &result,
ager@chromium.org381abbb2009-02-25 13:23:22 +00005676 &caught_exception);
5677 if (value->IsFailure()) return value;
5678 Handle<Object> value_handle(value);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005679
ager@chromium.org32912102009-01-16 10:38:43 +00005680 // If the callback object is a fixed array then it contains JavaScript
5681 // getter and/or setter.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005682 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5683 result_callback_obj->IsFixedArray();
ager@chromium.org32912102009-01-16 10:38:43 +00005684 Handle<FixedArray> details =
5685 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005686 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005687 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005688 if (hasJavaScriptAccessors) {
5689 details->set(2,
5690 caught_exception ? Heap::true_value() : Heap::false_value());
5691 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5692 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5693 }
5694
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005695 return *Factory::NewJSArrayWithElements(details);
5696 }
5697 return Heap::undefined_value();
5698}
5699
5700
5701static Object* Runtime_DebugGetProperty(Arguments args) {
5702 HandleScope scope;
5703
5704 ASSERT(args.length() == 2);
5705
5706 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5707 CONVERT_ARG_CHECKED(String, name, 1);
5708
5709 LookupResult result;
5710 obj->Lookup(*name, &result);
5711 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005712 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005713 }
5714 return Heap::undefined_value();
5715}
5716
5717
5718// Return the names of the local named properties.
5719// args[0]: object
5720static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5721 HandleScope scope;
5722 ASSERT(args.length() == 1);
5723 if (!args[0]->IsJSObject()) {
5724 return Heap::undefined_value();
5725 }
5726 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5727
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005728 // Skip the global proxy as it has no properties and always delegates to the
5729 // real global object.
5730 if (obj->IsJSGlobalProxy()) {
5731 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5732 }
5733
5734 // Find the number of objects making up this.
5735 int length = LocalPrototypeChainLength(*obj);
5736
5737 // Find the number of local properties for each of the objects.
5738 int* local_property_count = NewArray<int>(length);
5739 int total_property_count = 0;
5740 Handle<JSObject> jsproto = obj;
5741 for (int i = 0; i < length; i++) {
5742 int n;
5743 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5744 local_property_count[i] = n;
5745 total_property_count += n;
5746 if (i < length - 1) {
5747 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5748 }
5749 }
5750
5751 // Allocate an array with storage for all the property names.
5752 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5753
5754 // Get the property names.
5755 jsproto = obj;
5756 for (int i = 0; i < length; i++) {
5757 jsproto->GetLocalPropertyNames(*names,
5758 i == 0 ? 0 : local_property_count[i - 1]);
5759 if (i < length - 1) {
5760 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5761 }
5762 }
5763
5764 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005765 return *Factory::NewJSArrayWithElements(names);
5766}
5767
5768
5769// Return the names of the local indexed properties.
5770// args[0]: object
5771static Object* Runtime_DebugLocalElementNames(Arguments args) {
5772 HandleScope scope;
5773 ASSERT(args.length() == 1);
5774 if (!args[0]->IsJSObject()) {
5775 return Heap::undefined_value();
5776 }
5777 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5778
5779 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5780 Handle<FixedArray> names = Factory::NewFixedArray(n);
5781 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5782 return *Factory::NewJSArrayWithElements(names);
5783}
5784
5785
5786// Return the property type calculated from the property details.
5787// args[0]: smi with property details.
5788static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5789 ASSERT(args.length() == 1);
5790 CONVERT_CHECKED(Smi, details, args[0]);
5791 PropertyType type = PropertyDetails(details).type();
5792 return Smi::FromInt(static_cast<int>(type));
5793}
5794
5795
5796// Return the property attribute calculated from the property details.
5797// args[0]: smi with property details.
5798static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5799 ASSERT(args.length() == 1);
5800 CONVERT_CHECKED(Smi, details, args[0]);
5801 PropertyAttributes attributes = PropertyDetails(details).attributes();
5802 return Smi::FromInt(static_cast<int>(attributes));
5803}
5804
5805
5806// Return the property insertion index calculated from the property details.
5807// args[0]: smi with property details.
5808static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5809 ASSERT(args.length() == 1);
5810 CONVERT_CHECKED(Smi, details, args[0]);
5811 int index = PropertyDetails(details).index();
5812 return Smi::FromInt(index);
5813}
5814
5815
5816// Return information on whether an object has a named or indexed interceptor.
5817// args[0]: object
5818static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5819 HandleScope scope;
5820 ASSERT(args.length() == 1);
5821 if (!args[0]->IsJSObject()) {
5822 return Smi::FromInt(0);
5823 }
5824 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5825
5826 int result = 0;
5827 if (obj->HasNamedInterceptor()) result |= 2;
5828 if (obj->HasIndexedInterceptor()) result |= 1;
5829
5830 return Smi::FromInt(result);
5831}
5832
5833
5834// Return property names from named interceptor.
5835// args[0]: object
5836static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5837 HandleScope scope;
5838 ASSERT(args.length() == 1);
5839 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005840
ager@chromium.org32912102009-01-16 10:38:43 +00005841 if (obj->HasNamedInterceptor()) {
5842 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5843 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5844 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005845 return Heap::undefined_value();
5846}
5847
5848
5849// Return element names from indexed interceptor.
5850// args[0]: object
5851static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5852 HandleScope scope;
5853 ASSERT(args.length() == 1);
5854 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005855
ager@chromium.org32912102009-01-16 10:38:43 +00005856 if (obj->HasIndexedInterceptor()) {
5857 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5858 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5859 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005860 return Heap::undefined_value();
5861}
5862
5863
5864// Return property value from named interceptor.
5865// args[0]: object
5866// args[1]: property name
5867static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5868 HandleScope scope;
5869 ASSERT(args.length() == 2);
5870 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5871 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5872 CONVERT_ARG_CHECKED(String, name, 1);
5873
5874 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005875 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005876}
5877
5878
5879// Return element value from indexed interceptor.
5880// args[0]: object
5881// args[1]: index
5882static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5883 HandleScope scope;
5884 ASSERT(args.length() == 2);
5885 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5886 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5887 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5888
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005889 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005890}
5891
5892
5893static Object* Runtime_CheckExecutionState(Arguments args) {
5894 ASSERT(args.length() >= 1);
5895 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005896 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005897 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005898 return Top::Throw(Heap::illegal_execution_state_symbol());
5899 }
5900
5901 return Heap::true_value();
5902}
5903
5904
5905static Object* Runtime_GetFrameCount(Arguments args) {
5906 HandleScope scope;
5907 ASSERT(args.length() == 1);
5908
5909 // Check arguments.
5910 Object* result = Runtime_CheckExecutionState(args);
5911 if (result->IsFailure()) return result;
5912
5913 // Count all frames which are relevant to debugging stack trace.
5914 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005915 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005916 if (id == StackFrame::NO_ID) {
5917 // If there is no JavaScript stack frame count is 0.
5918 return Smi::FromInt(0);
5919 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005920 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5921 return Smi::FromInt(n);
5922}
5923
5924
5925static const int kFrameDetailsFrameIdIndex = 0;
5926static const int kFrameDetailsReceiverIndex = 1;
5927static const int kFrameDetailsFunctionIndex = 2;
5928static const int kFrameDetailsArgumentCountIndex = 3;
5929static const int kFrameDetailsLocalCountIndex = 4;
5930static const int kFrameDetailsSourcePositionIndex = 5;
5931static const int kFrameDetailsConstructCallIndex = 6;
5932static const int kFrameDetailsDebuggerFrameIndex = 7;
5933static const int kFrameDetailsFirstDynamicIndex = 8;
5934
5935// Return an array with frame details
5936// args[0]: number: break id
5937// args[1]: number: frame index
5938//
5939// The array returned contains the following information:
5940// 0: Frame id
5941// 1: Receiver
5942// 2: Function
5943// 3: Argument count
5944// 4: Local count
5945// 5: Source position
5946// 6: Constructor call
5947// 7: Debugger frame
5948// Arguments name, value
5949// Locals name, value
5950static Object* Runtime_GetFrameDetails(Arguments args) {
5951 HandleScope scope;
5952 ASSERT(args.length() == 2);
5953
5954 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005955 Object* check = Runtime_CheckExecutionState(args);
5956 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005957 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5958
5959 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005960 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005961 if (id == StackFrame::NO_ID) {
5962 // If there are no JavaScript stack frames return undefined.
5963 return Heap::undefined_value();
5964 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005965 int count = 0;
5966 JavaScriptFrameIterator it(id);
5967 for (; !it.done(); it.Advance()) {
5968 if (count == index) break;
5969 count++;
5970 }
5971 if (it.done()) return Heap::undefined_value();
5972
5973 // Traverse the saved contexts chain to find the active context for the
5974 // selected frame.
5975 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005976 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005977 save = save->prev();
5978 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005979 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005980
5981 // Get the frame id.
5982 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
5983
5984 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005985 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005986
5987 // Check for constructor frame.
5988 bool constructor = it.frame()->IsConstructor();
5989
5990 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005991 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005992 ScopeInfo<> info(*code);
5993
5994 // Get the context.
5995 Handle<Context> context(Context::cast(it.frame()->context()));
5996
5997 // Get the locals names and values into a temporary array.
5998 //
5999 // TODO(1240907): Hide compiler-introduced stack variables
6000 // (e.g. .result)? For users of the debugger, they will probably be
6001 // confusing.
6002 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6003 for (int i = 0; i < info.NumberOfLocals(); i++) {
6004 // Name of the local.
6005 locals->set(i * 2, *info.LocalName(i));
6006
6007 // Fetch the value of the local - either from the stack or from a
6008 // heap-allocated context.
6009 if (i < info.number_of_stack_slots()) {
6010 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6011 } else {
6012 Handle<String> name = info.LocalName(i);
6013 // Traverse the context chain to the function context as all local
6014 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006015 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006016 context = Handle<Context>(context->previous());
6017 }
6018 ASSERT(context->is_function_context());
6019 locals->set(i * 2 + 1,
6020 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6021 NULL)));
6022 }
6023 }
6024
6025 // Now advance to the arguments adapter frame (if any). If contains all
6026 // the provided parameters and
6027
6028 // Now advance to the arguments adapter frame (if any). It contains all
6029 // the provided parameters whereas the function frame always have the number
6030 // of arguments matching the functions parameters. The rest of the
6031 // information (except for what is collected above) is the same.
6032 it.AdvanceToArgumentsFrame();
6033
6034 // Find the number of arguments to fill. At least fill the number of
6035 // parameters for the function and fill more if more parameters are provided.
6036 int argument_count = info.number_of_parameters();
6037 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6038 argument_count = it.frame()->GetProvidedParametersCount();
6039 }
6040
6041 // Calculate the size of the result.
6042 int details_size = kFrameDetailsFirstDynamicIndex +
6043 2 * (argument_count + info.NumberOfLocals());
6044 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6045
6046 // Add the frame id.
6047 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6048
6049 // Add the function (same as in function frame).
6050 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6051
6052 // Add the arguments count.
6053 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6054
6055 // Add the locals count
6056 details->set(kFrameDetailsLocalCountIndex,
6057 Smi::FromInt(info.NumberOfLocals()));
6058
6059 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006060 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006061 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6062 } else {
6063 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6064 }
6065
6066 // Add the constructor information.
6067 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6068
6069 // Add information on whether this frame is invoked in the debugger context.
6070 details->set(kFrameDetailsDebuggerFrameIndex,
6071 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6072
6073 // Fill the dynamic part.
6074 int details_index = kFrameDetailsFirstDynamicIndex;
6075
6076 // Add arguments name and value.
6077 for (int i = 0; i < argument_count; i++) {
6078 // Name of the argument.
6079 if (i < info.number_of_parameters()) {
6080 details->set(details_index++, *info.parameter_name(i));
6081 } else {
6082 details->set(details_index++, Heap::undefined_value());
6083 }
6084
6085 // Parameter value.
6086 if (i < it.frame()->GetProvidedParametersCount()) {
6087 details->set(details_index++, it.frame()->GetParameter(i));
6088 } else {
6089 details->set(details_index++, Heap::undefined_value());
6090 }
6091 }
6092
6093 // Add locals name and value from the temporary copy from the function frame.
6094 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6095 details->set(details_index++, locals->get(i));
6096 }
6097
6098 // Add the receiver (same as in function frame).
6099 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6100 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6101 Handle<Object> receiver(it.frame()->receiver());
6102 if (!receiver->IsJSObject()) {
6103 // If the receiver is NOT a JSObject we have hit an optimization
6104 // where a value object is not converted into a wrapped JS objects.
6105 // To hide this optimization from the debugger, we wrap the receiver
6106 // by creating correct wrapper object based on the calling frame's
6107 // global context.
6108 it.Advance();
6109 Handle<Context> calling_frames_global_context(
6110 Context::cast(Context::cast(it.frame()->context())->global_context()));
6111 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6112 }
6113 details->set(kFrameDetailsReceiverIndex, *receiver);
6114
6115 ASSERT_EQ(details_size, details_index);
6116 return *Factory::NewJSArrayWithElements(details);
6117}
6118
6119
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006120// Copy all the context locals into an object used to materialize a scope.
6121static void CopyContextLocalsToScopeObject(Handle<Code> code,
6122 ScopeInfo<>& scope_info,
6123 Handle<Context> context,
6124 Handle<JSObject> scope_object) {
6125 // Fill all context locals to the context extension.
6126 for (int i = Context::MIN_CONTEXT_SLOTS;
6127 i < scope_info.number_of_context_slots();
6128 i++) {
6129 int context_index =
6130 ScopeInfo<>::ContextSlotIndex(*code,
6131 *scope_info.context_slot_name(i),
6132 NULL);
6133
6134 // Don't include the arguments shadow (.arguments) context variable.
6135 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6136 SetProperty(scope_object,
6137 scope_info.context_slot_name(i),
6138 Handle<Object>(context->get(context_index)), NONE);
6139 }
6140 }
6141}
6142
6143
6144// Create a plain JSObject which materializes the local scope for the specified
6145// frame.
6146static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6147 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6148 Handle<Code> code(function->code());
6149 ScopeInfo<> scope_info(*code);
6150
6151 // Allocate and initialize a JSObject with all the arguments, stack locals
6152 // heap locals and extension properties of the debugged function.
6153 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6154
6155 // First fill all parameters.
6156 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6157 SetProperty(local_scope,
6158 scope_info.parameter_name(i),
6159 Handle<Object>(frame->GetParameter(i)), NONE);
6160 }
6161
6162 // Second fill all stack locals.
6163 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6164 SetProperty(local_scope,
6165 scope_info.stack_slot_name(i),
6166 Handle<Object>(frame->GetExpression(i)), NONE);
6167 }
6168
6169 // Third fill all context locals.
6170 Handle<Context> frame_context(Context::cast(frame->context()));
6171 Handle<Context> function_context(frame_context->fcontext());
6172 CopyContextLocalsToScopeObject(code, scope_info,
6173 function_context, local_scope);
6174
6175 // Finally copy any properties from the function context extension. This will
6176 // be variables introduced by eval.
6177 if (function_context->closure() == *function) {
6178 if (function_context->has_extension() &&
6179 !function_context->IsGlobalContext()) {
6180 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6181 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6182 for (int i = 0; i < keys->length(); i++) {
6183 // Names of variables introduced by eval are strings.
6184 ASSERT(keys->get(i)->IsString());
6185 Handle<String> key(String::cast(keys->get(i)));
6186 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6187 }
6188 }
6189 }
6190 return local_scope;
6191}
6192
6193
6194// Create a plain JSObject which materializes the closure content for the
6195// context.
6196static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6197 ASSERT(context->is_function_context());
6198
6199 Handle<Code> code(context->closure()->code());
6200 ScopeInfo<> scope_info(*code);
6201
6202 // Allocate and initialize a JSObject with all the content of theis function
6203 // closure.
6204 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6205
6206 // Check whether the arguments shadow object exists.
6207 int arguments_shadow_index =
6208 ScopeInfo<>::ContextSlotIndex(*code,
6209 Heap::arguments_shadow_symbol(),
6210 NULL);
6211 if (arguments_shadow_index >= 0) {
6212 // In this case all the arguments are available in the arguments shadow
6213 // object.
6214 Handle<JSObject> arguments_shadow(
6215 JSObject::cast(context->get(arguments_shadow_index)));
6216 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6217 SetProperty(closure_scope,
6218 scope_info.parameter_name(i),
6219 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6220 }
6221 }
6222
6223 // Fill all context locals to the context extension.
6224 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6225
6226 // Finally copy any properties from the function context extension. This will
6227 // be variables introduced by eval.
6228 if (context->has_extension()) {
6229 Handle<JSObject> ext(JSObject::cast(context->extension()));
6230 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6231 for (int i = 0; i < keys->length(); i++) {
6232 // Names of variables introduced by eval are strings.
6233 ASSERT(keys->get(i)->IsString());
6234 Handle<String> key(String::cast(keys->get(i)));
6235 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6236 }
6237 }
6238
6239 return closure_scope;
6240}
6241
6242
6243// Iterate over the actual scopes visible from a stack frame. All scopes are
6244// backed by an actual context except the local scope, which is inserted
6245// "artifically" in the context chain.
6246class ScopeIterator {
6247 public:
6248 enum ScopeType {
6249 ScopeTypeGlobal = 0,
6250 ScopeTypeLocal,
6251 ScopeTypeWith,
6252 ScopeTypeClosure
6253 };
6254
6255 explicit ScopeIterator(JavaScriptFrame* frame)
6256 : frame_(frame),
6257 function_(JSFunction::cast(frame->function())),
6258 context_(Context::cast(frame->context())),
6259 local_done_(false),
6260 at_local_(false) {
6261
6262 // Check whether the first scope is actually a local scope.
6263 if (context_->IsGlobalContext()) {
6264 // If there is a stack slot for .result then this local scope has been
6265 // created for evaluating top level code and it is not a real local scope.
6266 // Checking for the existence of .result seems fragile, but the scope info
6267 // saved with the code object does not otherwise have that information.
6268 Handle<Code> code(function_->code());
6269 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6270 at_local_ = index < 0;
6271 } else if (context_->is_function_context()) {
6272 at_local_ = true;
6273 }
6274 }
6275
6276 // More scopes?
6277 bool Done() { return context_.is_null(); }
6278
6279 // Move to the next scope.
6280 void Next() {
6281 // If at a local scope mark the local scope as passed.
6282 if (at_local_) {
6283 at_local_ = false;
6284 local_done_ = true;
6285
6286 // If the current context is not associated with the local scope the
6287 // current context is the next real scope, so don't move to the next
6288 // context in this case.
6289 if (context_->closure() != *function_) {
6290 return;
6291 }
6292 }
6293
6294 // The global scope is always the last in the chain.
6295 if (context_->IsGlobalContext()) {
6296 context_ = Handle<Context>();
6297 return;
6298 }
6299
6300 // Move to the next context.
6301 if (context_->is_function_context()) {
6302 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6303 } else {
6304 context_ = Handle<Context>(context_->previous());
6305 }
6306
6307 // If passing the local scope indicate that the current scope is now the
6308 // local scope.
6309 if (!local_done_ &&
6310 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6311 at_local_ = true;
6312 }
6313 }
6314
6315 // Return the type of the current scope.
6316 int Type() {
6317 if (at_local_) {
6318 return ScopeTypeLocal;
6319 }
6320 if (context_->IsGlobalContext()) {
6321 ASSERT(context_->global()->IsGlobalObject());
6322 return ScopeTypeGlobal;
6323 }
6324 if (context_->is_function_context()) {
6325 return ScopeTypeClosure;
6326 }
6327 ASSERT(context_->has_extension());
6328 ASSERT(!context_->extension()->IsJSContextExtensionObject());
6329 return ScopeTypeWith;
6330 }
6331
6332 // Return the JavaScript object with the content of the current scope.
6333 Handle<JSObject> ScopeObject() {
6334 switch (Type()) {
6335 case ScopeIterator::ScopeTypeGlobal:
6336 return Handle<JSObject>(CurrentContext()->global());
6337 break;
6338 case ScopeIterator::ScopeTypeLocal:
6339 // Materialize the content of the local scope into a JSObject.
6340 return MaterializeLocalScope(frame_);
6341 break;
6342 case ScopeIterator::ScopeTypeWith:
6343 // Return the with object.
6344 return Handle<JSObject>(CurrentContext()->extension());
6345 break;
6346 case ScopeIterator::ScopeTypeClosure:
6347 // Materialize the content of the closure scope into a JSObject.
6348 return MaterializeClosure(CurrentContext());
6349 break;
6350 }
6351 UNREACHABLE();
6352 return Handle<JSObject>();
6353 }
6354
6355 // Return the context for this scope. For the local context there might not
6356 // be an actual context.
6357 Handle<Context> CurrentContext() {
6358 if (at_local_ && context_->closure() != *function_) {
6359 return Handle<Context>();
6360 }
6361 return context_;
6362 }
6363
6364#ifdef DEBUG
6365 // Debug print of the content of the current scope.
6366 void DebugPrint() {
6367 switch (Type()) {
6368 case ScopeIterator::ScopeTypeGlobal:
6369 PrintF("Global:\n");
6370 CurrentContext()->Print();
6371 break;
6372
6373 case ScopeIterator::ScopeTypeLocal: {
6374 PrintF("Local:\n");
6375 Handle<Code> code(function_->code());
6376 ScopeInfo<> scope_info(*code);
6377 scope_info.Print();
6378 if (!CurrentContext().is_null()) {
6379 CurrentContext()->Print();
6380 if (CurrentContext()->has_extension()) {
6381 Handle<JSObject> extension =
6382 Handle<JSObject>(CurrentContext()->extension());
6383 if (extension->IsJSContextExtensionObject()) {
6384 extension->Print();
6385 }
6386 }
6387 }
6388 break;
6389 }
6390
6391 case ScopeIterator::ScopeTypeWith: {
6392 PrintF("With:\n");
6393 Handle<JSObject> extension =
6394 Handle<JSObject>(CurrentContext()->extension());
6395 extension->Print();
6396 break;
6397 }
6398
6399 case ScopeIterator::ScopeTypeClosure: {
6400 PrintF("Closure:\n");
6401 CurrentContext()->Print();
6402 if (CurrentContext()->has_extension()) {
6403 Handle<JSObject> extension =
6404 Handle<JSObject>(CurrentContext()->extension());
6405 if (extension->IsJSContextExtensionObject()) {
6406 extension->Print();
6407 }
6408 }
6409 break;
6410 }
6411
6412 default:
6413 UNREACHABLE();
6414 }
6415 PrintF("\n");
6416 }
6417#endif
6418
6419 private:
6420 JavaScriptFrame* frame_;
6421 Handle<JSFunction> function_;
6422 Handle<Context> context_;
6423 bool local_done_;
6424 bool at_local_;
6425
6426 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6427};
6428
6429
6430static Object* Runtime_GetScopeCount(Arguments args) {
6431 HandleScope scope;
6432 ASSERT(args.length() == 2);
6433
6434 // Check arguments.
6435 Object* check = Runtime_CheckExecutionState(args);
6436 if (check->IsFailure()) return check;
6437 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6438
6439 // Get the frame where the debugging is performed.
6440 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6441 JavaScriptFrameIterator it(id);
6442 JavaScriptFrame* frame = it.frame();
6443
6444 // Count the visible scopes.
6445 int n = 0;
6446 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6447 n++;
6448 }
6449
6450 return Smi::FromInt(n);
6451}
6452
6453
6454static const int kScopeDetailsTypeIndex = 0;
6455static const int kScopeDetailsObjectIndex = 1;
6456static const int kScopeDetailsSize = 2;
6457
6458// Return an array with scope details
6459// args[0]: number: break id
6460// args[1]: number: frame index
6461// args[2]: number: scope index
6462//
6463// The array returned contains the following information:
6464// 0: Scope type
6465// 1: Scope object
6466static Object* Runtime_GetScopeDetails(Arguments args) {
6467 HandleScope scope;
6468 ASSERT(args.length() == 3);
6469
6470 // Check arguments.
6471 Object* check = Runtime_CheckExecutionState(args);
6472 if (check->IsFailure()) return check;
6473 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6474 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6475
6476 // Get the frame where the debugging is performed.
6477 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6478 JavaScriptFrameIterator frame_it(id);
6479 JavaScriptFrame* frame = frame_it.frame();
6480
6481 // Find the requested scope.
6482 int n = 0;
6483 ScopeIterator it(frame);
6484 for (; !it.Done() && n < index; it.Next()) {
6485 n++;
6486 }
6487 if (it.Done()) {
6488 return Heap::undefined_value();
6489 }
6490
6491 // Calculate the size of the result.
6492 int details_size = kScopeDetailsSize;
6493 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6494
6495 // Fill in scope details.
6496 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6497 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6498
6499 return *Factory::NewJSArrayWithElements(details);
6500}
6501
6502
6503static Object* Runtime_DebugPrintScopes(Arguments args) {
6504 HandleScope scope;
6505 ASSERT(args.length() == 0);
6506
6507#ifdef DEBUG
6508 // Print the scopes for the top frame.
6509 StackFrameLocator locator;
6510 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6511 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6512 it.DebugPrint();
6513 }
6514#endif
6515 return Heap::undefined_value();
6516}
6517
6518
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006519static Object* Runtime_GetCFrames(Arguments args) {
6520 HandleScope scope;
6521 ASSERT(args.length() == 1);
6522 Object* result = Runtime_CheckExecutionState(args);
6523 if (result->IsFailure()) return result;
6524
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006525#if V8_HOST_ARCH_64_BIT
6526 UNIMPLEMENTED();
6527 return Heap::undefined_value();
6528#else
6529
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006530 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006531 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6532 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006533 if (frames_count == OS::kStackWalkError) {
6534 return Heap::undefined_value();
6535 }
6536
6537 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6538 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6539 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6540 for (int i = 0; i < frames_count; i++) {
6541 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6542 frame_value->SetProperty(
6543 *address_str,
6544 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6545 NONE);
6546
6547 // Get the stack walk text for this frame.
6548 Handle<String> frame_text;
6549 if (strlen(frames[i].text) > 0) {
6550 Vector<const char> str(frames[i].text, strlen(frames[i].text));
6551 frame_text = Factory::NewStringFromAscii(str);
6552 }
6553
6554 if (!frame_text.is_null()) {
6555 frame_value->SetProperty(*text_str, *frame_text, NONE);
6556 }
6557
6558 frames_array->set(i, *frame_value);
6559 }
6560 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006561#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006562}
6563
6564
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006565static Object* Runtime_GetThreadCount(Arguments args) {
6566 HandleScope scope;
6567 ASSERT(args.length() == 1);
6568
6569 // Check arguments.
6570 Object* result = Runtime_CheckExecutionState(args);
6571 if (result->IsFailure()) return result;
6572
6573 // Count all archived V8 threads.
6574 int n = 0;
6575 for (ThreadState* thread = ThreadState::FirstInUse();
6576 thread != NULL;
6577 thread = thread->Next()) {
6578 n++;
6579 }
6580
6581 // Total number of threads is current thread and archived threads.
6582 return Smi::FromInt(n + 1);
6583}
6584
6585
6586static const int kThreadDetailsCurrentThreadIndex = 0;
6587static const int kThreadDetailsThreadIdIndex = 1;
6588static const int kThreadDetailsSize = 2;
6589
6590// Return an array with thread details
6591// args[0]: number: break id
6592// args[1]: number: thread index
6593//
6594// The array returned contains the following information:
6595// 0: Is current thread?
6596// 1: Thread id
6597static Object* Runtime_GetThreadDetails(Arguments args) {
6598 HandleScope scope;
6599 ASSERT(args.length() == 2);
6600
6601 // Check arguments.
6602 Object* check = Runtime_CheckExecutionState(args);
6603 if (check->IsFailure()) return check;
6604 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6605
6606 // Allocate array for result.
6607 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6608
6609 // Thread index 0 is current thread.
6610 if (index == 0) {
6611 // Fill the details.
6612 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6613 details->set(kThreadDetailsThreadIdIndex,
6614 Smi::FromInt(ThreadManager::CurrentId()));
6615 } else {
6616 // Find the thread with the requested index.
6617 int n = 1;
6618 ThreadState* thread = ThreadState::FirstInUse();
6619 while (index != n && thread != NULL) {
6620 thread = thread->Next();
6621 n++;
6622 }
6623 if (thread == NULL) {
6624 return Heap::undefined_value();
6625 }
6626
6627 // Fill the details.
6628 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6629 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6630 }
6631
6632 // Convert to JS array and return.
6633 return *Factory::NewJSArrayWithElements(details);
6634}
6635
6636
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006637static Object* Runtime_GetBreakLocations(Arguments args) {
6638 HandleScope scope;
6639 ASSERT(args.length() == 1);
6640
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006641 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6642 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006643 // Find the number of break points
6644 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6645 if (break_locations->IsUndefined()) return Heap::undefined_value();
6646 // Return array as JS array
6647 return *Factory::NewJSArrayWithElements(
6648 Handle<FixedArray>::cast(break_locations));
6649}
6650
6651
6652// Set a break point in a function
6653// args[0]: function
6654// args[1]: number: break source position (within the function source)
6655// args[2]: number: break point object
6656static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6657 HandleScope scope;
6658 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006659 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6660 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006661 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6662 RUNTIME_ASSERT(source_position >= 0);
6663 Handle<Object> break_point_object_arg = args.at<Object>(2);
6664
6665 // Set break point.
6666 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6667
6668 return Heap::undefined_value();
6669}
6670
6671
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006672Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6673 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006674 // Iterate the heap looking for SharedFunctionInfo generated from the
6675 // script. The inner most SharedFunctionInfo containing the source position
6676 // for the requested break point is found.
6677 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6678 // which is found is not compiled it is compiled and the heap is iterated
6679 // again as the compilation might create inner functions from the newly
6680 // compiled function and the actual requested break point might be in one of
6681 // these functions.
6682 bool done = false;
6683 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006684 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006685 Handle<SharedFunctionInfo> target;
6686 // The current candidate for the last function in script:
6687 Handle<SharedFunctionInfo> last;
6688 while (!done) {
6689 HeapIterator iterator;
6690 while (iterator.has_next()) {
6691 HeapObject* obj = iterator.next();
6692 ASSERT(obj != NULL);
6693 if (obj->IsSharedFunctionInfo()) {
6694 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6695 if (shared->script() == *script) {
6696 // If the SharedFunctionInfo found has the requested script data and
6697 // contains the source position it is a candidate.
6698 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006699 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006700 start_position = shared->start_position();
6701 }
6702 if (start_position <= position &&
6703 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006704 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006705 // candidate this is the new candidate.
6706 if (target.is_null()) {
6707 target_start_position = start_position;
6708 target = shared;
6709 } else {
6710 if (target_start_position < start_position &&
6711 shared->end_position() < target->end_position()) {
6712 target_start_position = start_position;
6713 target = shared;
6714 }
6715 }
6716 }
6717
6718 // Keep track of the last function in the script.
6719 if (last.is_null() ||
6720 shared->end_position() > last->start_position()) {
6721 last = shared;
6722 }
6723 }
6724 }
6725 }
6726
6727 // Make sure some candidate is selected.
6728 if (target.is_null()) {
6729 if (!last.is_null()) {
6730 // Position after the last function - use last.
6731 target = last;
6732 } else {
6733 // Unable to find function - possibly script without any function.
6734 return Heap::undefined_value();
6735 }
6736 }
6737
6738 // If the candidate found is compiled we are done. NOTE: when lazy
6739 // compilation of inner functions is introduced some additional checking
6740 // needs to be done here to compile inner functions.
6741 done = target->is_compiled();
6742 if (!done) {
6743 // If the candidate is not compiled compile it to reveal any inner
6744 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006745 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006746 }
6747 }
6748
6749 return *target;
6750}
6751
6752
6753// Change the state of a break point in a script. NOTE: Regarding performance
6754// see the NOTE for GetScriptFromScriptData.
6755// args[0]: script to set break point in
6756// args[1]: number: break source position (within the script source)
6757// args[2]: number: break point object
6758static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6759 HandleScope scope;
6760 ASSERT(args.length() == 3);
6761 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6762 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6763 RUNTIME_ASSERT(source_position >= 0);
6764 Handle<Object> break_point_object_arg = args.at<Object>(2);
6765
6766 // Get the script from the script wrapper.
6767 RUNTIME_ASSERT(wrapper->value()->IsScript());
6768 Handle<Script> script(Script::cast(wrapper->value()));
6769
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006770 Object* result = Runtime::FindSharedFunctionInfoInScript(
6771 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006772 if (!result->IsUndefined()) {
6773 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6774 // Find position within function. The script position might be before the
6775 // source position of the first function.
6776 int position;
6777 if (shared->start_position() > source_position) {
6778 position = 0;
6779 } else {
6780 position = source_position - shared->start_position();
6781 }
6782 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6783 }
6784 return Heap::undefined_value();
6785}
6786
6787
6788// Clear a break point
6789// args[0]: number: break point object
6790static Object* Runtime_ClearBreakPoint(Arguments args) {
6791 HandleScope scope;
6792 ASSERT(args.length() == 1);
6793 Handle<Object> break_point_object_arg = args.at<Object>(0);
6794
6795 // Clear break point.
6796 Debug::ClearBreakPoint(break_point_object_arg);
6797
6798 return Heap::undefined_value();
6799}
6800
6801
6802// Change the state of break on exceptions
6803// args[0]: boolean indicating uncaught exceptions
6804// args[1]: boolean indicating on/off
6805static Object* Runtime_ChangeBreakOnException(Arguments args) {
6806 HandleScope scope;
6807 ASSERT(args.length() == 2);
6808 ASSERT(args[0]->IsNumber());
6809 ASSERT(args[1]->IsBoolean());
6810
6811 // Update break point state
6812 ExceptionBreakType type =
6813 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6814 bool enable = args[1]->ToBoolean()->IsTrue();
6815 Debug::ChangeBreakOnException(type, enable);
6816 return Heap::undefined_value();
6817}
6818
6819
6820// Prepare for stepping
6821// args[0]: break id for checking execution state
6822// args[1]: step action from the enumeration StepAction
6823// args[2]: number of times to perform the step
6824static Object* Runtime_PrepareStep(Arguments args) {
6825 HandleScope scope;
6826 ASSERT(args.length() == 3);
6827 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006828 Object* check = Runtime_CheckExecutionState(args);
6829 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006830 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6831 return Top::Throw(Heap::illegal_argument_symbol());
6832 }
6833
6834 // Get the step action and check validity.
6835 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6836 if (step_action != StepIn &&
6837 step_action != StepNext &&
6838 step_action != StepOut &&
6839 step_action != StepInMin &&
6840 step_action != StepMin) {
6841 return Top::Throw(Heap::illegal_argument_symbol());
6842 }
6843
6844 // Get the number of steps.
6845 int step_count = NumberToInt32(args[2]);
6846 if (step_count < 1) {
6847 return Top::Throw(Heap::illegal_argument_symbol());
6848 }
6849
6850 // Prepare step.
6851 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6852 return Heap::undefined_value();
6853}
6854
6855
6856// Clear all stepping set by PrepareStep.
6857static Object* Runtime_ClearStepping(Arguments args) {
6858 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006859 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006860 Debug::ClearStepping();
6861 return Heap::undefined_value();
6862}
6863
6864
6865// Creates a copy of the with context chain. The copy of the context chain is
6866// is linked to the function context supplied.
6867static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6868 Handle<Context> function_context) {
6869 // At the bottom of the chain. Return the function context to link to.
6870 if (context_chain->is_function_context()) {
6871 return function_context;
6872 }
6873
6874 // Recursively copy the with contexts.
6875 Handle<Context> previous(context_chain->previous());
6876 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6877 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006878 CopyWithContextChain(function_context, previous),
6879 extension,
6880 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006881}
6882
6883
6884// Helper function to find or create the arguments object for
6885// Runtime_DebugEvaluate.
6886static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6887 Handle<JSFunction> function,
6888 Handle<Code> code,
6889 const ScopeInfo<>* sinfo,
6890 Handle<Context> function_context) {
6891 // Try to find the value of 'arguments' to pass as parameter. If it is not
6892 // found (that is the debugged function does not reference 'arguments' and
6893 // does not support eval) then create an 'arguments' object.
6894 int index;
6895 if (sinfo->number_of_stack_slots() > 0) {
6896 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6897 if (index != -1) {
6898 return Handle<Object>(frame->GetExpression(index));
6899 }
6900 }
6901
6902 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6903 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6904 NULL);
6905 if (index != -1) {
6906 return Handle<Object>(function_context->get(index));
6907 }
6908 }
6909
6910 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006911 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6912 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006913 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006914 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006915 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006916 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006917 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006918 return arguments;
6919}
6920
6921
6922// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006923// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006924// extension part has all the parameters and locals of the function on the
6925// stack frame. A function which calls eval with the code to evaluate is then
6926// compiled in this context and called in this context. As this context
6927// replaces the context of the function on the stack frame a new (empty)
6928// function is created as well to be used as the closure for the context.
6929// This function and the context acts as replacements for the function on the
6930// stack frame presenting the same view of the values of parameters and
6931// local variables as if the piece of JavaScript was evaluated at the point
6932// where the function on the stack frame is currently stopped.
6933static Object* Runtime_DebugEvaluate(Arguments args) {
6934 HandleScope scope;
6935
6936 // Check the execution state and decode arguments frame and source to be
6937 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006938 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006939 Object* check_result = Runtime_CheckExecutionState(args);
6940 if (check_result->IsFailure()) return check_result;
6941 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6942 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006943 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6944
6945 // Handle the processing of break.
6946 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006947
6948 // Get the frame where the debugging is performed.
6949 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6950 JavaScriptFrameIterator it(id);
6951 JavaScriptFrame* frame = it.frame();
6952 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6953 Handle<Code> code(function->code());
6954 ScopeInfo<> sinfo(*code);
6955
6956 // Traverse the saved contexts chain to find the active context for the
6957 // selected frame.
6958 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006959 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006960 save = save->prev();
6961 }
6962 ASSERT(save != NULL);
6963 SaveContext savex;
6964 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006965
6966 // Create the (empty) function replacing the function on the stack frame for
6967 // the purpose of evaluating in the context created below. It is important
6968 // that this function does not describe any parameters and local variables
6969 // in the context. If it does then this will cause problems with the lookup
6970 // in Context::Lookup, where context slots for parameters and local variables
6971 // are looked at before the extension object.
6972 Handle<JSFunction> go_between =
6973 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
6974 go_between->set_context(function->context());
6975#ifdef DEBUG
6976 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
6977 ASSERT(go_between_sinfo.number_of_parameters() == 0);
6978 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
6979#endif
6980
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006981 // Materialize the content of the local scope into a JSObject.
6982 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006983
6984 // Allocate a new context for the debug evaluation and set the extension
6985 // object build.
6986 Handle<Context> context =
6987 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006988 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006989 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006990 Handle<Context> frame_context(Context::cast(frame->context()));
6991 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006992 context = CopyWithContextChain(frame_context, context);
6993
6994 // Wrap the evaluation statement in a new function compiled in the newly
6995 // created context. The function has one parameter which has to be called
6996 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00006997 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006998 // function(arguments,__source__) {return eval(__source__);}
6999 static const char* source_str =
7000 "function(arguments,__source__){return eval(__source__);}";
7001 static const int source_str_length = strlen(source_str);
7002 Handle<String> function_source =
7003 Factory::NewStringFromAscii(Vector<const char>(source_str,
7004 source_str_length));
7005 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007006 Compiler::CompileEval(function_source,
7007 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007008 context->IsGlobalContext(),
7009 false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007010 if (boilerplate.is_null()) return Failure::Exception();
7011 Handle<JSFunction> compiled_function =
7012 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7013
7014 // Invoke the result of the compilation to get the evaluation function.
7015 bool has_pending_exception;
7016 Handle<Object> receiver(frame->receiver());
7017 Handle<Object> evaluation_function =
7018 Execution::Call(compiled_function, receiver, 0, NULL,
7019 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007020 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007021
7022 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7023 function_context);
7024
7025 // Invoke the evaluation function and return the result.
7026 const int argc = 2;
7027 Object** argv[argc] = { arguments.location(),
7028 Handle<Object>::cast(source).location() };
7029 Handle<Object> result =
7030 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7031 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007032 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007033
7034 // Skip the global proxy as it has no properties and always delegates to the
7035 // real global object.
7036 if (result->IsJSGlobalProxy()) {
7037 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7038 }
7039
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007040 return *result;
7041}
7042
7043
7044static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7045 HandleScope scope;
7046
7047 // Check the execution state and decode arguments frame and source to be
7048 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007049 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007050 Object* check_result = Runtime_CheckExecutionState(args);
7051 if (check_result->IsFailure()) return check_result;
7052 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007053 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7054
7055 // Handle the processing of break.
7056 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007057
7058 // Enter the top context from before the debugger was invoked.
7059 SaveContext save;
7060 SaveContext* top = &save;
7061 while (top != NULL && *top->context() == *Debug::debug_context()) {
7062 top = top->prev();
7063 }
7064 if (top != NULL) {
7065 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007066 }
7067
7068 // Get the global context now set to the top context from before the
7069 // debugger was invoked.
7070 Handle<Context> context = Top::global_context();
7071
7072 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007073 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007074 Handle<JSFunction>(Compiler::CompileEval(source,
7075 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007076 true,
7077 false));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007078 if (boilerplate.is_null()) return Failure::Exception();
7079 Handle<JSFunction> compiled_function =
7080 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7081 context));
7082
7083 // Invoke the result of the compilation to get the evaluation function.
7084 bool has_pending_exception;
7085 Handle<Object> receiver = Top::global();
7086 Handle<Object> result =
7087 Execution::Call(compiled_function, receiver, 0, NULL,
7088 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007089 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007090 return *result;
7091}
7092
7093
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007094static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7095 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007096 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007097
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007098 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007099 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007100
7101 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007102 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007103 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7104 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7105 // because using
7106 // instances->set(i, *GetScriptWrapper(script))
7107 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7108 // already have deferenced the instances handle.
7109 Handle<JSValue> wrapper = GetScriptWrapper(script);
7110 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007111 }
7112
7113 // Return result as a JS array.
7114 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7115 Handle<JSArray>::cast(result)->SetContent(*instances);
7116 return *result;
7117}
7118
7119
7120// Helper function used by Runtime_DebugReferencedBy below.
7121static int DebugReferencedBy(JSObject* target,
7122 Object* instance_filter, int max_references,
7123 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007124 JSFunction* arguments_function) {
7125 NoHandleAllocation ha;
7126 AssertNoAllocation no_alloc;
7127
7128 // Iterate the heap.
7129 int count = 0;
7130 JSObject* last = NULL;
7131 HeapIterator iterator;
7132 while (iterator.has_next() &&
7133 (max_references == 0 || count < max_references)) {
7134 // Only look at all JSObjects.
7135 HeapObject* heap_obj = iterator.next();
7136 if (heap_obj->IsJSObject()) {
7137 // Skip context extension objects and argument arrays as these are
7138 // checked in the context of functions using them.
7139 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007140 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007141 obj->map()->constructor() == arguments_function) {
7142 continue;
7143 }
7144
7145 // Check if the JS object has a reference to the object looked for.
7146 if (obj->ReferencesObject(target)) {
7147 // Check instance filter if supplied. This is normally used to avoid
7148 // references from mirror objects (see Runtime_IsInPrototypeChain).
7149 if (!instance_filter->IsUndefined()) {
7150 Object* V = obj;
7151 while (true) {
7152 Object* prototype = V->GetPrototype();
7153 if (prototype->IsNull()) {
7154 break;
7155 }
7156 if (instance_filter == prototype) {
7157 obj = NULL; // Don't add this object.
7158 break;
7159 }
7160 V = prototype;
7161 }
7162 }
7163
7164 if (obj != NULL) {
7165 // Valid reference found add to instance array if supplied an update
7166 // count.
7167 if (instances != NULL && count < instances_size) {
7168 instances->set(count, obj);
7169 }
7170 last = obj;
7171 count++;
7172 }
7173 }
7174 }
7175 }
7176
7177 // Check for circular reference only. This can happen when the object is only
7178 // referenced from mirrors and has a circular reference in which case the
7179 // object is not really alive and would have been garbage collected if not
7180 // referenced from the mirror.
7181 if (count == 1 && last == target) {
7182 count = 0;
7183 }
7184
7185 // Return the number of referencing objects found.
7186 return count;
7187}
7188
7189
7190// Scan the heap for objects with direct references to an object
7191// args[0]: the object to find references to
7192// args[1]: constructor function for instances to exclude (Mirror)
7193// args[2]: the the maximum number of objects to return
7194static Object* Runtime_DebugReferencedBy(Arguments args) {
7195 ASSERT(args.length() == 3);
7196
7197 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007198 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007199
7200 // Check parameters.
7201 CONVERT_CHECKED(JSObject, target, args[0]);
7202 Object* instance_filter = args[1];
7203 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7204 instance_filter->IsJSObject());
7205 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7206 RUNTIME_ASSERT(max_references >= 0);
7207
7208 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007209 JSObject* arguments_boilerplate =
7210 Top::context()->global_context()->arguments_boilerplate();
7211 JSFunction* arguments_function =
7212 JSFunction::cast(arguments_boilerplate->map()->constructor());
7213
7214 // Get the number of referencing objects.
7215 int count;
7216 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007217 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007218
7219 // Allocate an array to hold the result.
7220 Object* object = Heap::AllocateFixedArray(count);
7221 if (object->IsFailure()) return object;
7222 FixedArray* instances = FixedArray::cast(object);
7223
7224 // Fill the referencing objects.
7225 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007226 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007227
7228 // Return result as JS array.
7229 Object* result =
7230 Heap::AllocateJSObject(
7231 Top::context()->global_context()->array_function());
7232 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7233 return result;
7234}
7235
7236
7237// Helper function used by Runtime_DebugConstructedBy below.
7238static int DebugConstructedBy(JSFunction* constructor, int max_references,
7239 FixedArray* instances, int instances_size) {
7240 AssertNoAllocation no_alloc;
7241
7242 // Iterate the heap.
7243 int count = 0;
7244 HeapIterator iterator;
7245 while (iterator.has_next() &&
7246 (max_references == 0 || count < max_references)) {
7247 // Only look at all JSObjects.
7248 HeapObject* heap_obj = iterator.next();
7249 if (heap_obj->IsJSObject()) {
7250 JSObject* obj = JSObject::cast(heap_obj);
7251 if (obj->map()->constructor() == constructor) {
7252 // Valid reference found add to instance array if supplied an update
7253 // count.
7254 if (instances != NULL && count < instances_size) {
7255 instances->set(count, obj);
7256 }
7257 count++;
7258 }
7259 }
7260 }
7261
7262 // Return the number of referencing objects found.
7263 return count;
7264}
7265
7266
7267// Scan the heap for objects constructed by a specific function.
7268// args[0]: the constructor to find instances of
7269// args[1]: the the maximum number of objects to return
7270static Object* Runtime_DebugConstructedBy(Arguments args) {
7271 ASSERT(args.length() == 2);
7272
7273 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007274 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007275
7276 // Check parameters.
7277 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7278 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7279 RUNTIME_ASSERT(max_references >= 0);
7280
7281 // Get the number of referencing objects.
7282 int count;
7283 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7284
7285 // Allocate an array to hold the result.
7286 Object* object = Heap::AllocateFixedArray(count);
7287 if (object->IsFailure()) return object;
7288 FixedArray* instances = FixedArray::cast(object);
7289
7290 // Fill the referencing objects.
7291 count = DebugConstructedBy(constructor, max_references, instances, count);
7292
7293 // Return result as JS array.
7294 Object* result =
7295 Heap::AllocateJSObject(
7296 Top::context()->global_context()->array_function());
7297 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7298 return result;
7299}
7300
7301
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007302// Find the effective prototype object as returned by __proto__.
7303// args[0]: the object to find the prototype for.
7304static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007305 ASSERT(args.length() == 1);
7306
7307 CONVERT_CHECKED(JSObject, obj, args[0]);
7308
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007309 // Use the __proto__ accessor.
7310 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007311}
7312
7313
7314static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007315 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007316 CPU::DebugBreak();
7317 return Heap::undefined_value();
7318}
7319
7320
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007321static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
7322#ifdef DEBUG
7323 HandleScope scope;
7324 ASSERT(args.length() == 1);
7325 // Get the function and make sure it is compiled.
7326 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7327 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7328 return Failure::Exception();
7329 }
7330 func->code()->PrintLn();
7331#endif // DEBUG
7332 return Heap::undefined_value();
7333}
ager@chromium.org9085a012009-05-11 19:22:57 +00007334
7335
7336static Object* Runtime_FunctionGetInferredName(Arguments args) {
7337 NoHandleAllocation ha;
7338 ASSERT(args.length() == 1);
7339
7340 CONVERT_CHECKED(JSFunction, f, args[0]);
7341 return f->shared()->inferred_name();
7342}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007343#endif // ENABLE_DEBUGGER_SUPPORT
7344
7345
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007346// Finds the script object from the script data. NOTE: This operation uses
7347// heap traversal to find the function generated for the source position
7348// for the requested break point. For lazily compiled functions several heap
7349// traversals might be required rendering this operation as a rather slow
7350// operation. However for setting break points which is normally done through
7351// some kind of user interaction the performance is not crucial.
7352static Handle<Object> Runtime_GetScriptFromScriptName(
7353 Handle<String> script_name) {
7354 // Scan the heap for Script objects to find the script with the requested
7355 // script data.
7356 Handle<Script> script;
7357 HeapIterator iterator;
7358 while (script.is_null() && iterator.has_next()) {
7359 HeapObject* obj = iterator.next();
7360 // If a script is found check if it has the script data requested.
7361 if (obj->IsScript()) {
7362 if (Script::cast(obj)->name()->IsString()) {
7363 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7364 script = Handle<Script>(Script::cast(obj));
7365 }
7366 }
7367 }
7368 }
7369
7370 // If no script with the requested script data is found return undefined.
7371 if (script.is_null()) return Factory::undefined_value();
7372
7373 // Return the script found.
7374 return GetScriptWrapper(script);
7375}
7376
7377
7378// Get the script object from script data. NOTE: Regarding performance
7379// see the NOTE for GetScriptFromScriptData.
7380// args[0]: script data for the script to find the source for
7381static Object* Runtime_GetScript(Arguments args) {
7382 HandleScope scope;
7383
7384 ASSERT(args.length() == 1);
7385
7386 CONVERT_CHECKED(String, script_name, args[0]);
7387
7388 // Find the requested script.
7389 Handle<Object> result =
7390 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7391 return *result;
7392}
7393
7394
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007395// Determines whether the given stack frame should be displayed in
7396// a stack trace. The caller is the error constructor that asked
7397// for the stack trace to be collected. The first time a construct
7398// call to this function is encountered it is skipped. The seen_caller
7399// in/out parameter is used to remember if the caller has been seen
7400// yet.
7401static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
7402 bool* seen_caller) {
7403 // Only display JS frames.
7404 if (!raw_frame->is_java_script())
7405 return false;
7406 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7407 Object* raw_fun = frame->function();
7408 // Not sure when this can happen but skip it just in case.
7409 if (!raw_fun->IsJSFunction())
7410 return false;
7411 if ((raw_fun == caller) && !(*seen_caller) && frame->IsConstructor()) {
7412 *seen_caller = true;
7413 return false;
7414 }
7415 // Skip the most obvious builtin calls. Some builtin calls (such as
7416 // Number.ADD which is invoked using 'call') are very difficult to
7417 // recognize so we're leaving them in for now.
7418 return !frame->receiver()->IsJSBuiltinsObject();
7419}
7420
7421
7422// Collect the raw data for a stack trace. Returns an array of three
7423// element segments each containing a receiver, function and native
7424// code offset.
7425static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007426 ASSERT_EQ(args.length(), 2);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007427 Object* caller = args[0];
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007428 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
7429
7430 HandleScope scope;
7431
7432 int initial_size = limit < 10 ? limit : 10;
7433 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007434
7435 StackFrameIterator iter;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007436 bool seen_caller = false;
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007437 int cursor = 0;
7438 int frames_seen = 0;
7439 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007440 StackFrame* raw_frame = iter.frame();
7441 if (ShowFrameInStackTrace(raw_frame, caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007442 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007443 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007444 Object* recv = frame->receiver();
7445 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007446 Address pc = frame->pc();
7447 Address start = frame->code()->address();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007448 Smi* offset = Smi::FromInt(pc - start);
7449 FixedArray* elements = result->elements();
7450 if (cursor + 2 < elements->length()) {
7451 elements->set(cursor++, recv);
7452 elements->set(cursor++, fun);
7453 elements->set(cursor++, offset, SKIP_WRITE_BARRIER);
7454 } else {
7455 HandleScope scope;
7456 SetElement(result, cursor++, Handle<Object>(recv));
7457 SetElement(result, cursor++, Handle<Object>(fun));
7458 SetElement(result, cursor++, Handle<Smi>(offset));
7459 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007460 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007461 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007462 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007463
7464 result->set_length(Smi::FromInt(cursor), SKIP_WRITE_BARRIER);
7465
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007466 return *result;
7467}
7468
7469
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007470static Object* Runtime_Abort(Arguments args) {
7471 ASSERT(args.length() == 2);
7472 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7473 Smi::cast(args[1])->value());
7474 Top::PrintStack();
7475 OS::Abort();
7476 UNREACHABLE();
7477 return NULL;
7478}
7479
7480
kasper.lund44510672008-07-25 07:37:58 +00007481#ifdef DEBUG
7482// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7483// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007484static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007485 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007486 HandleScope scope;
7487 Handle<JSArray> result = Factory::NewJSArray(0);
7488 int index = 0;
7489#define ADD_ENTRY(Name, argc) \
7490 { \
7491 HandleScope inner; \
7492 Handle<String> name = \
7493 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
7494 Handle<JSArray> pair = Factory::NewJSArray(0); \
7495 SetElement(pair, 0, name); \
7496 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7497 SetElement(result, index++, pair); \
7498 }
7499 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7500#undef ADD_ENTRY
7501 return *result;
7502}
kasper.lund44510672008-07-25 07:37:58 +00007503#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007504
7505
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007506static Object* Runtime_Log(Arguments args) {
7507 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007508 CONVERT_CHECKED(String, format, args[0]);
7509 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007510 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007511 Logger::LogRuntime(chars, elms);
7512 return Heap::undefined_value();
7513}
7514
7515
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007516static Object* Runtime_IS_VAR(Arguments args) {
7517 UNREACHABLE(); // implemented as macro in the parser
7518 return NULL;
7519}
7520
7521
7522// ----------------------------------------------------------------------------
7523// Implementation of Runtime
7524
7525#define F(name, nargs) \
7526 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
7527 static_cast<int>(Runtime::k##name) },
7528
7529static Runtime::Function Runtime_functions[] = {
7530 RUNTIME_FUNCTION_LIST(F)
7531 { NULL, NULL, NULL, 0, -1 }
7532};
7533
7534#undef F
7535
7536
7537Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7538 ASSERT(0 <= fid && fid < kNofFunctions);
7539 return &Runtime_functions[fid];
7540}
7541
7542
7543Runtime::Function* Runtime::FunctionForName(const char* name) {
7544 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7545 if (strcmp(f->name, name) == 0) {
7546 return f;
7547 }
7548 }
7549 return NULL;
7550}
7551
7552
7553void Runtime::PerformGC(Object* result) {
7554 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007555 if (failure->IsRetryAfterGC()) {
7556 // Try to do a garbage collection; ignore it if it fails. The C
7557 // entry stub will throw an out-of-memory exception in that case.
7558 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7559 } else {
7560 // Handle last resort GC and make sure to allow future allocations
7561 // to grow the heap without causing GCs (if possible).
7562 Counters::gc_last_resort_from_js.Increment();
7563 Heap::CollectAllGarbage();
7564 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007565}
7566
7567
7568} } // namespace v8::internal