blob: e0eab744902e235cbb98f4fa919992fda9791c59 [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 {
171 Dictionary* element_dictionary = copy->element_dictionary();
172 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.org7be3c992009-03-12 07:19:55 +00001019 // Due to the way the JS files are constructed this must be less than the
1020 // length of a string, i.e. it is always a Smi. We check anyway for security.
1021 CONVERT_CHECKED(Smi, 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());
1024 RUNTIME_ASSERT(index->value() >= 0);
1025 RUNTIME_ASSERT(index->value() <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001026 Handle<Object> result = RegExpImpl::Exec(regexp,
1027 subject,
1028 index->value(),
1029 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 }
2601 // Lookup cache miss. Perform lookup and update the cache if
2602 // appropriate.
2603 LookupResult result;
2604 receiver->LocalLookup(key, &result);
2605 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2606 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002607 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002608 Object* value = receiver->FastPropertyAt(offset);
2609 return value->IsTheHole() ? Heap::undefined_value() : value;
2610 }
2611 } else {
2612 // Attempt dictionary lookup.
2613 Dictionary* dictionary = receiver->property_dictionary();
2614 int entry = dictionary->FindStringEntry(key);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002615 if ((entry != Dictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002616 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002617 Object* value = dictionary->ValueAt(entry);
2618 if (receiver->IsGlobalObject()) {
2619 value = JSGlobalPropertyCell::cast(value)->value();
2620 }
2621 return value;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002622 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002623 }
2624 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002625
2626 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002627 return Runtime::GetObjectProperty(args.at<Object>(0),
2628 args.at<Object>(1));
2629}
2630
2631
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002632Object* Runtime::SetObjectProperty(Handle<Object> object,
2633 Handle<Object> key,
2634 Handle<Object> value,
2635 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002636 HandleScope scope;
2637
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002638 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002639 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002640 Handle<Object> error =
2641 Factory::NewTypeError("non_object_property_store",
2642 HandleVector(args, 2));
2643 return Top::Throw(*error);
2644 }
2645
2646 // If the object isn't a JavaScript object, we ignore the store.
2647 if (!object->IsJSObject()) return *value;
2648
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002649 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2650
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002651 // Check if the given key is an array index.
2652 uint32_t index;
2653 if (Array::IndexFromObject(*key, &index)) {
2654 ASSERT(attr == NONE);
2655
2656 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2657 // of a string using [] notation. We need to support this too in
2658 // JavaScript.
2659 // In the case of a String object we just need to redirect the assignment to
2660 // the underlying string if the index is in range. Since the underlying
2661 // string does nothing with the assignment then we can ignore such
2662 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002663 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002664 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002665 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002666
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002667 Handle<Object> result = SetElement(js_object, index, value);
2668 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002669 return *value;
2670 }
2671
2672 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002673 Handle<Object> result;
2674 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002675 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002676 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002677 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002678 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002679 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002680 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002681 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002682 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002683 return *value;
2684 }
2685
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002686 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002687 bool has_pending_exception = false;
2688 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2689 if (has_pending_exception) return Failure::Exception();
2690 Handle<String> name = Handle<String>::cast(converted);
2691
2692 if (name->AsArrayIndex(&index)) {
2693 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002694 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002695 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002696 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002697 }
2698}
2699
2700
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002701Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2702 Handle<Object> key,
2703 Handle<Object> value,
2704 PropertyAttributes attr) {
2705 HandleScope scope;
2706
2707 // Check if the given key is an array index.
2708 uint32_t index;
2709 if (Array::IndexFromObject(*key, &index)) {
2710 ASSERT(attr == NONE);
2711
2712 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2713 // of a string using [] notation. We need to support this too in
2714 // JavaScript.
2715 // In the case of a String object we just need to redirect the assignment to
2716 // the underlying string if the index is in range. Since the underlying
2717 // string does nothing with the assignment then we can ignore such
2718 // assignments.
2719 if (js_object->IsStringObjectWithCharacterAt(index)) {
2720 return *value;
2721 }
2722
2723 return js_object->SetElement(index, *value);
2724 }
2725
2726 if (key->IsString()) {
2727 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2728 ASSERT(attr == NONE);
2729 return js_object->SetElement(index, *value);
2730 } else {
2731 Handle<String> key_string = Handle<String>::cast(key);
2732 key_string->TryFlattenIfNotFlat();
2733 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2734 *value,
2735 attr);
2736 }
2737 }
2738
2739 // Call-back into JavaScript to convert the key to a string.
2740 bool has_pending_exception = false;
2741 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2742 if (has_pending_exception) return Failure::Exception();
2743 Handle<String> name = Handle<String>::cast(converted);
2744
2745 if (name->AsArrayIndex(&index)) {
2746 ASSERT(attr == NONE);
2747 return js_object->SetElement(index, *value);
2748 } else {
2749 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2750 }
2751}
2752
2753
ager@chromium.orge2902be2009-06-08 12:21:35 +00002754Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2755 Handle<Object> key) {
2756 HandleScope scope;
2757
2758 // Check if the given key is an array index.
2759 uint32_t index;
2760 if (Array::IndexFromObject(*key, &index)) {
2761 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2762 // characters of a string using [] notation. In the case of a
2763 // String object we just need to redirect the deletion to the
2764 // underlying string if the index is in range. Since the
2765 // underlying string does nothing with the deletion, we can ignore
2766 // such deletions.
2767 if (js_object->IsStringObjectWithCharacterAt(index)) {
2768 return Heap::true_value();
2769 }
2770
2771 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2772 }
2773
2774 Handle<String> key_string;
2775 if (key->IsString()) {
2776 key_string = Handle<String>::cast(key);
2777 } else {
2778 // Call-back into JavaScript to convert the key to a string.
2779 bool has_pending_exception = false;
2780 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2781 if (has_pending_exception) return Failure::Exception();
2782 key_string = Handle<String>::cast(converted);
2783 }
2784
2785 key_string->TryFlattenIfNotFlat();
2786 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2787}
2788
2789
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002790static Object* Runtime_SetProperty(Arguments args) {
2791 NoHandleAllocation ha;
2792 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2793
2794 Handle<Object> object = args.at<Object>(0);
2795 Handle<Object> key = args.at<Object>(1);
2796 Handle<Object> value = args.at<Object>(2);
2797
2798 // Compute attributes.
2799 PropertyAttributes attributes = NONE;
2800 if (args.length() == 4) {
2801 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002802 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002803 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002804 RUNTIME_ASSERT(
2805 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2806 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002807 }
2808 return Runtime::SetObjectProperty(object, key, value, attributes);
2809}
2810
2811
2812// Set a local property, even if it is READ_ONLY. If the property does not
2813// exist, it will be added with attributes NONE.
2814static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2815 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002816 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002817 CONVERT_CHECKED(JSObject, object, args[0]);
2818 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002819 // Compute attributes.
2820 PropertyAttributes attributes = NONE;
2821 if (args.length() == 4) {
2822 CONVERT_CHECKED(Smi, value_obj, args[3]);
2823 int unchecked_value = value_obj->value();
2824 // Only attribute bits should be set.
2825 RUNTIME_ASSERT(
2826 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2827 attributes = static_cast<PropertyAttributes>(unchecked_value);
2828 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002829
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002830 return object->
2831 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002832}
2833
2834
2835static Object* Runtime_DeleteProperty(Arguments args) {
2836 NoHandleAllocation ha;
2837 ASSERT(args.length() == 2);
2838
2839 CONVERT_CHECKED(JSObject, object, args[0]);
2840 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002841 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002842}
2843
2844
ager@chromium.org9085a012009-05-11 19:22:57 +00002845static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2846 Handle<String> key) {
2847 if (object->HasLocalProperty(*key)) return Heap::true_value();
2848 // Handle hidden prototypes. If there's a hidden prototype above this thing
2849 // then we have to check it for properties, because they are supposed to
2850 // look like they are on this object.
2851 Handle<Object> proto(object->GetPrototype());
2852 if (proto->IsJSObject() &&
2853 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2854 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2855 }
2856 return Heap::false_value();
2857}
2858
2859
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002860static Object* Runtime_HasLocalProperty(Arguments args) {
2861 NoHandleAllocation ha;
2862 ASSERT(args.length() == 2);
2863 CONVERT_CHECKED(String, key, args[1]);
2864
ager@chromium.org9085a012009-05-11 19:22:57 +00002865 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002866 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002867 if (obj->IsJSObject()) {
2868 JSObject* object = JSObject::cast(obj);
2869 // Fast case - no interceptors.
2870 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2871 // Slow case. Either it's not there or we have an interceptor. We should
2872 // have handles for this kind of deal.
2873 HandleScope scope;
2874 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2875 Handle<String>(key));
2876 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002877 // Well, there is one exception: Handle [] on strings.
2878 uint32_t index;
2879 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002880 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002881 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002882 return Heap::true_value();
2883 }
2884 }
2885 return Heap::false_value();
2886}
2887
2888
2889static Object* Runtime_HasProperty(Arguments args) {
2890 NoHandleAllocation na;
2891 ASSERT(args.length() == 2);
2892
2893 // Only JS objects can have properties.
2894 if (args[0]->IsJSObject()) {
2895 JSObject* object = JSObject::cast(args[0]);
2896 CONVERT_CHECKED(String, key, args[1]);
2897 if (object->HasProperty(key)) return Heap::true_value();
2898 }
2899 return Heap::false_value();
2900}
2901
2902
2903static Object* Runtime_HasElement(Arguments args) {
2904 NoHandleAllocation na;
2905 ASSERT(args.length() == 2);
2906
2907 // Only JS objects can have elements.
2908 if (args[0]->IsJSObject()) {
2909 JSObject* object = JSObject::cast(args[0]);
2910 CONVERT_CHECKED(Smi, index_obj, args[1]);
2911 uint32_t index = index_obj->value();
2912 if (object->HasElement(index)) return Heap::true_value();
2913 }
2914 return Heap::false_value();
2915}
2916
2917
2918static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2919 NoHandleAllocation ha;
2920 ASSERT(args.length() == 2);
2921
2922 CONVERT_CHECKED(JSObject, object, args[0]);
2923 CONVERT_CHECKED(String, key, args[1]);
2924
2925 uint32_t index;
2926 if (key->AsArrayIndex(&index)) {
2927 return Heap::ToBoolean(object->HasElement(index));
2928 }
2929
ager@chromium.org870a0b62008-11-04 11:43:05 +00002930 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2931 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002932}
2933
2934
2935static Object* Runtime_GetPropertyNames(Arguments args) {
2936 HandleScope scope;
2937 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002938 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002939 return *GetKeysFor(object);
2940}
2941
2942
2943// Returns either a FixedArray as Runtime_GetPropertyNames,
2944// or, if the given object has an enum cache that contains
2945// all enumerable properties of the object and its prototypes
2946// have none, the map of the object. This is used to speed up
2947// the check for deletions during a for-in.
2948static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2949 ASSERT(args.length() == 1);
2950
2951 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2952
2953 if (raw_object->IsSimpleEnum()) return raw_object->map();
2954
2955 HandleScope scope;
2956 Handle<JSObject> object(raw_object);
2957 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2958
2959 // Test again, since cache may have been built by preceding call.
2960 if (object->IsSimpleEnum()) return object->map();
2961
2962 return *content;
2963}
2964
2965
2966static Object* Runtime_GetArgumentsProperty(Arguments args) {
2967 NoHandleAllocation ha;
2968 ASSERT(args.length() == 1);
2969
2970 // Compute the frame holding the arguments.
2971 JavaScriptFrameIterator it;
2972 it.AdvanceToArgumentsFrame();
2973 JavaScriptFrame* frame = it.frame();
2974
2975 // Get the actual number of provided arguments.
2976 const uint32_t n = frame->GetProvidedParametersCount();
2977
2978 // Try to convert the key to an index. If successful and within
2979 // index return the the argument from the frame.
2980 uint32_t index;
2981 if (Array::IndexFromObject(args[0], &index) && index < n) {
2982 return frame->GetParameter(index);
2983 }
2984
2985 // Convert the key to a string.
2986 HandleScope scope;
2987 bool exception = false;
2988 Handle<Object> converted =
2989 Execution::ToString(args.at<Object>(0), &exception);
2990 if (exception) return Failure::Exception();
2991 Handle<String> key = Handle<String>::cast(converted);
2992
2993 // Try to convert the string key into an array index.
2994 if (key->AsArrayIndex(&index)) {
2995 if (index < n) {
2996 return frame->GetParameter(index);
2997 } else {
2998 return Top::initial_object_prototype()->GetElement(index);
2999 }
3000 }
3001
3002 // Handle special arguments properties.
3003 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3004 if (key->Equals(Heap::callee_symbol())) return frame->function();
3005
3006 // Lookup in the initial Object.prototype object.
3007 return Top::initial_object_prototype()->GetProperty(*key);
3008}
3009
3010
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003011static Object* Runtime_ToFastProperties(Arguments args) {
3012 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003013 Handle<Object> object = args.at<Object>(0);
3014 if (object->IsJSObject()) {
3015 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3016 js_object->TransformToFastProperties(0);
3017 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003018 return *object;
3019}
3020
3021
3022static Object* Runtime_ToSlowProperties(Arguments args) {
3023 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003024 Handle<Object> object = args.at<Object>(0);
3025 if (object->IsJSObject()) {
3026 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3027 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
3028 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003029 return *object;
3030}
3031
3032
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003033static Object* Runtime_ToBool(Arguments args) {
3034 NoHandleAllocation ha;
3035 ASSERT(args.length() == 1);
3036
3037 return args[0]->ToBoolean();
3038}
3039
3040
3041// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3042// Possible optimizations: put the type string into the oddballs.
3043static Object* Runtime_Typeof(Arguments args) {
3044 NoHandleAllocation ha;
3045
3046 Object* obj = args[0];
3047 if (obj->IsNumber()) return Heap::number_symbol();
3048 HeapObject* heap_obj = HeapObject::cast(obj);
3049
3050 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003051 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003052
3053 InstanceType instance_type = heap_obj->map()->instance_type();
3054 if (instance_type < FIRST_NONSTRING_TYPE) {
3055 return Heap::string_symbol();
3056 }
3057
3058 switch (instance_type) {
3059 case ODDBALL_TYPE:
3060 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3061 return Heap::boolean_symbol();
3062 }
3063 if (heap_obj->IsNull()) {
3064 return Heap::object_symbol();
3065 }
3066 ASSERT(heap_obj->IsUndefined());
3067 return Heap::undefined_symbol();
3068 case JS_FUNCTION_TYPE:
3069 return Heap::function_symbol();
3070 default:
3071 // For any kind of object not handled above, the spec rule for
3072 // host objects gives that it is okay to return "object"
3073 return Heap::object_symbol();
3074 }
3075}
3076
3077
3078static Object* Runtime_StringToNumber(Arguments args) {
3079 NoHandleAllocation ha;
3080 ASSERT(args.length() == 1);
3081 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003082 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003083 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3084}
3085
3086
3087static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3088 NoHandleAllocation ha;
3089 ASSERT(args.length() == 1);
3090
3091 CONVERT_CHECKED(JSArray, codes, args[0]);
3092 int length = Smi::cast(codes->length())->value();
3093
3094 // Check if the string can be ASCII.
3095 int i;
3096 for (i = 0; i < length; i++) {
3097 Object* element = codes->GetElement(i);
3098 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3099 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3100 break;
3101 }
3102
3103 Object* object = NULL;
3104 if (i == length) { // The string is ASCII.
3105 object = Heap::AllocateRawAsciiString(length);
3106 } else { // The string is not ASCII.
3107 object = Heap::AllocateRawTwoByteString(length);
3108 }
3109
3110 if (object->IsFailure()) return object;
3111 String* result = String::cast(object);
3112 for (int i = 0; i < length; i++) {
3113 Object* element = codes->GetElement(i);
3114 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003115 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003116 }
3117 return result;
3118}
3119
3120
3121// kNotEscaped is generated by the following:
3122//
3123// #!/bin/perl
3124// for (my $i = 0; $i < 256; $i++) {
3125// print "\n" if $i % 16 == 0;
3126// my $c = chr($i);
3127// my $escaped = 1;
3128// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3129// print $escaped ? "0, " : "1, ";
3130// }
3131
3132
3133static bool IsNotEscaped(uint16_t character) {
3134 // Only for 8 bit characters, the rest are always escaped (in a different way)
3135 ASSERT(character < 256);
3136 static const char kNotEscaped[256] = {
3137 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3138 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3139 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3140 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3141 1, 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, 1,
3143 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3144 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3152 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3153 };
3154 return kNotEscaped[character] != 0;
3155}
3156
3157
3158static Object* Runtime_URIEscape(Arguments args) {
3159 const char hex_chars[] = "0123456789ABCDEF";
3160 NoHandleAllocation ha;
3161 ASSERT(args.length() == 1);
3162 CONVERT_CHECKED(String, source, args[0]);
3163
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003164 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003165
3166 int escaped_length = 0;
3167 int length = source->length();
3168 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003169 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003170 buffer->Reset(source);
3171 while (buffer->has_more()) {
3172 uint16_t character = buffer->GetNext();
3173 if (character >= 256) {
3174 escaped_length += 6;
3175 } else if (IsNotEscaped(character)) {
3176 escaped_length++;
3177 } else {
3178 escaped_length += 3;
3179 }
3180 // We don't allow strings that are longer than Smi range.
3181 if (!Smi::IsValid(escaped_length)) {
3182 Top::context()->mark_out_of_memory();
3183 return Failure::OutOfMemoryException();
3184 }
3185 }
3186 }
3187 // No length change implies no change. Return original string if no change.
3188 if (escaped_length == length) {
3189 return source;
3190 }
3191 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3192 if (o->IsFailure()) return o;
3193 String* destination = String::cast(o);
3194 int dest_position = 0;
3195
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003196 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003197 buffer->Rewind();
3198 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003199 uint16_t chr = buffer->GetNext();
3200 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003201 destination->Set(dest_position, '%');
3202 destination->Set(dest_position+1, 'u');
3203 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3204 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3205 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3206 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003207 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003208 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003209 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003210 dest_position++;
3211 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003212 destination->Set(dest_position, '%');
3213 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3214 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003215 dest_position += 3;
3216 }
3217 }
3218 return destination;
3219}
3220
3221
3222static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3223 static const signed char kHexValue['g'] = {
3224 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3225 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3226 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3227 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3228 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3229 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3230 -1, 10, 11, 12, 13, 14, 15 };
3231
3232 if (character1 > 'f') return -1;
3233 int hi = kHexValue[character1];
3234 if (hi == -1) return -1;
3235 if (character2 > 'f') return -1;
3236 int lo = kHexValue[character2];
3237 if (lo == -1) return -1;
3238 return (hi << 4) + lo;
3239}
3240
3241
ager@chromium.org870a0b62008-11-04 11:43:05 +00003242static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003243 int i,
3244 int length,
3245 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003246 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003247 int32_t hi = 0;
3248 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003249 if (character == '%' &&
3250 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003251 source->Get(i + 1) == 'u' &&
3252 (hi = TwoDigitHex(source->Get(i + 2),
3253 source->Get(i + 3))) != -1 &&
3254 (lo = TwoDigitHex(source->Get(i + 4),
3255 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003256 *step = 6;
3257 return (hi << 8) + lo;
3258 } else if (character == '%' &&
3259 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003260 (lo = TwoDigitHex(source->Get(i + 1),
3261 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003262 *step = 3;
3263 return lo;
3264 } else {
3265 *step = 1;
3266 return character;
3267 }
3268}
3269
3270
3271static Object* Runtime_URIUnescape(Arguments args) {
3272 NoHandleAllocation ha;
3273 ASSERT(args.length() == 1);
3274 CONVERT_CHECKED(String, source, args[0]);
3275
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003276 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003277
3278 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003279 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003280
3281 int unescaped_length = 0;
3282 for (int i = 0; i < length; unescaped_length++) {
3283 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003284 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003285 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003286 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003287 i += step;
3288 }
3289
3290 // No length change implies no change. Return original string if no change.
3291 if (unescaped_length == length)
3292 return source;
3293
3294 Object* o = ascii ?
3295 Heap::AllocateRawAsciiString(unescaped_length) :
3296 Heap::AllocateRawTwoByteString(unescaped_length);
3297 if (o->IsFailure()) return o;
3298 String* destination = String::cast(o);
3299
3300 int dest_position = 0;
3301 for (int i = 0; i < length; dest_position++) {
3302 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003303 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003304 i += step;
3305 }
3306 return destination;
3307}
3308
3309
3310static Object* Runtime_StringParseInt(Arguments args) {
3311 NoHandleAllocation ha;
3312
3313 CONVERT_CHECKED(String, s, args[0]);
3314 CONVERT_DOUBLE_CHECKED(n, args[1]);
3315 int radix = FastD2I(n);
3316
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003317 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003318
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003319 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003320 int i;
3321
3322 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003323 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003324 if (i == len) return Heap::nan_value();
3325
3326 // Compute the sign (default to +).
3327 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003328 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003329 sign = -1;
3330 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003331 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003332 i++;
3333 }
3334
3335 // Compute the radix if 0.
3336 if (radix == 0) {
3337 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003338 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003339 radix = 8;
3340 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003341 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003342 if (c == 'x' || c == 'X') {
3343 radix = 16;
3344 i += 2;
3345 }
3346 }
3347 }
3348 } else if (radix == 16) {
3349 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003350 if (i + 1 < len && s->Get(i) == '0') {
3351 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003352 if (c == 'x' || c == 'X') i += 2;
3353 }
3354 }
3355
3356 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3357 double value;
3358 int end_index = StringToInt(s, i, radix, &value);
3359 if (end_index != i) {
3360 return Heap::NumberFromDouble(sign * value);
3361 }
3362 return Heap::nan_value();
3363}
3364
3365
3366static Object* Runtime_StringParseFloat(Arguments args) {
3367 NoHandleAllocation ha;
3368 CONVERT_CHECKED(String, str, args[0]);
3369
3370 // ECMA-262 section 15.1.2.3, empty string is NaN
3371 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3372
3373 // Create a number object from the value.
3374 return Heap::NumberFromDouble(value);
3375}
3376
3377
3378static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3379static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3380
3381
3382template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003383static Object* ConvertCaseHelper(String* s,
3384 int length,
3385 int input_string_length,
3386 unibrow::Mapping<Converter, 128>* mapping) {
3387 // We try this twice, once with the assumption that the result is no longer
3388 // than the input and, if that assumption breaks, again with the exact
3389 // length. This may not be pretty, but it is nicer than what was here before
3390 // and I hereby claim my vaffel-is.
3391 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003392 // Allocate the resulting string.
3393 //
3394 // NOTE: This assumes that the upper/lower case of an ascii
3395 // character is also ascii. This is currently the case, but it
3396 // might break in the future if we implement more context and locale
3397 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003398 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003399 ? Heap::AllocateRawAsciiString(length)
3400 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003401 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003402 String* result = String::cast(o);
3403 bool has_changed_character = false;
3404
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003405 // Convert all characters to upper case, assuming that they will fit
3406 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003407 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003408 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003409 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003410 // We can assume that the string is not empty
3411 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003412 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003413 bool has_next = buffer->has_more();
3414 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003415 int char_length = mapping->get(current, next, chars);
3416 if (char_length == 0) {
3417 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003418 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003419 i++;
3420 } else if (char_length == 1) {
3421 // Common case: converting the letter resulted in one character.
3422 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003423 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003424 has_changed_character = true;
3425 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003426 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003427 // We've assumed that the result would be as long as the
3428 // input but here is a character that converts to several
3429 // characters. No matter, we calculate the exact length
3430 // of the result and try the whole thing again.
3431 //
3432 // Note that this leaves room for optimization. We could just
3433 // memcpy what we already have to the result string. Also,
3434 // the result string is the last object allocated we could
3435 // "realloc" it and probably, in the vast majority of cases,
3436 // extend the existing string to be able to hold the full
3437 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003438 int next_length = 0;
3439 if (has_next) {
3440 next_length = mapping->get(next, 0, chars);
3441 if (next_length == 0) next_length = 1;
3442 }
3443 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003444 while (buffer->has_more()) {
3445 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003446 // NOTE: we use 0 as the next character here because, while
3447 // the next character may affect what a character converts to,
3448 // it does not in any case affect the length of what it convert
3449 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003450 int char_length = mapping->get(current, 0, chars);
3451 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003452 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003453 if (current_length > Smi::kMaxValue) {
3454 Top::context()->mark_out_of_memory();
3455 return Failure::OutOfMemoryException();
3456 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003457 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003458 // Try again with the real length.
3459 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003460 } else {
3461 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003462 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003463 i++;
3464 }
3465 has_changed_character = true;
3466 }
3467 current = next;
3468 }
3469 if (has_changed_character) {
3470 return result;
3471 } else {
3472 // If we didn't actually change anything in doing the conversion
3473 // we simple return the result and let the converted string
3474 // become garbage; there is no reason to keep two identical strings
3475 // alive.
3476 return s;
3477 }
3478}
3479
3480
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003481template <class Converter>
3482static Object* ConvertCase(Arguments args,
3483 unibrow::Mapping<Converter, 128>* mapping) {
3484 NoHandleAllocation ha;
3485
3486 CONVERT_CHECKED(String, s, args[0]);
3487 s->TryFlattenIfNotFlat();
3488
3489 int input_string_length = s->length();
3490 // Assume that the string is not empty; we need this assumption later
3491 if (input_string_length == 0) return s;
3492 int length = input_string_length;
3493
3494 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3495 if (answer->IsSmi()) {
3496 // Retry with correct length.
3497 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3498 }
3499 return answer; // This may be a failure.
3500}
3501
3502
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003503static Object* Runtime_StringToLowerCase(Arguments args) {
3504 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3505}
3506
3507
3508static Object* Runtime_StringToUpperCase(Arguments args) {
3509 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3510}
3511
3512
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003513bool Runtime::IsUpperCaseChar(uint16_t ch) {
3514 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3515 int char_length = to_upper_mapping.get(ch, 0, chars);
3516 return char_length == 0;
3517}
3518
3519
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003520static Object* Runtime_NumberToString(Arguments args) {
3521 NoHandleAllocation ha;
3522 ASSERT(args.length() == 1);
3523
3524 Object* number = args[0];
3525 RUNTIME_ASSERT(number->IsNumber());
3526
3527 Object* cached = Heap::GetNumberStringCache(number);
3528 if (cached != Heap::undefined_value()) {
3529 return cached;
3530 }
3531
3532 char arr[100];
3533 Vector<char> buffer(arr, ARRAY_SIZE(arr));
3534 const char* str;
3535 if (number->IsSmi()) {
3536 int num = Smi::cast(number)->value();
3537 str = IntToCString(num, buffer);
3538 } else {
3539 double num = HeapNumber::cast(number)->value();
3540 str = DoubleToCString(num, buffer);
3541 }
3542 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
3543
3544 if (!result->IsFailure()) {
3545 Heap::SetNumberStringCache(number, String::cast(result));
3546 }
3547 return result;
3548}
3549
3550
3551static Object* Runtime_NumberToInteger(Arguments args) {
3552 NoHandleAllocation ha;
3553 ASSERT(args.length() == 1);
3554
3555 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003556 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003557 CONVERT_DOUBLE_CHECKED(number, obj);
3558 return Heap::NumberFromDouble(DoubleToInteger(number));
3559}
3560
3561
3562static Object* Runtime_NumberToJSUint32(Arguments args) {
3563 NoHandleAllocation ha;
3564 ASSERT(args.length() == 1);
3565
3566 Object* obj = args[0];
3567 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3568 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3569 return Heap::NumberFromUint32(number);
3570}
3571
3572
3573static Object* Runtime_NumberToJSInt32(Arguments args) {
3574 NoHandleAllocation ha;
3575 ASSERT(args.length() == 1);
3576
3577 Object* obj = args[0];
3578 if (obj->IsSmi()) return obj;
3579 CONVERT_DOUBLE_CHECKED(number, obj);
3580 return Heap::NumberFromInt32(DoubleToInt32(number));
3581}
3582
3583
ager@chromium.org870a0b62008-11-04 11:43:05 +00003584// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3585// a small integer.
3586static Object* Runtime_NumberToSmi(Arguments args) {
3587 NoHandleAllocation ha;
3588 ASSERT(args.length() == 1);
3589
3590 Object* obj = args[0];
3591 if (obj->IsSmi()) {
3592 return obj;
3593 }
3594 if (obj->IsHeapNumber()) {
3595 double value = HeapNumber::cast(obj)->value();
3596 int int_value = FastD2I(value);
3597 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3598 return Smi::FromInt(int_value);
3599 }
3600 }
3601 return Heap::nan_value();
3602}
3603
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003604
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003605static Object* Runtime_NumberAdd(Arguments args) {
3606 NoHandleAllocation ha;
3607 ASSERT(args.length() == 2);
3608
3609 CONVERT_DOUBLE_CHECKED(x, args[0]);
3610 CONVERT_DOUBLE_CHECKED(y, args[1]);
3611 return Heap::AllocateHeapNumber(x + y);
3612}
3613
3614
3615static Object* Runtime_NumberSub(Arguments args) {
3616 NoHandleAllocation ha;
3617 ASSERT(args.length() == 2);
3618
3619 CONVERT_DOUBLE_CHECKED(x, args[0]);
3620 CONVERT_DOUBLE_CHECKED(y, args[1]);
3621 return Heap::AllocateHeapNumber(x - y);
3622}
3623
3624
3625static Object* Runtime_NumberMul(Arguments args) {
3626 NoHandleAllocation ha;
3627 ASSERT(args.length() == 2);
3628
3629 CONVERT_DOUBLE_CHECKED(x, args[0]);
3630 CONVERT_DOUBLE_CHECKED(y, args[1]);
3631 return Heap::AllocateHeapNumber(x * y);
3632}
3633
3634
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003635static Object* Runtime_NumberUnaryMinus(Arguments args) {
3636 NoHandleAllocation ha;
3637 ASSERT(args.length() == 1);
3638
3639 CONVERT_DOUBLE_CHECKED(x, args[0]);
3640 return Heap::AllocateHeapNumber(-x);
3641}
3642
3643
3644static Object* Runtime_NumberDiv(Arguments args) {
3645 NoHandleAllocation ha;
3646 ASSERT(args.length() == 2);
3647
3648 CONVERT_DOUBLE_CHECKED(x, args[0]);
3649 CONVERT_DOUBLE_CHECKED(y, args[1]);
3650 return Heap::NewNumberFromDouble(x / y);
3651}
3652
3653
3654static Object* Runtime_NumberMod(Arguments args) {
3655 NoHandleAllocation ha;
3656 ASSERT(args.length() == 2);
3657
3658 CONVERT_DOUBLE_CHECKED(x, args[0]);
3659 CONVERT_DOUBLE_CHECKED(y, args[1]);
3660
3661#ifdef WIN32
3662 // Workaround MS fmod bugs. ECMA-262 says:
3663 // dividend is finite and divisor is an infinity => result equals dividend
3664 // dividend is a zero and divisor is nonzero finite => result equals dividend
3665 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3666 !(x == 0 && (y != 0 && isfinite(y))))
3667#endif
3668 x = fmod(x, y);
3669 // NewNumberFromDouble may return a Smi instead of a Number object
3670 return Heap::NewNumberFromDouble(x);
3671}
3672
3673
3674static Object* Runtime_StringAdd(Arguments args) {
3675 NoHandleAllocation ha;
3676 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003677 CONVERT_CHECKED(String, str1, args[0]);
3678 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003679 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003680}
3681
3682
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003683template<typename sinkchar>
3684static inline void StringBuilderConcatHelper(String* special,
3685 sinkchar* sink,
3686 FixedArray* fixed_array,
3687 int array_length) {
3688 int position = 0;
3689 for (int i = 0; i < array_length; i++) {
3690 Object* element = fixed_array->get(i);
3691 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003692 int encoded_slice = Smi::cast(element)->value();
3693 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3694 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003695 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003696 sink + position,
3697 pos,
3698 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003699 position += len;
3700 } else {
3701 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003702 int element_length = string->length();
3703 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003704 position += element_length;
3705 }
3706 }
3707}
3708
3709
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003710static Object* Runtime_StringBuilderConcat(Arguments args) {
3711 NoHandleAllocation ha;
3712 ASSERT(args.length() == 2);
3713 CONVERT_CHECKED(JSArray, array, args[0]);
3714 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003715 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003716 Object* smi_array_length = array->length();
3717 if (!smi_array_length->IsSmi()) {
3718 Top::context()->mark_out_of_memory();
3719 return Failure::OutOfMemoryException();
3720 }
3721 int array_length = Smi::cast(smi_array_length)->value();
3722 if (!array->HasFastElements()) {
3723 return Top::Throw(Heap::illegal_argument_symbol());
3724 }
3725 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003726 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003727 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003728 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003729
3730 if (array_length == 0) {
3731 return Heap::empty_string();
3732 } else if (array_length == 1) {
3733 Object* first = fixed_array->get(0);
3734 if (first->IsString()) return first;
3735 }
3736
ager@chromium.org5ec48922009-05-05 07:25:34 +00003737 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003738 int position = 0;
3739 for (int i = 0; i < array_length; i++) {
3740 Object* elt = fixed_array->get(i);
3741 if (elt->IsSmi()) {
3742 int len = Smi::cast(elt)->value();
3743 int pos = len >> 11;
3744 len &= 0x7ff;
3745 if (pos + len > special_length) {
3746 return Top::Throw(Heap::illegal_argument_symbol());
3747 }
3748 position += len;
3749 } else if (elt->IsString()) {
3750 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003751 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003752 if (!Smi::IsValid(element_length + position)) {
3753 Top::context()->mark_out_of_memory();
3754 return Failure::OutOfMemoryException();
3755 }
3756 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003757 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003758 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003759 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003760 } else {
3761 return Top::Throw(Heap::illegal_argument_symbol());
3762 }
3763 }
3764
3765 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003766 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003767
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003768 if (ascii) {
3769 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003770 if (object->IsFailure()) return object;
3771 SeqAsciiString* answer = SeqAsciiString::cast(object);
3772 StringBuilderConcatHelper(special,
3773 answer->GetChars(),
3774 fixed_array,
3775 array_length);
3776 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003777 } else {
3778 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003779 if (object->IsFailure()) return object;
3780 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3781 StringBuilderConcatHelper(special,
3782 answer->GetChars(),
3783 fixed_array,
3784 array_length);
3785 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003786 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003787}
3788
3789
3790static Object* Runtime_NumberOr(Arguments args) {
3791 NoHandleAllocation ha;
3792 ASSERT(args.length() == 2);
3793
3794 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3795 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3796 return Heap::NumberFromInt32(x | y);
3797}
3798
3799
3800static Object* Runtime_NumberAnd(Arguments args) {
3801 NoHandleAllocation ha;
3802 ASSERT(args.length() == 2);
3803
3804 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3805 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3806 return Heap::NumberFromInt32(x & y);
3807}
3808
3809
3810static Object* Runtime_NumberXor(Arguments args) {
3811 NoHandleAllocation ha;
3812 ASSERT(args.length() == 2);
3813
3814 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3815 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3816 return Heap::NumberFromInt32(x ^ y);
3817}
3818
3819
3820static Object* Runtime_NumberNot(Arguments args) {
3821 NoHandleAllocation ha;
3822 ASSERT(args.length() == 1);
3823
3824 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3825 return Heap::NumberFromInt32(~x);
3826}
3827
3828
3829static Object* Runtime_NumberShl(Arguments args) {
3830 NoHandleAllocation ha;
3831 ASSERT(args.length() == 2);
3832
3833 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3834 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3835 return Heap::NumberFromInt32(x << (y & 0x1f));
3836}
3837
3838
3839static Object* Runtime_NumberShr(Arguments args) {
3840 NoHandleAllocation ha;
3841 ASSERT(args.length() == 2);
3842
3843 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3844 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3845 return Heap::NumberFromUint32(x >> (y & 0x1f));
3846}
3847
3848
3849static Object* Runtime_NumberSar(Arguments args) {
3850 NoHandleAllocation ha;
3851 ASSERT(args.length() == 2);
3852
3853 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3854 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3855 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3856}
3857
3858
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003859static Object* Runtime_NumberEquals(Arguments args) {
3860 NoHandleAllocation ha;
3861 ASSERT(args.length() == 2);
3862
3863 CONVERT_DOUBLE_CHECKED(x, args[0]);
3864 CONVERT_DOUBLE_CHECKED(y, args[1]);
3865 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3866 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3867 if (x == y) return Smi::FromInt(EQUAL);
3868 Object* result;
3869 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3870 result = Smi::FromInt(EQUAL);
3871 } else {
3872 result = Smi::FromInt(NOT_EQUAL);
3873 }
3874 return result;
3875}
3876
3877
3878static Object* Runtime_StringEquals(Arguments args) {
3879 NoHandleAllocation ha;
3880 ASSERT(args.length() == 2);
3881
3882 CONVERT_CHECKED(String, x, args[0]);
3883 CONVERT_CHECKED(String, y, args[1]);
3884
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003885 bool not_equal = !x->Equals(y);
3886 // This is slightly convoluted because the value that signifies
3887 // equality is 0 and inequality is 1 so we have to negate the result
3888 // from String::Equals.
3889 ASSERT(not_equal == 0 || not_equal == 1);
3890 STATIC_CHECK(EQUAL == 0);
3891 STATIC_CHECK(NOT_EQUAL == 1);
3892 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003893}
3894
3895
3896static Object* Runtime_NumberCompare(Arguments args) {
3897 NoHandleAllocation ha;
3898 ASSERT(args.length() == 3);
3899
3900 CONVERT_DOUBLE_CHECKED(x, args[0]);
3901 CONVERT_DOUBLE_CHECKED(y, args[1]);
3902 if (isnan(x) || isnan(y)) return args[2];
3903 if (x == y) return Smi::FromInt(EQUAL);
3904 if (isless(x, y)) return Smi::FromInt(LESS);
3905 return Smi::FromInt(GREATER);
3906}
3907
3908
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003909// Compare two Smis as if they were converted to strings and then
3910// compared lexicographically.
3911static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3912 NoHandleAllocation ha;
3913 ASSERT(args.length() == 2);
3914
3915 // Arrays for the individual characters of the two Smis. Smis are
3916 // 31 bit integers and 10 decimal digits are therefore enough.
3917 static int x_elms[10];
3918 static int y_elms[10];
3919
3920 // Extract the integer values from the Smis.
3921 CONVERT_CHECKED(Smi, x, args[0]);
3922 CONVERT_CHECKED(Smi, y, args[1]);
3923 int x_value = x->value();
3924 int y_value = y->value();
3925
3926 // If the integers are equal so are the string representations.
3927 if (x_value == y_value) return Smi::FromInt(EQUAL);
3928
3929 // If one of the integers are zero the normal integer order is the
3930 // same as the lexicographic order of the string representations.
3931 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3932
ager@chromium.org32912102009-01-16 10:38:43 +00003933 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003934 // smallest because the char code of '-' is less than the char code
3935 // of any digit. Otherwise, we make both values positive.
3936 if (x_value < 0 || y_value < 0) {
3937 if (y_value >= 0) return Smi::FromInt(LESS);
3938 if (x_value >= 0) return Smi::FromInt(GREATER);
3939 x_value = -x_value;
3940 y_value = -y_value;
3941 }
3942
3943 // Convert the integers to arrays of their decimal digits.
3944 int x_index = 0;
3945 int y_index = 0;
3946 while (x_value > 0) {
3947 x_elms[x_index++] = x_value % 10;
3948 x_value /= 10;
3949 }
3950 while (y_value > 0) {
3951 y_elms[y_index++] = y_value % 10;
3952 y_value /= 10;
3953 }
3954
3955 // Loop through the arrays of decimal digits finding the first place
3956 // where they differ.
3957 while (--x_index >= 0 && --y_index >= 0) {
3958 int diff = x_elms[x_index] - y_elms[y_index];
3959 if (diff != 0) return Smi::FromInt(diff);
3960 }
3961
3962 // If one array is a suffix of the other array, the longest array is
3963 // the representation of the largest of the Smis in the
3964 // lexicographic ordering.
3965 return Smi::FromInt(x_index - y_index);
3966}
3967
3968
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003969static Object* Runtime_StringCompare(Arguments args) {
3970 NoHandleAllocation ha;
3971 ASSERT(args.length() == 2);
3972
3973 CONVERT_CHECKED(String, x, args[0]);
3974 CONVERT_CHECKED(String, y, args[1]);
3975
3976 // A few fast case tests before we flatten.
3977 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003978 if (y->length() == 0) {
3979 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003980 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003981 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003982 return Smi::FromInt(LESS);
3983 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003984
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003985 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003986 if (d < 0) return Smi::FromInt(LESS);
3987 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003988
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003989 x->TryFlattenIfNotFlat();
3990 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003991
3992 static StringInputBuffer bufx;
3993 static StringInputBuffer bufy;
3994 bufx.Reset(x);
3995 bufy.Reset(y);
3996 while (bufx.has_more() && bufy.has_more()) {
3997 int d = bufx.GetNext() - bufy.GetNext();
3998 if (d < 0) return Smi::FromInt(LESS);
3999 else if (d > 0) return Smi::FromInt(GREATER);
4000 }
4001
4002 // x is (non-trivial) prefix of y:
4003 if (bufy.has_more()) return Smi::FromInt(LESS);
4004 // y is prefix of x:
4005 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4006}
4007
4008
4009static Object* Runtime_Math_abs(Arguments args) {
4010 NoHandleAllocation ha;
4011 ASSERT(args.length() == 1);
4012
4013 CONVERT_DOUBLE_CHECKED(x, args[0]);
4014 return Heap::AllocateHeapNumber(fabs(x));
4015}
4016
4017
4018static Object* Runtime_Math_acos(Arguments args) {
4019 NoHandleAllocation ha;
4020 ASSERT(args.length() == 1);
4021
4022 CONVERT_DOUBLE_CHECKED(x, args[0]);
4023 return Heap::AllocateHeapNumber(acos(x));
4024}
4025
4026
4027static Object* Runtime_Math_asin(Arguments args) {
4028 NoHandleAllocation ha;
4029 ASSERT(args.length() == 1);
4030
4031 CONVERT_DOUBLE_CHECKED(x, args[0]);
4032 return Heap::AllocateHeapNumber(asin(x));
4033}
4034
4035
4036static Object* Runtime_Math_atan(Arguments args) {
4037 NoHandleAllocation ha;
4038 ASSERT(args.length() == 1);
4039
4040 CONVERT_DOUBLE_CHECKED(x, args[0]);
4041 return Heap::AllocateHeapNumber(atan(x));
4042}
4043
4044
4045static Object* Runtime_Math_atan2(Arguments args) {
4046 NoHandleAllocation ha;
4047 ASSERT(args.length() == 2);
4048
4049 CONVERT_DOUBLE_CHECKED(x, args[0]);
4050 CONVERT_DOUBLE_CHECKED(y, args[1]);
4051 double result;
4052 if (isinf(x) && isinf(y)) {
4053 // Make sure that the result in case of two infinite arguments
4054 // is a multiple of Pi / 4. The sign of the result is determined
4055 // by the first argument (x) and the sign of the second argument
4056 // determines the multiplier: one or three.
4057 static double kPiDividedBy4 = 0.78539816339744830962;
4058 int multiplier = (x < 0) ? -1 : 1;
4059 if (y < 0) multiplier *= 3;
4060 result = multiplier * kPiDividedBy4;
4061 } else {
4062 result = atan2(x, y);
4063 }
4064 return Heap::AllocateHeapNumber(result);
4065}
4066
4067
4068static Object* Runtime_Math_ceil(Arguments args) {
4069 NoHandleAllocation ha;
4070 ASSERT(args.length() == 1);
4071
4072 CONVERT_DOUBLE_CHECKED(x, args[0]);
4073 return Heap::NumberFromDouble(ceiling(x));
4074}
4075
4076
4077static Object* Runtime_Math_cos(Arguments args) {
4078 NoHandleAllocation ha;
4079 ASSERT(args.length() == 1);
4080
4081 CONVERT_DOUBLE_CHECKED(x, args[0]);
4082 return Heap::AllocateHeapNumber(cos(x));
4083}
4084
4085
4086static Object* Runtime_Math_exp(Arguments args) {
4087 NoHandleAllocation ha;
4088 ASSERT(args.length() == 1);
4089
4090 CONVERT_DOUBLE_CHECKED(x, args[0]);
4091 return Heap::AllocateHeapNumber(exp(x));
4092}
4093
4094
4095static Object* Runtime_Math_floor(Arguments args) {
4096 NoHandleAllocation ha;
4097 ASSERT(args.length() == 1);
4098
4099 CONVERT_DOUBLE_CHECKED(x, args[0]);
4100 return Heap::NumberFromDouble(floor(x));
4101}
4102
4103
4104static Object* Runtime_Math_log(Arguments args) {
4105 NoHandleAllocation ha;
4106 ASSERT(args.length() == 1);
4107
4108 CONVERT_DOUBLE_CHECKED(x, args[0]);
4109 return Heap::AllocateHeapNumber(log(x));
4110}
4111
4112
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004113// Helper function to compute x^y, where y is known to be an
4114// integer. Uses binary decomposition to limit the number of
4115// multiplications; see the discussion in "Hacker's Delight" by Henry
4116// S. Warren, Jr., figure 11-6, page 213.
4117static double powi(double x, int y) {
4118 ASSERT(y != kMinInt);
4119 unsigned n = (y < 0) ? -y : y;
4120 double m = x;
4121 double p = 1;
4122 while (true) {
4123 if ((n & 1) != 0) p *= m;
4124 n >>= 1;
4125 if (n == 0) {
4126 if (y < 0) {
4127 // Unfortunately, we have to be careful when p has reached
4128 // infinity in the computation, because sometimes the higher
4129 // internal precision in the pow() implementation would have
4130 // given us a finite p. This happens very rarely.
4131 double result = 1.0 / p;
4132 return (result == 0 && isinf(p))
4133 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4134 : result;
4135 } else {
4136 return p;
4137 }
4138 }
4139 m *= m;
4140 }
4141}
4142
4143
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004144static Object* Runtime_Math_pow(Arguments args) {
4145 NoHandleAllocation ha;
4146 ASSERT(args.length() == 2);
4147
4148 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004149
4150 // If the second argument is a smi, it is much faster to call the
4151 // custom powi() function than the generic pow().
4152 if (args[1]->IsSmi()) {
4153 int y = Smi::cast(args[1])->value();
4154 return Heap::AllocateHeapNumber(powi(x, y));
4155 }
4156
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004157 CONVERT_DOUBLE_CHECKED(y, args[1]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004158 if (y == 0.5) {
4159 // It's not uncommon to use Math.pow(x, 0.5) to compute the square
4160 // root of a number. To speed up such computations, we explictly
4161 // check for this case and use the sqrt() function which is faster
4162 // 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));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004167 } else if (y == 0) {
4168 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004169 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4170 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004171 } else {
4172 return Heap::AllocateHeapNumber(pow(x, y));
4173 }
4174}
4175
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004176
4177static Object* Runtime_Math_round(Arguments args) {
4178 NoHandleAllocation ha;
4179 ASSERT(args.length() == 1);
4180
4181 CONVERT_DOUBLE_CHECKED(x, args[0]);
4182 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4183 return Heap::NumberFromDouble(floor(x + 0.5));
4184}
4185
4186
4187static Object* Runtime_Math_sin(Arguments args) {
4188 NoHandleAllocation ha;
4189 ASSERT(args.length() == 1);
4190
4191 CONVERT_DOUBLE_CHECKED(x, args[0]);
4192 return Heap::AllocateHeapNumber(sin(x));
4193}
4194
4195
4196static Object* Runtime_Math_sqrt(Arguments args) {
4197 NoHandleAllocation ha;
4198 ASSERT(args.length() == 1);
4199
4200 CONVERT_DOUBLE_CHECKED(x, args[0]);
4201 return Heap::AllocateHeapNumber(sqrt(x));
4202}
4203
4204
4205static Object* Runtime_Math_tan(Arguments args) {
4206 NoHandleAllocation ha;
4207 ASSERT(args.length() == 1);
4208
4209 CONVERT_DOUBLE_CHECKED(x, args[0]);
4210 return Heap::AllocateHeapNumber(tan(x));
4211}
4212
4213
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004214// The NewArguments function is only used when constructing the
4215// arguments array when calling non-functions from JavaScript in
4216// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004217static Object* Runtime_NewArguments(Arguments args) {
4218 NoHandleAllocation ha;
4219 ASSERT(args.length() == 1);
4220
4221 // ECMA-262, 3rd., 10.1.8, p.39
4222 CONVERT_CHECKED(JSFunction, callee, args[0]);
4223
4224 // Compute the frame holding the arguments.
4225 JavaScriptFrameIterator it;
4226 it.AdvanceToArgumentsFrame();
4227 JavaScriptFrame* frame = it.frame();
4228
4229 const int length = frame->GetProvidedParametersCount();
4230 Object* result = Heap::AllocateArgumentsObject(callee, length);
4231 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004232 if (length > 0) {
4233 Object* obj = Heap::AllocateFixedArray(length);
4234 if (obj->IsFailure()) return obj;
4235 FixedArray* array = FixedArray::cast(obj);
4236 ASSERT(array->length() == length);
4237 WriteBarrierMode mode = array->GetWriteBarrierMode();
4238 for (int i = 0; i < length; i++) {
4239 array->set(i, frame->GetParameter(i), mode);
4240 }
4241 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004242 }
4243 return result;
4244}
4245
4246
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004247static Object* Runtime_NewArgumentsFast(Arguments args) {
4248 NoHandleAllocation ha;
4249 ASSERT(args.length() == 3);
4250
4251 JSFunction* callee = JSFunction::cast(args[0]);
4252 Object** parameters = reinterpret_cast<Object**>(args[1]);
4253 const int length = Smi::cast(args[2])->value();
4254
4255 Object* result = Heap::AllocateArgumentsObject(callee, length);
4256 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004257 ASSERT(Heap::InNewSpace(result));
4258
4259 // Allocate the elements if needed.
4260 if (length > 0) {
4261 // Allocate the fixed array.
4262 Object* obj = Heap::AllocateRawFixedArray(length);
4263 if (obj->IsFailure()) return obj;
4264 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4265 FixedArray* array = FixedArray::cast(obj);
4266 array->set_length(length);
4267 WriteBarrierMode mode = array->GetWriteBarrierMode();
4268 for (int i = 0; i < length; i++) {
4269 array->set(i, *--parameters, mode);
4270 }
4271 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4272 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004273 }
4274 return result;
4275}
4276
4277
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004278static Object* Runtime_NewClosure(Arguments args) {
4279 HandleScope scope;
4280 ASSERT(args.length() == 2);
4281 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4282 CONVERT_ARG_CHECKED(Context, context, 1);
4283
4284 Handle<JSFunction> result =
4285 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4286 return *result;
4287}
4288
4289
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004290static Handle<Code> ComputeConstructStub(Handle<Map> map) {
4291 // TODO(385): Change this to create a construct stub specialized for
4292 // the given map to make allocation of simple objects - and maybe
4293 // arrays - much faster.
4294 return Handle<Code>(Builtins::builtin(Builtins::JSConstructStubGeneric));
4295}
4296
4297
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004298static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004299 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004300 ASSERT(args.length() == 1);
4301
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004302 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004303
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004304 // If the constructor isn't a proper function we throw a type error.
4305 if (!constructor->IsJSFunction()) {
4306 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4307 Handle<Object> type_error =
4308 Factory::NewTypeError("not_constructor", arguments);
4309 return Top::Throw(*type_error);
4310 }
4311
4312 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004313#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004314 // Handle stepping into constructors if step into is active.
4315 if (Debug::StepInActive()) {
4316 Debug::HandleStepIn(function, 0, true);
4317 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004318#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004319
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004320 if (function->has_initial_map()) {
4321 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004322 // The 'Function' function ignores the receiver object when
4323 // called using 'new' and creates a new JSFunction object that
4324 // is returned. The receiver object is only used for error
4325 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004326 // JSFunction. Factory::NewJSObject() should not be used to
4327 // allocate JSFunctions since it does not properly initialize
4328 // the shared part of the function. Since the receiver is
4329 // ignored anyway, we use the global object as the receiver
4330 // instead of a new JSFunction object. This way, errors are
4331 // reported the same way whether or not 'Function' is called
4332 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004333 return Top::context()->global();
4334 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004335 }
4336
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004337 bool first_allocation = !function->has_initial_map();
4338 Handle<JSObject> result = Factory::NewJSObject(function);
4339 if (first_allocation) {
4340 Handle<Map> map = Handle<Map>(function->initial_map());
4341 Handle<Code> stub = ComputeConstructStub(map);
4342 function->shared()->set_construct_stub(*stub);
4343 }
4344 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004345}
4346
4347
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004348static Object* Runtime_LazyCompile(Arguments args) {
4349 HandleScope scope;
4350 ASSERT(args.length() == 1);
4351
4352 Handle<JSFunction> function = args.at<JSFunction>(0);
4353#ifdef DEBUG
4354 if (FLAG_trace_lazy) {
4355 PrintF("[lazy: ");
4356 function->shared()->name()->Print();
4357 PrintF("]\n");
4358 }
4359#endif
4360
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004361 // Compile the target function. Here we compile using CompileLazyInLoop in
4362 // order to get the optimized version. This helps code like delta-blue
4363 // that calls performance-critical routines through constructors. A
4364 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4365 // direct call. Since the in-loop tracking takes place through CallICs
4366 // this means that things called through constructors are never known to
4367 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004368 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004369 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004370 return Failure::Exception();
4371 }
4372
4373 return function->code();
4374}
4375
4376
4377static Object* Runtime_GetCalledFunction(Arguments args) {
4378 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004379 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004380 StackFrameIterator it;
4381 // Get past the JS-to-C exit frame.
4382 ASSERT(it.frame()->is_exit());
4383 it.Advance();
4384 // Get past the CALL_NON_FUNCTION activation frame.
4385 ASSERT(it.frame()->is_java_script());
4386 it.Advance();
4387 // Argument adaptor frames do not copy the function; we have to skip
4388 // past them to get to the real calling frame.
4389 if (it.frame()->is_arguments_adaptor()) it.Advance();
4390 // Get the function from the top of the expression stack of the
4391 // calling frame.
4392 StandardFrame* frame = StandardFrame::cast(it.frame());
4393 int index = frame->ComputeExpressionsCount() - 1;
4394 Object* result = frame->GetExpression(index);
4395 return result;
4396}
4397
4398
4399static Object* Runtime_GetFunctionDelegate(Arguments args) {
4400 HandleScope scope;
4401 ASSERT(args.length() == 1);
4402 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4403 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4404}
4405
4406
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004407static Object* Runtime_GetConstructorDelegate(Arguments args) {
4408 HandleScope scope;
4409 ASSERT(args.length() == 1);
4410 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4411 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4412}
4413
4414
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004415static Object* Runtime_NewContext(Arguments args) {
4416 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004417 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004418
kasper.lund7276f142008-07-30 08:49:36 +00004419 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004420 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4421 Object* result = Heap::AllocateFunctionContext(length, function);
4422 if (result->IsFailure()) return result;
4423
4424 Top::set_context(Context::cast(result));
4425
kasper.lund7276f142008-07-30 08:49:36 +00004426 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004427}
4428
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004429static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004430 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004431 Object* js_object = object;
4432 if (!js_object->IsJSObject()) {
4433 js_object = js_object->ToObject();
4434 if (js_object->IsFailure()) {
4435 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004436 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004437 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004438 Handle<Object> result =
4439 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4440 return Top::Throw(*result);
4441 }
4442 }
4443
4444 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004445 Heap::AllocateWithContext(Top::context(),
4446 JSObject::cast(js_object),
4447 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004448 if (result->IsFailure()) return result;
4449
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004450 Context* context = Context::cast(result);
4451 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004452
kasper.lund7276f142008-07-30 08:49:36 +00004453 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004454}
4455
4456
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004457static Object* Runtime_PushContext(Arguments args) {
4458 NoHandleAllocation ha;
4459 ASSERT(args.length() == 1);
4460 return PushContextHelper(args[0], false);
4461}
4462
4463
4464static Object* Runtime_PushCatchContext(Arguments args) {
4465 NoHandleAllocation ha;
4466 ASSERT(args.length() == 1);
4467 return PushContextHelper(args[0], true);
4468}
4469
4470
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004471static Object* Runtime_LookupContext(Arguments args) {
4472 HandleScope scope;
4473 ASSERT(args.length() == 2);
4474
4475 CONVERT_ARG_CHECKED(Context, context, 0);
4476 CONVERT_ARG_CHECKED(String, name, 1);
4477
4478 int index;
4479 PropertyAttributes attributes;
4480 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004481 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004482 context->Lookup(name, flags, &index, &attributes);
4483
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004484 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004485 ASSERT(holder->IsJSObject());
4486 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004487 }
4488
4489 // No intermediate context found. Use global object by default.
4490 return Top::context()->global();
4491}
4492
4493
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004494// A mechanism to return pairs of Object*'s. This is somewhat
4495// compiler-dependent as it assumes that a 64-bit value (a long long)
4496// is returned via two registers (edx:eax on ia32). Both the ia32 and
4497// arm platform support this; it is mostly an issue of "coaxing" the
4498// compiler to do the right thing.
4499//
4500// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004501#ifdef V8_HOST_ARCH_64_BIT
4502// Tested with GCC, not with MSVC.
4503struct ObjectPair {
4504 Object* x;
4505 Object* y;
4506};
4507static inline ObjectPair MakePair(Object* x, Object* y) {
4508 ObjectPair result = {x, y};
4509 return result; // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
4510}
4511#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004512typedef uint64_t ObjectPair;
4513static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004514 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004515 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004516}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004517#endif
4518
4519
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004520
4521
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004522static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004523 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4524 USE(attributes);
4525 return x->IsTheHole() ? Heap::undefined_value() : x;
4526}
4527
4528
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004529static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4530 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004531 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004532 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004533 JSFunction* context_extension_function =
4534 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004535 // If the holder isn't a context extension object, we just return it
4536 // as the receiver. This allows arguments objects to be used as
4537 // receivers, but only if they are put in the context scope chain
4538 // explicitly via a with-statement.
4539 Object* constructor = holder->map()->constructor();
4540 if (constructor != context_extension_function) return holder;
4541 // Fall back to using the global object as the receiver if the
4542 // property turns out to be a local variable allocated in a context
4543 // extension object - introduced via eval.
4544 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004545}
4546
4547
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004548static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004549 HandleScope scope;
4550 ASSERT(args.length() == 2);
4551
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004552 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004553 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004554 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004555 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004556 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004557
4558 int index;
4559 PropertyAttributes attributes;
4560 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004561 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004562 context->Lookup(name, flags, &index, &attributes);
4563
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004564 // If the index is non-negative, the slot has been found in a local
4565 // variable or a parameter. Read it from the context object or the
4566 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004567 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004568 // If the "property" we were looking for is a local variable or an
4569 // argument in a context, the receiver is the global object; see
4570 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4571 JSObject* receiver = Top::context()->global()->global_receiver();
4572 Object* value = (holder->IsContext())
4573 ? Context::cast(*holder)->get(index)
4574 : JSObject::cast(*holder)->GetElement(index);
4575 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004576 }
4577
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004578 // If the holder is found, we read the property from it.
4579 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004580 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004581 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004582 JSObject* receiver;
4583 if (object->IsGlobalObject()) {
4584 receiver = GlobalObject::cast(object)->global_receiver();
4585 } else if (context->is_exception_holder(*holder)) {
4586 receiver = Top::context()->global()->global_receiver();
4587 } else {
4588 receiver = ComputeReceiverForNonGlobal(object);
4589 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004590 // No need to unhole the value here. This is taken care of by the
4591 // GetProperty function.
4592 Object* value = object->GetProperty(*name);
4593 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004594 }
4595
4596 if (throw_error) {
4597 // The property doesn't exist - throw exception.
4598 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004599 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004600 return MakePair(Top::Throw(*reference_error), NULL);
4601 } else {
4602 // The property doesn't exist - return undefined
4603 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4604 }
4605}
4606
4607
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004608static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004609 return LoadContextSlotHelper(args, true);
4610}
4611
4612
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004613static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004614 return LoadContextSlotHelper(args, false);
4615}
4616
4617
4618static Object* Runtime_StoreContextSlot(Arguments args) {
4619 HandleScope scope;
4620 ASSERT(args.length() == 3);
4621
4622 Handle<Object> value(args[0]);
4623 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004624 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004625
4626 int index;
4627 PropertyAttributes attributes;
4628 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004629 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004630 context->Lookup(name, flags, &index, &attributes);
4631
4632 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004633 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004634 // Ignore if read_only variable.
4635 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004636 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004637 }
4638 } else {
4639 ASSERT((attributes & READ_ONLY) == 0);
4640 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004641 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004642 USE(result);
4643 ASSERT(!result->IsFailure());
4644 }
4645 return *value;
4646 }
4647
4648 // Slow case: The property is not in a FixedArray context.
4649 // It is either in an JSObject extension context or it was not found.
4650 Handle<JSObject> context_ext;
4651
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004652 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004653 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004654 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004655 } else {
4656 // The property was not found. It needs to be stored in the global context.
4657 ASSERT(attributes == ABSENT);
4658 attributes = NONE;
4659 context_ext = Handle<JSObject>(Top::context()->global());
4660 }
4661
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004662 // Set the property, but ignore if read_only variable on the context
4663 // extension object itself.
4664 if ((attributes & READ_ONLY) == 0 ||
4665 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004666 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4667 if (set.is_null()) {
4668 // Failure::Exception is converted to a null handle in the
4669 // handle-based methods such as SetProperty. We therefore need
4670 // to convert null handles back to exceptions.
4671 ASSERT(Top::has_pending_exception());
4672 return Failure::Exception();
4673 }
4674 }
4675 return *value;
4676}
4677
4678
4679static Object* Runtime_Throw(Arguments args) {
4680 HandleScope scope;
4681 ASSERT(args.length() == 1);
4682
4683 return Top::Throw(args[0]);
4684}
4685
4686
4687static Object* Runtime_ReThrow(Arguments args) {
4688 HandleScope scope;
4689 ASSERT(args.length() == 1);
4690
4691 return Top::ReThrow(args[0]);
4692}
4693
4694
4695static Object* Runtime_ThrowReferenceError(Arguments args) {
4696 HandleScope scope;
4697 ASSERT(args.length() == 1);
4698
4699 Handle<Object> name(args[0]);
4700 Handle<Object> reference_error =
4701 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4702 return Top::Throw(*reference_error);
4703}
4704
4705
4706static Object* Runtime_StackOverflow(Arguments args) {
4707 NoHandleAllocation na;
4708 return Top::StackOverflow();
4709}
4710
4711
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004712static Object* Runtime_StackGuard(Arguments args) {
4713 ASSERT(args.length() == 1);
4714
4715 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004716 if (StackGuard::IsStackOverflow()) {
4717 return Runtime_StackOverflow(args);
4718 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004719
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004720 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004721}
4722
4723
4724// NOTE: These PrintXXX functions are defined for all builds (not just
4725// DEBUG builds) because we may want to be able to trace function
4726// calls in all modes.
4727static void PrintString(String* str) {
4728 // not uncommon to have empty strings
4729 if (str->length() > 0) {
4730 SmartPointer<char> s =
4731 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4732 PrintF("%s", *s);
4733 }
4734}
4735
4736
4737static void PrintObject(Object* obj) {
4738 if (obj->IsSmi()) {
4739 PrintF("%d", Smi::cast(obj)->value());
4740 } else if (obj->IsString() || obj->IsSymbol()) {
4741 PrintString(String::cast(obj));
4742 } else if (obj->IsNumber()) {
4743 PrintF("%g", obj->Number());
4744 } else if (obj->IsFailure()) {
4745 PrintF("<failure>");
4746 } else if (obj->IsUndefined()) {
4747 PrintF("<undefined>");
4748 } else if (obj->IsNull()) {
4749 PrintF("<null>");
4750 } else if (obj->IsTrue()) {
4751 PrintF("<true>");
4752 } else if (obj->IsFalse()) {
4753 PrintF("<false>");
4754 } else {
4755 PrintF("%p", obj);
4756 }
4757}
4758
4759
4760static int StackSize() {
4761 int n = 0;
4762 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4763 return n;
4764}
4765
4766
4767static void PrintTransition(Object* result) {
4768 // indentation
4769 { const int nmax = 80;
4770 int n = StackSize();
4771 if (n <= nmax)
4772 PrintF("%4d:%*s", n, n, "");
4773 else
4774 PrintF("%4d:%*s", n, nmax, "...");
4775 }
4776
4777 if (result == NULL) {
4778 // constructor calls
4779 JavaScriptFrameIterator it;
4780 JavaScriptFrame* frame = it.frame();
4781 if (frame->IsConstructor()) PrintF("new ");
4782 // function name
4783 Object* fun = frame->function();
4784 if (fun->IsJSFunction()) {
4785 PrintObject(JSFunction::cast(fun)->shared()->name());
4786 } else {
4787 PrintObject(fun);
4788 }
4789 // function arguments
4790 // (we are intentionally only printing the actually
4791 // supplied parameters, not all parameters required)
4792 PrintF("(this=");
4793 PrintObject(frame->receiver());
4794 const int length = frame->GetProvidedParametersCount();
4795 for (int i = 0; i < length; i++) {
4796 PrintF(", ");
4797 PrintObject(frame->GetParameter(i));
4798 }
4799 PrintF(") {\n");
4800
4801 } else {
4802 // function result
4803 PrintF("} -> ");
4804 PrintObject(result);
4805 PrintF("\n");
4806 }
4807}
4808
4809
4810static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004811 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004812 NoHandleAllocation ha;
4813 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004814 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004815}
4816
4817
4818static Object* Runtime_TraceExit(Arguments args) {
4819 NoHandleAllocation ha;
4820 PrintTransition(args[0]);
4821 return args[0]; // return TOS
4822}
4823
4824
4825static Object* Runtime_DebugPrint(Arguments args) {
4826 NoHandleAllocation ha;
4827 ASSERT(args.length() == 1);
4828
4829#ifdef DEBUG
4830 if (args[0]->IsString()) {
4831 // If we have a string, assume it's a code "marker"
4832 // and print some interesting cpu debugging info.
4833 JavaScriptFrameIterator it;
4834 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004835 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
4836 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004837 } else {
4838 PrintF("DebugPrint: ");
4839 }
4840 args[0]->Print();
4841#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004842 // ShortPrint is available in release mode. Print is not.
4843 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004844#endif
4845 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004846 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004847
4848 return args[0]; // return TOS
4849}
4850
4851
4852static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004853 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004854 NoHandleAllocation ha;
4855 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004856 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004857}
4858
4859
mads.s.ager31e71382008-08-13 09:32:07 +00004860static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004861 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004862 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004863
4864 // According to ECMA-262, section 15.9.1, page 117, the precision of
4865 // the number in a Date object representing a particular instant in
4866 // time is milliseconds. Therefore, we floor the result of getting
4867 // the OS time.
4868 double millis = floor(OS::TimeCurrentMillis());
4869 return Heap::NumberFromDouble(millis);
4870}
4871
4872
4873static Object* Runtime_DateParseString(Arguments args) {
4874 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004875 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004876
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004877 CONVERT_ARG_CHECKED(String, str, 0);
4878 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004879
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004880 CONVERT_ARG_CHECKED(JSArray, output, 1);
4881 RUNTIME_ASSERT(output->HasFastElements());
4882
4883 AssertNoAllocation no_allocation;
4884
4885 FixedArray* output_array = output->elements();
4886 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4887 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00004888 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004889 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004890 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00004891 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004892 result = DateParser::Parse(str->ToUC16Vector(), output_array);
4893 }
4894
4895 if (result) {
4896 return *output;
4897 } else {
4898 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004899 }
4900}
4901
4902
4903static Object* Runtime_DateLocalTimezone(Arguments args) {
4904 NoHandleAllocation ha;
4905 ASSERT(args.length() == 1);
4906
4907 CONVERT_DOUBLE_CHECKED(x, args[0]);
4908 char* zone = OS::LocalTimezone(x);
4909 return Heap::AllocateStringFromUtf8(CStrVector(zone));
4910}
4911
4912
4913static Object* Runtime_DateLocalTimeOffset(Arguments args) {
4914 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004915 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004916
4917 return Heap::NumberFromDouble(OS::LocalTimeOffset());
4918}
4919
4920
4921static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
4922 NoHandleAllocation ha;
4923 ASSERT(args.length() == 1);
4924
4925 CONVERT_DOUBLE_CHECKED(x, args[0]);
4926 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
4927}
4928
4929
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004930static Object* Runtime_NumberIsFinite(Arguments args) {
4931 NoHandleAllocation ha;
4932 ASSERT(args.length() == 1);
4933
4934 CONVERT_DOUBLE_CHECKED(value, args[0]);
4935 Object* result;
4936 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
4937 result = Heap::false_value();
4938 } else {
4939 result = Heap::true_value();
4940 }
4941 return result;
4942}
4943
4944
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004945static Object* Runtime_GlobalReceiver(Arguments args) {
4946 ASSERT(args.length() == 1);
4947 Object* global = args[0];
4948 if (!global->IsJSGlobalObject()) return Heap::null_value();
4949 return JSGlobalObject::cast(global)->global_receiver();
4950}
4951
4952
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004953static Object* Runtime_CompileString(Arguments args) {
4954 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004955 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004956 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004957 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004958
ager@chromium.org381abbb2009-02-25 13:23:22 +00004959 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004960 Handle<Context> context(Top::context()->global_context());
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004961 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
4962 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00004963 true,
4964 is_json->IsTrue());
ager@chromium.org381abbb2009-02-25 13:23:22 +00004965 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004966 Handle<JSFunction> fun =
4967 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4968 return *fun;
4969}
4970
4971
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004972static Handle<JSFunction> GetBuiltinFunction(String* name) {
4973 LookupResult result;
4974 Top::global_context()->builtins()->LocalLookup(name, &result);
4975 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
4976}
4977
4978
4979static Object* CompileDirectEval(Handle<String> source) {
4980 // Compute the eval context.
4981 HandleScope scope;
4982 StackFrameLocator locator;
4983 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4984 Handle<Context> context(Context::cast(frame->context()));
4985 bool is_global = context->IsGlobalContext();
4986
ager@chromium.org381abbb2009-02-25 13:23:22 +00004987 // Compile source string in the current context.
4988 Handle<JSFunction> boilerplate =
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004989 Compiler::CompileEval(source, context, is_global, false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004990 if (boilerplate.is_null()) return Failure::Exception();
4991 Handle<JSFunction> fun =
4992 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4993 return *fun;
4994}
4995
4996
4997static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
4998 ASSERT(args.length() == 2);
4999
5000 HandleScope scope;
5001
5002 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5003
5004 Handle<Object> receiver;
5005
5006 // Find where the 'eval' symbol is bound. It is unaliased only if
5007 // it is bound in the global context.
5008 StackFrameLocator locator;
5009 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5010 Handle<Context> context(Context::cast(frame->context()));
5011 int index;
5012 PropertyAttributes attributes;
5013 while (!context.is_null()) {
5014 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5015 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005016 // Stop search when eval is found or when the global context is
5017 // reached.
5018 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005019 if (context->is_function_context()) {
5020 context = Handle<Context>(Context::cast(context->closure()->context()));
5021 } else {
5022 context = Handle<Context>(context->previous());
5023 }
5024 }
5025
iposva@chromium.org245aa852009-02-10 00:49:54 +00005026 // If eval could not be resolved, it has been deleted and we need to
5027 // throw a reference error.
5028 if (attributes == ABSENT) {
5029 Handle<Object> name = Factory::eval_symbol();
5030 Handle<Object> reference_error =
5031 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5032 return Top::Throw(*reference_error);
5033 }
5034
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005035 if (context->IsGlobalContext()) {
5036 // 'eval' is bound in the global context, but it may have been overwritten.
5037 // Compare it to the builtin 'GlobalEval' function to make sure.
5038 Handle<JSFunction> global_eval =
5039 GetBuiltinFunction(Heap::global_eval_symbol());
5040 if (global_eval.is_identical_to(callee)) {
5041 // A direct eval call.
5042 if (args[1]->IsString()) {
5043 CONVERT_ARG_CHECKED(String, source, 1);
5044 // A normal eval call on a string. Compile it and return the
5045 // compiled function bound in the local context.
5046 Object* compiled_source = CompileDirectEval(source);
5047 if (compiled_source->IsFailure()) return compiled_source;
5048 receiver = Handle<Object>(frame->receiver());
5049 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5050 } else {
5051 // An eval call that is not called on a string. Global eval
5052 // deals better with this.
5053 receiver = Handle<Object>(Top::global_context()->global());
5054 }
5055 } else {
5056 // 'eval' is overwritten. Just call the function with the given arguments.
5057 receiver = Handle<Object>(Top::global_context()->global());
5058 }
5059 } else {
5060 // 'eval' is not bound in the global context. Just call the function
5061 // with the given arguments. This is not necessarily the global eval.
5062 if (receiver->IsContext()) {
5063 context = Handle<Context>::cast(receiver);
5064 receiver = Handle<Object>(context->get(index));
5065 }
5066 }
5067
5068 Handle<FixedArray> call = Factory::NewFixedArray(2);
5069 call->set(0, *callee);
5070 call->set(1, *receiver);
5071 return *call;
5072}
5073
5074
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005075static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5076 // This utility adjusts the property attributes for newly created Function
5077 // object ("new Function(...)") by changing the map.
5078 // All it does is changing the prototype property to enumerable
5079 // as specified in ECMA262, 15.3.5.2.
5080 HandleScope scope;
5081 ASSERT(args.length() == 1);
5082 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5083 ASSERT(func->map()->instance_type() ==
5084 Top::function_instance_map()->instance_type());
5085 ASSERT(func->map()->instance_size() ==
5086 Top::function_instance_map()->instance_size());
5087 func->set_map(*Top::function_instance_map());
5088 return *func;
5089}
5090
5091
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005092// Push an array unto an array of arrays if it is not already in the
5093// array. Returns true if the element was pushed on the stack and
5094// false otherwise.
5095static Object* Runtime_PushIfAbsent(Arguments args) {
5096 ASSERT(args.length() == 2);
5097 CONVERT_CHECKED(JSArray, array, args[0]);
5098 CONVERT_CHECKED(JSArray, element, args[1]);
5099 RUNTIME_ASSERT(array->HasFastElements());
5100 int length = Smi::cast(array->length())->value();
5101 FixedArray* elements = FixedArray::cast(array->elements());
5102 for (int i = 0; i < length; i++) {
5103 if (elements->get(i) == element) return Heap::false_value();
5104 }
5105 Object* obj = array->SetFastElement(length, element);
5106 if (obj->IsFailure()) return obj;
5107 return Heap::true_value();
5108}
5109
5110
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005111/**
5112 * A simple visitor visits every element of Array's.
5113 * The backend storage can be a fixed array for fast elements case,
5114 * or a dictionary for sparse array. Since Dictionary is a subtype
5115 * of FixedArray, the class can be used by both fast and slow cases.
5116 * The second parameter of the constructor, fast_elements, specifies
5117 * whether the storage is a FixedArray or Dictionary.
5118 *
5119 * An index limit is used to deal with the situation that a result array
5120 * length overflows 32-bit non-negative integer.
5121 */
5122class ArrayConcatVisitor {
5123 public:
5124 ArrayConcatVisitor(Handle<FixedArray> storage,
5125 uint32_t index_limit,
5126 bool fast_elements) :
5127 storage_(storage), index_limit_(index_limit),
5128 fast_elements_(fast_elements), index_offset_(0) { }
5129
5130 void visit(uint32_t i, Handle<Object> elm) {
5131 uint32_t index = i + index_offset_;
5132 if (index >= index_limit_) return;
5133
5134 if (fast_elements_) {
5135 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5136 storage_->set(index, *elm);
5137
5138 } else {
5139 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
5140 Handle<Dictionary> result =
5141 Factory::DictionaryAtNumberPut(dict, index, elm);
5142 if (!result.is_identical_to(dict))
5143 storage_ = result;
5144 }
5145 }
5146
5147 void increase_index_offset(uint32_t delta) {
5148 index_offset_ += delta;
5149 }
5150
5151 private:
5152 Handle<FixedArray> storage_;
5153 uint32_t index_limit_;
5154 bool fast_elements_;
5155 uint32_t index_offset_;
5156};
5157
5158
5159/**
5160 * A helper function that visits elements of a JSObject. Only elements
5161 * whose index between 0 and range (exclusive) are visited.
5162 *
5163 * If the third parameter, visitor, is not NULL, the visitor is called
5164 * with parameters, 'visitor_index_offset + element index' and the element.
5165 *
5166 * It returns the number of visisted elements.
5167 */
5168static uint32_t IterateElements(Handle<JSObject> receiver,
5169 uint32_t range,
5170 ArrayConcatVisitor* visitor) {
5171 uint32_t num_of_elements = 0;
5172
5173 if (receiver->HasFastElements()) {
5174 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5175 uint32_t len = elements->length();
5176 if (range < len) len = range;
5177
5178 for (uint32_t j = 0; j < len; j++) {
5179 Handle<Object> e(elements->get(j));
5180 if (!e->IsTheHole()) {
5181 num_of_elements++;
5182 if (visitor)
5183 visitor->visit(j, e);
5184 }
5185 }
5186
5187 } else {
5188 Handle<Dictionary> dict(receiver->element_dictionary());
5189 uint32_t capacity = dict->Capacity();
5190 for (uint32_t j = 0; j < capacity; j++) {
5191 Handle<Object> k(dict->KeyAt(j));
5192 if (dict->IsKey(*k)) {
5193 ASSERT(k->IsNumber());
5194 uint32_t index = static_cast<uint32_t>(k->Number());
5195 if (index < range) {
5196 num_of_elements++;
5197 if (visitor) {
5198 visitor->visit(index,
5199 Handle<Object>(dict->ValueAt(j)));
5200 }
5201 }
5202 }
5203 }
5204 }
5205
5206 return num_of_elements;
5207}
5208
5209
5210/**
5211 * A helper function that visits elements of an Array object, and elements
5212 * on its prototypes.
5213 *
5214 * Elements on prototypes are visited first, and only elements whose indices
5215 * less than Array length are visited.
5216 *
5217 * If a ArrayConcatVisitor object is given, the visitor is called with
5218 * parameters, element's index + visitor_index_offset and the element.
5219 */
5220static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5221 ArrayConcatVisitor* visitor) {
5222 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5223 Handle<Object> obj = array;
5224
5225 static const int kEstimatedPrototypes = 3;
5226 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5227
5228 // Visit prototype first. If an element on the prototype is shadowed by
5229 // the inheritor using the same index, the ArrayConcatVisitor visits
5230 // the prototype element before the shadowing element.
5231 // The visitor can simply overwrite the old value by new value using
5232 // the same index. This follows Array::concat semantics.
5233 while (!obj->IsNull()) {
5234 objects.Add(Handle<JSObject>::cast(obj));
5235 obj = Handle<Object>(obj->GetPrototype());
5236 }
5237
5238 uint32_t nof_elements = 0;
5239 for (int i = objects.length() - 1; i >= 0; i--) {
5240 Handle<JSObject> obj = objects[i];
5241 nof_elements +=
5242 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5243 }
5244
5245 return nof_elements;
5246}
5247
5248
5249/**
5250 * A helper function of Runtime_ArrayConcat.
5251 *
5252 * The first argument is an Array of arrays and objects. It is the
5253 * same as the arguments array of Array::concat JS function.
5254 *
5255 * If an argument is an Array object, the function visits array
5256 * elements. If an argument is not an Array object, the function
5257 * visits the object as if it is an one-element array.
5258 *
5259 * If the result array index overflows 32-bit integer, the rounded
5260 * non-negative number is used as new length. For example, if one
5261 * array length is 2^32 - 1, second array length is 1, the
5262 * concatenated array length is 0.
5263 */
5264static uint32_t IterateArguments(Handle<JSArray> arguments,
5265 ArrayConcatVisitor* visitor) {
5266 uint32_t visited_elements = 0;
5267 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5268
5269 for (uint32_t i = 0; i < num_of_args; i++) {
5270 Handle<Object> obj(arguments->GetElement(i));
5271 if (obj->IsJSArray()) {
5272 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5273 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5274 uint32_t nof_elements =
5275 IterateArrayAndPrototypeElements(array, visitor);
5276 // Total elements of array and its prototype chain can be more than
5277 // the array length, but ArrayConcat can only concatenate at most
5278 // the array length number of elements.
5279 visited_elements += (nof_elements > len) ? len : nof_elements;
5280 if (visitor) visitor->increase_index_offset(len);
5281
5282 } else {
5283 if (visitor) {
5284 visitor->visit(0, obj);
5285 visitor->increase_index_offset(1);
5286 }
5287 visited_elements++;
5288 }
5289 }
5290 return visited_elements;
5291}
5292
5293
5294/**
5295 * Array::concat implementation.
5296 * See ECMAScript 262, 15.4.4.4.
5297 */
5298static Object* Runtime_ArrayConcat(Arguments args) {
5299 ASSERT(args.length() == 1);
5300 HandleScope handle_scope;
5301
5302 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5303 Handle<JSArray> arguments(arg_arrays);
5304
5305 // Pass 1: estimate the number of elements of the result
5306 // (it could be more than real numbers if prototype has elements).
5307 uint32_t result_length = 0;
5308 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5309
5310 { AssertNoAllocation nogc;
5311 for (uint32_t i = 0; i < num_of_args; i++) {
5312 Object* obj = arguments->GetElement(i);
5313 if (obj->IsJSArray()) {
5314 result_length +=
5315 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5316 } else {
5317 result_length++;
5318 }
5319 }
5320 }
5321
5322 // Allocate an empty array, will set length and content later.
5323 Handle<JSArray> result = Factory::NewJSArray(0);
5324
5325 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5326 // If estimated number of elements is more than half of length, a
5327 // fixed array (fast case) is more time and space-efficient than a
5328 // dictionary.
5329 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5330
5331 Handle<FixedArray> storage;
5332 if (fast_case) {
5333 // The backing storage array must have non-existing elements to
5334 // preserve holes across concat operations.
5335 storage = Factory::NewFixedArrayWithHoles(result_length);
5336
5337 } else {
5338 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5339 uint32_t at_least_space_for = estimate_nof_elements +
5340 (estimate_nof_elements >> 2);
5341 storage = Handle<FixedArray>::cast(
5342 Factory::NewDictionary(at_least_space_for));
5343 }
5344
5345 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5346
5347 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5348
5349 IterateArguments(arguments, &visitor);
5350
5351 result->set_length(*len);
5352 result->set_elements(*storage);
5353
5354 return *result;
5355}
5356
5357
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005358// This will not allocate (flatten the string), but it may run
5359// very slowly for very deeply nested ConsStrings. For debugging use only.
5360static Object* Runtime_GlobalPrint(Arguments args) {
5361 NoHandleAllocation ha;
5362 ASSERT(args.length() == 1);
5363
5364 CONVERT_CHECKED(String, string, args[0]);
5365 StringInputBuffer buffer(string);
5366 while (buffer.has_more()) {
5367 uint16_t character = buffer.GetNext();
5368 PrintF("%c", character);
5369 }
5370 return string;
5371}
5372
ager@chromium.org5ec48922009-05-05 07:25:34 +00005373// Moves all own elements of an object, that are below a limit, to positions
5374// starting at zero. All undefined values are placed after non-undefined values,
5375// and are followed by non-existing element. Does not change the length
5376// property.
5377// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005378static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005379 ASSERT(args.length() == 2);
5380 CONVERT_CHECKED(JSObject, object, args[0]);
5381 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5382 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005383}
5384
5385
5386// Move contents of argument 0 (an array) to argument 1 (an array)
5387static Object* Runtime_MoveArrayContents(Arguments args) {
5388 ASSERT(args.length() == 2);
5389 CONVERT_CHECKED(JSArray, from, args[0]);
5390 CONVERT_CHECKED(JSArray, to, args[1]);
5391 to->SetContent(FixedArray::cast(from->elements()));
5392 to->set_length(from->length());
5393 from->SetContent(Heap::empty_fixed_array());
5394 from->set_length(0);
5395 return to;
5396}
5397
5398
5399// How many elements does this array have?
5400static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5401 ASSERT(args.length() == 1);
5402 CONVERT_CHECKED(JSArray, array, args[0]);
5403 HeapObject* elements = array->elements();
5404 if (elements->IsDictionary()) {
5405 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
5406 } else {
5407 return array->length();
5408 }
5409}
5410
5411
5412// Returns an array that tells you where in the [0, length) interval an array
5413// might have elements. Can either return keys or intervals. Keys can have
5414// gaps in (undefined). Intervals can also span over some undefined keys.
5415static Object* Runtime_GetArrayKeys(Arguments args) {
5416 ASSERT(args.length() == 2);
5417 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005418 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005419 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005420 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005421 // Create an array and get all the keys into it, then remove all the
5422 // keys that are not integers in the range 0 to length-1.
5423 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
5424 int keys_length = keys->length();
5425 for (int i = 0; i < keys_length; i++) {
5426 Object* key = keys->get(i);
5427 uint32_t index;
5428 if (!Array::IndexFromObject(key, &index) || index >= length) {
5429 // Zap invalid keys.
5430 keys->set_undefined(i);
5431 }
5432 }
5433 return *Factory::NewJSArrayWithElements(keys);
5434 } else {
5435 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5436 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005437 single_interval->set(0,
5438 Smi::FromInt(-1),
5439 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005440 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5441 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005442 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005443 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005444 single_interval->set(1, *length_object);
5445 return *Factory::NewJSArrayWithElements(single_interval);
5446 }
5447}
5448
5449
5450// DefineAccessor takes an optional final argument which is the
5451// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5452// to the way accessors are implemented, it is set for both the getter
5453// and setter on the first call to DefineAccessor and ignored on
5454// subsequent calls.
5455static Object* Runtime_DefineAccessor(Arguments args) {
5456 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5457 // Compute attributes.
5458 PropertyAttributes attributes = NONE;
5459 if (args.length() == 5) {
5460 CONVERT_CHECKED(Smi, attrs, args[4]);
5461 int value = attrs->value();
5462 // Only attribute bits should be set.
5463 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5464 attributes = static_cast<PropertyAttributes>(value);
5465 }
5466
5467 CONVERT_CHECKED(JSObject, obj, args[0]);
5468 CONVERT_CHECKED(String, name, args[1]);
5469 CONVERT_CHECKED(Smi, flag, args[2]);
5470 CONVERT_CHECKED(JSFunction, fun, args[3]);
5471 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5472}
5473
5474
5475static Object* Runtime_LookupAccessor(Arguments args) {
5476 ASSERT(args.length() == 3);
5477 CONVERT_CHECKED(JSObject, obj, args[0]);
5478 CONVERT_CHECKED(String, name, args[1]);
5479 CONVERT_CHECKED(Smi, flag, args[2]);
5480 return obj->LookupAccessor(name, flag->value() == 0);
5481}
5482
5483
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005484#ifdef ENABLE_DEBUGGER_SUPPORT
5485static Object* Runtime_DebugBreak(Arguments args) {
5486 ASSERT(args.length() == 0);
5487 return Execution::DebugBreakHelper();
5488}
5489
5490
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005491// Helper functions for wrapping and unwrapping stack frame ids.
5492static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005493 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005494 return Smi::FromInt(id >> 2);
5495}
5496
5497
5498static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5499 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5500}
5501
5502
5503// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005504// args[0]: debug event listener function to set or null or undefined for
5505// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005506// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005507static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005508 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005509 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5510 args[0]->IsUndefined() ||
5511 args[0]->IsNull());
5512 Handle<Object> callback = args.at<Object>(0);
5513 Handle<Object> data = args.at<Object>(1);
5514 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005515
5516 return Heap::undefined_value();
5517}
5518
5519
5520static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005521 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005522 StackGuard::DebugBreak();
5523 return Heap::undefined_value();
5524}
5525
5526
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005527// Find the length of the prototype chain that is to to handled as one. If a
5528// prototype object is hidden it is to be viewed as part of the the object it
5529// is prototype for.
5530static int LocalPrototypeChainLength(JSObject* obj) {
5531 int count = 1;
5532 Object* proto = obj->GetPrototype();
5533 while (proto->IsJSObject() &&
5534 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5535 count++;
5536 proto = JSObject::cast(proto)->GetPrototype();
5537 }
5538 return count;
5539}
5540
5541
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005542static Object* DebugLookupResultValue(Object* receiver, String* name,
5543 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005544 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005545 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005546 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005547 case NORMAL:
5548 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005549 if (value->IsTheHole()) {
5550 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005551 }
5552 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005553 case FIELD:
5554 value =
5555 JSObject::cast(
5556 result->holder())->FastPropertyAt(result->GetFieldIndex());
5557 if (value->IsTheHole()) {
5558 return Heap::undefined_value();
5559 }
5560 return value;
5561 case CONSTANT_FUNCTION:
5562 return result->GetConstantFunction();
5563 case CALLBACKS: {
5564 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005565 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005566 value = receiver->GetPropertyWithCallback(
5567 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005568 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005569 value = Top::pending_exception();
5570 Top::clear_pending_exception();
5571 if (caught_exception != NULL) {
5572 *caught_exception = true;
5573 }
5574 }
5575 return value;
5576 } else {
5577 return Heap::undefined_value();
5578 }
5579 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005580 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005581 case MAP_TRANSITION:
5582 case CONSTANT_TRANSITION:
5583 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005584 return Heap::undefined_value();
5585 default:
5586 UNREACHABLE();
5587 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005588 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005589 return Heap::undefined_value();
5590}
5591
5592
ager@chromium.org32912102009-01-16 10:38:43 +00005593// Get debugger related details for an object property.
5594// args[0]: object holding property
5595// args[1]: name of the property
5596//
5597// The array returned contains the following information:
5598// 0: Property value
5599// 1: Property details
5600// 2: Property value is exception
5601// 3: Getter function if defined
5602// 4: Setter function if defined
5603// Items 2-4 are only filled if the property has either a getter or a setter
5604// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005605static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005606 HandleScope scope;
5607
5608 ASSERT(args.length() == 2);
5609
5610 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5611 CONVERT_ARG_CHECKED(String, name, 1);
5612
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005613 // Make sure to set the current context to the context before the debugger was
5614 // entered (if the debugger is entered). The reason for switching context here
5615 // is that for some property lookups (accessors and interceptors) callbacks
5616 // into the embedding application can occour, and the embedding application
5617 // could have the assumption that its own global context is the current
5618 // context and not some internal debugger context.
5619 SaveContext save;
5620 if (Debug::InDebugger()) {
5621 Top::set_context(*Debug::debugger_entry()->GetContext());
5622 }
5623
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005624 // Skip the global proxy as it has no properties and always delegates to the
5625 // real global object.
5626 if (obj->IsJSGlobalProxy()) {
5627 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5628 }
5629
5630
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005631 // Check if the name is trivially convertible to an index and get the element
5632 // if so.
5633 uint32_t index;
5634 if (name->AsArrayIndex(&index)) {
5635 Handle<FixedArray> details = Factory::NewFixedArray(2);
5636 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5637 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5638 return *Factory::NewJSArrayWithElements(details);
5639 }
5640
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005641 // Find the number of objects making up this.
5642 int length = LocalPrototypeChainLength(*obj);
5643
5644 // Try local lookup on each of the objects.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005645 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005646 Handle<JSObject> jsproto = obj;
5647 for (int i = 0; i < length; i++) {
5648 jsproto->LocalLookup(*name, &result);
5649 if (result.IsProperty()) {
5650 break;
5651 }
5652 if (i < length - 1) {
5653 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5654 }
5655 }
5656
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005657 if (result.IsProperty()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005658 // LookupResult is not GC safe as all its members are raw object pointers.
5659 // When calling DebugLookupResultValue GC can happen as this might invoke
5660 // callbacks. After the call to DebugLookupResultValue the callback object
5661 // in the LookupResult might still be needed. Put it into a handle for later
5662 // use.
5663 PropertyType result_type = result.type();
5664 Handle<Object> result_callback_obj;
5665 if (result_type == CALLBACKS) {
5666 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5667 }
5668
5669 // Find the actual value. Don't use result after this call as it's content
5670 // can be invalid.
ager@chromium.org32912102009-01-16 10:38:43 +00005671 bool caught_exception = false;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005672 Object* value = DebugLookupResultValue(*obj, *name, &result,
ager@chromium.org381abbb2009-02-25 13:23:22 +00005673 &caught_exception);
5674 if (value->IsFailure()) return value;
5675 Handle<Object> value_handle(value);
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005676
ager@chromium.org32912102009-01-16 10:38:43 +00005677 // If the callback object is a fixed array then it contains JavaScript
5678 // getter and/or setter.
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005679 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5680 result_callback_obj->IsFixedArray();
ager@chromium.org32912102009-01-16 10:38:43 +00005681 Handle<FixedArray> details =
5682 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005683 details->set(0, *value_handle);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005684 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00005685 if (hasJavaScriptAccessors) {
5686 details->set(2,
5687 caught_exception ? Heap::true_value() : Heap::false_value());
5688 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
5689 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
5690 }
5691
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005692 return *Factory::NewJSArrayWithElements(details);
5693 }
5694 return Heap::undefined_value();
5695}
5696
5697
5698static Object* Runtime_DebugGetProperty(Arguments args) {
5699 HandleScope scope;
5700
5701 ASSERT(args.length() == 2);
5702
5703 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5704 CONVERT_ARG_CHECKED(String, name, 1);
5705
5706 LookupResult result;
5707 obj->Lookup(*name, &result);
5708 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005709 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005710 }
5711 return Heap::undefined_value();
5712}
5713
5714
5715// Return the names of the local named properties.
5716// args[0]: object
5717static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5718 HandleScope scope;
5719 ASSERT(args.length() == 1);
5720 if (!args[0]->IsJSObject()) {
5721 return Heap::undefined_value();
5722 }
5723 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5724
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005725 // Skip the global proxy as it has no properties and always delegates to the
5726 // real global object.
5727 if (obj->IsJSGlobalProxy()) {
5728 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5729 }
5730
5731 // Find the number of objects making up this.
5732 int length = LocalPrototypeChainLength(*obj);
5733
5734 // Find the number of local properties for each of the objects.
5735 int* local_property_count = NewArray<int>(length);
5736 int total_property_count = 0;
5737 Handle<JSObject> jsproto = obj;
5738 for (int i = 0; i < length; i++) {
5739 int n;
5740 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5741 local_property_count[i] = n;
5742 total_property_count += n;
5743 if (i < length - 1) {
5744 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5745 }
5746 }
5747
5748 // Allocate an array with storage for all the property names.
5749 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5750
5751 // Get the property names.
5752 jsproto = obj;
5753 for (int i = 0; i < length; i++) {
5754 jsproto->GetLocalPropertyNames(*names,
5755 i == 0 ? 0 : local_property_count[i - 1]);
5756 if (i < length - 1) {
5757 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5758 }
5759 }
5760
5761 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005762 return *Factory::NewJSArrayWithElements(names);
5763}
5764
5765
5766// Return the names of the local indexed properties.
5767// args[0]: object
5768static Object* Runtime_DebugLocalElementNames(Arguments args) {
5769 HandleScope scope;
5770 ASSERT(args.length() == 1);
5771 if (!args[0]->IsJSObject()) {
5772 return Heap::undefined_value();
5773 }
5774 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5775
5776 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5777 Handle<FixedArray> names = Factory::NewFixedArray(n);
5778 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5779 return *Factory::NewJSArrayWithElements(names);
5780}
5781
5782
5783// Return the property type calculated from the property details.
5784// args[0]: smi with property details.
5785static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5786 ASSERT(args.length() == 1);
5787 CONVERT_CHECKED(Smi, details, args[0]);
5788 PropertyType type = PropertyDetails(details).type();
5789 return Smi::FromInt(static_cast<int>(type));
5790}
5791
5792
5793// Return the property attribute calculated from the property details.
5794// args[0]: smi with property details.
5795static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5796 ASSERT(args.length() == 1);
5797 CONVERT_CHECKED(Smi, details, args[0]);
5798 PropertyAttributes attributes = PropertyDetails(details).attributes();
5799 return Smi::FromInt(static_cast<int>(attributes));
5800}
5801
5802
5803// Return the property insertion index calculated from the property details.
5804// args[0]: smi with property details.
5805static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5806 ASSERT(args.length() == 1);
5807 CONVERT_CHECKED(Smi, details, args[0]);
5808 int index = PropertyDetails(details).index();
5809 return Smi::FromInt(index);
5810}
5811
5812
5813// Return information on whether an object has a named or indexed interceptor.
5814// args[0]: object
5815static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5816 HandleScope scope;
5817 ASSERT(args.length() == 1);
5818 if (!args[0]->IsJSObject()) {
5819 return Smi::FromInt(0);
5820 }
5821 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5822
5823 int result = 0;
5824 if (obj->HasNamedInterceptor()) result |= 2;
5825 if (obj->HasIndexedInterceptor()) result |= 1;
5826
5827 return Smi::FromInt(result);
5828}
5829
5830
5831// Return property names from named interceptor.
5832// args[0]: object
5833static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5834 HandleScope scope;
5835 ASSERT(args.length() == 1);
5836 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005837
ager@chromium.org32912102009-01-16 10:38:43 +00005838 if (obj->HasNamedInterceptor()) {
5839 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5840 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5841 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005842 return Heap::undefined_value();
5843}
5844
5845
5846// Return element names from indexed interceptor.
5847// args[0]: object
5848static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5849 HandleScope scope;
5850 ASSERT(args.length() == 1);
5851 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005852
ager@chromium.org32912102009-01-16 10:38:43 +00005853 if (obj->HasIndexedInterceptor()) {
5854 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5855 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5856 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005857 return Heap::undefined_value();
5858}
5859
5860
5861// Return property value from named interceptor.
5862// args[0]: object
5863// args[1]: property name
5864static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
5865 HandleScope scope;
5866 ASSERT(args.length() == 2);
5867 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5868 RUNTIME_ASSERT(obj->HasNamedInterceptor());
5869 CONVERT_ARG_CHECKED(String, name, 1);
5870
5871 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005872 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005873}
5874
5875
5876// Return element value from indexed interceptor.
5877// args[0]: object
5878// args[1]: index
5879static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
5880 HandleScope scope;
5881 ASSERT(args.length() == 2);
5882 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5883 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
5884 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
5885
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005886 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005887}
5888
5889
5890static Object* Runtime_CheckExecutionState(Arguments args) {
5891 ASSERT(args.length() >= 1);
5892 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00005893 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005894 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005895 return Top::Throw(Heap::illegal_execution_state_symbol());
5896 }
5897
5898 return Heap::true_value();
5899}
5900
5901
5902static Object* Runtime_GetFrameCount(Arguments args) {
5903 HandleScope scope;
5904 ASSERT(args.length() == 1);
5905
5906 // Check arguments.
5907 Object* result = Runtime_CheckExecutionState(args);
5908 if (result->IsFailure()) return result;
5909
5910 // Count all frames which are relevant to debugging stack trace.
5911 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005912 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005913 if (id == StackFrame::NO_ID) {
5914 // If there is no JavaScript stack frame count is 0.
5915 return Smi::FromInt(0);
5916 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005917 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
5918 return Smi::FromInt(n);
5919}
5920
5921
5922static const int kFrameDetailsFrameIdIndex = 0;
5923static const int kFrameDetailsReceiverIndex = 1;
5924static const int kFrameDetailsFunctionIndex = 2;
5925static const int kFrameDetailsArgumentCountIndex = 3;
5926static const int kFrameDetailsLocalCountIndex = 4;
5927static const int kFrameDetailsSourcePositionIndex = 5;
5928static const int kFrameDetailsConstructCallIndex = 6;
5929static const int kFrameDetailsDebuggerFrameIndex = 7;
5930static const int kFrameDetailsFirstDynamicIndex = 8;
5931
5932// Return an array with frame details
5933// args[0]: number: break id
5934// args[1]: number: frame index
5935//
5936// The array returned contains the following information:
5937// 0: Frame id
5938// 1: Receiver
5939// 2: Function
5940// 3: Argument count
5941// 4: Local count
5942// 5: Source position
5943// 6: Constructor call
5944// 7: Debugger frame
5945// Arguments name, value
5946// Locals name, value
5947static Object* Runtime_GetFrameDetails(Arguments args) {
5948 HandleScope scope;
5949 ASSERT(args.length() == 2);
5950
5951 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005952 Object* check = Runtime_CheckExecutionState(args);
5953 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005954 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
5955
5956 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00005957 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00005958 if (id == StackFrame::NO_ID) {
5959 // If there are no JavaScript stack frames return undefined.
5960 return Heap::undefined_value();
5961 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005962 int count = 0;
5963 JavaScriptFrameIterator it(id);
5964 for (; !it.done(); it.Advance()) {
5965 if (count == index) break;
5966 count++;
5967 }
5968 if (it.done()) return Heap::undefined_value();
5969
5970 // Traverse the saved contexts chain to find the active context for the
5971 // selected frame.
5972 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005973 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005974 save = save->prev();
5975 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005976 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005977
5978 // Get the frame id.
5979 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
5980
5981 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005982 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005983
5984 // Check for constructor frame.
5985 bool constructor = it.frame()->IsConstructor();
5986
5987 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00005988 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005989 ScopeInfo<> info(*code);
5990
5991 // Get the context.
5992 Handle<Context> context(Context::cast(it.frame()->context()));
5993
5994 // Get the locals names and values into a temporary array.
5995 //
5996 // TODO(1240907): Hide compiler-introduced stack variables
5997 // (e.g. .result)? For users of the debugger, they will probably be
5998 // confusing.
5999 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6000 for (int i = 0; i < info.NumberOfLocals(); i++) {
6001 // Name of the local.
6002 locals->set(i * 2, *info.LocalName(i));
6003
6004 // Fetch the value of the local - either from the stack or from a
6005 // heap-allocated context.
6006 if (i < info.number_of_stack_slots()) {
6007 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6008 } else {
6009 Handle<String> name = info.LocalName(i);
6010 // Traverse the context chain to the function context as all local
6011 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006012 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006013 context = Handle<Context>(context->previous());
6014 }
6015 ASSERT(context->is_function_context());
6016 locals->set(i * 2 + 1,
6017 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6018 NULL)));
6019 }
6020 }
6021
6022 // Now advance to the arguments adapter frame (if any). If contains all
6023 // the provided parameters and
6024
6025 // Now advance to the arguments adapter frame (if any). It contains all
6026 // the provided parameters whereas the function frame always have the number
6027 // of arguments matching the functions parameters. The rest of the
6028 // information (except for what is collected above) is the same.
6029 it.AdvanceToArgumentsFrame();
6030
6031 // Find the number of arguments to fill. At least fill the number of
6032 // parameters for the function and fill more if more parameters are provided.
6033 int argument_count = info.number_of_parameters();
6034 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6035 argument_count = it.frame()->GetProvidedParametersCount();
6036 }
6037
6038 // Calculate the size of the result.
6039 int details_size = kFrameDetailsFirstDynamicIndex +
6040 2 * (argument_count + info.NumberOfLocals());
6041 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6042
6043 // Add the frame id.
6044 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6045
6046 // Add the function (same as in function frame).
6047 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6048
6049 // Add the arguments count.
6050 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6051
6052 // Add the locals count
6053 details->set(kFrameDetailsLocalCountIndex,
6054 Smi::FromInt(info.NumberOfLocals()));
6055
6056 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006057 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006058 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6059 } else {
6060 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6061 }
6062
6063 // Add the constructor information.
6064 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6065
6066 // Add information on whether this frame is invoked in the debugger context.
6067 details->set(kFrameDetailsDebuggerFrameIndex,
6068 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6069
6070 // Fill the dynamic part.
6071 int details_index = kFrameDetailsFirstDynamicIndex;
6072
6073 // Add arguments name and value.
6074 for (int i = 0; i < argument_count; i++) {
6075 // Name of the argument.
6076 if (i < info.number_of_parameters()) {
6077 details->set(details_index++, *info.parameter_name(i));
6078 } else {
6079 details->set(details_index++, Heap::undefined_value());
6080 }
6081
6082 // Parameter value.
6083 if (i < it.frame()->GetProvidedParametersCount()) {
6084 details->set(details_index++, it.frame()->GetParameter(i));
6085 } else {
6086 details->set(details_index++, Heap::undefined_value());
6087 }
6088 }
6089
6090 // Add locals name and value from the temporary copy from the function frame.
6091 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6092 details->set(details_index++, locals->get(i));
6093 }
6094
6095 // Add the receiver (same as in function frame).
6096 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6097 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6098 Handle<Object> receiver(it.frame()->receiver());
6099 if (!receiver->IsJSObject()) {
6100 // If the receiver is NOT a JSObject we have hit an optimization
6101 // where a value object is not converted into a wrapped JS objects.
6102 // To hide this optimization from the debugger, we wrap the receiver
6103 // by creating correct wrapper object based on the calling frame's
6104 // global context.
6105 it.Advance();
6106 Handle<Context> calling_frames_global_context(
6107 Context::cast(Context::cast(it.frame()->context())->global_context()));
6108 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6109 }
6110 details->set(kFrameDetailsReceiverIndex, *receiver);
6111
6112 ASSERT_EQ(details_size, details_index);
6113 return *Factory::NewJSArrayWithElements(details);
6114}
6115
6116
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006117// Copy all the context locals into an object used to materialize a scope.
6118static void CopyContextLocalsToScopeObject(Handle<Code> code,
6119 ScopeInfo<>& scope_info,
6120 Handle<Context> context,
6121 Handle<JSObject> scope_object) {
6122 // Fill all context locals to the context extension.
6123 for (int i = Context::MIN_CONTEXT_SLOTS;
6124 i < scope_info.number_of_context_slots();
6125 i++) {
6126 int context_index =
6127 ScopeInfo<>::ContextSlotIndex(*code,
6128 *scope_info.context_slot_name(i),
6129 NULL);
6130
6131 // Don't include the arguments shadow (.arguments) context variable.
6132 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6133 SetProperty(scope_object,
6134 scope_info.context_slot_name(i),
6135 Handle<Object>(context->get(context_index)), NONE);
6136 }
6137 }
6138}
6139
6140
6141// Create a plain JSObject which materializes the local scope for the specified
6142// frame.
6143static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6144 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6145 Handle<Code> code(function->code());
6146 ScopeInfo<> scope_info(*code);
6147
6148 // Allocate and initialize a JSObject with all the arguments, stack locals
6149 // heap locals and extension properties of the debugged function.
6150 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6151
6152 // First fill all parameters.
6153 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6154 SetProperty(local_scope,
6155 scope_info.parameter_name(i),
6156 Handle<Object>(frame->GetParameter(i)), NONE);
6157 }
6158
6159 // Second fill all stack locals.
6160 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6161 SetProperty(local_scope,
6162 scope_info.stack_slot_name(i),
6163 Handle<Object>(frame->GetExpression(i)), NONE);
6164 }
6165
6166 // Third fill all context locals.
6167 Handle<Context> frame_context(Context::cast(frame->context()));
6168 Handle<Context> function_context(frame_context->fcontext());
6169 CopyContextLocalsToScopeObject(code, scope_info,
6170 function_context, local_scope);
6171
6172 // Finally copy any properties from the function context extension. This will
6173 // be variables introduced by eval.
6174 if (function_context->closure() == *function) {
6175 if (function_context->has_extension() &&
6176 !function_context->IsGlobalContext()) {
6177 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
6178 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6179 for (int i = 0; i < keys->length(); i++) {
6180 // Names of variables introduced by eval are strings.
6181 ASSERT(keys->get(i)->IsString());
6182 Handle<String> key(String::cast(keys->get(i)));
6183 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6184 }
6185 }
6186 }
6187 return local_scope;
6188}
6189
6190
6191// Create a plain JSObject which materializes the closure content for the
6192// context.
6193static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6194 ASSERT(context->is_function_context());
6195
6196 Handle<Code> code(context->closure()->code());
6197 ScopeInfo<> scope_info(*code);
6198
6199 // Allocate and initialize a JSObject with all the content of theis function
6200 // closure.
6201 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6202
6203 // Check whether the arguments shadow object exists.
6204 int arguments_shadow_index =
6205 ScopeInfo<>::ContextSlotIndex(*code,
6206 Heap::arguments_shadow_symbol(),
6207 NULL);
6208 if (arguments_shadow_index >= 0) {
6209 // In this case all the arguments are available in the arguments shadow
6210 // object.
6211 Handle<JSObject> arguments_shadow(
6212 JSObject::cast(context->get(arguments_shadow_index)));
6213 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6214 SetProperty(closure_scope,
6215 scope_info.parameter_name(i),
6216 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6217 }
6218 }
6219
6220 // Fill all context locals to the context extension.
6221 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6222
6223 // Finally copy any properties from the function context extension. This will
6224 // be variables introduced by eval.
6225 if (context->has_extension()) {
6226 Handle<JSObject> ext(JSObject::cast(context->extension()));
6227 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
6228 for (int i = 0; i < keys->length(); i++) {
6229 // Names of variables introduced by eval are strings.
6230 ASSERT(keys->get(i)->IsString());
6231 Handle<String> key(String::cast(keys->get(i)));
6232 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6233 }
6234 }
6235
6236 return closure_scope;
6237}
6238
6239
6240// Iterate over the actual scopes visible from a stack frame. All scopes are
6241// backed by an actual context except the local scope, which is inserted
6242// "artifically" in the context chain.
6243class ScopeIterator {
6244 public:
6245 enum ScopeType {
6246 ScopeTypeGlobal = 0,
6247 ScopeTypeLocal,
6248 ScopeTypeWith,
6249 ScopeTypeClosure
6250 };
6251
6252 explicit ScopeIterator(JavaScriptFrame* frame)
6253 : frame_(frame),
6254 function_(JSFunction::cast(frame->function())),
6255 context_(Context::cast(frame->context())),
6256 local_done_(false),
6257 at_local_(false) {
6258
6259 // Check whether the first scope is actually a local scope.
6260 if (context_->IsGlobalContext()) {
6261 // If there is a stack slot for .result then this local scope has been
6262 // created for evaluating top level code and it is not a real local scope.
6263 // Checking for the existence of .result seems fragile, but the scope info
6264 // saved with the code object does not otherwise have that information.
6265 Handle<Code> code(function_->code());
6266 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6267 at_local_ = index < 0;
6268 } else if (context_->is_function_context()) {
6269 at_local_ = true;
6270 }
6271 }
6272
6273 // More scopes?
6274 bool Done() { return context_.is_null(); }
6275
6276 // Move to the next scope.
6277 void Next() {
6278 // If at a local scope mark the local scope as passed.
6279 if (at_local_) {
6280 at_local_ = false;
6281 local_done_ = true;
6282
6283 // If the current context is not associated with the local scope the
6284 // current context is the next real scope, so don't move to the next
6285 // context in this case.
6286 if (context_->closure() != *function_) {
6287 return;
6288 }
6289 }
6290
6291 // The global scope is always the last in the chain.
6292 if (context_->IsGlobalContext()) {
6293 context_ = Handle<Context>();
6294 return;
6295 }
6296
6297 // Move to the next context.
6298 if (context_->is_function_context()) {
6299 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6300 } else {
6301 context_ = Handle<Context>(context_->previous());
6302 }
6303
6304 // If passing the local scope indicate that the current scope is now the
6305 // local scope.
6306 if (!local_done_ &&
6307 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6308 at_local_ = true;
6309 }
6310 }
6311
6312 // Return the type of the current scope.
6313 int Type() {
6314 if (at_local_) {
6315 return ScopeTypeLocal;
6316 }
6317 if (context_->IsGlobalContext()) {
6318 ASSERT(context_->global()->IsGlobalObject());
6319 return ScopeTypeGlobal;
6320 }
6321 if (context_->is_function_context()) {
6322 return ScopeTypeClosure;
6323 }
6324 ASSERT(context_->has_extension());
6325 ASSERT(!context_->extension()->IsJSContextExtensionObject());
6326 return ScopeTypeWith;
6327 }
6328
6329 // Return the JavaScript object with the content of the current scope.
6330 Handle<JSObject> ScopeObject() {
6331 switch (Type()) {
6332 case ScopeIterator::ScopeTypeGlobal:
6333 return Handle<JSObject>(CurrentContext()->global());
6334 break;
6335 case ScopeIterator::ScopeTypeLocal:
6336 // Materialize the content of the local scope into a JSObject.
6337 return MaterializeLocalScope(frame_);
6338 break;
6339 case ScopeIterator::ScopeTypeWith:
6340 // Return the with object.
6341 return Handle<JSObject>(CurrentContext()->extension());
6342 break;
6343 case ScopeIterator::ScopeTypeClosure:
6344 // Materialize the content of the closure scope into a JSObject.
6345 return MaterializeClosure(CurrentContext());
6346 break;
6347 }
6348 UNREACHABLE();
6349 return Handle<JSObject>();
6350 }
6351
6352 // Return the context for this scope. For the local context there might not
6353 // be an actual context.
6354 Handle<Context> CurrentContext() {
6355 if (at_local_ && context_->closure() != *function_) {
6356 return Handle<Context>();
6357 }
6358 return context_;
6359 }
6360
6361#ifdef DEBUG
6362 // Debug print of the content of the current scope.
6363 void DebugPrint() {
6364 switch (Type()) {
6365 case ScopeIterator::ScopeTypeGlobal:
6366 PrintF("Global:\n");
6367 CurrentContext()->Print();
6368 break;
6369
6370 case ScopeIterator::ScopeTypeLocal: {
6371 PrintF("Local:\n");
6372 Handle<Code> code(function_->code());
6373 ScopeInfo<> scope_info(*code);
6374 scope_info.Print();
6375 if (!CurrentContext().is_null()) {
6376 CurrentContext()->Print();
6377 if (CurrentContext()->has_extension()) {
6378 Handle<JSObject> extension =
6379 Handle<JSObject>(CurrentContext()->extension());
6380 if (extension->IsJSContextExtensionObject()) {
6381 extension->Print();
6382 }
6383 }
6384 }
6385 break;
6386 }
6387
6388 case ScopeIterator::ScopeTypeWith: {
6389 PrintF("With:\n");
6390 Handle<JSObject> extension =
6391 Handle<JSObject>(CurrentContext()->extension());
6392 extension->Print();
6393 break;
6394 }
6395
6396 case ScopeIterator::ScopeTypeClosure: {
6397 PrintF("Closure:\n");
6398 CurrentContext()->Print();
6399 if (CurrentContext()->has_extension()) {
6400 Handle<JSObject> extension =
6401 Handle<JSObject>(CurrentContext()->extension());
6402 if (extension->IsJSContextExtensionObject()) {
6403 extension->Print();
6404 }
6405 }
6406 break;
6407 }
6408
6409 default:
6410 UNREACHABLE();
6411 }
6412 PrintF("\n");
6413 }
6414#endif
6415
6416 private:
6417 JavaScriptFrame* frame_;
6418 Handle<JSFunction> function_;
6419 Handle<Context> context_;
6420 bool local_done_;
6421 bool at_local_;
6422
6423 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6424};
6425
6426
6427static Object* Runtime_GetScopeCount(Arguments args) {
6428 HandleScope scope;
6429 ASSERT(args.length() == 2);
6430
6431 // Check arguments.
6432 Object* check = Runtime_CheckExecutionState(args);
6433 if (check->IsFailure()) return check;
6434 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6435
6436 // Get the frame where the debugging is performed.
6437 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6438 JavaScriptFrameIterator it(id);
6439 JavaScriptFrame* frame = it.frame();
6440
6441 // Count the visible scopes.
6442 int n = 0;
6443 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6444 n++;
6445 }
6446
6447 return Smi::FromInt(n);
6448}
6449
6450
6451static const int kScopeDetailsTypeIndex = 0;
6452static const int kScopeDetailsObjectIndex = 1;
6453static const int kScopeDetailsSize = 2;
6454
6455// Return an array with scope details
6456// args[0]: number: break id
6457// args[1]: number: frame index
6458// args[2]: number: scope index
6459//
6460// The array returned contains the following information:
6461// 0: Scope type
6462// 1: Scope object
6463static Object* Runtime_GetScopeDetails(Arguments args) {
6464 HandleScope scope;
6465 ASSERT(args.length() == 3);
6466
6467 // Check arguments.
6468 Object* check = Runtime_CheckExecutionState(args);
6469 if (check->IsFailure()) return check;
6470 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6471 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6472
6473 // Get the frame where the debugging is performed.
6474 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6475 JavaScriptFrameIterator frame_it(id);
6476 JavaScriptFrame* frame = frame_it.frame();
6477
6478 // Find the requested scope.
6479 int n = 0;
6480 ScopeIterator it(frame);
6481 for (; !it.Done() && n < index; it.Next()) {
6482 n++;
6483 }
6484 if (it.Done()) {
6485 return Heap::undefined_value();
6486 }
6487
6488 // Calculate the size of the result.
6489 int details_size = kScopeDetailsSize;
6490 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6491
6492 // Fill in scope details.
6493 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6494 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6495
6496 return *Factory::NewJSArrayWithElements(details);
6497}
6498
6499
6500static Object* Runtime_DebugPrintScopes(Arguments args) {
6501 HandleScope scope;
6502 ASSERT(args.length() == 0);
6503
6504#ifdef DEBUG
6505 // Print the scopes for the top frame.
6506 StackFrameLocator locator;
6507 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6508 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6509 it.DebugPrint();
6510 }
6511#endif
6512 return Heap::undefined_value();
6513}
6514
6515
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006516static Object* Runtime_GetCFrames(Arguments args) {
6517 HandleScope scope;
6518 ASSERT(args.length() == 1);
6519 Object* result = Runtime_CheckExecutionState(args);
6520 if (result->IsFailure()) return result;
6521
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006522#if V8_HOST_ARCH_64_BIT
6523 UNIMPLEMENTED();
6524 return Heap::undefined_value();
6525#else
6526
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006527 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006528 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6529 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006530 if (frames_count == OS::kStackWalkError) {
6531 return Heap::undefined_value();
6532 }
6533
6534 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6535 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6536 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6537 for (int i = 0; i < frames_count; i++) {
6538 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6539 frame_value->SetProperty(
6540 *address_str,
6541 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6542 NONE);
6543
6544 // Get the stack walk text for this frame.
6545 Handle<String> frame_text;
6546 if (strlen(frames[i].text) > 0) {
6547 Vector<const char> str(frames[i].text, strlen(frames[i].text));
6548 frame_text = Factory::NewStringFromAscii(str);
6549 }
6550
6551 if (!frame_text.is_null()) {
6552 frame_value->SetProperty(*text_str, *frame_text, NONE);
6553 }
6554
6555 frames_array->set(i, *frame_value);
6556 }
6557 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006558#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006559}
6560
6561
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006562static Object* Runtime_GetThreadCount(Arguments args) {
6563 HandleScope scope;
6564 ASSERT(args.length() == 1);
6565
6566 // Check arguments.
6567 Object* result = Runtime_CheckExecutionState(args);
6568 if (result->IsFailure()) return result;
6569
6570 // Count all archived V8 threads.
6571 int n = 0;
6572 for (ThreadState* thread = ThreadState::FirstInUse();
6573 thread != NULL;
6574 thread = thread->Next()) {
6575 n++;
6576 }
6577
6578 // Total number of threads is current thread and archived threads.
6579 return Smi::FromInt(n + 1);
6580}
6581
6582
6583static const int kThreadDetailsCurrentThreadIndex = 0;
6584static const int kThreadDetailsThreadIdIndex = 1;
6585static const int kThreadDetailsSize = 2;
6586
6587// Return an array with thread details
6588// args[0]: number: break id
6589// args[1]: number: thread index
6590//
6591// The array returned contains the following information:
6592// 0: Is current thread?
6593// 1: Thread id
6594static Object* Runtime_GetThreadDetails(Arguments args) {
6595 HandleScope scope;
6596 ASSERT(args.length() == 2);
6597
6598 // Check arguments.
6599 Object* check = Runtime_CheckExecutionState(args);
6600 if (check->IsFailure()) return check;
6601 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6602
6603 // Allocate array for result.
6604 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6605
6606 // Thread index 0 is current thread.
6607 if (index == 0) {
6608 // Fill the details.
6609 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6610 details->set(kThreadDetailsThreadIdIndex,
6611 Smi::FromInt(ThreadManager::CurrentId()));
6612 } else {
6613 // Find the thread with the requested index.
6614 int n = 1;
6615 ThreadState* thread = ThreadState::FirstInUse();
6616 while (index != n && thread != NULL) {
6617 thread = thread->Next();
6618 n++;
6619 }
6620 if (thread == NULL) {
6621 return Heap::undefined_value();
6622 }
6623
6624 // Fill the details.
6625 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6626 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6627 }
6628
6629 // Convert to JS array and return.
6630 return *Factory::NewJSArrayWithElements(details);
6631}
6632
6633
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006634static Object* Runtime_GetBreakLocations(Arguments args) {
6635 HandleScope scope;
6636 ASSERT(args.length() == 1);
6637
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006638 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6639 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006640 // Find the number of break points
6641 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6642 if (break_locations->IsUndefined()) return Heap::undefined_value();
6643 // Return array as JS array
6644 return *Factory::NewJSArrayWithElements(
6645 Handle<FixedArray>::cast(break_locations));
6646}
6647
6648
6649// Set a break point in a function
6650// args[0]: function
6651// args[1]: number: break source position (within the function source)
6652// args[2]: number: break point object
6653static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6654 HandleScope scope;
6655 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006656 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6657 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006658 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6659 RUNTIME_ASSERT(source_position >= 0);
6660 Handle<Object> break_point_object_arg = args.at<Object>(2);
6661
6662 // Set break point.
6663 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6664
6665 return Heap::undefined_value();
6666}
6667
6668
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006669Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6670 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006671 // Iterate the heap looking for SharedFunctionInfo generated from the
6672 // script. The inner most SharedFunctionInfo containing the source position
6673 // for the requested break point is found.
6674 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6675 // which is found is not compiled it is compiled and the heap is iterated
6676 // again as the compilation might create inner functions from the newly
6677 // compiled function and the actual requested break point might be in one of
6678 // these functions.
6679 bool done = false;
6680 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006681 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006682 Handle<SharedFunctionInfo> target;
6683 // The current candidate for the last function in script:
6684 Handle<SharedFunctionInfo> last;
6685 while (!done) {
6686 HeapIterator iterator;
6687 while (iterator.has_next()) {
6688 HeapObject* obj = iterator.next();
6689 ASSERT(obj != NULL);
6690 if (obj->IsSharedFunctionInfo()) {
6691 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6692 if (shared->script() == *script) {
6693 // If the SharedFunctionInfo found has the requested script data and
6694 // contains the source position it is a candidate.
6695 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006696 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006697 start_position = shared->start_position();
6698 }
6699 if (start_position <= position &&
6700 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006701 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006702 // candidate this is the new candidate.
6703 if (target.is_null()) {
6704 target_start_position = start_position;
6705 target = shared;
6706 } else {
6707 if (target_start_position < start_position &&
6708 shared->end_position() < target->end_position()) {
6709 target_start_position = start_position;
6710 target = shared;
6711 }
6712 }
6713 }
6714
6715 // Keep track of the last function in the script.
6716 if (last.is_null() ||
6717 shared->end_position() > last->start_position()) {
6718 last = shared;
6719 }
6720 }
6721 }
6722 }
6723
6724 // Make sure some candidate is selected.
6725 if (target.is_null()) {
6726 if (!last.is_null()) {
6727 // Position after the last function - use last.
6728 target = last;
6729 } else {
6730 // Unable to find function - possibly script without any function.
6731 return Heap::undefined_value();
6732 }
6733 }
6734
6735 // If the candidate found is compiled we are done. NOTE: when lazy
6736 // compilation of inner functions is introduced some additional checking
6737 // needs to be done here to compile inner functions.
6738 done = target->is_compiled();
6739 if (!done) {
6740 // If the candidate is not compiled compile it to reveal any inner
6741 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006742 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006743 }
6744 }
6745
6746 return *target;
6747}
6748
6749
6750// Change the state of a break point in a script. NOTE: Regarding performance
6751// see the NOTE for GetScriptFromScriptData.
6752// args[0]: script to set break point in
6753// args[1]: number: break source position (within the script source)
6754// args[2]: number: break point object
6755static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6756 HandleScope scope;
6757 ASSERT(args.length() == 3);
6758 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6759 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6760 RUNTIME_ASSERT(source_position >= 0);
6761 Handle<Object> break_point_object_arg = args.at<Object>(2);
6762
6763 // Get the script from the script wrapper.
6764 RUNTIME_ASSERT(wrapper->value()->IsScript());
6765 Handle<Script> script(Script::cast(wrapper->value()));
6766
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006767 Object* result = Runtime::FindSharedFunctionInfoInScript(
6768 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006769 if (!result->IsUndefined()) {
6770 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6771 // Find position within function. The script position might be before the
6772 // source position of the first function.
6773 int position;
6774 if (shared->start_position() > source_position) {
6775 position = 0;
6776 } else {
6777 position = source_position - shared->start_position();
6778 }
6779 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6780 }
6781 return Heap::undefined_value();
6782}
6783
6784
6785// Clear a break point
6786// args[0]: number: break point object
6787static Object* Runtime_ClearBreakPoint(Arguments args) {
6788 HandleScope scope;
6789 ASSERT(args.length() == 1);
6790 Handle<Object> break_point_object_arg = args.at<Object>(0);
6791
6792 // Clear break point.
6793 Debug::ClearBreakPoint(break_point_object_arg);
6794
6795 return Heap::undefined_value();
6796}
6797
6798
6799// Change the state of break on exceptions
6800// args[0]: boolean indicating uncaught exceptions
6801// args[1]: boolean indicating on/off
6802static Object* Runtime_ChangeBreakOnException(Arguments args) {
6803 HandleScope scope;
6804 ASSERT(args.length() == 2);
6805 ASSERT(args[0]->IsNumber());
6806 ASSERT(args[1]->IsBoolean());
6807
6808 // Update break point state
6809 ExceptionBreakType type =
6810 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6811 bool enable = args[1]->ToBoolean()->IsTrue();
6812 Debug::ChangeBreakOnException(type, enable);
6813 return Heap::undefined_value();
6814}
6815
6816
6817// Prepare for stepping
6818// args[0]: break id for checking execution state
6819// args[1]: step action from the enumeration StepAction
6820// args[2]: number of times to perform the step
6821static Object* Runtime_PrepareStep(Arguments args) {
6822 HandleScope scope;
6823 ASSERT(args.length() == 3);
6824 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006825 Object* check = Runtime_CheckExecutionState(args);
6826 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006827 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
6828 return Top::Throw(Heap::illegal_argument_symbol());
6829 }
6830
6831 // Get the step action and check validity.
6832 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
6833 if (step_action != StepIn &&
6834 step_action != StepNext &&
6835 step_action != StepOut &&
6836 step_action != StepInMin &&
6837 step_action != StepMin) {
6838 return Top::Throw(Heap::illegal_argument_symbol());
6839 }
6840
6841 // Get the number of steps.
6842 int step_count = NumberToInt32(args[2]);
6843 if (step_count < 1) {
6844 return Top::Throw(Heap::illegal_argument_symbol());
6845 }
6846
6847 // Prepare step.
6848 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
6849 return Heap::undefined_value();
6850}
6851
6852
6853// Clear all stepping set by PrepareStep.
6854static Object* Runtime_ClearStepping(Arguments args) {
6855 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00006856 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006857 Debug::ClearStepping();
6858 return Heap::undefined_value();
6859}
6860
6861
6862// Creates a copy of the with context chain. The copy of the context chain is
6863// is linked to the function context supplied.
6864static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
6865 Handle<Context> function_context) {
6866 // At the bottom of the chain. Return the function context to link to.
6867 if (context_chain->is_function_context()) {
6868 return function_context;
6869 }
6870
6871 // Recursively copy the with contexts.
6872 Handle<Context> previous(context_chain->previous());
6873 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
6874 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00006875 CopyWithContextChain(function_context, previous),
6876 extension,
6877 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006878}
6879
6880
6881// Helper function to find or create the arguments object for
6882// Runtime_DebugEvaluate.
6883static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
6884 Handle<JSFunction> function,
6885 Handle<Code> code,
6886 const ScopeInfo<>* sinfo,
6887 Handle<Context> function_context) {
6888 // Try to find the value of 'arguments' to pass as parameter. If it is not
6889 // found (that is the debugged function does not reference 'arguments' and
6890 // does not support eval) then create an 'arguments' object.
6891 int index;
6892 if (sinfo->number_of_stack_slots() > 0) {
6893 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
6894 if (index != -1) {
6895 return Handle<Object>(frame->GetExpression(index));
6896 }
6897 }
6898
6899 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
6900 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
6901 NULL);
6902 if (index != -1) {
6903 return Handle<Object>(function_context->get(index));
6904 }
6905 }
6906
6907 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006908 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
6909 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006910 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006911 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00006912 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006913 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006914 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006915 return arguments;
6916}
6917
6918
6919// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00006920// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006921// extension part has all the parameters and locals of the function on the
6922// stack frame. A function which calls eval with the code to evaluate is then
6923// compiled in this context and called in this context. As this context
6924// replaces the context of the function on the stack frame a new (empty)
6925// function is created as well to be used as the closure for the context.
6926// This function and the context acts as replacements for the function on the
6927// stack frame presenting the same view of the values of parameters and
6928// local variables as if the piece of JavaScript was evaluated at the point
6929// where the function on the stack frame is currently stopped.
6930static Object* Runtime_DebugEvaluate(Arguments args) {
6931 HandleScope scope;
6932
6933 // Check the execution state and decode arguments frame and source to be
6934 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006935 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006936 Object* check_result = Runtime_CheckExecutionState(args);
6937 if (check_result->IsFailure()) return check_result;
6938 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6939 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00006940 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
6941
6942 // Handle the processing of break.
6943 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006944
6945 // Get the frame where the debugging is performed.
6946 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6947 JavaScriptFrameIterator it(id);
6948 JavaScriptFrame* frame = it.frame();
6949 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6950 Handle<Code> code(function->code());
6951 ScopeInfo<> sinfo(*code);
6952
6953 // Traverse the saved contexts chain to find the active context for the
6954 // selected frame.
6955 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006956 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006957 save = save->prev();
6958 }
6959 ASSERT(save != NULL);
6960 SaveContext savex;
6961 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006962
6963 // Create the (empty) function replacing the function on the stack frame for
6964 // the purpose of evaluating in the context created below. It is important
6965 // that this function does not describe any parameters and local variables
6966 // in the context. If it does then this will cause problems with the lookup
6967 // in Context::Lookup, where context slots for parameters and local variables
6968 // are looked at before the extension object.
6969 Handle<JSFunction> go_between =
6970 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
6971 go_between->set_context(function->context());
6972#ifdef DEBUG
6973 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
6974 ASSERT(go_between_sinfo.number_of_parameters() == 0);
6975 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
6976#endif
6977
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006978 // Materialize the content of the local scope into a JSObject.
6979 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006980
6981 // Allocate a new context for the debug evaluation and set the extension
6982 // object build.
6983 Handle<Context> context =
6984 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006985 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006986 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006987 Handle<Context> frame_context(Context::cast(frame->context()));
6988 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006989 context = CopyWithContextChain(frame_context, context);
6990
6991 // Wrap the evaluation statement in a new function compiled in the newly
6992 // created context. The function has one parameter which has to be called
6993 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00006994 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006995 // function(arguments,__source__) {return eval(__source__);}
6996 static const char* source_str =
6997 "function(arguments,__source__){return eval(__source__);}";
6998 static const int source_str_length = strlen(source_str);
6999 Handle<String> function_source =
7000 Factory::NewStringFromAscii(Vector<const char>(source_str,
7001 source_str_length));
7002 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007003 Compiler::CompileEval(function_source,
7004 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007005 context->IsGlobalContext(),
7006 false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007007 if (boilerplate.is_null()) return Failure::Exception();
7008 Handle<JSFunction> compiled_function =
7009 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7010
7011 // Invoke the result of the compilation to get the evaluation function.
7012 bool has_pending_exception;
7013 Handle<Object> receiver(frame->receiver());
7014 Handle<Object> evaluation_function =
7015 Execution::Call(compiled_function, receiver, 0, NULL,
7016 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007017 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007018
7019 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7020 function_context);
7021
7022 // Invoke the evaluation function and return the result.
7023 const int argc = 2;
7024 Object** argv[argc] = { arguments.location(),
7025 Handle<Object>::cast(source).location() };
7026 Handle<Object> result =
7027 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7028 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007029 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007030
7031 // Skip the global proxy as it has no properties and always delegates to the
7032 // real global object.
7033 if (result->IsJSGlobalProxy()) {
7034 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7035 }
7036
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007037 return *result;
7038}
7039
7040
7041static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7042 HandleScope scope;
7043
7044 // Check the execution state and decode arguments frame and source to be
7045 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007046 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007047 Object* check_result = Runtime_CheckExecutionState(args);
7048 if (check_result->IsFailure()) return check_result;
7049 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007050 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7051
7052 // Handle the processing of break.
7053 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007054
7055 // Enter the top context from before the debugger was invoked.
7056 SaveContext save;
7057 SaveContext* top = &save;
7058 while (top != NULL && *top->context() == *Debug::debug_context()) {
7059 top = top->prev();
7060 }
7061 if (top != NULL) {
7062 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007063 }
7064
7065 // Get the global context now set to the top context from before the
7066 // debugger was invoked.
7067 Handle<Context> context = Top::global_context();
7068
7069 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007070 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007071 Handle<JSFunction>(Compiler::CompileEval(source,
7072 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007073 true,
7074 false));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007075 if (boilerplate.is_null()) return Failure::Exception();
7076 Handle<JSFunction> compiled_function =
7077 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7078 context));
7079
7080 // Invoke the result of the compilation to get the evaluation function.
7081 bool has_pending_exception;
7082 Handle<Object> receiver = Top::global();
7083 Handle<Object> result =
7084 Execution::Call(compiled_function, receiver, 0, NULL,
7085 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007086 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007087 return *result;
7088}
7089
7090
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007091static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7092 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007093 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007094
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007095 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007096 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007097
7098 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007099 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007100 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7101 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7102 // because using
7103 // instances->set(i, *GetScriptWrapper(script))
7104 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7105 // already have deferenced the instances handle.
7106 Handle<JSValue> wrapper = GetScriptWrapper(script);
7107 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007108 }
7109
7110 // Return result as a JS array.
7111 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7112 Handle<JSArray>::cast(result)->SetContent(*instances);
7113 return *result;
7114}
7115
7116
7117// Helper function used by Runtime_DebugReferencedBy below.
7118static int DebugReferencedBy(JSObject* target,
7119 Object* instance_filter, int max_references,
7120 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007121 JSFunction* arguments_function) {
7122 NoHandleAllocation ha;
7123 AssertNoAllocation no_alloc;
7124
7125 // Iterate the heap.
7126 int count = 0;
7127 JSObject* last = NULL;
7128 HeapIterator iterator;
7129 while (iterator.has_next() &&
7130 (max_references == 0 || count < max_references)) {
7131 // Only look at all JSObjects.
7132 HeapObject* heap_obj = iterator.next();
7133 if (heap_obj->IsJSObject()) {
7134 // Skip context extension objects and argument arrays as these are
7135 // checked in the context of functions using them.
7136 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007137 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007138 obj->map()->constructor() == arguments_function) {
7139 continue;
7140 }
7141
7142 // Check if the JS object has a reference to the object looked for.
7143 if (obj->ReferencesObject(target)) {
7144 // Check instance filter if supplied. This is normally used to avoid
7145 // references from mirror objects (see Runtime_IsInPrototypeChain).
7146 if (!instance_filter->IsUndefined()) {
7147 Object* V = obj;
7148 while (true) {
7149 Object* prototype = V->GetPrototype();
7150 if (prototype->IsNull()) {
7151 break;
7152 }
7153 if (instance_filter == prototype) {
7154 obj = NULL; // Don't add this object.
7155 break;
7156 }
7157 V = prototype;
7158 }
7159 }
7160
7161 if (obj != NULL) {
7162 // Valid reference found add to instance array if supplied an update
7163 // count.
7164 if (instances != NULL && count < instances_size) {
7165 instances->set(count, obj);
7166 }
7167 last = obj;
7168 count++;
7169 }
7170 }
7171 }
7172 }
7173
7174 // Check for circular reference only. This can happen when the object is only
7175 // referenced from mirrors and has a circular reference in which case the
7176 // object is not really alive and would have been garbage collected if not
7177 // referenced from the mirror.
7178 if (count == 1 && last == target) {
7179 count = 0;
7180 }
7181
7182 // Return the number of referencing objects found.
7183 return count;
7184}
7185
7186
7187// Scan the heap for objects with direct references to an object
7188// args[0]: the object to find references to
7189// args[1]: constructor function for instances to exclude (Mirror)
7190// args[2]: the the maximum number of objects to return
7191static Object* Runtime_DebugReferencedBy(Arguments args) {
7192 ASSERT(args.length() == 3);
7193
7194 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007195 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007196
7197 // Check parameters.
7198 CONVERT_CHECKED(JSObject, target, args[0]);
7199 Object* instance_filter = args[1];
7200 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7201 instance_filter->IsJSObject());
7202 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7203 RUNTIME_ASSERT(max_references >= 0);
7204
7205 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007206 JSObject* arguments_boilerplate =
7207 Top::context()->global_context()->arguments_boilerplate();
7208 JSFunction* arguments_function =
7209 JSFunction::cast(arguments_boilerplate->map()->constructor());
7210
7211 // Get the number of referencing objects.
7212 int count;
7213 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007214 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007215
7216 // Allocate an array to hold the result.
7217 Object* object = Heap::AllocateFixedArray(count);
7218 if (object->IsFailure()) return object;
7219 FixedArray* instances = FixedArray::cast(object);
7220
7221 // Fill the referencing objects.
7222 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007223 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007224
7225 // Return result as JS array.
7226 Object* result =
7227 Heap::AllocateJSObject(
7228 Top::context()->global_context()->array_function());
7229 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7230 return result;
7231}
7232
7233
7234// Helper function used by Runtime_DebugConstructedBy below.
7235static int DebugConstructedBy(JSFunction* constructor, int max_references,
7236 FixedArray* instances, int instances_size) {
7237 AssertNoAllocation no_alloc;
7238
7239 // Iterate the heap.
7240 int count = 0;
7241 HeapIterator iterator;
7242 while (iterator.has_next() &&
7243 (max_references == 0 || count < max_references)) {
7244 // Only look at all JSObjects.
7245 HeapObject* heap_obj = iterator.next();
7246 if (heap_obj->IsJSObject()) {
7247 JSObject* obj = JSObject::cast(heap_obj);
7248 if (obj->map()->constructor() == constructor) {
7249 // Valid reference found add to instance array if supplied an update
7250 // count.
7251 if (instances != NULL && count < instances_size) {
7252 instances->set(count, obj);
7253 }
7254 count++;
7255 }
7256 }
7257 }
7258
7259 // Return the number of referencing objects found.
7260 return count;
7261}
7262
7263
7264// Scan the heap for objects constructed by a specific function.
7265// args[0]: the constructor to find instances of
7266// args[1]: the the maximum number of objects to return
7267static Object* Runtime_DebugConstructedBy(Arguments args) {
7268 ASSERT(args.length() == 2);
7269
7270 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00007271 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007272
7273 // Check parameters.
7274 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7275 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7276 RUNTIME_ASSERT(max_references >= 0);
7277
7278 // Get the number of referencing objects.
7279 int count;
7280 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7281
7282 // Allocate an array to hold the result.
7283 Object* object = Heap::AllocateFixedArray(count);
7284 if (object->IsFailure()) return object;
7285 FixedArray* instances = FixedArray::cast(object);
7286
7287 // Fill the referencing objects.
7288 count = DebugConstructedBy(constructor, max_references, instances, count);
7289
7290 // Return result as JS array.
7291 Object* result =
7292 Heap::AllocateJSObject(
7293 Top::context()->global_context()->array_function());
7294 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7295 return result;
7296}
7297
7298
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007299// Find the effective prototype object as returned by __proto__.
7300// args[0]: the object to find the prototype for.
7301static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007302 ASSERT(args.length() == 1);
7303
7304 CONVERT_CHECKED(JSObject, obj, args[0]);
7305
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007306 // Use the __proto__ accessor.
7307 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007308}
7309
7310
7311static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007312 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007313 CPU::DebugBreak();
7314 return Heap::undefined_value();
7315}
7316
7317
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007318static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
7319#ifdef DEBUG
7320 HandleScope scope;
7321 ASSERT(args.length() == 1);
7322 // Get the function and make sure it is compiled.
7323 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7324 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7325 return Failure::Exception();
7326 }
7327 func->code()->PrintLn();
7328#endif // DEBUG
7329 return Heap::undefined_value();
7330}
ager@chromium.org9085a012009-05-11 19:22:57 +00007331
7332
7333static Object* Runtime_FunctionGetInferredName(Arguments args) {
7334 NoHandleAllocation ha;
7335 ASSERT(args.length() == 1);
7336
7337 CONVERT_CHECKED(JSFunction, f, args[0]);
7338 return f->shared()->inferred_name();
7339}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007340#endif // ENABLE_DEBUGGER_SUPPORT
7341
7342
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007343// Finds the script object from the script data. NOTE: This operation uses
7344// heap traversal to find the function generated for the source position
7345// for the requested break point. For lazily compiled functions several heap
7346// traversals might be required rendering this operation as a rather slow
7347// operation. However for setting break points which is normally done through
7348// some kind of user interaction the performance is not crucial.
7349static Handle<Object> Runtime_GetScriptFromScriptName(
7350 Handle<String> script_name) {
7351 // Scan the heap for Script objects to find the script with the requested
7352 // script data.
7353 Handle<Script> script;
7354 HeapIterator iterator;
7355 while (script.is_null() && iterator.has_next()) {
7356 HeapObject* obj = iterator.next();
7357 // If a script is found check if it has the script data requested.
7358 if (obj->IsScript()) {
7359 if (Script::cast(obj)->name()->IsString()) {
7360 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7361 script = Handle<Script>(Script::cast(obj));
7362 }
7363 }
7364 }
7365 }
7366
7367 // If no script with the requested script data is found return undefined.
7368 if (script.is_null()) return Factory::undefined_value();
7369
7370 // Return the script found.
7371 return GetScriptWrapper(script);
7372}
7373
7374
7375// Get the script object from script data. NOTE: Regarding performance
7376// see the NOTE for GetScriptFromScriptData.
7377// args[0]: script data for the script to find the source for
7378static Object* Runtime_GetScript(Arguments args) {
7379 HandleScope scope;
7380
7381 ASSERT(args.length() == 1);
7382
7383 CONVERT_CHECKED(String, script_name, args[0]);
7384
7385 // Find the requested script.
7386 Handle<Object> result =
7387 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7388 return *result;
7389}
7390
7391
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007392// Determines whether the given stack frame should be displayed in
7393// a stack trace. The caller is the error constructor that asked
7394// for the stack trace to be collected. The first time a construct
7395// call to this function is encountered it is skipped. The seen_caller
7396// in/out parameter is used to remember if the caller has been seen
7397// yet.
7398static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
7399 bool* seen_caller) {
7400 // Only display JS frames.
7401 if (!raw_frame->is_java_script())
7402 return false;
7403 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7404 Object* raw_fun = frame->function();
7405 // Not sure when this can happen but skip it just in case.
7406 if (!raw_fun->IsJSFunction())
7407 return false;
7408 if ((raw_fun == caller) && !(*seen_caller) && frame->IsConstructor()) {
7409 *seen_caller = true;
7410 return false;
7411 }
7412 // Skip the most obvious builtin calls. Some builtin calls (such as
7413 // Number.ADD which is invoked using 'call') are very difficult to
7414 // recognize so we're leaving them in for now.
7415 return !frame->receiver()->IsJSBuiltinsObject();
7416}
7417
7418
7419// Collect the raw data for a stack trace. Returns an array of three
7420// element segments each containing a receiver, function and native
7421// code offset.
7422static Object* Runtime_CollectStackTrace(Arguments args) {
7423 ASSERT_EQ(args.length(), 1);
7424 Object* caller = args[0];
7425
7426 StackFrameIterator iter;
7427 int frame_count = 0;
7428 bool seen_caller = false;
7429 while (!iter.done()) {
7430 if (ShowFrameInStackTrace(iter.frame(), caller, &seen_caller))
7431 frame_count++;
7432 iter.Advance();
7433 }
7434 HandleScope scope;
7435 Handle<JSArray> result = Factory::NewJSArray(frame_count * 3);
7436 int i = 0;
7437 seen_caller = false;
7438 for (iter.Reset(); !iter.done(); iter.Advance()) {
7439 StackFrame* raw_frame = iter.frame();
7440 if (ShowFrameInStackTrace(raw_frame, caller, &seen_caller)) {
7441 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7442 result->SetElement(i++, frame->receiver());
7443 result->SetElement(i++, frame->function());
7444 Address pc = frame->pc();
7445 Address start = frame->code()->address();
7446 result->SetElement(i++, Smi::FromInt(pc - start));
7447 }
7448 }
7449 return *result;
7450}
7451
7452
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007453static Object* Runtime_Abort(Arguments args) {
7454 ASSERT(args.length() == 2);
7455 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7456 Smi::cast(args[1])->value());
7457 Top::PrintStack();
7458 OS::Abort();
7459 UNREACHABLE();
7460 return NULL;
7461}
7462
7463
kasper.lund44510672008-07-25 07:37:58 +00007464#ifdef DEBUG
7465// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7466// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007467static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007468 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007469 HandleScope scope;
7470 Handle<JSArray> result = Factory::NewJSArray(0);
7471 int index = 0;
7472#define ADD_ENTRY(Name, argc) \
7473 { \
7474 HandleScope inner; \
7475 Handle<String> name = \
7476 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
7477 Handle<JSArray> pair = Factory::NewJSArray(0); \
7478 SetElement(pair, 0, name); \
7479 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7480 SetElement(result, index++, pair); \
7481 }
7482 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7483#undef ADD_ENTRY
7484 return *result;
7485}
kasper.lund44510672008-07-25 07:37:58 +00007486#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007487
7488
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007489static Object* Runtime_Log(Arguments args) {
7490 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007491 CONVERT_CHECKED(String, format, args[0]);
7492 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007493 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007494 Logger::LogRuntime(chars, elms);
7495 return Heap::undefined_value();
7496}
7497
7498
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007499static Object* Runtime_IS_VAR(Arguments args) {
7500 UNREACHABLE(); // implemented as macro in the parser
7501 return NULL;
7502}
7503
7504
7505// ----------------------------------------------------------------------------
7506// Implementation of Runtime
7507
7508#define F(name, nargs) \
7509 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
7510 static_cast<int>(Runtime::k##name) },
7511
7512static Runtime::Function Runtime_functions[] = {
7513 RUNTIME_FUNCTION_LIST(F)
7514 { NULL, NULL, NULL, 0, -1 }
7515};
7516
7517#undef F
7518
7519
7520Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7521 ASSERT(0 <= fid && fid < kNofFunctions);
7522 return &Runtime_functions[fid];
7523}
7524
7525
7526Runtime::Function* Runtime::FunctionForName(const char* name) {
7527 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7528 if (strcmp(f->name, name) == 0) {
7529 return f;
7530 }
7531 }
7532 return NULL;
7533}
7534
7535
7536void Runtime::PerformGC(Object* result) {
7537 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007538 if (failure->IsRetryAfterGC()) {
7539 // Try to do a garbage collection; ignore it if it fails. The C
7540 // entry stub will throw an out-of-memory exception in that case.
7541 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7542 } else {
7543 // Handle last resort GC and make sure to allow future allocations
7544 // to grow the heap without causing GCs (if possible).
7545 Counters::gc_last_resort_from_js.Increment();
7546 Heap::CollectAllGarbage();
7547 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007548}
7549
7550
7551} } // namespace v8::internal