blob: 9eeffd10c0f1097d60226c3753d402c101fcc422 [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"
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000037#include "dateparser-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000038#include "debug.h"
39#include "execution.h"
40#include "jsregexp.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000041#include "parser.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000042#include "platform.h"
43#include "runtime.h"
44#include "scopeinfo.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000045#include "smart-pointer.h"
ager@chromium.org18ad94b2009-09-02 08:22:29 +000046#include "stub-cache.h"
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000047#include "v8threads.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.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000158 // Pixel elements cannot be created using an object literal.
159 ASSERT(!copy->HasPixelElements());
160 switch (copy->GetElementsKind()) {
161 case JSObject::FAST_ELEMENTS: {
162 FixedArray* elements = FixedArray::cast(copy->elements());
163 WriteBarrierMode mode = elements->GetWriteBarrierMode();
164 for (int i = 0; i < elements->length(); i++) {
165 Object* value = elements->get(i);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000166 if (value->IsJSObject()) {
167 JSObject* jsObject = JSObject::cast(value);
168 result = DeepCopyBoilerplate(jsObject);
169 if (result->IsFailure()) return result;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000170 elements->set(i, result, mode);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000171 }
172 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000173 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000174 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000175 case JSObject::DICTIONARY_ELEMENTS: {
176 NumberDictionary* element_dictionary = copy->element_dictionary();
177 int capacity = element_dictionary->Capacity();
178 for (int i = 0; i < capacity; i++) {
179 Object* k = element_dictionary->KeyAt(i);
180 if (element_dictionary->IsKey(k)) {
181 Object* value = element_dictionary->ValueAt(i);
182 if (value->IsJSObject()) {
183 JSObject* jsObject = JSObject::cast(value);
184 result = DeepCopyBoilerplate(jsObject);
185 if (result->IsFailure()) return result;
186 element_dictionary->ValueAtPut(i, result);
187 }
188 }
189 }
190 break;
191 }
192 default:
193 UNREACHABLE();
194 break;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000195 }
196 return copy;
197}
198
199
200static Object* Runtime_CloneLiteralBoilerplate(Arguments args) {
201 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
202 return DeepCopyBoilerplate(boilerplate);
203}
204
205
206static Object* Runtime_CloneShallowLiteralBoilerplate(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000207 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000208 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000209}
210
211
ager@chromium.org236ad962008-09-25 09:45:57 +0000212static Handle<Map> ComputeObjectLiteralMap(
213 Handle<Context> context,
214 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000215 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000216 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000217 if (FLAG_canonicalize_object_literal_maps) {
218 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000219 int number_of_symbol_keys = 0;
220 while ((number_of_symbol_keys < number_of_properties) &&
221 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
222 number_of_symbol_keys++;
223 }
224 // Based on the number of prefix symbols key we decide whether
225 // to use the map cache in the global context.
226 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000227 if ((number_of_symbol_keys == number_of_properties) &&
228 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000229 // Create the fixed array with the key.
230 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
231 for (int i = 0; i < number_of_symbol_keys; i++) {
232 keys->set(i, constant_properties->get(i*2));
233 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000234 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000235 return Factory::ObjectLiteralMapFromCache(context, keys);
236 }
237 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000238 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000239 return Factory::CopyMap(
240 Handle<Map>(context->object_function()->initial_map()),
241 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000242}
243
244
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000245static Handle<Object> CreateLiteralBoilerplate(
246 Handle<FixedArray> literals,
247 Handle<FixedArray> constant_properties);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000248
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000249
250static Handle<Object> CreateObjectLiteralBoilerplate(
251 Handle<FixedArray> literals,
252 Handle<FixedArray> constant_properties) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000253 // Get the global context from the literals array. This is the
254 // context in which the function was created and we use the object
255 // function from this context to create the object literal. We do
256 // not use the object function from the current global context
257 // because this might be the object function from another context
258 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000259 Handle<Context> context =
260 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
261
262 bool is_result_from_cache;
263 Handle<Map> map = ComputeObjectLiteralMap(context,
264 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000265 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
ager@chromium.org236ad962008-09-25 09:45:57 +0000267 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
ager@chromium.org32912102009-01-16 10:38:43 +0000268 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000269 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000270 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000271 length / 2,
ager@chromium.org236ad962008-09-25 09:45:57 +0000272 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000273 for (int index = 0; index < length; index +=2) {
274 Handle<Object> key(constant_properties->get(index+0));
275 Handle<Object> value(constant_properties->get(index+1));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000276 if (value->IsFixedArray()) {
277 // The value contains the constant_properties of a
278 // simple object literal.
279 Handle<FixedArray> array = Handle<FixedArray>::cast(value);
280 value = CreateLiteralBoilerplate(literals, array);
281 if (value.is_null()) return value;
282 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000283 Handle<Object> result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000284 uint32_t element_index = 0;
285 if (key->IsSymbol()) {
286 // If key is a symbol it is not an array element.
287 Handle<String> name(String::cast(*key));
288 ASSERT(!name->AsArrayIndex(&element_index));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000289 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000290 } else if (Array::IndexFromObject(*key, &element_index)) {
291 // Array index (uint32).
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000292 result = SetElement(boilerplate, element_index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000293 } else {
294 // Non-uint32 number.
295 ASSERT(key->IsNumber());
296 double num = key->Number();
297 char arr[100];
298 Vector<char> buffer(arr, ARRAY_SIZE(arr));
299 const char* str = DoubleToCString(num, buffer);
300 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000301 result = SetProperty(boilerplate, name, value, NONE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000302 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000303 // If setting the property on the boilerplate throws an
304 // exception, the exception is converted to an empty handle in
305 // the handle based operations. In that case, we need to
306 // convert back to an exception.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000307 if (result.is_null()) return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000308 }
309 }
310
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000311 return boilerplate;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +0000312}
313
314
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000315static Handle<Object> CreateArrayLiteralBoilerplate(
316 Handle<FixedArray> literals,
317 Handle<FixedArray> elements) {
318 // Create the JSArray.
319 Handle<JSFunction> constructor(
320 JSFunction::GlobalContextFromLiterals(*literals)->array_function());
321 Handle<Object> object = Factory::NewJSObject(constructor);
322
323 Handle<Object> copied_elements = Factory::CopyFixedArray(elements);
324
325 Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements);
326 for (int i = 0; i < content->length(); i++) {
327 if (content->get(i)->IsFixedArray()) {
328 // The value contains the constant_properties of a
329 // simple object literal.
330 Handle<FixedArray> fa(FixedArray::cast(content->get(i)));
331 Handle<Object> result =
332 CreateLiteralBoilerplate(literals, fa);
333 if (result.is_null()) return result;
334 content->set(i, *result);
335 }
336 }
337
338 // Set the elements.
339 Handle<JSArray>::cast(object)->SetContent(*content);
340 return object;
341}
342
343
344static Handle<Object> CreateLiteralBoilerplate(
345 Handle<FixedArray> literals,
346 Handle<FixedArray> array) {
347 Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
348 switch (CompileTimeValue::GetType(array)) {
349 case CompileTimeValue::OBJECT_LITERAL:
350 return CreateObjectLiteralBoilerplate(literals, elements);
351 case CompileTimeValue::ARRAY_LITERAL:
352 return CreateArrayLiteralBoilerplate(literals, elements);
353 default:
354 UNREACHABLE();
355 return Handle<Object>::null();
356 }
357}
358
359
360static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
361 HandleScope scope;
362 ASSERT(args.length() == 3);
363 // Copy the arguments.
364 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
365 CONVERT_SMI_CHECKED(literals_index, args[1]);
366 CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
367
368 Handle<Object> result =
369 CreateObjectLiteralBoilerplate(literals, constant_properties);
370
371 if (result.is_null()) return Failure::Exception();
372
373 // Update the functions literal and return the boilerplate.
374 literals->set(literals_index, *result);
375
376 return *result;
377}
378
379
380static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000381 // Takes a FixedArray of elements containing the literal elements of
382 // the array literal and produces JSArray with those elements.
383 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000384 // which contains the context from which to get the Array function
385 // to use for creating the array literal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000386 HandleScope scope;
387 ASSERT(args.length() == 3);
388 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
389 CONVERT_SMI_CHECKED(literals_index, args[1]);
390 CONVERT_ARG_CHECKED(FixedArray, elements, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000391
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000392 Handle<Object> object = CreateArrayLiteralBoilerplate(literals, elements);
393 if (object.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000394
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000395 // Update the functions literal and return the boilerplate.
396 literals->set(literals_index, *object);
397 return *object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000398}
399
400
ager@chromium.org32912102009-01-16 10:38:43 +0000401static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
402 ASSERT(args.length() == 2);
403 CONVERT_CHECKED(String, key, args[0]);
404 Object* value = args[1];
405 // Create a catch context extension object.
406 JSFunction* constructor =
407 Top::context()->global_context()->context_extension_function();
408 Object* object = Heap::AllocateJSObject(constructor);
409 if (object->IsFailure()) return object;
410 // Assign the exception value to the catch variable and make sure
411 // that the catch variable is DontDelete.
412 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
413 if (value->IsFailure()) return value;
414 return object;
415}
416
417
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000418static Object* Runtime_ClassOf(Arguments args) {
419 NoHandleAllocation ha;
420 ASSERT(args.length() == 1);
421 Object* obj = args[0];
422 if (!obj->IsJSObject()) return Heap::null_value();
423 return JSObject::cast(obj)->class_name();
424}
425
ager@chromium.org7c537e22008-10-16 08:43:32 +0000426
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000427static Object* Runtime_IsInPrototypeChain(Arguments args) {
428 NoHandleAllocation ha;
429 ASSERT(args.length() == 2);
430 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
431 Object* O = args[0];
432 Object* V = args[1];
433 while (true) {
434 Object* prototype = V->GetPrototype();
435 if (prototype->IsNull()) return Heap::false_value();
436 if (O == prototype) return Heap::true_value();
437 V = prototype;
438 }
439}
440
441
ager@chromium.org9085a012009-05-11 19:22:57 +0000442// Inserts an object as the hidden prototype of another object.
443static Object* Runtime_SetHiddenPrototype(Arguments args) {
444 NoHandleAllocation ha;
445 ASSERT(args.length() == 2);
446 CONVERT_CHECKED(JSObject, jsobject, args[0]);
447 CONVERT_CHECKED(JSObject, proto, args[1]);
448
449 // Sanity checks. The old prototype (that we are replacing) could
450 // theoretically be null, but if it is not null then check that we
451 // didn't already install a hidden prototype here.
452 RUNTIME_ASSERT(!jsobject->GetPrototype()->IsHeapObject() ||
453 !HeapObject::cast(jsobject->GetPrototype())->map()->is_hidden_prototype());
454 RUNTIME_ASSERT(!proto->map()->is_hidden_prototype());
455
456 // Allocate up front before we start altering state in case we get a GC.
457 Object* map_or_failure = proto->map()->CopyDropTransitions();
458 if (map_or_failure->IsFailure()) return map_or_failure;
459 Map* new_proto_map = Map::cast(map_or_failure);
460
461 map_or_failure = jsobject->map()->CopyDropTransitions();
462 if (map_or_failure->IsFailure()) return map_or_failure;
463 Map* new_map = Map::cast(map_or_failure);
464
465 // Set proto's prototype to be the old prototype of the object.
466 new_proto_map->set_prototype(jsobject->GetPrototype());
467 proto->set_map(new_proto_map);
468 new_proto_map->set_is_hidden_prototype();
469
470 // Set the object's prototype to proto.
471 new_map->set_prototype(proto);
472 jsobject->set_map(new_map);
473
474 return Heap::undefined_value();
475}
476
477
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000478static Object* Runtime_IsConstructCall(Arguments args) {
479 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000480 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000481 JavaScriptFrameIterator it;
482 return Heap::ToBoolean(it.frame()->IsConstructor());
483}
484
485
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000486static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000487 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000489 CONVERT_ARG_CHECKED(JSRegExp, re, 0);
490 CONVERT_ARG_CHECKED(String, pattern, 1);
491 CONVERT_ARG_CHECKED(String, flags, 2);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000492 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
493 if (result.is_null()) return Failure::Exception();
494 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000495}
496
497
498static Object* Runtime_CreateApiFunction(Arguments args) {
499 HandleScope scope;
500 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000501 CONVERT_ARG_CHECKED(FunctionTemplateInfo, data, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000502 return *Factory::CreateApiFunction(data);
503}
504
505
506static Object* Runtime_IsTemplate(Arguments args) {
507 ASSERT(args.length() == 1);
508 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000509 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000510 return Heap::ToBoolean(result);
511}
512
513
514static Object* Runtime_GetTemplateField(Arguments args) {
515 ASSERT(args.length() == 2);
516 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000517 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000518 int index = field->value();
519 int offset = index * kPointerSize + HeapObject::kHeaderSize;
520 InstanceType type = templ->map()->instance_type();
521 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
522 type == OBJECT_TEMPLATE_INFO_TYPE);
523 RUNTIME_ASSERT(offset > 0);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000524 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000525 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
526 } else {
527 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
528 }
529 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000530}
531
532
ager@chromium.org870a0b62008-11-04 11:43:05 +0000533static Object* Runtime_DisableAccessChecks(Arguments args) {
534 ASSERT(args.length() == 1);
535 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000536 Map* old_map = object->map();
537 bool needs_access_checks = old_map->is_access_check_needed();
538 if (needs_access_checks) {
539 // Copy map so it won't interfere constructor's initial map.
540 Object* new_map = old_map->CopyDropTransitions();
541 if (new_map->IsFailure()) return new_map;
542
543 Map::cast(new_map)->set_is_access_check_needed(false);
544 object->set_map(Map::cast(new_map));
545 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000546 return needs_access_checks ? Heap::true_value() : Heap::false_value();
547}
548
549
550static Object* Runtime_EnableAccessChecks(Arguments args) {
551 ASSERT(args.length() == 1);
552 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000553 Map* old_map = object->map();
554 if (!old_map->is_access_check_needed()) {
555 // Copy map so it won't interfere constructor's initial map.
556 Object* new_map = old_map->CopyDropTransitions();
557 if (new_map->IsFailure()) return new_map;
558
559 Map::cast(new_map)->set_is_access_check_needed(true);
560 object->set_map(Map::cast(new_map));
561 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000562 return Heap::undefined_value();
563}
564
565
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000566static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
567 HandleScope scope;
568 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
569 Handle<Object> args[2] = { type_handle, name };
570 Handle<Object> error =
571 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
572 return Top::Throw(*error);
573}
574
575
576static Object* Runtime_DeclareGlobals(Arguments args) {
577 HandleScope scope;
578 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
579
580 CONVERT_ARG_CHECKED(FixedArray, pairs, 0);
581 Handle<Context> context = args.at<Context>(1);
582 bool is_eval = Smi::cast(args[2])->value() == 1;
583
584 // Compute the property attributes. According to ECMA-262, section
585 // 13, page 71, the property must be read-only and
586 // non-deletable. However, neither SpiderMonkey nor KJS creates the
587 // property as read-only, so we don't either.
588 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
589
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000590 // Traverse the name/value pairs and set the properties.
591 int length = pairs->length();
592 for (int i = 0; i < length; i += 2) {
593 HandleScope scope;
594 Handle<String> name(String::cast(pairs->get(i)));
595 Handle<Object> value(pairs->get(i + 1));
596
597 // We have to declare a global const property. To capture we only
598 // assign to it when evaluating the assignment for "const x =
599 // <expr>" the initial value is the hole.
600 bool is_const_property = value->IsTheHole();
601
602 if (value->IsUndefined() || is_const_property) {
603 // Lookup the property in the global object, and don't set the
604 // value of the variable if the property is already there.
605 LookupResult lookup;
606 global->Lookup(*name, &lookup);
607 if (lookup.IsProperty()) {
608 // Determine if the property is local by comparing the holder
609 // against the global object. The information will be used to
610 // avoid throwing re-declaration errors when declaring
611 // variables or constants that exist in the prototype chain.
612 bool is_local = (*global == lookup.holder());
613 // Get the property attributes and determine if the property is
614 // read-only.
615 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
616 bool is_read_only = (attributes & READ_ONLY) != 0;
617 if (lookup.type() == INTERCEPTOR) {
618 // If the interceptor says the property is there, we
619 // just return undefined without overwriting the property.
620 // Otherwise, we continue to setting the property.
621 if (attributes != ABSENT) {
622 // Check if the existing property conflicts with regards to const.
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 // Fall-through and introduce the absent property by using
632 // SetProperty.
633 } else {
634 if (is_local && (is_read_only || is_const_property)) {
635 const char* type = (is_read_only) ? "const" : "var";
636 return ThrowRedeclarationError(type, name);
637 }
638 // The property already exists without conflicting: Go to
639 // the next declaration.
640 continue;
641 }
642 }
643 } else {
644 // Copy the function and update its context. Use it as value.
645 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
646 Handle<JSFunction> function =
647 Factory::NewFunctionFromBoilerplate(boilerplate, context);
648 value = function;
649 }
650
651 LookupResult lookup;
652 global->LocalLookup(*name, &lookup);
653
654 PropertyAttributes attributes = is_const_property
655 ? static_cast<PropertyAttributes>(base | READ_ONLY)
656 : base;
657
658 if (lookup.IsProperty()) {
659 // There's a local property that we need to overwrite because
660 // we're either declaring a function or there's an interceptor
661 // that claims the property is absent.
662
663 // Check for conflicting re-declarations. We cannot have
664 // conflicting types in case of intercepted properties because
665 // they are absent.
666 if (lookup.type() != INTERCEPTOR &&
667 (lookup.IsReadOnly() || is_const_property)) {
668 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
669 return ThrowRedeclarationError(type, name);
670 }
671 SetProperty(global, name, value, attributes);
672 } else {
673 // If a property with this name does not already exist on the
674 // global object add the property locally. We take special
675 // precautions to always add it as a local property even in case
676 // of callbacks in the prototype chain (this rules out using
677 // SetProperty). Also, we must use the handle-based version to
678 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000679 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000680 }
681 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000682
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000683 return Heap::undefined_value();
684}
685
686
687static Object* Runtime_DeclareContextSlot(Arguments args) {
688 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000689 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000690
ager@chromium.org7c537e22008-10-16 08:43:32 +0000691 CONVERT_ARG_CHECKED(Context, context, 0);
692 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000693 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000694 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000695 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000696 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000697
698 // Declarations are always done in the function context.
699 context = Handle<Context>(context->fcontext());
700
701 int index;
702 PropertyAttributes attributes;
703 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000704 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000705 context->Lookup(name, flags, &index, &attributes);
706
707 if (attributes != ABSENT) {
708 // The name was declared before; check for conflicting
709 // re-declarations: This is similar to the code in parser.cc in
710 // the AstBuildingParser::Declare function.
711 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
712 // Functions are not read-only.
713 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
714 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
715 return ThrowRedeclarationError(type, name);
716 }
717
718 // Initialize it if necessary.
719 if (*initial_value != NULL) {
720 if (index >= 0) {
721 // The variable or constant context slot should always be in
722 // the function context; not in any outer context nor in the
723 // arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000724 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000725 if (((attributes & READ_ONLY) == 0) ||
726 context->get(index)->IsTheHole()) {
727 context->set(index, *initial_value);
728 }
729 } else {
730 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000731 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000732 SetProperty(context_ext, name, initial_value, mode);
733 }
734 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000735
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000737 // The property is not in the function context. It needs to be
738 // "declared" in the function context's extension context, or in the
739 // global context.
740 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000741 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000742 // The function context's extension context exists - use it.
743 context_ext = Handle<JSObject>(context->extension());
744 } else {
745 // The function context's extension context does not exists - allocate
746 // it.
747 context_ext = Factory::NewJSObject(Top::context_extension_function());
748 // And store it in the extension slot.
749 context->set_extension(*context_ext);
750 }
751 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000752
ager@chromium.org7c537e22008-10-16 08:43:32 +0000753 // Declare the property by setting it to the initial value if provided,
754 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
755 // constant declarations).
756 ASSERT(!context_ext->HasLocalProperty(*name));
757 Handle<Object> value(Heap::undefined_value());
758 if (*initial_value != NULL) value = initial_value;
759 SetProperty(context_ext, name, value, mode);
760 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
761 }
762
763 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000764}
765
766
767static Object* Runtime_InitializeVarGlobal(Arguments args) {
768 NoHandleAllocation nha;
769
770 // Determine if we need to assign to the variable if it already
771 // exists (based on the number of arguments).
772 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
773 bool assign = args.length() == 2;
774
775 CONVERT_ARG_CHECKED(String, name, 0);
776 GlobalObject* global = Top::context()->global();
777
778 // According to ECMA-262, section 12.2, page 62, the property must
779 // not be deletable.
780 PropertyAttributes attributes = DONT_DELETE;
781
782 // Lookup the property locally in the global object. If it isn't
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000783 // there, there is a property with this name in the prototype chain.
784 // We follow Safari and Firefox behavior and only set the property
785 // locally if there is an explicit initialization value that we have
786 // to assign to the property. When adding the property we take
787 // special precautions to always add it as a local property even in
788 // case of callbacks in the prototype chain (this rules out using
789 // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
790 // this.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000791 LookupResult lookup;
792 global->LocalLookup(*name, &lookup);
793 if (!lookup.IsProperty()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000794 if (assign) {
795 return global->IgnoreAttributesAndSetLocalProperty(*name,
796 args[1],
797 attributes);
798 }
799 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000800 }
801
802 // Determine if this is a redeclaration of something read-only.
803 if (lookup.IsReadOnly()) {
804 return ThrowRedeclarationError("const", name);
805 }
806
807 // Determine if this is a redeclaration of an intercepted read-only
808 // property and figure out if the property exists at all.
809 bool found = true;
810 PropertyType type = lookup.type();
811 if (type == INTERCEPTOR) {
812 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
813 if (intercepted == ABSENT) {
814 // The interceptor claims the property isn't there. We need to
815 // make sure to introduce it.
816 found = false;
817 } else if ((intercepted & READ_ONLY) != 0) {
818 // The property is present, but read-only. Since we're trying to
819 // overwrite it with a variable declaration we must throw a
820 // re-declaration error.
821 return ThrowRedeclarationError("const", name);
822 }
823 // Restore global object from context (in case of GC).
824 global = Top::context()->global();
825 }
826
827 if (found && !assign) {
828 // The global property is there and we're not assigning any value
829 // to it. Just return.
830 return Heap::undefined_value();
831 }
832
833 // Assign the value (or undefined) to the property.
834 Object* value = (assign) ? args[1] : Heap::undefined_value();
835 return global->SetProperty(&lookup, *name, value, attributes);
836}
837
838
839static Object* Runtime_InitializeConstGlobal(Arguments args) {
840 // All constants are declared with an initial value. The name
841 // of the constant is the first argument and the initial value
842 // is the second.
843 RUNTIME_ASSERT(args.length() == 2);
844 CONVERT_ARG_CHECKED(String, name, 0);
845 Handle<Object> value = args.at<Object>(1);
846
847 // Get the current global object from top.
848 GlobalObject* global = Top::context()->global();
849
850 // According to ECMA-262, section 12.2, page 62, the property must
851 // not be deletable. Since it's a const, it must be READ_ONLY too.
852 PropertyAttributes attributes =
853 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
854
855 // Lookup the property locally in the global object. If it isn't
856 // there, we add the property and take special precautions to always
857 // add it as a local property even in case of callbacks in the
858 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000859 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000860 LookupResult lookup;
861 global->LocalLookup(*name, &lookup);
862 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000863 return global->IgnoreAttributesAndSetLocalProperty(*name,
864 *value,
865 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000866 }
867
868 // Determine if this is a redeclaration of something not
869 // read-only. In case the result is hidden behind an interceptor we
870 // need to ask it for the property attributes.
871 if (!lookup.IsReadOnly()) {
872 if (lookup.type() != INTERCEPTOR) {
873 return ThrowRedeclarationError("var", name);
874 }
875
876 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
877
878 // Throw re-declaration error if the intercepted property is present
879 // but not read-only.
880 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
881 return ThrowRedeclarationError("var", name);
882 }
883
884 // Restore global object from context (in case of GC) and continue
885 // with setting the value because the property is either absent or
886 // read-only. We also have to do redo the lookup.
887 global = Top::context()->global();
888
889 // BUG 1213579: Handle the case where we have to set a read-only
890 // property through an interceptor and only do it if it's
891 // uninitialized, e.g. the hole. Nirk...
892 global->SetProperty(*name, *value, attributes);
893 return *value;
894 }
895
896 // Set the value, but only we're assigning the initial value to a
897 // constant. For now, we determine this by checking if the
898 // current value is the hole.
899 PropertyType type = lookup.type();
900 if (type == FIELD) {
901 FixedArray* properties = global->properties();
902 int index = lookup.GetFieldIndex();
903 if (properties->get(index)->IsTheHole()) {
904 properties->set(index, *value);
905 }
906 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000907 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
908 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000909 }
910 } else {
911 // Ignore re-initialization of constants that have already been
912 // assigned a function value.
913 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
914 }
915
916 // Use the set value as the result of the operation.
917 return *value;
918}
919
920
921static Object* Runtime_InitializeConstContextSlot(Arguments args) {
922 HandleScope scope;
923 ASSERT(args.length() == 3);
924
925 Handle<Object> value(args[0]);
926 ASSERT(!value->IsTheHole());
927 CONVERT_ARG_CHECKED(Context, context, 1);
928 Handle<String> name(String::cast(args[2]));
929
930 // Initializations are always done in the function context.
931 context = Handle<Context>(context->fcontext());
932
933 int index;
934 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000935 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000936 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000937 context->Lookup(name, flags, &index, &attributes);
938
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000939 // In most situations, the property introduced by the const
940 // declaration should be present in the context extension object.
941 // However, because declaration and initialization are separate, the
942 // property might have been deleted (if it was introduced by eval)
943 // before we reach the initialization point.
944 //
945 // Example:
946 //
947 // function f() { eval("delete x; const x;"); }
948 //
949 // In that case, the initialization behaves like a normal assignment
950 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000951 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000952 // Property was found in a context.
953 if (holder->IsContext()) {
954 // The holder cannot be the function context. If it is, there
955 // should have been a const redeclaration error when declaring
956 // the const property.
957 ASSERT(!holder.is_identical_to(context));
958 if ((attributes & READ_ONLY) == 0) {
959 Handle<Context>::cast(holder)->set(index, *value);
960 }
961 } else {
962 // The holder is an arguments object.
963 ASSERT((attributes & READ_ONLY) == 0);
964 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000965 }
966 return *value;
967 }
968
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000969 // The property could not be found, we introduce it in the global
970 // context.
971 if (attributes == ABSENT) {
972 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
973 SetProperty(global, name, value, NONE);
974 return *value;
975 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000976
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000977 // The property was present in a context extension object.
978 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000979
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000980 if (*context_ext == context->extension()) {
981 // This is the property that was introduced by the const
982 // declaration. Set it if it hasn't been set before. NOTE: We
983 // cannot use GetProperty() to get the current value as it
984 // 'unholes' the value.
985 LookupResult lookup;
986 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
987 ASSERT(lookup.IsProperty()); // the property was declared
988 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
989
990 PropertyType type = lookup.type();
991 if (type == FIELD) {
992 FixedArray* properties = context_ext->properties();
993 int index = lookup.GetFieldIndex();
994 if (properties->get(index)->IsTheHole()) {
995 properties->set(index, *value);
996 }
997 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000998 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
999 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001000 }
1001 } else {
1002 // We should not reach here. Any real, named property should be
1003 // either a field or a dictionary slot.
1004 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001005 }
1006 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001007 // The property was found in a different context extension object.
1008 // Set it if it is not a read-only property.
1009 if ((attributes & READ_ONLY) == 0) {
1010 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1011 // Setting a property might throw an exception. Exceptions
1012 // are converted to empty handles in handle operations. We
1013 // need to convert back to exceptions here.
1014 if (set.is_null()) {
1015 ASSERT(Top::has_pending_exception());
1016 return Failure::Exception();
1017 }
1018 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001019 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001020
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001021 return *value;
1022}
1023
1024
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001025static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1026 Arguments args) {
1027 HandleScope scope;
1028 ASSERT(args.length() == 2);
1029 CONVERT_ARG_CHECKED(JSObject, object, 0);
1030 CONVERT_SMI_CHECKED(properties, args[1]);
1031 if (object->HasFastProperties()) {
1032 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1033 }
1034 return *object;
1035}
1036
1037
1038static Object* Runtime_TransformToFastProperties(Arguments args) {
1039 HandleScope scope;
1040 ASSERT(args.length() == 1);
1041 CONVERT_ARG_CHECKED(JSObject, object, 0);
1042 if (!object->HasFastProperties() && !object->IsGlobalObject()) {
1043 TransformToFastProperties(object, 0);
1044 }
1045 return *object;
1046}
1047
1048
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001049static Object* Runtime_RegExpExec(Arguments args) {
1050 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001051 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001052 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1053 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001054 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001055 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001056 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001057 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001058 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001059 RUNTIME_ASSERT(index >= 0);
1060 RUNTIME_ASSERT(index <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001061 Handle<Object> result = RegExpImpl::Exec(regexp,
1062 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001063 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001064 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001065 if (result.is_null()) return Failure::Exception();
1066 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001067}
1068
1069
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001070static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1071 HandleScope scope;
1072 ASSERT(args.length() == 4);
1073 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1074 int index = Smi::cast(args[1])->value();
1075 Handle<String> pattern = args.at<String>(2);
1076 Handle<String> flags = args.at<String>(3);
1077
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001078 // Get the RegExp function from the context in the literals array.
1079 // This is the RegExp function from the context in which the
1080 // function was created. We do not use the RegExp function from the
1081 // current global context because this might be the RegExp function
1082 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001083 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001084 Handle<JSFunction>(
1085 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001086 // Compute the regular expression literal.
1087 bool has_pending_exception;
1088 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001089 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1090 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001091 if (has_pending_exception) {
1092 ASSERT(Top::has_pending_exception());
1093 return Failure::Exception();
1094 }
1095 literals->set(index, *regexp);
1096 return *regexp;
1097}
1098
1099
1100static Object* Runtime_FunctionGetName(Arguments args) {
1101 NoHandleAllocation ha;
1102 ASSERT(args.length() == 1);
1103
1104 CONVERT_CHECKED(JSFunction, f, args[0]);
1105 return f->shared()->name();
1106}
1107
1108
ager@chromium.org236ad962008-09-25 09:45:57 +00001109static Object* Runtime_FunctionSetName(Arguments args) {
1110 NoHandleAllocation ha;
1111 ASSERT(args.length() == 2);
1112
1113 CONVERT_CHECKED(JSFunction, f, args[0]);
1114 CONVERT_CHECKED(String, name, args[1]);
1115 f->shared()->set_name(name);
1116 return Heap::undefined_value();
1117}
1118
1119
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001120static Object* Runtime_FunctionGetScript(Arguments args) {
1121 HandleScope scope;
1122 ASSERT(args.length() == 1);
1123
1124 CONVERT_CHECKED(JSFunction, fun, args[0]);
1125 Handle<Object> script = Handle<Object>(fun->shared()->script());
1126 if (!script->IsScript()) return Heap::undefined_value();
1127
1128 return *GetScriptWrapper(Handle<Script>::cast(script));
1129}
1130
1131
1132static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1133 NoHandleAllocation ha;
1134 ASSERT(args.length() == 1);
1135
1136 CONVERT_CHECKED(JSFunction, f, args[0]);
1137 return f->shared()->GetSourceCode();
1138}
1139
1140
1141static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1142 NoHandleAllocation ha;
1143 ASSERT(args.length() == 1);
1144
1145 CONVERT_CHECKED(JSFunction, fun, args[0]);
1146 int pos = fun->shared()->start_position();
1147 return Smi::FromInt(pos);
1148}
1149
1150
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001151static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1152 ASSERT(args.length() == 2);
1153
1154 CONVERT_CHECKED(JSFunction, fun, args[0]);
1155 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1156
1157 Code* code = fun->code();
1158 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1159
1160 Address pc = code->address() + offset;
1161 return Smi::FromInt(fun->code()->SourcePosition(pc));
1162}
1163
1164
1165
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001166static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1167 NoHandleAllocation ha;
1168 ASSERT(args.length() == 2);
1169
1170 CONVERT_CHECKED(JSFunction, fun, args[0]);
1171 CONVERT_CHECKED(String, name, args[1]);
1172 fun->SetInstanceClassName(name);
1173 return Heap::undefined_value();
1174}
1175
1176
1177static Object* Runtime_FunctionSetLength(Arguments args) {
1178 NoHandleAllocation ha;
1179 ASSERT(args.length() == 2);
1180
1181 CONVERT_CHECKED(JSFunction, fun, args[0]);
1182 CONVERT_CHECKED(Smi, length, args[1]);
1183 fun->shared()->set_length(length->value());
1184 return length;
1185}
1186
1187
1188static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001189 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001190 ASSERT(args.length() == 2);
1191
1192 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001193 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1194 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001195 return args[0]; // return TOS
1196}
1197
1198
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001199static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1200 NoHandleAllocation ha;
1201 ASSERT(args.length() == 1);
1202
1203 CONVERT_CHECKED(JSFunction, f, args[0]);
1204 // The function_data field of the shared function info is used exclusively by
1205 // the API.
1206 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1207 : Heap::false_value();
1208}
1209
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001210static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1211 NoHandleAllocation ha;
1212 ASSERT(args.length() == 1);
1213
1214 CONVERT_CHECKED(JSFunction, f, args[0]);
1215 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1216}
1217
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001218
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001219static Object* Runtime_SetCode(Arguments args) {
1220 HandleScope scope;
1221 ASSERT(args.length() == 2);
1222
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001223 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001224 Handle<Object> code = args.at<Object>(1);
1225
1226 Handle<Context> context(target->context());
1227
1228 if (!code->IsNull()) {
1229 RUNTIME_ASSERT(code->IsJSFunction());
1230 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1231 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1232 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1233 return Failure::Exception();
1234 }
1235 // Set the code, formal parameter count, and the length of the target
1236 // function.
1237 target->set_code(fun->code());
1238 target->shared()->set_length(fun->shared()->length());
1239 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001240 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001241 // Set the source code of the target function to undefined.
1242 // SetCode is only used for built-in constructors like String,
1243 // Array, and Object, and some web code
1244 // doesn't like seeing source code for constructors.
1245 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001246 // Clear the optimization hints related to the compiled code as these are no
1247 // longer valid when the code is overwritten.
1248 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001249 context = Handle<Context>(fun->context());
1250
1251 // Make sure we get a fresh copy of the literal vector to avoid
1252 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001253 int number_of_literals = fun->NumberOfLiterals();
1254 Handle<FixedArray> literals =
1255 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001256 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001257 // Insert the object, regexp and array functions in the literals
1258 // array prefix. These are the functions that will be used when
1259 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001260 literals->set(JSFunction::kLiteralGlobalContextIndex,
1261 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001262 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001263 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001264 }
1265
1266 target->set_context(*context);
1267 return *target;
1268}
1269
1270
1271static Object* CharCodeAt(String* subject, Object* index) {
1272 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001273 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001274 // Flatten the string. If someone wants to get a char at an index
1275 // in a cons string, it is likely that more indices will be
1276 // accessed.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001277 subject->TryFlattenIfNotFlat();
1278 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001279 return Heap::nan_value();
1280 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001281 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001282}
1283
1284
1285static Object* Runtime_StringCharCodeAt(Arguments args) {
1286 NoHandleAllocation ha;
1287 ASSERT(args.length() == 2);
1288
1289 CONVERT_CHECKED(String, subject, args[0]);
1290 Object* index = args[1];
1291 return CharCodeAt(subject, index);
1292}
1293
1294
1295static Object* Runtime_CharFromCode(Arguments args) {
1296 NoHandleAllocation ha;
1297 ASSERT(args.length() == 1);
1298 uint32_t code;
1299 if (Array::IndexFromObject(args[0], &code)) {
1300 if (code <= 0xffff) {
1301 return Heap::LookupSingleCharacterStringFromCode(code);
1302 }
1303 }
1304 return Heap::empty_string();
1305}
1306
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001307// Forward declarations.
1308static const int kStringBuilderConcatHelperLengthBits = 11;
1309static const int kStringBuilderConcatHelperPositionBits = 19;
1310
1311template <typename schar>
1312static inline void StringBuilderConcatHelper(String*,
1313 schar*,
1314 FixedArray*,
1315 int);
1316
1317typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1318typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1319
1320class ReplacementStringBuilder {
1321 public:
1322 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1323 : subject_(subject),
1324 parts_(Factory::NewFixedArray(estimated_part_count)),
1325 part_count_(0),
1326 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001327 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001328 // Require a non-zero initial size. Ensures that doubling the size to
1329 // extend the array will work.
1330 ASSERT(estimated_part_count > 0);
1331 }
1332
1333 void EnsureCapacity(int elements) {
1334 int length = parts_->length();
1335 int required_length = part_count_ + elements;
1336 if (length < required_length) {
1337 int new_length = length;
1338 do {
1339 new_length *= 2;
1340 } while (new_length < required_length);
1341 Handle<FixedArray> extended_array =
1342 Factory::NewFixedArray(new_length);
1343 parts_->CopyTo(0, *extended_array, 0, part_count_);
1344 parts_ = extended_array;
1345 }
1346 }
1347
1348 void AddSubjectSlice(int from, int to) {
1349 ASSERT(from >= 0);
1350 int length = to - from;
1351 ASSERT(length > 0);
1352 // Can we encode the slice in 11 bits for length and 19 bits for
1353 // start position - as used by StringBuilderConcatHelper?
1354 if (StringBuilderSubstringLength::is_valid(length) &&
1355 StringBuilderSubstringPosition::is_valid(from)) {
1356 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1357 StringBuilderSubstringPosition::encode(from);
1358 AddElement(Smi::FromInt(encoded_slice));
1359 } else {
1360 Handle<String> slice = Factory::NewStringSlice(subject_, from, to);
1361 AddElement(*slice);
1362 }
1363 IncrementCharacterCount(length);
1364 }
1365
1366
1367 void AddString(Handle<String> string) {
1368 int length = string->length();
1369 ASSERT(length > 0);
1370 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001371 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001372 is_ascii_ = false;
1373 }
1374 IncrementCharacterCount(length);
1375 }
1376
1377
1378 Handle<String> ToString() {
1379 if (part_count_ == 0) {
1380 return Factory::empty_string();
1381 }
1382
1383 Handle<String> joined_string;
1384 if (is_ascii_) {
1385 joined_string = NewRawAsciiString(character_count_);
1386 AssertNoAllocation no_alloc;
1387 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1388 char* char_buffer = seq->GetChars();
1389 StringBuilderConcatHelper(*subject_,
1390 char_buffer,
1391 *parts_,
1392 part_count_);
1393 } else {
1394 // Non-ASCII.
1395 joined_string = NewRawTwoByteString(character_count_);
1396 AssertNoAllocation no_alloc;
1397 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1398 uc16* char_buffer = seq->GetChars();
1399 StringBuilderConcatHelper(*subject_,
1400 char_buffer,
1401 *parts_,
1402 part_count_);
1403 }
1404 return joined_string;
1405 }
1406
1407
1408 void IncrementCharacterCount(int by) {
1409 if (character_count_ > Smi::kMaxValue - by) {
1410 V8::FatalProcessOutOfMemory("String.replace result too large.");
1411 }
1412 character_count_ += by;
1413 }
1414
1415 private:
1416
1417 Handle<String> NewRawAsciiString(int size) {
1418 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1419 }
1420
1421
1422 Handle<String> NewRawTwoByteString(int size) {
1423 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1424 }
1425
1426
1427 void AddElement(Object* element) {
1428 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001429 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001430 parts_->set(part_count_, element);
1431 part_count_++;
1432 }
1433
1434 Handle<String> subject_;
1435 Handle<FixedArray> parts_;
1436 int part_count_;
1437 int character_count_;
1438 bool is_ascii_;
1439};
1440
1441
1442class CompiledReplacement {
1443 public:
1444 CompiledReplacement()
1445 : parts_(1), replacement_substrings_(0) {}
1446
1447 void Compile(Handle<String> replacement,
1448 int capture_count,
1449 int subject_length);
1450
1451 void Apply(ReplacementStringBuilder* builder,
1452 int match_from,
1453 int match_to,
1454 Handle<JSArray> last_match_info);
1455
1456 // Number of distinct parts of the replacement pattern.
1457 int parts() {
1458 return parts_.length();
1459 }
1460 private:
1461 enum PartType {
1462 SUBJECT_PREFIX = 1,
1463 SUBJECT_SUFFIX,
1464 SUBJECT_CAPTURE,
1465 REPLACEMENT_SUBSTRING,
1466 REPLACEMENT_STRING,
1467
1468 NUMBER_OF_PART_TYPES
1469 };
1470
1471 struct ReplacementPart {
1472 static inline ReplacementPart SubjectMatch() {
1473 return ReplacementPart(SUBJECT_CAPTURE, 0);
1474 }
1475 static inline ReplacementPart SubjectCapture(int capture_index) {
1476 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1477 }
1478 static inline ReplacementPart SubjectPrefix() {
1479 return ReplacementPart(SUBJECT_PREFIX, 0);
1480 }
1481 static inline ReplacementPart SubjectSuffix(int subject_length) {
1482 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1483 }
1484 static inline ReplacementPart ReplacementString() {
1485 return ReplacementPart(REPLACEMENT_STRING, 0);
1486 }
1487 static inline ReplacementPart ReplacementSubString(int from, int to) {
1488 ASSERT(from >= 0);
1489 ASSERT(to > from);
1490 return ReplacementPart(-from, to);
1491 }
1492
1493 // If tag <= 0 then it is the negation of a start index of a substring of
1494 // the replacement pattern, otherwise it's a value from PartType.
1495 ReplacementPart(int tag, int data)
1496 : tag(tag), data(data) {
1497 // Must be non-positive or a PartType value.
1498 ASSERT(tag < NUMBER_OF_PART_TYPES);
1499 }
1500 // Either a value of PartType or a non-positive number that is
1501 // the negation of an index into the replacement string.
1502 int tag;
1503 // The data value's interpretation depends on the value of tag:
1504 // tag == SUBJECT_PREFIX ||
1505 // tag == SUBJECT_SUFFIX: data is unused.
1506 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1507 // tag == REPLACEMENT_SUBSTRING ||
1508 // tag == REPLACEMENT_STRING: data is index into array of substrings
1509 // of the replacement string.
1510 // tag <= 0: Temporary representation of the substring of the replacement
1511 // string ranging over -tag .. data.
1512 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1513 // substring objects.
1514 int data;
1515 };
1516
1517 template<typename Char>
1518 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1519 Vector<Char> characters,
1520 int capture_count,
1521 int subject_length) {
1522 int length = characters.length();
1523 int last = 0;
1524 for (int i = 0; i < length; i++) {
1525 Char c = characters[i];
1526 if (c == '$') {
1527 int next_index = i + 1;
1528 if (next_index == length) { // No next character!
1529 break;
1530 }
1531 Char c2 = characters[next_index];
1532 switch (c2) {
1533 case '$':
1534 if (i > last) {
1535 // There is a substring before. Include the first "$".
1536 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1537 last = next_index + 1; // Continue after the second "$".
1538 } else {
1539 // Let the next substring start with the second "$".
1540 last = next_index;
1541 }
1542 i = next_index;
1543 break;
1544 case '`':
1545 if (i > last) {
1546 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1547 }
1548 parts->Add(ReplacementPart::SubjectPrefix());
1549 i = next_index;
1550 last = i + 1;
1551 break;
1552 case '\'':
1553 if (i > last) {
1554 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1555 }
1556 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1557 i = next_index;
1558 last = i + 1;
1559 break;
1560 case '&':
1561 if (i > last) {
1562 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1563 }
1564 parts->Add(ReplacementPart::SubjectMatch());
1565 i = next_index;
1566 last = i + 1;
1567 break;
1568 case '0':
1569 case '1':
1570 case '2':
1571 case '3':
1572 case '4':
1573 case '5':
1574 case '6':
1575 case '7':
1576 case '8':
1577 case '9': {
1578 int capture_ref = c2 - '0';
1579 if (capture_ref > capture_count) {
1580 i = next_index;
1581 continue;
1582 }
1583 int second_digit_index = next_index + 1;
1584 if (second_digit_index < length) {
1585 // Peek ahead to see if we have two digits.
1586 Char c3 = characters[second_digit_index];
1587 if ('0' <= c3 && c3 <= '9') { // Double digits.
1588 int double_digit_ref = capture_ref * 10 + c3 - '0';
1589 if (double_digit_ref <= capture_count) {
1590 next_index = second_digit_index;
1591 capture_ref = double_digit_ref;
1592 }
1593 }
1594 }
1595 if (capture_ref > 0) {
1596 if (i > last) {
1597 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1598 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001599 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001600 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1601 last = next_index + 1;
1602 }
1603 i = next_index;
1604 break;
1605 }
1606 default:
1607 i = next_index;
1608 break;
1609 }
1610 }
1611 }
1612 if (length > last) {
1613 if (last == 0) {
1614 parts->Add(ReplacementPart::ReplacementString());
1615 } else {
1616 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1617 }
1618 }
1619 }
1620
1621 ZoneList<ReplacementPart> parts_;
1622 ZoneList<Handle<String> > replacement_substrings_;
1623};
1624
1625
1626void CompiledReplacement::Compile(Handle<String> replacement,
1627 int capture_count,
1628 int subject_length) {
1629 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001630 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001631 AssertNoAllocation no_alloc;
1632 ParseReplacementPattern(&parts_,
1633 replacement->ToAsciiVector(),
1634 capture_count,
1635 subject_length);
1636 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001637 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001638 AssertNoAllocation no_alloc;
1639
1640 ParseReplacementPattern(&parts_,
1641 replacement->ToUC16Vector(),
1642 capture_count,
1643 subject_length);
1644 }
1645 // Find substrings of replacement string and create them as String objects..
1646 int substring_index = 0;
1647 for (int i = 0, n = parts_.length(); i < n; i++) {
1648 int tag = parts_[i].tag;
1649 if (tag <= 0) { // A replacement string slice.
1650 int from = -tag;
1651 int to = parts_[i].data;
1652 replacement_substrings_.Add(Factory::NewStringSlice(replacement,
1653 from,
1654 to));
1655 parts_[i].tag = REPLACEMENT_SUBSTRING;
1656 parts_[i].data = substring_index;
1657 substring_index++;
1658 } else if (tag == REPLACEMENT_STRING) {
1659 replacement_substrings_.Add(replacement);
1660 parts_[i].data = substring_index;
1661 substring_index++;
1662 }
1663 }
1664}
1665
1666
1667void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1668 int match_from,
1669 int match_to,
1670 Handle<JSArray> last_match_info) {
1671 for (int i = 0, n = parts_.length(); i < n; i++) {
1672 ReplacementPart part = parts_[i];
1673 switch (part.tag) {
1674 case SUBJECT_PREFIX:
1675 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1676 break;
1677 case SUBJECT_SUFFIX: {
1678 int subject_length = part.data;
1679 if (match_to < subject_length) {
1680 builder->AddSubjectSlice(match_to, subject_length);
1681 }
1682 break;
1683 }
1684 case SUBJECT_CAPTURE: {
1685 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001686 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001687 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1688 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1689 if (from >= 0 && to > from) {
1690 builder->AddSubjectSlice(from, to);
1691 }
1692 break;
1693 }
1694 case REPLACEMENT_SUBSTRING:
1695 case REPLACEMENT_STRING:
1696 builder->AddString(replacement_substrings_[part.data]);
1697 break;
1698 default:
1699 UNREACHABLE();
1700 }
1701 }
1702}
1703
1704
1705
1706static Object* StringReplaceRegExpWithString(String* subject,
1707 JSRegExp* regexp,
1708 String* replacement,
1709 JSArray* last_match_info) {
1710 ASSERT(subject->IsFlat());
1711 ASSERT(replacement->IsFlat());
1712
1713 HandleScope handles;
1714
1715 int length = subject->length();
1716 Handle<String> subject_handle(subject);
1717 Handle<JSRegExp> regexp_handle(regexp);
1718 Handle<String> replacement_handle(replacement);
1719 Handle<JSArray> last_match_info_handle(last_match_info);
1720 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1721 subject_handle,
1722 0,
1723 last_match_info_handle);
1724 if (match.is_null()) {
1725 return Failure::Exception();
1726 }
1727 if (match->IsNull()) {
1728 return *subject_handle;
1729 }
1730
1731 int capture_count = regexp_handle->CaptureCount();
1732
1733 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001734 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001735 CompiledReplacement compiled_replacement;
1736 compiled_replacement.Compile(replacement_handle,
1737 capture_count,
1738 length);
1739
1740 bool is_global = regexp_handle->GetFlags().is_global();
1741
1742 // Guessing the number of parts that the final result string is built
1743 // from. Global regexps can match any number of times, so we guess
1744 // conservatively.
1745 int expected_parts =
1746 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1747 ReplacementStringBuilder builder(subject_handle, expected_parts);
1748
1749 // Index of end of last match.
1750 int prev = 0;
1751
1752 // Number of parts added by compiled replacement plus preceeding string
1753 // and possibly suffix after last match.
1754 const int parts_added_per_loop = compiled_replacement.parts() + 2;
1755 bool matched = true;
1756 do {
1757 ASSERT(last_match_info_handle->HasFastElements());
1758 // Increase the capacity of the builder before entering local handle-scope,
1759 // so its internal buffer can safely allocate a new handle if it grows.
1760 builder.EnsureCapacity(parts_added_per_loop);
1761
1762 HandleScope loop_scope;
1763 int start, end;
1764 {
1765 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001766 FixedArray* match_info_array =
1767 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001768
1769 ASSERT_EQ(capture_count * 2 + 2,
1770 RegExpImpl::GetLastCaptureCount(match_info_array));
1771 start = RegExpImpl::GetCapture(match_info_array, 0);
1772 end = RegExpImpl::GetCapture(match_info_array, 1);
1773 }
1774
1775 if (prev < start) {
1776 builder.AddSubjectSlice(prev, start);
1777 }
1778 compiled_replacement.Apply(&builder,
1779 start,
1780 end,
1781 last_match_info_handle);
1782 prev = end;
1783
1784 // Only continue checking for global regexps.
1785 if (!is_global) break;
1786
1787 // Continue from where the match ended, unless it was an empty match.
1788 int next = end;
1789 if (start == end) {
1790 next = end + 1;
1791 if (next > length) break;
1792 }
1793
1794 match = RegExpImpl::Exec(regexp_handle,
1795 subject_handle,
1796 next,
1797 last_match_info_handle);
1798 if (match.is_null()) {
1799 return Failure::Exception();
1800 }
1801 matched = !match->IsNull();
1802 } while (matched);
1803
1804 if (prev < length) {
1805 builder.AddSubjectSlice(prev, length);
1806 }
1807
1808 return *(builder.ToString());
1809}
1810
1811
1812static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1813 ASSERT(args.length() == 4);
1814
1815 CONVERT_CHECKED(String, subject, args[0]);
1816 if (!subject->IsFlat()) {
1817 Object* flat_subject = subject->TryFlatten();
1818 if (flat_subject->IsFailure()) {
1819 return flat_subject;
1820 }
1821 subject = String::cast(flat_subject);
1822 }
1823
1824 CONVERT_CHECKED(String, replacement, args[2]);
1825 if (!replacement->IsFlat()) {
1826 Object* flat_replacement = replacement->TryFlatten();
1827 if (flat_replacement->IsFailure()) {
1828 return flat_replacement;
1829 }
1830 replacement = String::cast(flat_replacement);
1831 }
1832
1833 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1834 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1835
1836 ASSERT(last_match_info->HasFastElements());
1837
1838 return StringReplaceRegExpWithString(subject,
1839 regexp,
1840 replacement,
1841 last_match_info);
1842}
1843
1844
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001845
ager@chromium.org7c537e22008-10-16 08:43:32 +00001846// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1847// limit, we can fix the size of tables.
1848static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001849// Reduce alphabet to this size.
1850static const int kBMAlphabetSize = 0x100;
1851// For patterns below this length, the skip length of Boyer-Moore is too short
1852// to compensate for the algorithmic overhead compared to simple brute force.
1853static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001854
ager@chromium.org7c537e22008-10-16 08:43:32 +00001855// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1856// shift. Only allows the last kBMMaxShift characters of the needle
1857// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001858class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001859 public:
1860 BMGoodSuffixBuffers() {}
1861 inline void init(int needle_length) {
1862 ASSERT(needle_length > 1);
1863 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1864 int len = needle_length - start;
1865 biased_suffixes_ = suffixes_ - start;
1866 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1867 for (int i = 0; i <= len; i++) {
1868 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001869 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001870 }
1871 inline int& suffix(int index) {
1872 ASSERT(biased_suffixes_ + index >= suffixes_);
1873 return biased_suffixes_[index];
1874 }
1875 inline int& shift(int index) {
1876 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1877 return biased_good_suffix_shift_[index];
1878 }
1879 private:
1880 int suffixes_[kBMMaxShift + 1];
1881 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001882 int* biased_suffixes_;
1883 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001884 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1885};
1886
1887// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001888static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001889static BMGoodSuffixBuffers bmgs_buffers;
1890
1891// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001892template <typename pchar>
1893static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1894 int start) {
1895 // Run forwards to populate bad_char_table, so that *last* instance
1896 // of character equivalence class is the one registered.
1897 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001898 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1899 : kBMAlphabetSize;
1900 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001901 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001902 } else {
1903 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001904 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001905 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001906 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001907 for (int i = start; i < pattern.length() - 1; i++) {
1908 pchar c = pattern[i];
1909 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001910 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001911 }
1912}
1913
1914template <typename pchar>
1915static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001916 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001917 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001918 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001919 // Compute Good Suffix tables.
1920 bmgs_buffers.init(m);
1921
1922 bmgs_buffers.shift(m-1) = 1;
1923 bmgs_buffers.suffix(m) = m + 1;
1924 pchar last_char = pattern[m - 1];
1925 int suffix = m + 1;
1926 for (int i = m; i > start;) {
1927 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1928 if (bmgs_buffers.shift(suffix) == len) {
1929 bmgs_buffers.shift(suffix) = suffix - i;
1930 }
1931 suffix = bmgs_buffers.suffix(suffix);
1932 }
1933 i--;
1934 suffix--;
1935 bmgs_buffers.suffix(i) = suffix;
1936 if (suffix == m) {
1937 // No suffix to extend, so we check against last_char only.
1938 while (i > start && pattern[i - 1] != last_char) {
1939 if (bmgs_buffers.shift(m) == len) {
1940 bmgs_buffers.shift(m) = m - i;
1941 }
1942 i--;
1943 bmgs_buffers.suffix(i) = m;
1944 }
1945 if (i > start) {
1946 i--;
1947 suffix--;
1948 bmgs_buffers.suffix(i) = suffix;
1949 }
1950 }
1951 }
1952 if (suffix < m) {
1953 for (int i = start; i <= m; i++) {
1954 if (bmgs_buffers.shift(i) == len) {
1955 bmgs_buffers.shift(i) = suffix - start;
1956 }
1957 if (i == suffix) {
1958 suffix = bmgs_buffers.suffix(suffix);
1959 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001960 }
1961 }
1962}
1963
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001964template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001965static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001966 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001967 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001968 }
1969 if (sizeof(pchar) == 1) {
1970 if (char_code > String::kMaxAsciiCharCode) {
1971 return -1;
1972 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001973 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001974 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001975 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001976}
1977
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001978// Restricted simplified Boyer-Moore string matching.
1979// Uses only the bad-shift table of Boyer-Moore and only uses it
1980// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001981template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001982static int BoyerMooreHorspool(Vector<const schar> subject,
1983 Vector<const pchar> pattern,
1984 int start_index,
1985 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001986 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001987 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001988 // Only preprocess at most kBMMaxShift last characters of pattern.
1989 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001990
ager@chromium.org7c537e22008-10-16 08:43:32 +00001991 BoyerMoorePopulateBadCharTable(pattern, start);
1992
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001993 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001994 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001995 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001996 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001997 // Perform search
1998 for (idx = start_index; idx <= n - m;) {
1999 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002000 int c;
2001 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002002 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002003 int shift = j - bc_occ;
2004 idx += shift;
2005 badness += 1 - shift; // at most zero, so badness cannot increase.
2006 if (idx > n - m) {
2007 *complete = true;
2008 return -1;
2009 }
2010 }
2011 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002012 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002013 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002014 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002015 return idx;
2016 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002017 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002018 // Badness increases by the number of characters we have
2019 // checked, and decreases by the number of characters we
2020 // can skip by shifting. It's a measure of how we are doing
2021 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002022 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002023 if (badness > 0) {
2024 *complete = false;
2025 return idx;
2026 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002027 }
2028 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002029 *complete = true;
2030 return -1;
2031}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002032
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002033
2034template <typename schar, typename pchar>
2035static int BoyerMooreIndexOf(Vector<const schar> subject,
2036 Vector<const pchar> pattern,
2037 int idx) {
2038 int n = subject.length();
2039 int m = pattern.length();
2040 // Only preprocess at most kBMMaxShift last characters of pattern.
2041 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2042
2043 // Build the Good Suffix table and continue searching.
2044 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2045 pchar last_char = pattern[m - 1];
2046 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002047 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002048 int j = m - 1;
2049 schar c;
2050 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002051 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002052 idx += shift;
2053 if (idx > n - m) {
2054 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002055 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002056 }
2057 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2058 if (j < 0) {
2059 return idx;
2060 } else if (j < start) {
2061 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002062 // Fall back on BMH shift.
2063 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002064 } else {
2065 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002066 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002067 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002068 if (gs_shift > shift) {
2069 shift = gs_shift;
2070 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002071 idx += shift;
2072 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002073 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002074
2075 return -1;
2076}
2077
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002078
2079template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002080static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002081 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002082 int start_index) {
2083 for (int i = start_index, n = string.length(); i < n; i++) {
2084 if (pattern_char == string[i]) {
2085 return i;
2086 }
2087 }
2088 return -1;
2089}
2090
2091// Trivial string search for shorter strings.
2092// On return, if "complete" is set to true, the return value is the
2093// final result of searching for the patter in the subject.
2094// If "complete" is set to false, the return value is the index where
2095// further checking should start, i.e., it's guaranteed that the pattern
2096// does not occur at a position prior to the returned index.
2097template <typename pchar, typename schar>
2098static int SimpleIndexOf(Vector<const schar> subject,
2099 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002100 int idx,
2101 bool* complete) {
2102 // Badness is a count of how much work we have done. When we have
2103 // done enough work we decide it's probably worth switching to a better
2104 // algorithm.
2105 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002106 // We know our pattern is at least 2 characters, we cache the first so
2107 // the common case of the first character not matching is faster.
2108 pchar pattern_first_char = pattern[0];
2109
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002110 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2111 badness++;
2112 if (badness > 0) {
2113 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002114 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002115 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002116 if (subject[i] != pattern_first_char) continue;
2117 int j = 1;
2118 do {
2119 if (pattern[j] != subject[i+j]) {
2120 break;
2121 }
2122 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002123 } while (j < pattern.length());
2124 if (j == pattern.length()) {
2125 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002126 return i;
2127 }
2128 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002129 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002130 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002131 return -1;
2132}
2133
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002134// Simple indexOf that never bails out. For short patterns only.
2135template <typename pchar, typename schar>
2136static int SimpleIndexOf(Vector<const schar> subject,
2137 Vector<const pchar> pattern,
2138 int idx) {
2139 pchar pattern_first_char = pattern[0];
2140 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2141 if (subject[i] != pattern_first_char) continue;
2142 int j = 1;
2143 do {
2144 if (pattern[j] != subject[i+j]) {
2145 break;
2146 }
2147 j++;
2148 } while (j < pattern.length());
2149 if (j == pattern.length()) {
2150 return i;
2151 }
2152 }
2153 return -1;
2154}
2155
2156
2157// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002158template <typename schar, typename pchar>
2159static int StringMatchStrategy(Vector<const schar> sub,
2160 Vector<const pchar> pat,
2161 int start_index) {
2162 ASSERT(pat.length() > 1);
2163
2164 // We have an ASCII haystack and a non-ASCII needle. Check if there
2165 // really is a non-ASCII character in the needle and bail out if there
2166 // is.
2167 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2168 for (int i = 0; i < pat.length(); i++) {
2169 uc16 c = pat[i];
2170 if (c > String::kMaxAsciiCharCode) {
2171 return -1;
2172 }
2173 }
2174 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002175 if (pat.length() < kBMMinPatternLength) {
2176 // We don't believe fancy searching can ever be more efficient.
2177 // The max shift of Boyer-Moore on a pattern of this length does
2178 // not compensate for the overhead.
2179 return SimpleIndexOf(sub, pat, start_index);
2180 }
2181 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002182 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002183 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2184 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002185 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002186 if (complete) return idx;
2187 return BoyerMooreIndexOf(sub, pat, idx);
2188}
2189
2190// Perform string match of pattern on subject, starting at start index.
2191// Caller must ensure that 0 <= start_index <= sub->length(),
2192// and should check that pat->length() + start_index <= sub->length()
2193int Runtime::StringMatch(Handle<String> sub,
2194 Handle<String> pat,
2195 int start_index) {
2196 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002197 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002198
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002199 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002200 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002201
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002202 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002203 if (start_index + pattern_length > subject_length) return -1;
2204
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002205 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002206 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002207 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002208 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002209 // character patterns linear search is necessary, so any smart
2210 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002211 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002212 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002213 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002214 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002215 if (pchar > String::kMaxAsciiCharCode) {
2216 return -1;
2217 }
2218 Vector<const char> ascii_vector =
2219 sub->ToAsciiVector().SubVector(start_index, subject_length);
2220 const void* pos = memchr(ascii_vector.start(),
2221 static_cast<const char>(pchar),
2222 static_cast<size_t>(ascii_vector.length()));
2223 if (pos == NULL) {
2224 return -1;
2225 }
2226 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
2227 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002228 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002229 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002230 }
2231
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002232 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002233 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002234 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002235
ager@chromium.org7c537e22008-10-16 08:43:32 +00002236 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2237 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002238 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002239 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002240 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002241 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002242 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002243 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002244 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002245 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002246 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002247 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002248 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002249 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002250}
2251
2252
2253static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002254 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002255 ASSERT(args.length() == 3);
2256
ager@chromium.org7c537e22008-10-16 08:43:32 +00002257 CONVERT_ARG_CHECKED(String, sub, 0);
2258 CONVERT_ARG_CHECKED(String, pat, 1);
2259
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002260 Object* index = args[2];
2261 uint32_t start_index;
2262 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2263
ager@chromium.org870a0b62008-11-04 11:43:05 +00002264 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002265 int position = Runtime::StringMatch(sub, pat, start_index);
2266 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002267}
2268
2269
2270static Object* Runtime_StringLastIndexOf(Arguments args) {
2271 NoHandleAllocation ha;
2272 ASSERT(args.length() == 3);
2273
2274 CONVERT_CHECKED(String, sub, args[0]);
2275 CONVERT_CHECKED(String, pat, args[1]);
2276 Object* index = args[2];
2277
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002278 sub->TryFlattenIfNotFlat();
2279 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002280
2281 uint32_t start_index;
2282 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2283
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002284 uint32_t pattern_length = pat->length();
2285 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002286
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002287 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002288 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002289 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002290
2291 for (int i = start_index; i >= 0; i--) {
2292 bool found = true;
2293 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002294 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002295 found = false;
2296 break;
2297 }
2298 }
2299 if (found) return Smi::FromInt(i);
2300 }
2301
2302 return Smi::FromInt(-1);
2303}
2304
2305
2306static Object* Runtime_StringLocaleCompare(Arguments args) {
2307 NoHandleAllocation ha;
2308 ASSERT(args.length() == 2);
2309
2310 CONVERT_CHECKED(String, str1, args[0]);
2311 CONVERT_CHECKED(String, str2, args[1]);
2312
2313 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002314 int str1_length = str1->length();
2315 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002316
2317 // Decide trivial cases without flattening.
2318 if (str1_length == 0) {
2319 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2320 return Smi::FromInt(-str2_length);
2321 } else {
2322 if (str2_length == 0) return Smi::FromInt(str1_length);
2323 }
2324
2325 int end = str1_length < str2_length ? str1_length : str2_length;
2326
2327 // No need to flatten if we are going to find the answer on the first
2328 // character. At this point we know there is at least one character
2329 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002330 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002331 if (d != 0) return Smi::FromInt(d);
2332
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002333 str1->TryFlattenIfNotFlat();
2334 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002335
2336 static StringInputBuffer buf1;
2337 static StringInputBuffer buf2;
2338
2339 buf1.Reset(str1);
2340 buf2.Reset(str2);
2341
2342 for (int i = 0; i < end; i++) {
2343 uint16_t char1 = buf1.GetNext();
2344 uint16_t char2 = buf2.GetNext();
2345 if (char1 != char2) return Smi::FromInt(char1 - char2);
2346 }
2347
2348 return Smi::FromInt(str1_length - str2_length);
2349}
2350
2351
2352static Object* Runtime_StringSlice(Arguments args) {
2353 NoHandleAllocation ha;
2354 ASSERT(args.length() == 3);
2355
2356 CONVERT_CHECKED(String, value, args[0]);
2357 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
2358 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
2359
2360 int start = FastD2I(from_number);
2361 int end = FastD2I(to_number);
2362
2363 RUNTIME_ASSERT(end >= start);
2364 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002365 RUNTIME_ASSERT(end <= value->length());
2366 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002367}
2368
2369
ager@chromium.org41826e72009-03-30 13:30:57 +00002370static Object* Runtime_StringMatch(Arguments args) {
2371 ASSERT_EQ(3, args.length());
2372
2373 CONVERT_ARG_CHECKED(String, subject, 0);
2374 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2375 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2376 HandleScope handles;
2377
2378 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2379
2380 if (match.is_null()) {
2381 return Failure::Exception();
2382 }
2383 if (match->IsNull()) {
2384 return Heap::null_value();
2385 }
2386 int length = subject->length();
2387
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002388 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002389 ZoneList<int> offsets(8);
2390 do {
2391 int start;
2392 int end;
2393 {
2394 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002395 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002396 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2397 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2398 }
2399 offsets.Add(start);
2400 offsets.Add(end);
2401 int index = start < end ? end : end + 1;
2402 if (index > length) break;
2403 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2404 if (match.is_null()) {
2405 return Failure::Exception();
2406 }
2407 } while (!match->IsNull());
2408 int matches = offsets.length() / 2;
2409 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2410 for (int i = 0; i < matches ; i++) {
2411 int from = offsets.at(i * 2);
2412 int to = offsets.at(i * 2 + 1);
2413 elements->set(i, *Factory::NewStringSlice(subject, from, to));
2414 }
2415 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2416 result->set_length(Smi::FromInt(matches));
2417 return *result;
2418}
2419
2420
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002421static Object* Runtime_NumberToRadixString(Arguments args) {
2422 NoHandleAllocation ha;
2423 ASSERT(args.length() == 2);
2424
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002425 // Fast case where the result is a one character string.
2426 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2427 int value = Smi::cast(args[0])->value();
2428 int radix = Smi::cast(args[1])->value();
2429 if (value >= 0 && value < radix) {
2430 RUNTIME_ASSERT(radix <= 36);
2431 // Character array used for conversion.
2432 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2433 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2434 }
2435 }
2436
2437 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002438 CONVERT_DOUBLE_CHECKED(value, args[0]);
2439 if (isnan(value)) {
2440 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2441 }
2442 if (isinf(value)) {
2443 if (value < 0) {
2444 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2445 }
2446 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2447 }
2448 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2449 int radix = FastD2I(radix_number);
2450 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2451 char* str = DoubleToRadixCString(value, radix);
2452 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2453 DeleteArray(str);
2454 return result;
2455}
2456
2457
2458static Object* Runtime_NumberToFixed(Arguments args) {
2459 NoHandleAllocation ha;
2460 ASSERT(args.length() == 2);
2461
2462 CONVERT_DOUBLE_CHECKED(value, args[0]);
2463 if (isnan(value)) {
2464 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2465 }
2466 if (isinf(value)) {
2467 if (value < 0) {
2468 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2469 }
2470 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2471 }
2472 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2473 int f = FastD2I(f_number);
2474 RUNTIME_ASSERT(f >= 0);
2475 char* str = DoubleToFixedCString(value, f);
2476 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2477 DeleteArray(str);
2478 return res;
2479}
2480
2481
2482static Object* Runtime_NumberToExponential(Arguments args) {
2483 NoHandleAllocation ha;
2484 ASSERT(args.length() == 2);
2485
2486 CONVERT_DOUBLE_CHECKED(value, args[0]);
2487 if (isnan(value)) {
2488 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2489 }
2490 if (isinf(value)) {
2491 if (value < 0) {
2492 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2493 }
2494 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2495 }
2496 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2497 int f = FastD2I(f_number);
2498 RUNTIME_ASSERT(f >= -1 && f <= 20);
2499 char* str = DoubleToExponentialCString(value, f);
2500 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2501 DeleteArray(str);
2502 return res;
2503}
2504
2505
2506static Object* Runtime_NumberToPrecision(Arguments args) {
2507 NoHandleAllocation ha;
2508 ASSERT(args.length() == 2);
2509
2510 CONVERT_DOUBLE_CHECKED(value, args[0]);
2511 if (isnan(value)) {
2512 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2513 }
2514 if (isinf(value)) {
2515 if (value < 0) {
2516 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2517 }
2518 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2519 }
2520 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2521 int f = FastD2I(f_number);
2522 RUNTIME_ASSERT(f >= 1 && f <= 21);
2523 char* str = DoubleToPrecisionCString(value, f);
2524 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2525 DeleteArray(str);
2526 return res;
2527}
2528
2529
2530// Returns a single character string where first character equals
2531// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002532static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002533 if (index < static_cast<uint32_t>(string->length())) {
2534 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002535 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002536 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002537 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002538 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002539}
2540
2541
2542Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2543 // Handle [] indexing on Strings
2544 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002545 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2546 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002547 }
2548
2549 // Handle [] indexing on String objects
2550 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002551 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2552 Handle<Object> result =
2553 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2554 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002555 }
2556
2557 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002558 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002559 return prototype->GetElement(index);
2560 }
2561
2562 return object->GetElement(index);
2563}
2564
2565
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002566Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2567 HandleScope scope;
2568
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002569 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002570 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002571 Handle<Object> error =
2572 Factory::NewTypeError("non_object_property_load",
2573 HandleVector(args, 2));
2574 return Top::Throw(*error);
2575 }
2576
2577 // Check if the given key is an array index.
2578 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002579 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002580 return GetElementOrCharAt(object, index);
2581 }
2582
2583 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002584 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002585 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002586 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002587 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002588 bool has_pending_exception = false;
2589 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002590 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002591 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002592 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002593 }
2594
ager@chromium.org32912102009-01-16 10:38:43 +00002595 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002596 // the element if so.
2597 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002598 return GetElementOrCharAt(object, index);
2599 } else {
2600 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002601 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002602 }
2603}
2604
2605
2606static Object* Runtime_GetProperty(Arguments args) {
2607 NoHandleAllocation ha;
2608 ASSERT(args.length() == 2);
2609
2610 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002611 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002612
2613 return Runtime::GetObjectProperty(object, key);
2614}
2615
2616
ager@chromium.org7c537e22008-10-16 08:43:32 +00002617
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002618// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002619static Object* Runtime_KeyedGetProperty(Arguments args) {
2620 NoHandleAllocation ha;
2621 ASSERT(args.length() == 2);
2622
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002623 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002624 // itself.
2625 //
2626 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002627 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002628 // global proxy object never has properties. This is the case
2629 // because the global proxy object forwards everything to its hidden
2630 // prototype including local lookups.
2631 //
2632 // Additionally, we need to make sure that we do not cache results
2633 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002634 if (args[0]->IsJSObject() &&
2635 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002636 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002637 args[1]->IsString()) {
2638 JSObject* receiver = JSObject::cast(args[0]);
2639 String* key = String::cast(args[1]);
2640 if (receiver->HasFastProperties()) {
2641 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002642 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002643 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2644 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002645 Object* value = receiver->FastPropertyAt(offset);
2646 return value->IsTheHole() ? Heap::undefined_value() : value;
2647 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002648 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002649 LookupResult result;
2650 receiver->LocalLookup(key, &result);
2651 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2652 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002653 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002654 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002655 }
2656 } else {
2657 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002658 StringDictionary* dictionary = receiver->property_dictionary();
2659 int entry = dictionary->FindEntry(key);
2660 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002661 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002662 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002663 if (!receiver->IsGlobalObject()) return value;
2664 value = JSGlobalPropertyCell::cast(value)->value();
2665 if (!value->IsTheHole()) return value;
2666 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002667 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002668 }
2669 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002670
2671 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002672 return Runtime::GetObjectProperty(args.at<Object>(0),
2673 args.at<Object>(1));
2674}
2675
2676
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002677Object* Runtime::SetObjectProperty(Handle<Object> object,
2678 Handle<Object> key,
2679 Handle<Object> value,
2680 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002681 HandleScope scope;
2682
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002683 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002684 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002685 Handle<Object> error =
2686 Factory::NewTypeError("non_object_property_store",
2687 HandleVector(args, 2));
2688 return Top::Throw(*error);
2689 }
2690
2691 // If the object isn't a JavaScript object, we ignore the store.
2692 if (!object->IsJSObject()) return *value;
2693
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002694 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2695
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002696 // Check if the given key is an array index.
2697 uint32_t index;
2698 if (Array::IndexFromObject(*key, &index)) {
2699 ASSERT(attr == NONE);
2700
2701 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2702 // of a string using [] notation. We need to support this too in
2703 // JavaScript.
2704 // In the case of a String object we just need to redirect the assignment to
2705 // the underlying string if the index is in range. Since the underlying
2706 // string does nothing with the assignment then we can ignore such
2707 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002708 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002709 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002710 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002711
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002712 Handle<Object> result = SetElement(js_object, index, value);
2713 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002714 return *value;
2715 }
2716
2717 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002718 Handle<Object> result;
2719 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002720 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002721 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002722 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002723 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002724 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002725 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002726 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002727 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002728 return *value;
2729 }
2730
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002731 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002732 bool has_pending_exception = false;
2733 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2734 if (has_pending_exception) return Failure::Exception();
2735 Handle<String> name = Handle<String>::cast(converted);
2736
2737 if (name->AsArrayIndex(&index)) {
2738 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002739 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002740 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002741 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002742 }
2743}
2744
2745
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002746Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2747 Handle<Object> key,
2748 Handle<Object> value,
2749 PropertyAttributes attr) {
2750 HandleScope scope;
2751
2752 // Check if the given key is an array index.
2753 uint32_t index;
2754 if (Array::IndexFromObject(*key, &index)) {
2755 ASSERT(attr == NONE);
2756
2757 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2758 // of a string using [] notation. We need to support this too in
2759 // JavaScript.
2760 // In the case of a String object we just need to redirect the assignment to
2761 // the underlying string if the index is in range. Since the underlying
2762 // string does nothing with the assignment then we can ignore such
2763 // assignments.
2764 if (js_object->IsStringObjectWithCharacterAt(index)) {
2765 return *value;
2766 }
2767
2768 return js_object->SetElement(index, *value);
2769 }
2770
2771 if (key->IsString()) {
2772 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2773 ASSERT(attr == NONE);
2774 return js_object->SetElement(index, *value);
2775 } else {
2776 Handle<String> key_string = Handle<String>::cast(key);
2777 key_string->TryFlattenIfNotFlat();
2778 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2779 *value,
2780 attr);
2781 }
2782 }
2783
2784 // Call-back into JavaScript to convert the key to a string.
2785 bool has_pending_exception = false;
2786 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2787 if (has_pending_exception) return Failure::Exception();
2788 Handle<String> name = Handle<String>::cast(converted);
2789
2790 if (name->AsArrayIndex(&index)) {
2791 ASSERT(attr == NONE);
2792 return js_object->SetElement(index, *value);
2793 } else {
2794 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2795 }
2796}
2797
2798
ager@chromium.orge2902be2009-06-08 12:21:35 +00002799Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2800 Handle<Object> key) {
2801 HandleScope scope;
2802
2803 // Check if the given key is an array index.
2804 uint32_t index;
2805 if (Array::IndexFromObject(*key, &index)) {
2806 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2807 // characters of a string using [] notation. In the case of a
2808 // String object we just need to redirect the deletion to the
2809 // underlying string if the index is in range. Since the
2810 // underlying string does nothing with the deletion, we can ignore
2811 // such deletions.
2812 if (js_object->IsStringObjectWithCharacterAt(index)) {
2813 return Heap::true_value();
2814 }
2815
2816 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2817 }
2818
2819 Handle<String> key_string;
2820 if (key->IsString()) {
2821 key_string = Handle<String>::cast(key);
2822 } else {
2823 // Call-back into JavaScript to convert the key to a string.
2824 bool has_pending_exception = false;
2825 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2826 if (has_pending_exception) return Failure::Exception();
2827 key_string = Handle<String>::cast(converted);
2828 }
2829
2830 key_string->TryFlattenIfNotFlat();
2831 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2832}
2833
2834
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002835static Object* Runtime_SetProperty(Arguments args) {
2836 NoHandleAllocation ha;
2837 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2838
2839 Handle<Object> object = args.at<Object>(0);
2840 Handle<Object> key = args.at<Object>(1);
2841 Handle<Object> value = args.at<Object>(2);
2842
2843 // Compute attributes.
2844 PropertyAttributes attributes = NONE;
2845 if (args.length() == 4) {
2846 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002847 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002848 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002849 RUNTIME_ASSERT(
2850 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2851 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002852 }
2853 return Runtime::SetObjectProperty(object, key, value, attributes);
2854}
2855
2856
2857// Set a local property, even if it is READ_ONLY. If the property does not
2858// exist, it will be added with attributes NONE.
2859static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2860 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002861 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002862 CONVERT_CHECKED(JSObject, object, args[0]);
2863 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002864 // Compute attributes.
2865 PropertyAttributes attributes = NONE;
2866 if (args.length() == 4) {
2867 CONVERT_CHECKED(Smi, value_obj, args[3]);
2868 int unchecked_value = value_obj->value();
2869 // Only attribute bits should be set.
2870 RUNTIME_ASSERT(
2871 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2872 attributes = static_cast<PropertyAttributes>(unchecked_value);
2873 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002874
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002875 return object->
2876 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002877}
2878
2879
2880static Object* Runtime_DeleteProperty(Arguments args) {
2881 NoHandleAllocation ha;
2882 ASSERT(args.length() == 2);
2883
2884 CONVERT_CHECKED(JSObject, object, args[0]);
2885 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002886 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002887}
2888
2889
ager@chromium.org9085a012009-05-11 19:22:57 +00002890static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2891 Handle<String> key) {
2892 if (object->HasLocalProperty(*key)) return Heap::true_value();
2893 // Handle hidden prototypes. If there's a hidden prototype above this thing
2894 // then we have to check it for properties, because they are supposed to
2895 // look like they are on this object.
2896 Handle<Object> proto(object->GetPrototype());
2897 if (proto->IsJSObject() &&
2898 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2899 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2900 }
2901 return Heap::false_value();
2902}
2903
2904
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002905static Object* Runtime_HasLocalProperty(Arguments args) {
2906 NoHandleAllocation ha;
2907 ASSERT(args.length() == 2);
2908 CONVERT_CHECKED(String, key, args[1]);
2909
ager@chromium.org9085a012009-05-11 19:22:57 +00002910 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002911 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002912 if (obj->IsJSObject()) {
2913 JSObject* object = JSObject::cast(obj);
2914 // Fast case - no interceptors.
2915 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2916 // Slow case. Either it's not there or we have an interceptor. We should
2917 // have handles for this kind of deal.
2918 HandleScope scope;
2919 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2920 Handle<String>(key));
2921 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002922 // Well, there is one exception: Handle [] on strings.
2923 uint32_t index;
2924 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002925 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002926 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002927 return Heap::true_value();
2928 }
2929 }
2930 return Heap::false_value();
2931}
2932
2933
2934static Object* Runtime_HasProperty(Arguments args) {
2935 NoHandleAllocation na;
2936 ASSERT(args.length() == 2);
2937
2938 // Only JS objects can have properties.
2939 if (args[0]->IsJSObject()) {
2940 JSObject* object = JSObject::cast(args[0]);
2941 CONVERT_CHECKED(String, key, args[1]);
2942 if (object->HasProperty(key)) return Heap::true_value();
2943 }
2944 return Heap::false_value();
2945}
2946
2947
2948static Object* Runtime_HasElement(Arguments args) {
2949 NoHandleAllocation na;
2950 ASSERT(args.length() == 2);
2951
2952 // Only JS objects can have elements.
2953 if (args[0]->IsJSObject()) {
2954 JSObject* object = JSObject::cast(args[0]);
2955 CONVERT_CHECKED(Smi, index_obj, args[1]);
2956 uint32_t index = index_obj->value();
2957 if (object->HasElement(index)) return Heap::true_value();
2958 }
2959 return Heap::false_value();
2960}
2961
2962
2963static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2964 NoHandleAllocation ha;
2965 ASSERT(args.length() == 2);
2966
2967 CONVERT_CHECKED(JSObject, object, args[0]);
2968 CONVERT_CHECKED(String, key, args[1]);
2969
2970 uint32_t index;
2971 if (key->AsArrayIndex(&index)) {
2972 return Heap::ToBoolean(object->HasElement(index));
2973 }
2974
ager@chromium.org870a0b62008-11-04 11:43:05 +00002975 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2976 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002977}
2978
2979
2980static Object* Runtime_GetPropertyNames(Arguments args) {
2981 HandleScope scope;
2982 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002983 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002984 return *GetKeysFor(object);
2985}
2986
2987
2988// Returns either a FixedArray as Runtime_GetPropertyNames,
2989// or, if the given object has an enum cache that contains
2990// all enumerable properties of the object and its prototypes
2991// have none, the map of the object. This is used to speed up
2992// the check for deletions during a for-in.
2993static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2994 ASSERT(args.length() == 1);
2995
2996 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2997
2998 if (raw_object->IsSimpleEnum()) return raw_object->map();
2999
3000 HandleScope scope;
3001 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003002 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
3003 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003004
3005 // Test again, since cache may have been built by preceding call.
3006 if (object->IsSimpleEnum()) return object->map();
3007
3008 return *content;
3009}
3010
3011
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003012static Object* Runtime_LocalKeys(Arguments args) {
3013 ASSERT_EQ(args.length(), 1);
3014 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3015 HandleScope scope;
3016 Handle<JSObject> object(raw_object);
3017 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
3018 LOCAL_ONLY);
3019 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
3020 // property array and since the result is mutable we have to create
3021 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003022 int length = contents->length();
3023 Handle<FixedArray> copy = Factory::NewFixedArray(length);
3024 for (int i = 0; i < length; i++) {
3025 Object* entry = contents->get(i);
3026 if (entry->IsString()) {
3027 copy->set(i, entry);
3028 } else {
3029 ASSERT(entry->IsNumber());
3030 HandleScope scope;
3031 Handle<Object> entry_handle(entry);
3032 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
3033 copy->set(i, *entry_str);
3034 }
3035 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003036 return *Factory::NewJSArrayWithElements(copy);
3037}
3038
3039
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003040static Object* Runtime_GetArgumentsProperty(Arguments args) {
3041 NoHandleAllocation ha;
3042 ASSERT(args.length() == 1);
3043
3044 // Compute the frame holding the arguments.
3045 JavaScriptFrameIterator it;
3046 it.AdvanceToArgumentsFrame();
3047 JavaScriptFrame* frame = it.frame();
3048
3049 // Get the actual number of provided arguments.
3050 const uint32_t n = frame->GetProvidedParametersCount();
3051
3052 // Try to convert the key to an index. If successful and within
3053 // index return the the argument from the frame.
3054 uint32_t index;
3055 if (Array::IndexFromObject(args[0], &index) && index < n) {
3056 return frame->GetParameter(index);
3057 }
3058
3059 // Convert the key to a string.
3060 HandleScope scope;
3061 bool exception = false;
3062 Handle<Object> converted =
3063 Execution::ToString(args.at<Object>(0), &exception);
3064 if (exception) return Failure::Exception();
3065 Handle<String> key = Handle<String>::cast(converted);
3066
3067 // Try to convert the string key into an array index.
3068 if (key->AsArrayIndex(&index)) {
3069 if (index < n) {
3070 return frame->GetParameter(index);
3071 } else {
3072 return Top::initial_object_prototype()->GetElement(index);
3073 }
3074 }
3075
3076 // Handle special arguments properties.
3077 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3078 if (key->Equals(Heap::callee_symbol())) return frame->function();
3079
3080 // Lookup in the initial Object.prototype object.
3081 return Top::initial_object_prototype()->GetProperty(*key);
3082}
3083
3084
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003085static Object* Runtime_ToFastProperties(Arguments args) {
3086 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003087 Handle<Object> object = args.at<Object>(0);
3088 if (object->IsJSObject()) {
3089 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3090 js_object->TransformToFastProperties(0);
3091 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003092 return *object;
3093}
3094
3095
3096static Object* Runtime_ToSlowProperties(Arguments args) {
3097 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003098 Handle<Object> object = args.at<Object>(0);
3099 if (object->IsJSObject()) {
3100 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003101 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003102 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003103 return *object;
3104}
3105
3106
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003107static Object* Runtime_ToBool(Arguments args) {
3108 NoHandleAllocation ha;
3109 ASSERT(args.length() == 1);
3110
3111 return args[0]->ToBoolean();
3112}
3113
3114
3115// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3116// Possible optimizations: put the type string into the oddballs.
3117static Object* Runtime_Typeof(Arguments args) {
3118 NoHandleAllocation ha;
3119
3120 Object* obj = args[0];
3121 if (obj->IsNumber()) return Heap::number_symbol();
3122 HeapObject* heap_obj = HeapObject::cast(obj);
3123
3124 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003125 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003126
3127 InstanceType instance_type = heap_obj->map()->instance_type();
3128 if (instance_type < FIRST_NONSTRING_TYPE) {
3129 return Heap::string_symbol();
3130 }
3131
3132 switch (instance_type) {
3133 case ODDBALL_TYPE:
3134 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3135 return Heap::boolean_symbol();
3136 }
3137 if (heap_obj->IsNull()) {
3138 return Heap::object_symbol();
3139 }
3140 ASSERT(heap_obj->IsUndefined());
3141 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00003142 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003143 return Heap::function_symbol();
3144 default:
3145 // For any kind of object not handled above, the spec rule for
3146 // host objects gives that it is okay to return "object"
3147 return Heap::object_symbol();
3148 }
3149}
3150
3151
3152static Object* Runtime_StringToNumber(Arguments args) {
3153 NoHandleAllocation ha;
3154 ASSERT(args.length() == 1);
3155 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003156 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003157 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3158}
3159
3160
3161static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3162 NoHandleAllocation ha;
3163 ASSERT(args.length() == 1);
3164
3165 CONVERT_CHECKED(JSArray, codes, args[0]);
3166 int length = Smi::cast(codes->length())->value();
3167
3168 // Check if the string can be ASCII.
3169 int i;
3170 for (i = 0; i < length; i++) {
3171 Object* element = codes->GetElement(i);
3172 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3173 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3174 break;
3175 }
3176
3177 Object* object = NULL;
3178 if (i == length) { // The string is ASCII.
3179 object = Heap::AllocateRawAsciiString(length);
3180 } else { // The string is not ASCII.
3181 object = Heap::AllocateRawTwoByteString(length);
3182 }
3183
3184 if (object->IsFailure()) return object;
3185 String* result = String::cast(object);
3186 for (int i = 0; i < length; i++) {
3187 Object* element = codes->GetElement(i);
3188 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003189 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003190 }
3191 return result;
3192}
3193
3194
3195// kNotEscaped is generated by the following:
3196//
3197// #!/bin/perl
3198// for (my $i = 0; $i < 256; $i++) {
3199// print "\n" if $i % 16 == 0;
3200// my $c = chr($i);
3201// my $escaped = 1;
3202// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3203// print $escaped ? "0, " : "1, ";
3204// }
3205
3206
3207static bool IsNotEscaped(uint16_t character) {
3208 // Only for 8 bit characters, the rest are always escaped (in a different way)
3209 ASSERT(character < 256);
3210 static const char kNotEscaped[256] = {
3211 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3212 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3213 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3214 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3215 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3216 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3217 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3218 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3219 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3220 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3221 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3222 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3223 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3224 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3225 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3226 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3227 };
3228 return kNotEscaped[character] != 0;
3229}
3230
3231
3232static Object* Runtime_URIEscape(Arguments args) {
3233 const char hex_chars[] = "0123456789ABCDEF";
3234 NoHandleAllocation ha;
3235 ASSERT(args.length() == 1);
3236 CONVERT_CHECKED(String, source, args[0]);
3237
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003238 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003239
3240 int escaped_length = 0;
3241 int length = source->length();
3242 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003243 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003244 buffer->Reset(source);
3245 while (buffer->has_more()) {
3246 uint16_t character = buffer->GetNext();
3247 if (character >= 256) {
3248 escaped_length += 6;
3249 } else if (IsNotEscaped(character)) {
3250 escaped_length++;
3251 } else {
3252 escaped_length += 3;
3253 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003254 // We don't allow strings that are longer than a maximal length.
3255 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003256 Top::context()->mark_out_of_memory();
3257 return Failure::OutOfMemoryException();
3258 }
3259 }
3260 }
3261 // No length change implies no change. Return original string if no change.
3262 if (escaped_length == length) {
3263 return source;
3264 }
3265 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3266 if (o->IsFailure()) return o;
3267 String* destination = String::cast(o);
3268 int dest_position = 0;
3269
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003270 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003271 buffer->Rewind();
3272 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003273 uint16_t chr = buffer->GetNext();
3274 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003275 destination->Set(dest_position, '%');
3276 destination->Set(dest_position+1, 'u');
3277 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3278 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3279 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3280 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003281 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003282 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003283 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003284 dest_position++;
3285 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003286 destination->Set(dest_position, '%');
3287 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3288 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003289 dest_position += 3;
3290 }
3291 }
3292 return destination;
3293}
3294
3295
3296static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3297 static const signed char kHexValue['g'] = {
3298 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3299 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3300 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3301 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3302 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3303 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3304 -1, 10, 11, 12, 13, 14, 15 };
3305
3306 if (character1 > 'f') return -1;
3307 int hi = kHexValue[character1];
3308 if (hi == -1) return -1;
3309 if (character2 > 'f') return -1;
3310 int lo = kHexValue[character2];
3311 if (lo == -1) return -1;
3312 return (hi << 4) + lo;
3313}
3314
3315
ager@chromium.org870a0b62008-11-04 11:43:05 +00003316static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003317 int i,
3318 int length,
3319 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003320 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003321 int32_t hi = 0;
3322 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003323 if (character == '%' &&
3324 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003325 source->Get(i + 1) == 'u' &&
3326 (hi = TwoDigitHex(source->Get(i + 2),
3327 source->Get(i + 3))) != -1 &&
3328 (lo = TwoDigitHex(source->Get(i + 4),
3329 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003330 *step = 6;
3331 return (hi << 8) + lo;
3332 } else if (character == '%' &&
3333 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003334 (lo = TwoDigitHex(source->Get(i + 1),
3335 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003336 *step = 3;
3337 return lo;
3338 } else {
3339 *step = 1;
3340 return character;
3341 }
3342}
3343
3344
3345static Object* Runtime_URIUnescape(Arguments args) {
3346 NoHandleAllocation ha;
3347 ASSERT(args.length() == 1);
3348 CONVERT_CHECKED(String, source, args[0]);
3349
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003350 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003351
3352 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003353 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003354
3355 int unescaped_length = 0;
3356 for (int i = 0; i < length; unescaped_length++) {
3357 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003358 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003359 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003360 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003361 i += step;
3362 }
3363
3364 // No length change implies no change. Return original string if no change.
3365 if (unescaped_length == length)
3366 return source;
3367
3368 Object* o = ascii ?
3369 Heap::AllocateRawAsciiString(unescaped_length) :
3370 Heap::AllocateRawTwoByteString(unescaped_length);
3371 if (o->IsFailure()) return o;
3372 String* destination = String::cast(o);
3373
3374 int dest_position = 0;
3375 for (int i = 0; i < length; dest_position++) {
3376 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003377 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003378 i += step;
3379 }
3380 return destination;
3381}
3382
3383
3384static Object* Runtime_StringParseInt(Arguments args) {
3385 NoHandleAllocation ha;
3386
3387 CONVERT_CHECKED(String, s, args[0]);
3388 CONVERT_DOUBLE_CHECKED(n, args[1]);
3389 int radix = FastD2I(n);
3390
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003391 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003392
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003393 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003394 int i;
3395
3396 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003397 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003398 if (i == len) return Heap::nan_value();
3399
3400 // Compute the sign (default to +).
3401 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003402 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003403 sign = -1;
3404 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003405 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003406 i++;
3407 }
3408
3409 // Compute the radix if 0.
3410 if (radix == 0) {
3411 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003412 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003413 radix = 8;
3414 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003415 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003416 if (c == 'x' || c == 'X') {
3417 radix = 16;
3418 i += 2;
3419 }
3420 }
3421 }
3422 } else if (radix == 16) {
3423 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003424 if (i + 1 < len && s->Get(i) == '0') {
3425 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003426 if (c == 'x' || c == 'X') i += 2;
3427 }
3428 }
3429
3430 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3431 double value;
3432 int end_index = StringToInt(s, i, radix, &value);
3433 if (end_index != i) {
3434 return Heap::NumberFromDouble(sign * value);
3435 }
3436 return Heap::nan_value();
3437}
3438
3439
3440static Object* Runtime_StringParseFloat(Arguments args) {
3441 NoHandleAllocation ha;
3442 CONVERT_CHECKED(String, str, args[0]);
3443
3444 // ECMA-262 section 15.1.2.3, empty string is NaN
3445 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3446
3447 // Create a number object from the value.
3448 return Heap::NumberFromDouble(value);
3449}
3450
3451
3452static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3453static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3454
3455
3456template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003457static Object* ConvertCaseHelper(String* s,
3458 int length,
3459 int input_string_length,
3460 unibrow::Mapping<Converter, 128>* mapping) {
3461 // We try this twice, once with the assumption that the result is no longer
3462 // than the input and, if that assumption breaks, again with the exact
3463 // length. This may not be pretty, but it is nicer than what was here before
3464 // and I hereby claim my vaffel-is.
3465 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003466 // Allocate the resulting string.
3467 //
3468 // NOTE: This assumes that the upper/lower case of an ascii
3469 // character is also ascii. This is currently the case, but it
3470 // might break in the future if we implement more context and locale
3471 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003472 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003473 ? Heap::AllocateRawAsciiString(length)
3474 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003475 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003476 String* result = String::cast(o);
3477 bool has_changed_character = false;
3478
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003479 // Convert all characters to upper case, assuming that they will fit
3480 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003481 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003482 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003483 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003484 // We can assume that the string is not empty
3485 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003486 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003487 bool has_next = buffer->has_more();
3488 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003489 int char_length = mapping->get(current, next, chars);
3490 if (char_length == 0) {
3491 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003492 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003493 i++;
3494 } else if (char_length == 1) {
3495 // Common case: converting the letter resulted in one character.
3496 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003497 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003498 has_changed_character = true;
3499 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003500 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003501 // We've assumed that the result would be as long as the
3502 // input but here is a character that converts to several
3503 // characters. No matter, we calculate the exact length
3504 // of the result and try the whole thing again.
3505 //
3506 // Note that this leaves room for optimization. We could just
3507 // memcpy what we already have to the result string. Also,
3508 // the result string is the last object allocated we could
3509 // "realloc" it and probably, in the vast majority of cases,
3510 // extend the existing string to be able to hold the full
3511 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003512 int next_length = 0;
3513 if (has_next) {
3514 next_length = mapping->get(next, 0, chars);
3515 if (next_length == 0) next_length = 1;
3516 }
3517 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003518 while (buffer->has_more()) {
3519 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003520 // NOTE: we use 0 as the next character here because, while
3521 // the next character may affect what a character converts to,
3522 // it does not in any case affect the length of what it convert
3523 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003524 int char_length = mapping->get(current, 0, chars);
3525 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003526 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003527 if (current_length > Smi::kMaxValue) {
3528 Top::context()->mark_out_of_memory();
3529 return Failure::OutOfMemoryException();
3530 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003531 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003532 // Try again with the real length.
3533 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003534 } else {
3535 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003536 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003537 i++;
3538 }
3539 has_changed_character = true;
3540 }
3541 current = next;
3542 }
3543 if (has_changed_character) {
3544 return result;
3545 } else {
3546 // If we didn't actually change anything in doing the conversion
3547 // we simple return the result and let the converted string
3548 // become garbage; there is no reason to keep two identical strings
3549 // alive.
3550 return s;
3551 }
3552}
3553
3554
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003555template <class Converter>
3556static Object* ConvertCase(Arguments args,
3557 unibrow::Mapping<Converter, 128>* mapping) {
3558 NoHandleAllocation ha;
3559
3560 CONVERT_CHECKED(String, s, args[0]);
3561 s->TryFlattenIfNotFlat();
3562
3563 int input_string_length = s->length();
3564 // Assume that the string is not empty; we need this assumption later
3565 if (input_string_length == 0) return s;
3566 int length = input_string_length;
3567
3568 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3569 if (answer->IsSmi()) {
3570 // Retry with correct length.
3571 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3572 }
3573 return answer; // This may be a failure.
3574}
3575
3576
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003577static Object* Runtime_StringToLowerCase(Arguments args) {
3578 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3579}
3580
3581
3582static Object* Runtime_StringToUpperCase(Arguments args) {
3583 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3584}
3585
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003586static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
3587 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
3588}
3589
3590static Object* Runtime_StringTrim(Arguments args) {
3591 NoHandleAllocation ha;
3592 ASSERT(args.length() == 3);
3593
3594 CONVERT_CHECKED(String, s, args[0]);
3595 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
3596 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
3597
3598 s->TryFlattenIfNotFlat();
3599 int length = s->length();
3600
3601 int left = 0;
3602 if (trimLeft) {
3603 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
3604 left++;
3605 }
3606 }
3607
3608 int right = length;
3609 if (trimRight) {
3610 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
3611 right--;
3612 }
3613 }
3614 return s->Slice(left, right);
3615}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003616
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003617bool Runtime::IsUpperCaseChar(uint16_t ch) {
3618 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3619 int char_length = to_upper_mapping.get(ch, 0, chars);
3620 return char_length == 0;
3621}
3622
3623
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003624static Object* Runtime_NumberToString(Arguments args) {
3625 NoHandleAllocation ha;
3626 ASSERT(args.length() == 1);
3627
3628 Object* number = args[0];
3629 RUNTIME_ASSERT(number->IsNumber());
3630
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003631 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003632}
3633
3634
3635static Object* Runtime_NumberToInteger(Arguments args) {
3636 NoHandleAllocation ha;
3637 ASSERT(args.length() == 1);
3638
3639 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003640 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003641 CONVERT_DOUBLE_CHECKED(number, obj);
3642 return Heap::NumberFromDouble(DoubleToInteger(number));
3643}
3644
3645
3646static Object* Runtime_NumberToJSUint32(Arguments args) {
3647 NoHandleAllocation ha;
3648 ASSERT(args.length() == 1);
3649
3650 Object* obj = args[0];
3651 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3652 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3653 return Heap::NumberFromUint32(number);
3654}
3655
3656
3657static Object* Runtime_NumberToJSInt32(Arguments args) {
3658 NoHandleAllocation ha;
3659 ASSERT(args.length() == 1);
3660
3661 Object* obj = args[0];
3662 if (obj->IsSmi()) return obj;
3663 CONVERT_DOUBLE_CHECKED(number, obj);
3664 return Heap::NumberFromInt32(DoubleToInt32(number));
3665}
3666
3667
ager@chromium.org870a0b62008-11-04 11:43:05 +00003668// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3669// a small integer.
3670static Object* Runtime_NumberToSmi(Arguments args) {
3671 NoHandleAllocation ha;
3672 ASSERT(args.length() == 1);
3673
3674 Object* obj = args[0];
3675 if (obj->IsSmi()) {
3676 return obj;
3677 }
3678 if (obj->IsHeapNumber()) {
3679 double value = HeapNumber::cast(obj)->value();
3680 int int_value = FastD2I(value);
3681 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3682 return Smi::FromInt(int_value);
3683 }
3684 }
3685 return Heap::nan_value();
3686}
3687
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003688
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003689static Object* Runtime_NumberAdd(Arguments args) {
3690 NoHandleAllocation ha;
3691 ASSERT(args.length() == 2);
3692
3693 CONVERT_DOUBLE_CHECKED(x, args[0]);
3694 CONVERT_DOUBLE_CHECKED(y, args[1]);
3695 return Heap::AllocateHeapNumber(x + y);
3696}
3697
3698
3699static Object* Runtime_NumberSub(Arguments args) {
3700 NoHandleAllocation ha;
3701 ASSERT(args.length() == 2);
3702
3703 CONVERT_DOUBLE_CHECKED(x, args[0]);
3704 CONVERT_DOUBLE_CHECKED(y, args[1]);
3705 return Heap::AllocateHeapNumber(x - y);
3706}
3707
3708
3709static Object* Runtime_NumberMul(Arguments args) {
3710 NoHandleAllocation ha;
3711 ASSERT(args.length() == 2);
3712
3713 CONVERT_DOUBLE_CHECKED(x, args[0]);
3714 CONVERT_DOUBLE_CHECKED(y, args[1]);
3715 return Heap::AllocateHeapNumber(x * y);
3716}
3717
3718
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003719static Object* Runtime_NumberUnaryMinus(Arguments args) {
3720 NoHandleAllocation ha;
3721 ASSERT(args.length() == 1);
3722
3723 CONVERT_DOUBLE_CHECKED(x, args[0]);
3724 return Heap::AllocateHeapNumber(-x);
3725}
3726
3727
3728static Object* Runtime_NumberDiv(Arguments args) {
3729 NoHandleAllocation ha;
3730 ASSERT(args.length() == 2);
3731
3732 CONVERT_DOUBLE_CHECKED(x, args[0]);
3733 CONVERT_DOUBLE_CHECKED(y, args[1]);
3734 return Heap::NewNumberFromDouble(x / y);
3735}
3736
3737
3738static Object* Runtime_NumberMod(Arguments args) {
3739 NoHandleAllocation ha;
3740 ASSERT(args.length() == 2);
3741
3742 CONVERT_DOUBLE_CHECKED(x, args[0]);
3743 CONVERT_DOUBLE_CHECKED(y, args[1]);
3744
ager@chromium.org4af710e2009-09-15 12:20:11 +00003745#if defined WIN32 || defined _WIN64
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003746 // Workaround MS fmod bugs. ECMA-262 says:
3747 // dividend is finite and divisor is an infinity => result equals dividend
3748 // dividend is a zero and divisor is nonzero finite => result equals dividend
3749 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
3750 !(x == 0 && (y != 0 && isfinite(y))))
3751#endif
3752 x = fmod(x, y);
3753 // NewNumberFromDouble may return a Smi instead of a Number object
3754 return Heap::NewNumberFromDouble(x);
3755}
3756
3757
3758static Object* Runtime_StringAdd(Arguments args) {
3759 NoHandleAllocation ha;
3760 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003761 CONVERT_CHECKED(String, str1, args[0]);
3762 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003763 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003764}
3765
3766
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003767template<typename sinkchar>
3768static inline void StringBuilderConcatHelper(String* special,
3769 sinkchar* sink,
3770 FixedArray* fixed_array,
3771 int array_length) {
3772 int position = 0;
3773 for (int i = 0; i < array_length; i++) {
3774 Object* element = fixed_array->get(i);
3775 if (element->IsSmi()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003776 int encoded_slice = Smi::cast(element)->value();
3777 int pos = StringBuilderSubstringPosition::decode(encoded_slice);
3778 int len = StringBuilderSubstringLength::decode(encoded_slice);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003779 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003780 sink + position,
3781 pos,
3782 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003783 position += len;
3784 } else {
3785 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003786 int element_length = string->length();
3787 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003788 position += element_length;
3789 }
3790 }
3791}
3792
3793
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003794static Object* Runtime_StringBuilderConcat(Arguments args) {
3795 NoHandleAllocation ha;
3796 ASSERT(args.length() == 2);
3797 CONVERT_CHECKED(JSArray, array, args[0]);
3798 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003799 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003800 Object* smi_array_length = array->length();
3801 if (!smi_array_length->IsSmi()) {
3802 Top::context()->mark_out_of_memory();
3803 return Failure::OutOfMemoryException();
3804 }
3805 int array_length = Smi::cast(smi_array_length)->value();
3806 if (!array->HasFastElements()) {
3807 return Top::Throw(Heap::illegal_argument_symbol());
3808 }
3809 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003810 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003811 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003812 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003813
3814 if (array_length == 0) {
3815 return Heap::empty_string();
3816 } else if (array_length == 1) {
3817 Object* first = fixed_array->get(0);
3818 if (first->IsString()) return first;
3819 }
3820
ager@chromium.org5ec48922009-05-05 07:25:34 +00003821 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003822 int position = 0;
3823 for (int i = 0; i < array_length; i++) {
3824 Object* elt = fixed_array->get(i);
3825 if (elt->IsSmi()) {
3826 int len = Smi::cast(elt)->value();
3827 int pos = len >> 11;
3828 len &= 0x7ff;
3829 if (pos + len > special_length) {
3830 return Top::Throw(Heap::illegal_argument_symbol());
3831 }
3832 position += len;
3833 } else if (elt->IsString()) {
3834 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003835 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003836 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003837 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003838 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003839 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003840 } else {
3841 return Top::Throw(Heap::illegal_argument_symbol());
3842 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003843 if (position > String::kMaxLength) {
3844 Top::context()->mark_out_of_memory();
3845 return Failure::OutOfMemoryException();
3846 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003847 }
3848
3849 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003850 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003851
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003852 if (ascii) {
3853 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003854 if (object->IsFailure()) return object;
3855 SeqAsciiString* answer = SeqAsciiString::cast(object);
3856 StringBuilderConcatHelper(special,
3857 answer->GetChars(),
3858 fixed_array,
3859 array_length);
3860 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003861 } else {
3862 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003863 if (object->IsFailure()) return object;
3864 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3865 StringBuilderConcatHelper(special,
3866 answer->GetChars(),
3867 fixed_array,
3868 array_length);
3869 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003870 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003871}
3872
3873
3874static Object* Runtime_NumberOr(Arguments args) {
3875 NoHandleAllocation ha;
3876 ASSERT(args.length() == 2);
3877
3878 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3879 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3880 return Heap::NumberFromInt32(x | y);
3881}
3882
3883
3884static Object* Runtime_NumberAnd(Arguments args) {
3885 NoHandleAllocation ha;
3886 ASSERT(args.length() == 2);
3887
3888 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3889 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3890 return Heap::NumberFromInt32(x & y);
3891}
3892
3893
3894static Object* Runtime_NumberXor(Arguments args) {
3895 NoHandleAllocation ha;
3896 ASSERT(args.length() == 2);
3897
3898 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3899 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3900 return Heap::NumberFromInt32(x ^ y);
3901}
3902
3903
3904static Object* Runtime_NumberNot(Arguments args) {
3905 NoHandleAllocation ha;
3906 ASSERT(args.length() == 1);
3907
3908 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3909 return Heap::NumberFromInt32(~x);
3910}
3911
3912
3913static Object* Runtime_NumberShl(Arguments args) {
3914 NoHandleAllocation ha;
3915 ASSERT(args.length() == 2);
3916
3917 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3918 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3919 return Heap::NumberFromInt32(x << (y & 0x1f));
3920}
3921
3922
3923static Object* Runtime_NumberShr(Arguments args) {
3924 NoHandleAllocation ha;
3925 ASSERT(args.length() == 2);
3926
3927 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3928 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3929 return Heap::NumberFromUint32(x >> (y & 0x1f));
3930}
3931
3932
3933static Object* Runtime_NumberSar(Arguments args) {
3934 NoHandleAllocation ha;
3935 ASSERT(args.length() == 2);
3936
3937 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3938 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3939 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3940}
3941
3942
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003943static Object* Runtime_NumberEquals(Arguments args) {
3944 NoHandleAllocation ha;
3945 ASSERT(args.length() == 2);
3946
3947 CONVERT_DOUBLE_CHECKED(x, args[0]);
3948 CONVERT_DOUBLE_CHECKED(y, args[1]);
3949 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3950 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3951 if (x == y) return Smi::FromInt(EQUAL);
3952 Object* result;
3953 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3954 result = Smi::FromInt(EQUAL);
3955 } else {
3956 result = Smi::FromInt(NOT_EQUAL);
3957 }
3958 return result;
3959}
3960
3961
3962static Object* Runtime_StringEquals(Arguments args) {
3963 NoHandleAllocation ha;
3964 ASSERT(args.length() == 2);
3965
3966 CONVERT_CHECKED(String, x, args[0]);
3967 CONVERT_CHECKED(String, y, args[1]);
3968
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003969 bool not_equal = !x->Equals(y);
3970 // This is slightly convoluted because the value that signifies
3971 // equality is 0 and inequality is 1 so we have to negate the result
3972 // from String::Equals.
3973 ASSERT(not_equal == 0 || not_equal == 1);
3974 STATIC_CHECK(EQUAL == 0);
3975 STATIC_CHECK(NOT_EQUAL == 1);
3976 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003977}
3978
3979
3980static Object* Runtime_NumberCompare(Arguments args) {
3981 NoHandleAllocation ha;
3982 ASSERT(args.length() == 3);
3983
3984 CONVERT_DOUBLE_CHECKED(x, args[0]);
3985 CONVERT_DOUBLE_CHECKED(y, args[1]);
3986 if (isnan(x) || isnan(y)) return args[2];
3987 if (x == y) return Smi::FromInt(EQUAL);
3988 if (isless(x, y)) return Smi::FromInt(LESS);
3989 return Smi::FromInt(GREATER);
3990}
3991
3992
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003993// Compare two Smis as if they were converted to strings and then
3994// compared lexicographically.
3995static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3996 NoHandleAllocation ha;
3997 ASSERT(args.length() == 2);
3998
3999 // Arrays for the individual characters of the two Smis. Smis are
4000 // 31 bit integers and 10 decimal digits are therefore enough.
4001 static int x_elms[10];
4002 static int y_elms[10];
4003
4004 // Extract the integer values from the Smis.
4005 CONVERT_CHECKED(Smi, x, args[0]);
4006 CONVERT_CHECKED(Smi, y, args[1]);
4007 int x_value = x->value();
4008 int y_value = y->value();
4009
4010 // If the integers are equal so are the string representations.
4011 if (x_value == y_value) return Smi::FromInt(EQUAL);
4012
4013 // If one of the integers are zero the normal integer order is the
4014 // same as the lexicographic order of the string representations.
4015 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
4016
ager@chromium.org32912102009-01-16 10:38:43 +00004017 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004018 // smallest because the char code of '-' is less than the char code
4019 // of any digit. Otherwise, we make both values positive.
4020 if (x_value < 0 || y_value < 0) {
4021 if (y_value >= 0) return Smi::FromInt(LESS);
4022 if (x_value >= 0) return Smi::FromInt(GREATER);
4023 x_value = -x_value;
4024 y_value = -y_value;
4025 }
4026
4027 // Convert the integers to arrays of their decimal digits.
4028 int x_index = 0;
4029 int y_index = 0;
4030 while (x_value > 0) {
4031 x_elms[x_index++] = x_value % 10;
4032 x_value /= 10;
4033 }
4034 while (y_value > 0) {
4035 y_elms[y_index++] = y_value % 10;
4036 y_value /= 10;
4037 }
4038
4039 // Loop through the arrays of decimal digits finding the first place
4040 // where they differ.
4041 while (--x_index >= 0 && --y_index >= 0) {
4042 int diff = x_elms[x_index] - y_elms[y_index];
4043 if (diff != 0) return Smi::FromInt(diff);
4044 }
4045
4046 // If one array is a suffix of the other array, the longest array is
4047 // the representation of the largest of the Smis in the
4048 // lexicographic ordering.
4049 return Smi::FromInt(x_index - y_index);
4050}
4051
4052
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004053static Object* Runtime_StringCompare(Arguments args) {
4054 NoHandleAllocation ha;
4055 ASSERT(args.length() == 2);
4056
4057 CONVERT_CHECKED(String, x, args[0]);
4058 CONVERT_CHECKED(String, y, args[1]);
4059
4060 // A few fast case tests before we flatten.
4061 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004062 if (y->length() == 0) {
4063 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004064 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004065 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004066 return Smi::FromInt(LESS);
4067 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004068
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004069 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004070 if (d < 0) return Smi::FromInt(LESS);
4071 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004072
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004073 x->TryFlattenIfNotFlat();
4074 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004075
4076 static StringInputBuffer bufx;
4077 static StringInputBuffer bufy;
4078 bufx.Reset(x);
4079 bufy.Reset(y);
4080 while (bufx.has_more() && bufy.has_more()) {
4081 int d = bufx.GetNext() - bufy.GetNext();
4082 if (d < 0) return Smi::FromInt(LESS);
4083 else if (d > 0) return Smi::FromInt(GREATER);
4084 }
4085
4086 // x is (non-trivial) prefix of y:
4087 if (bufy.has_more()) return Smi::FromInt(LESS);
4088 // y is prefix of x:
4089 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4090}
4091
4092
4093static Object* Runtime_Math_abs(Arguments args) {
4094 NoHandleAllocation ha;
4095 ASSERT(args.length() == 1);
4096
4097 CONVERT_DOUBLE_CHECKED(x, args[0]);
4098 return Heap::AllocateHeapNumber(fabs(x));
4099}
4100
4101
4102static Object* Runtime_Math_acos(Arguments args) {
4103 NoHandleAllocation ha;
4104 ASSERT(args.length() == 1);
4105
4106 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004107 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004108}
4109
4110
4111static Object* Runtime_Math_asin(Arguments args) {
4112 NoHandleAllocation ha;
4113 ASSERT(args.length() == 1);
4114
4115 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004116 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004117}
4118
4119
4120static Object* Runtime_Math_atan(Arguments args) {
4121 NoHandleAllocation ha;
4122 ASSERT(args.length() == 1);
4123
4124 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004125 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004126}
4127
4128
4129static Object* Runtime_Math_atan2(Arguments args) {
4130 NoHandleAllocation ha;
4131 ASSERT(args.length() == 2);
4132
4133 CONVERT_DOUBLE_CHECKED(x, args[0]);
4134 CONVERT_DOUBLE_CHECKED(y, args[1]);
4135 double result;
4136 if (isinf(x) && isinf(y)) {
4137 // Make sure that the result in case of two infinite arguments
4138 // is a multiple of Pi / 4. The sign of the result is determined
4139 // by the first argument (x) and the sign of the second argument
4140 // determines the multiplier: one or three.
4141 static double kPiDividedBy4 = 0.78539816339744830962;
4142 int multiplier = (x < 0) ? -1 : 1;
4143 if (y < 0) multiplier *= 3;
4144 result = multiplier * kPiDividedBy4;
4145 } else {
4146 result = atan2(x, y);
4147 }
4148 return Heap::AllocateHeapNumber(result);
4149}
4150
4151
4152static Object* Runtime_Math_ceil(Arguments args) {
4153 NoHandleAllocation ha;
4154 ASSERT(args.length() == 1);
4155
4156 CONVERT_DOUBLE_CHECKED(x, args[0]);
4157 return Heap::NumberFromDouble(ceiling(x));
4158}
4159
4160
4161static Object* Runtime_Math_cos(Arguments args) {
4162 NoHandleAllocation ha;
4163 ASSERT(args.length() == 1);
4164
4165 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004166 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004167}
4168
4169
4170static Object* Runtime_Math_exp(Arguments args) {
4171 NoHandleAllocation ha;
4172 ASSERT(args.length() == 1);
4173
4174 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004175 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004176}
4177
4178
4179static Object* Runtime_Math_floor(Arguments args) {
4180 NoHandleAllocation ha;
4181 ASSERT(args.length() == 1);
4182
4183 CONVERT_DOUBLE_CHECKED(x, args[0]);
4184 return Heap::NumberFromDouble(floor(x));
4185}
4186
4187
4188static Object* Runtime_Math_log(Arguments args) {
4189 NoHandleAllocation ha;
4190 ASSERT(args.length() == 1);
4191
4192 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004193 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004194}
4195
4196
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004197// Helper function to compute x^y, where y is known to be an
4198// integer. Uses binary decomposition to limit the number of
4199// multiplications; see the discussion in "Hacker's Delight" by Henry
4200// S. Warren, Jr., figure 11-6, page 213.
4201static double powi(double x, int y) {
4202 ASSERT(y != kMinInt);
4203 unsigned n = (y < 0) ? -y : y;
4204 double m = x;
4205 double p = 1;
4206 while (true) {
4207 if ((n & 1) != 0) p *= m;
4208 n >>= 1;
4209 if (n == 0) {
4210 if (y < 0) {
4211 // Unfortunately, we have to be careful when p has reached
4212 // infinity in the computation, because sometimes the higher
4213 // internal precision in the pow() implementation would have
4214 // given us a finite p. This happens very rarely.
4215 double result = 1.0 / p;
4216 return (result == 0 && isinf(p))
4217 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4218 : result;
4219 } else {
4220 return p;
4221 }
4222 }
4223 m *= m;
4224 }
4225}
4226
4227
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004228static Object* Runtime_Math_pow(Arguments args) {
4229 NoHandleAllocation ha;
4230 ASSERT(args.length() == 2);
4231
4232 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004233
4234 // If the second argument is a smi, it is much faster to call the
4235 // custom powi() function than the generic pow().
4236 if (args[1]->IsSmi()) {
4237 int y = Smi::cast(args[1])->value();
4238 return Heap::AllocateHeapNumber(powi(x, y));
4239 }
4240
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004241 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004242
4243 if (!isinf(x)) {
4244 if (y == 0.5) {
4245 // It's not uncommon to use Math.pow(x, 0.5) to compute the
4246 // square root of a number. To speed up such computations, we
4247 // explictly check for this case and use the sqrt() function
4248 // which is faster than pow().
4249 return Heap::AllocateHeapNumber(sqrt(x));
4250 } else if (y == -0.5) {
4251 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4252 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
4253 }
4254 }
4255
4256 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004257 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004258 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4259 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004260 } else {
4261 return Heap::AllocateHeapNumber(pow(x, y));
4262 }
4263}
4264
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004265
4266static Object* Runtime_Math_round(Arguments args) {
4267 NoHandleAllocation ha;
4268 ASSERT(args.length() == 1);
4269
4270 CONVERT_DOUBLE_CHECKED(x, args[0]);
4271 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4272 return Heap::NumberFromDouble(floor(x + 0.5));
4273}
4274
4275
4276static Object* Runtime_Math_sin(Arguments args) {
4277 NoHandleAllocation ha;
4278 ASSERT(args.length() == 1);
4279
4280 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004281 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004282}
4283
4284
4285static Object* Runtime_Math_sqrt(Arguments args) {
4286 NoHandleAllocation ha;
4287 ASSERT(args.length() == 1);
4288
4289 CONVERT_DOUBLE_CHECKED(x, args[0]);
4290 return Heap::AllocateHeapNumber(sqrt(x));
4291}
4292
4293
4294static Object* Runtime_Math_tan(Arguments args) {
4295 NoHandleAllocation ha;
4296 ASSERT(args.length() == 1);
4297
4298 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004299 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004300}
4301
4302
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004303// The NewArguments function is only used when constructing the
4304// arguments array when calling non-functions from JavaScript in
4305// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004306static Object* Runtime_NewArguments(Arguments args) {
4307 NoHandleAllocation ha;
4308 ASSERT(args.length() == 1);
4309
4310 // ECMA-262, 3rd., 10.1.8, p.39
4311 CONVERT_CHECKED(JSFunction, callee, args[0]);
4312
4313 // Compute the frame holding the arguments.
4314 JavaScriptFrameIterator it;
4315 it.AdvanceToArgumentsFrame();
4316 JavaScriptFrame* frame = it.frame();
4317
4318 const int length = frame->GetProvidedParametersCount();
4319 Object* result = Heap::AllocateArgumentsObject(callee, length);
4320 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004321 if (length > 0) {
4322 Object* obj = Heap::AllocateFixedArray(length);
4323 if (obj->IsFailure()) return obj;
4324 FixedArray* array = FixedArray::cast(obj);
4325 ASSERT(array->length() == length);
4326 WriteBarrierMode mode = array->GetWriteBarrierMode();
4327 for (int i = 0; i < length; i++) {
4328 array->set(i, frame->GetParameter(i), mode);
4329 }
4330 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004331 }
4332 return result;
4333}
4334
4335
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004336static Object* Runtime_NewArgumentsFast(Arguments args) {
4337 NoHandleAllocation ha;
4338 ASSERT(args.length() == 3);
4339
4340 JSFunction* callee = JSFunction::cast(args[0]);
4341 Object** parameters = reinterpret_cast<Object**>(args[1]);
4342 const int length = Smi::cast(args[2])->value();
4343
4344 Object* result = Heap::AllocateArgumentsObject(callee, length);
4345 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004346 ASSERT(Heap::InNewSpace(result));
4347
4348 // Allocate the elements if needed.
4349 if (length > 0) {
4350 // Allocate the fixed array.
4351 Object* obj = Heap::AllocateRawFixedArray(length);
4352 if (obj->IsFailure()) return obj;
4353 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4354 FixedArray* array = FixedArray::cast(obj);
4355 array->set_length(length);
4356 WriteBarrierMode mode = array->GetWriteBarrierMode();
4357 for (int i = 0; i < length; i++) {
4358 array->set(i, *--parameters, mode);
4359 }
4360 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
4361 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004362 }
4363 return result;
4364}
4365
4366
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004367static Object* Runtime_NewClosure(Arguments args) {
4368 HandleScope scope;
4369 ASSERT(args.length() == 2);
4370 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
4371 CONVERT_ARG_CHECKED(Context, context, 1);
4372
4373 Handle<JSFunction> result =
4374 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4375 return *result;
4376}
4377
4378
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004379static Code* ComputeConstructStub(Handle<SharedFunctionInfo> shared) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004380 // TODO(385): Change this to create a construct stub specialized for
4381 // the given map to make allocation of simple objects - and maybe
4382 // arrays - much faster.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004383 if (FLAG_inline_new
4384 && shared->has_only_simple_this_property_assignments()) {
4385 ConstructStubCompiler compiler;
4386 Object* code = compiler.CompileConstructStub(*shared);
4387 if (code->IsFailure()) {
4388 return Builtins::builtin(Builtins::JSConstructStubGeneric);
4389 }
4390 return Code::cast(code);
4391 }
4392
4393 return Builtins::builtin(Builtins::JSConstructStubGeneric);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004394}
4395
4396
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004397static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004398 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004399 ASSERT(args.length() == 1);
4400
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004401 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004402
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004403 // If the constructor isn't a proper function we throw a type error.
4404 if (!constructor->IsJSFunction()) {
4405 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4406 Handle<Object> type_error =
4407 Factory::NewTypeError("not_constructor", arguments);
4408 return Top::Throw(*type_error);
4409 }
4410
4411 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004412#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004413 // Handle stepping into constructors if step into is active.
4414 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004415 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004416 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004417#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004418
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004419 if (function->has_initial_map()) {
4420 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004421 // The 'Function' function ignores the receiver object when
4422 // called using 'new' and creates a new JSFunction object that
4423 // is returned. The receiver object is only used for error
4424 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004425 // JSFunction. Factory::NewJSObject() should not be used to
4426 // allocate JSFunctions since it does not properly initialize
4427 // the shared part of the function. Since the receiver is
4428 // ignored anyway, we use the global object as the receiver
4429 // instead of a new JSFunction object. This way, errors are
4430 // reported the same way whether or not 'Function' is called
4431 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004432 return Top::context()->global();
4433 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004434 }
4435
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004436 // The function should be compiled for the optimization hints to be available.
4437 if (!function->shared()->is_compiled()) {
4438 CompileLazyShared(Handle<SharedFunctionInfo>(function->shared()),
4439 CLEAR_EXCEPTION,
4440 0);
4441 }
4442
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004443 bool first_allocation = !function->has_initial_map();
4444 Handle<JSObject> result = Factory::NewJSObject(function);
4445 if (first_allocation) {
4446 Handle<Map> map = Handle<Map>(function->initial_map());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004447 Handle<Code> stub = Handle<Code>(
4448 ComputeConstructStub(Handle<SharedFunctionInfo>(function->shared())));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004449 function->shared()->set_construct_stub(*stub);
4450 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004451
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004452 Counters::constructed_objects.Increment();
4453 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004454
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004455 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004456}
4457
4458
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004459static Object* Runtime_LazyCompile(Arguments args) {
4460 HandleScope scope;
4461 ASSERT(args.length() == 1);
4462
4463 Handle<JSFunction> function = args.at<JSFunction>(0);
4464#ifdef DEBUG
4465 if (FLAG_trace_lazy) {
4466 PrintF("[lazy: ");
4467 function->shared()->name()->Print();
4468 PrintF("]\n");
4469 }
4470#endif
4471
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004472 // Compile the target function. Here we compile using CompileLazyInLoop in
4473 // order to get the optimized version. This helps code like delta-blue
4474 // that calls performance-critical routines through constructors. A
4475 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4476 // direct call. Since the in-loop tracking takes place through CallICs
4477 // this means that things called through constructors are never known to
4478 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004479 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004480 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004481 return Failure::Exception();
4482 }
4483
4484 return function->code();
4485}
4486
4487
4488static Object* Runtime_GetCalledFunction(Arguments args) {
4489 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004490 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004491 StackFrameIterator it;
4492 // Get past the JS-to-C exit frame.
4493 ASSERT(it.frame()->is_exit());
4494 it.Advance();
4495 // Get past the CALL_NON_FUNCTION activation frame.
4496 ASSERT(it.frame()->is_java_script());
4497 it.Advance();
4498 // Argument adaptor frames do not copy the function; we have to skip
4499 // past them to get to the real calling frame.
4500 if (it.frame()->is_arguments_adaptor()) it.Advance();
4501 // Get the function from the top of the expression stack of the
4502 // calling frame.
4503 StandardFrame* frame = StandardFrame::cast(it.frame());
4504 int index = frame->ComputeExpressionsCount() - 1;
4505 Object* result = frame->GetExpression(index);
4506 return result;
4507}
4508
4509
4510static Object* Runtime_GetFunctionDelegate(Arguments args) {
4511 HandleScope scope;
4512 ASSERT(args.length() == 1);
4513 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4514 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4515}
4516
4517
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004518static Object* Runtime_GetConstructorDelegate(Arguments args) {
4519 HandleScope scope;
4520 ASSERT(args.length() == 1);
4521 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4522 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4523}
4524
4525
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004526static Object* Runtime_NewContext(Arguments args) {
4527 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004528 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004529
kasper.lund7276f142008-07-30 08:49:36 +00004530 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004531 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4532 Object* result = Heap::AllocateFunctionContext(length, function);
4533 if (result->IsFailure()) return result;
4534
4535 Top::set_context(Context::cast(result));
4536
kasper.lund7276f142008-07-30 08:49:36 +00004537 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004538}
4539
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004540static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004541 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004542 Object* js_object = object;
4543 if (!js_object->IsJSObject()) {
4544 js_object = js_object->ToObject();
4545 if (js_object->IsFailure()) {
4546 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004547 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004548 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004549 Handle<Object> result =
4550 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4551 return Top::Throw(*result);
4552 }
4553 }
4554
4555 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004556 Heap::AllocateWithContext(Top::context(),
4557 JSObject::cast(js_object),
4558 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004559 if (result->IsFailure()) return result;
4560
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004561 Context* context = Context::cast(result);
4562 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004563
kasper.lund7276f142008-07-30 08:49:36 +00004564 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004565}
4566
4567
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004568static Object* Runtime_PushContext(Arguments args) {
4569 NoHandleAllocation ha;
4570 ASSERT(args.length() == 1);
4571 return PushContextHelper(args[0], false);
4572}
4573
4574
4575static Object* Runtime_PushCatchContext(Arguments args) {
4576 NoHandleAllocation ha;
4577 ASSERT(args.length() == 1);
4578 return PushContextHelper(args[0], true);
4579}
4580
4581
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004582static Object* Runtime_LookupContext(Arguments args) {
4583 HandleScope scope;
4584 ASSERT(args.length() == 2);
4585
4586 CONVERT_ARG_CHECKED(Context, context, 0);
4587 CONVERT_ARG_CHECKED(String, name, 1);
4588
4589 int index;
4590 PropertyAttributes attributes;
4591 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004592 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004593 context->Lookup(name, flags, &index, &attributes);
4594
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004595 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004596 ASSERT(holder->IsJSObject());
4597 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004598 }
4599
4600 // No intermediate context found. Use global object by default.
4601 return Top::context()->global();
4602}
4603
4604
ager@chromium.orga1645e22009-09-09 19:27:10 +00004605// A mechanism to return a pair of Object pointers in registers (if possible).
4606// How this is achieved is calling convention-dependent.
4607// All currently supported x86 compiles uses calling conventions that are cdecl
4608// variants where a 64-bit value is returned in two 32-bit registers
4609// (edx:eax on ia32, r1:r0 on ARM).
4610// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
4611// In Win64 calling convention, a struct of two pointers is returned in memory,
4612// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004613#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004614struct ObjectPair {
4615 Object* x;
4616 Object* y;
4617};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004618
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004619static inline ObjectPair MakePair(Object* x, Object* y) {
4620 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004621 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
4622 // In Win64 they are assigned to a hidden first argument.
4623 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004624}
4625#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004626typedef uint64_t ObjectPair;
4627static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004628 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004629 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004630}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004631#endif
4632
4633
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004634static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004635 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4636 USE(attributes);
4637 return x->IsTheHole() ? Heap::undefined_value() : x;
4638}
4639
4640
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004641static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4642 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004643 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004644 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004645 JSFunction* context_extension_function =
4646 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004647 // If the holder isn't a context extension object, we just return it
4648 // as the receiver. This allows arguments objects to be used as
4649 // receivers, but only if they are put in the context scope chain
4650 // explicitly via a with-statement.
4651 Object* constructor = holder->map()->constructor();
4652 if (constructor != context_extension_function) return holder;
4653 // Fall back to using the global object as the receiver if the
4654 // property turns out to be a local variable allocated in a context
4655 // extension object - introduced via eval.
4656 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004657}
4658
4659
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004660static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004661 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00004662 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004663
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004664 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004665 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004666 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004667 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004668 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004669
4670 int index;
4671 PropertyAttributes attributes;
4672 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004673 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004674 context->Lookup(name, flags, &index, &attributes);
4675
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004676 // If the index is non-negative, the slot has been found in a local
4677 // variable or a parameter. Read it from the context object or the
4678 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004679 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004680 // If the "property" we were looking for is a local variable or an
4681 // argument in a context, the receiver is the global object; see
4682 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4683 JSObject* receiver = Top::context()->global()->global_receiver();
4684 Object* value = (holder->IsContext())
4685 ? Context::cast(*holder)->get(index)
4686 : JSObject::cast(*holder)->GetElement(index);
4687 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004688 }
4689
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004690 // If the holder is found, we read the property from it.
4691 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004692 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004693 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004694 JSObject* receiver;
4695 if (object->IsGlobalObject()) {
4696 receiver = GlobalObject::cast(object)->global_receiver();
4697 } else if (context->is_exception_holder(*holder)) {
4698 receiver = Top::context()->global()->global_receiver();
4699 } else {
4700 receiver = ComputeReceiverForNonGlobal(object);
4701 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004702 // No need to unhole the value here. This is taken care of by the
4703 // GetProperty function.
4704 Object* value = object->GetProperty(*name);
4705 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004706 }
4707
4708 if (throw_error) {
4709 // The property doesn't exist - throw exception.
4710 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004711 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004712 return MakePair(Top::Throw(*reference_error), NULL);
4713 } else {
4714 // The property doesn't exist - return undefined
4715 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4716 }
4717}
4718
4719
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004720static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004721 return LoadContextSlotHelper(args, true);
4722}
4723
4724
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004725static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004726 return LoadContextSlotHelper(args, false);
4727}
4728
4729
4730static Object* Runtime_StoreContextSlot(Arguments args) {
4731 HandleScope scope;
4732 ASSERT(args.length() == 3);
4733
4734 Handle<Object> value(args[0]);
4735 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004736 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004737
4738 int index;
4739 PropertyAttributes attributes;
4740 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004741 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004742 context->Lookup(name, flags, &index, &attributes);
4743
4744 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004745 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004746 // Ignore if read_only variable.
4747 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004748 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004749 }
4750 } else {
4751 ASSERT((attributes & READ_ONLY) == 0);
4752 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004753 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004754 USE(result);
4755 ASSERT(!result->IsFailure());
4756 }
4757 return *value;
4758 }
4759
4760 // Slow case: The property is not in a FixedArray context.
4761 // It is either in an JSObject extension context or it was not found.
4762 Handle<JSObject> context_ext;
4763
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004764 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004765 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004766 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004767 } else {
4768 // The property was not found. It needs to be stored in the global context.
4769 ASSERT(attributes == ABSENT);
4770 attributes = NONE;
4771 context_ext = Handle<JSObject>(Top::context()->global());
4772 }
4773
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004774 // Set the property, but ignore if read_only variable on the context
4775 // extension object itself.
4776 if ((attributes & READ_ONLY) == 0 ||
4777 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004778 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4779 if (set.is_null()) {
4780 // Failure::Exception is converted to a null handle in the
4781 // handle-based methods such as SetProperty. We therefore need
4782 // to convert null handles back to exceptions.
4783 ASSERT(Top::has_pending_exception());
4784 return Failure::Exception();
4785 }
4786 }
4787 return *value;
4788}
4789
4790
4791static Object* Runtime_Throw(Arguments args) {
4792 HandleScope scope;
4793 ASSERT(args.length() == 1);
4794
4795 return Top::Throw(args[0]);
4796}
4797
4798
4799static Object* Runtime_ReThrow(Arguments args) {
4800 HandleScope scope;
4801 ASSERT(args.length() == 1);
4802
4803 return Top::ReThrow(args[0]);
4804}
4805
4806
4807static Object* Runtime_ThrowReferenceError(Arguments args) {
4808 HandleScope scope;
4809 ASSERT(args.length() == 1);
4810
4811 Handle<Object> name(args[0]);
4812 Handle<Object> reference_error =
4813 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4814 return Top::Throw(*reference_error);
4815}
4816
4817
4818static Object* Runtime_StackOverflow(Arguments args) {
4819 NoHandleAllocation na;
4820 return Top::StackOverflow();
4821}
4822
4823
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004824static Object* Runtime_StackGuard(Arguments args) {
4825 ASSERT(args.length() == 1);
4826
4827 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004828 if (StackGuard::IsStackOverflow()) {
4829 return Runtime_StackOverflow(args);
4830 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004831
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004832 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004833}
4834
4835
4836// NOTE: These PrintXXX functions are defined for all builds (not just
4837// DEBUG builds) because we may want to be able to trace function
4838// calls in all modes.
4839static void PrintString(String* str) {
4840 // not uncommon to have empty strings
4841 if (str->length() > 0) {
4842 SmartPointer<char> s =
4843 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4844 PrintF("%s", *s);
4845 }
4846}
4847
4848
4849static void PrintObject(Object* obj) {
4850 if (obj->IsSmi()) {
4851 PrintF("%d", Smi::cast(obj)->value());
4852 } else if (obj->IsString() || obj->IsSymbol()) {
4853 PrintString(String::cast(obj));
4854 } else if (obj->IsNumber()) {
4855 PrintF("%g", obj->Number());
4856 } else if (obj->IsFailure()) {
4857 PrintF("<failure>");
4858 } else if (obj->IsUndefined()) {
4859 PrintF("<undefined>");
4860 } else if (obj->IsNull()) {
4861 PrintF("<null>");
4862 } else if (obj->IsTrue()) {
4863 PrintF("<true>");
4864 } else if (obj->IsFalse()) {
4865 PrintF("<false>");
4866 } else {
4867 PrintF("%p", obj);
4868 }
4869}
4870
4871
4872static int StackSize() {
4873 int n = 0;
4874 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4875 return n;
4876}
4877
4878
4879static void PrintTransition(Object* result) {
4880 // indentation
4881 { const int nmax = 80;
4882 int n = StackSize();
4883 if (n <= nmax)
4884 PrintF("%4d:%*s", n, n, "");
4885 else
4886 PrintF("%4d:%*s", n, nmax, "...");
4887 }
4888
4889 if (result == NULL) {
4890 // constructor calls
4891 JavaScriptFrameIterator it;
4892 JavaScriptFrame* frame = it.frame();
4893 if (frame->IsConstructor()) PrintF("new ");
4894 // function name
4895 Object* fun = frame->function();
4896 if (fun->IsJSFunction()) {
4897 PrintObject(JSFunction::cast(fun)->shared()->name());
4898 } else {
4899 PrintObject(fun);
4900 }
4901 // function arguments
4902 // (we are intentionally only printing the actually
4903 // supplied parameters, not all parameters required)
4904 PrintF("(this=");
4905 PrintObject(frame->receiver());
4906 const int length = frame->GetProvidedParametersCount();
4907 for (int i = 0; i < length; i++) {
4908 PrintF(", ");
4909 PrintObject(frame->GetParameter(i));
4910 }
4911 PrintF(") {\n");
4912
4913 } else {
4914 // function result
4915 PrintF("} -> ");
4916 PrintObject(result);
4917 PrintF("\n");
4918 }
4919}
4920
4921
4922static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004923 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004924 NoHandleAllocation ha;
4925 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004926 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004927}
4928
4929
4930static Object* Runtime_TraceExit(Arguments args) {
4931 NoHandleAllocation ha;
4932 PrintTransition(args[0]);
4933 return args[0]; // return TOS
4934}
4935
4936
4937static Object* Runtime_DebugPrint(Arguments args) {
4938 NoHandleAllocation ha;
4939 ASSERT(args.length() == 1);
4940
4941#ifdef DEBUG
4942 if (args[0]->IsString()) {
4943 // If we have a string, assume it's a code "marker"
4944 // and print some interesting cpu debugging info.
4945 JavaScriptFrameIterator it;
4946 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004947 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
4948 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004949 } else {
4950 PrintF("DebugPrint: ");
4951 }
4952 args[0]->Print();
4953#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004954 // ShortPrint is available in release mode. Print is not.
4955 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004956#endif
4957 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004958 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004959
4960 return args[0]; // return TOS
4961}
4962
4963
4964static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004965 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004966 NoHandleAllocation ha;
4967 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004968 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004969}
4970
4971
mads.s.ager31e71382008-08-13 09:32:07 +00004972static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004973 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00004974 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004975
4976 // According to ECMA-262, section 15.9.1, page 117, the precision of
4977 // the number in a Date object representing a particular instant in
4978 // time is milliseconds. Therefore, we floor the result of getting
4979 // the OS time.
4980 double millis = floor(OS::TimeCurrentMillis());
4981 return Heap::NumberFromDouble(millis);
4982}
4983
4984
4985static Object* Runtime_DateParseString(Arguments args) {
4986 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004987 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004988
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004989 CONVERT_ARG_CHECKED(String, str, 0);
4990 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004991
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004992 CONVERT_ARG_CHECKED(JSArray, output, 1);
4993 RUNTIME_ASSERT(output->HasFastElements());
4994
4995 AssertNoAllocation no_allocation;
4996
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00004997 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004998 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
4999 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005000 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005001 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005002 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005003 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005004 result = DateParser::Parse(str->ToUC16Vector(), output_array);
5005 }
5006
5007 if (result) {
5008 return *output;
5009 } else {
5010 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005011 }
5012}
5013
5014
5015static Object* Runtime_DateLocalTimezone(Arguments args) {
5016 NoHandleAllocation ha;
5017 ASSERT(args.length() == 1);
5018
5019 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00005020 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005021 return Heap::AllocateStringFromUtf8(CStrVector(zone));
5022}
5023
5024
5025static Object* Runtime_DateLocalTimeOffset(Arguments args) {
5026 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005027 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005028
5029 return Heap::NumberFromDouble(OS::LocalTimeOffset());
5030}
5031
5032
5033static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
5034 NoHandleAllocation ha;
5035 ASSERT(args.length() == 1);
5036
5037 CONVERT_DOUBLE_CHECKED(x, args[0]);
5038 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
5039}
5040
5041
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005042static Object* Runtime_NumberIsFinite(Arguments args) {
5043 NoHandleAllocation ha;
5044 ASSERT(args.length() == 1);
5045
5046 CONVERT_DOUBLE_CHECKED(value, args[0]);
5047 Object* result;
5048 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
5049 result = Heap::false_value();
5050 } else {
5051 result = Heap::true_value();
5052 }
5053 return result;
5054}
5055
5056
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005057static Object* Runtime_GlobalReceiver(Arguments args) {
5058 ASSERT(args.length() == 1);
5059 Object* global = args[0];
5060 if (!global->IsJSGlobalObject()) return Heap::null_value();
5061 return JSGlobalObject::cast(global)->global_receiver();
5062}
5063
5064
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005065static Object* Runtime_CompileString(Arguments args) {
5066 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005067 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005068 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005069 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005070
ager@chromium.org381abbb2009-02-25 13:23:22 +00005071 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005072 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005073 Compiler::ValidationState validate = (is_json->IsTrue())
5074 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005075 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
5076 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005077 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005078 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005079 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005080 Handle<JSFunction> fun =
5081 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5082 return *fun;
5083}
5084
5085
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005086static Handle<JSFunction> GetBuiltinFunction(String* name) {
5087 LookupResult result;
5088 Top::global_context()->builtins()->LocalLookup(name, &result);
5089 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
5090}
5091
5092
5093static Object* CompileDirectEval(Handle<String> source) {
5094 // Compute the eval context.
5095 HandleScope scope;
5096 StackFrameLocator locator;
5097 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5098 Handle<Context> context(Context::cast(frame->context()));
5099 bool is_global = context->IsGlobalContext();
5100
ager@chromium.org381abbb2009-02-25 13:23:22 +00005101 // Compile source string in the current context.
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005102 Handle<JSFunction> boilerplate = Compiler::CompileEval(
5103 source,
5104 context,
5105 is_global,
5106 Compiler::DONT_VALIDATE_JSON);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005107 if (boilerplate.is_null()) return Failure::Exception();
5108 Handle<JSFunction> fun =
5109 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5110 return *fun;
5111}
5112
5113
5114static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
5115 ASSERT(args.length() == 2);
5116
5117 HandleScope scope;
5118
5119 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5120
5121 Handle<Object> receiver;
5122
5123 // Find where the 'eval' symbol is bound. It is unaliased only if
5124 // it is bound in the global context.
5125 StackFrameLocator locator;
5126 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5127 Handle<Context> context(Context::cast(frame->context()));
5128 int index;
5129 PropertyAttributes attributes;
5130 while (!context.is_null()) {
5131 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5132 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005133 // Stop search when eval is found or when the global context is
5134 // reached.
5135 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005136 if (context->is_function_context()) {
5137 context = Handle<Context>(Context::cast(context->closure()->context()));
5138 } else {
5139 context = Handle<Context>(context->previous());
5140 }
5141 }
5142
iposva@chromium.org245aa852009-02-10 00:49:54 +00005143 // If eval could not be resolved, it has been deleted and we need to
5144 // throw a reference error.
5145 if (attributes == ABSENT) {
5146 Handle<Object> name = Factory::eval_symbol();
5147 Handle<Object> reference_error =
5148 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5149 return Top::Throw(*reference_error);
5150 }
5151
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005152 if (context->IsGlobalContext()) {
5153 // 'eval' is bound in the global context, but it may have been overwritten.
5154 // Compare it to the builtin 'GlobalEval' function to make sure.
5155 Handle<JSFunction> global_eval =
5156 GetBuiltinFunction(Heap::global_eval_symbol());
5157 if (global_eval.is_identical_to(callee)) {
5158 // A direct eval call.
5159 if (args[1]->IsString()) {
5160 CONVERT_ARG_CHECKED(String, source, 1);
5161 // A normal eval call on a string. Compile it and return the
5162 // compiled function bound in the local context.
5163 Object* compiled_source = CompileDirectEval(source);
5164 if (compiled_source->IsFailure()) return compiled_source;
5165 receiver = Handle<Object>(frame->receiver());
5166 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5167 } else {
5168 // An eval call that is not called on a string. Global eval
5169 // deals better with this.
5170 receiver = Handle<Object>(Top::global_context()->global());
5171 }
5172 } else {
5173 // 'eval' is overwritten. Just call the function with the given arguments.
5174 receiver = Handle<Object>(Top::global_context()->global());
5175 }
5176 } else {
5177 // 'eval' is not bound in the global context. Just call the function
5178 // with the given arguments. This is not necessarily the global eval.
5179 if (receiver->IsContext()) {
5180 context = Handle<Context>::cast(receiver);
5181 receiver = Handle<Object>(context->get(index));
5182 }
5183 }
5184
5185 Handle<FixedArray> call = Factory::NewFixedArray(2);
5186 call->set(0, *callee);
5187 call->set(1, *receiver);
5188 return *call;
5189}
5190
5191
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005192static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5193 // This utility adjusts the property attributes for newly created Function
5194 // object ("new Function(...)") by changing the map.
5195 // All it does is changing the prototype property to enumerable
5196 // as specified in ECMA262, 15.3.5.2.
5197 HandleScope scope;
5198 ASSERT(args.length() == 1);
5199 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5200 ASSERT(func->map()->instance_type() ==
5201 Top::function_instance_map()->instance_type());
5202 ASSERT(func->map()->instance_size() ==
5203 Top::function_instance_map()->instance_size());
5204 func->set_map(*Top::function_instance_map());
5205 return *func;
5206}
5207
5208
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005209// Push an array unto an array of arrays if it is not already in the
5210// array. Returns true if the element was pushed on the stack and
5211// false otherwise.
5212static Object* Runtime_PushIfAbsent(Arguments args) {
5213 ASSERT(args.length() == 2);
5214 CONVERT_CHECKED(JSArray, array, args[0]);
5215 CONVERT_CHECKED(JSArray, element, args[1]);
5216 RUNTIME_ASSERT(array->HasFastElements());
5217 int length = Smi::cast(array->length())->value();
5218 FixedArray* elements = FixedArray::cast(array->elements());
5219 for (int i = 0; i < length; i++) {
5220 if (elements->get(i) == element) return Heap::false_value();
5221 }
5222 Object* obj = array->SetFastElement(length, element);
5223 if (obj->IsFailure()) return obj;
5224 return Heap::true_value();
5225}
5226
5227
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005228/**
5229 * A simple visitor visits every element of Array's.
5230 * The backend storage can be a fixed array for fast elements case,
5231 * or a dictionary for sparse array. Since Dictionary is a subtype
5232 * of FixedArray, the class can be used by both fast and slow cases.
5233 * The second parameter of the constructor, fast_elements, specifies
5234 * whether the storage is a FixedArray or Dictionary.
5235 *
5236 * An index limit is used to deal with the situation that a result array
5237 * length overflows 32-bit non-negative integer.
5238 */
5239class ArrayConcatVisitor {
5240 public:
5241 ArrayConcatVisitor(Handle<FixedArray> storage,
5242 uint32_t index_limit,
5243 bool fast_elements) :
5244 storage_(storage), index_limit_(index_limit),
5245 fast_elements_(fast_elements), index_offset_(0) { }
5246
5247 void visit(uint32_t i, Handle<Object> elm) {
5248 uint32_t index = i + index_offset_;
5249 if (index >= index_limit_) return;
5250
5251 if (fast_elements_) {
5252 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5253 storage_->set(index, *elm);
5254
5255 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005256 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5257 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005258 Factory::DictionaryAtNumberPut(dict, index, elm);
5259 if (!result.is_identical_to(dict))
5260 storage_ = result;
5261 }
5262 }
5263
5264 void increase_index_offset(uint32_t delta) {
5265 index_offset_ += delta;
5266 }
5267
5268 private:
5269 Handle<FixedArray> storage_;
5270 uint32_t index_limit_;
5271 bool fast_elements_;
5272 uint32_t index_offset_;
5273};
5274
5275
5276/**
5277 * A helper function that visits elements of a JSObject. Only elements
5278 * whose index between 0 and range (exclusive) are visited.
5279 *
5280 * If the third parameter, visitor, is not NULL, the visitor is called
5281 * with parameters, 'visitor_index_offset + element index' and the element.
5282 *
5283 * It returns the number of visisted elements.
5284 */
5285static uint32_t IterateElements(Handle<JSObject> receiver,
5286 uint32_t range,
5287 ArrayConcatVisitor* visitor) {
5288 uint32_t num_of_elements = 0;
5289
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005290 switch (receiver->GetElementsKind()) {
5291 case JSObject::FAST_ELEMENTS: {
5292 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5293 uint32_t len = elements->length();
5294 if (range < len) {
5295 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005296 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005297
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005298 for (uint32_t j = 0; j < len; j++) {
5299 Handle<Object> e(elements->get(j));
5300 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005301 num_of_elements++;
5302 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005303 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005304 }
5305 }
5306 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005307 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005308 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005309 case JSObject::PIXEL_ELEMENTS: {
5310 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
5311 uint32_t len = pixels->length();
5312 if (range < len) {
5313 len = range;
5314 }
5315
5316 for (uint32_t j = 0; j < len; j++) {
5317 num_of_elements++;
5318 if (visitor != NULL) {
5319 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
5320 visitor->visit(j, e);
5321 }
5322 }
5323 break;
5324 }
5325 case JSObject::DICTIONARY_ELEMENTS: {
5326 Handle<NumberDictionary> dict(receiver->element_dictionary());
5327 uint32_t capacity = dict->Capacity();
5328 for (uint32_t j = 0; j < capacity; j++) {
5329 Handle<Object> k(dict->KeyAt(j));
5330 if (dict->IsKey(*k)) {
5331 ASSERT(k->IsNumber());
5332 uint32_t index = static_cast<uint32_t>(k->Number());
5333 if (index < range) {
5334 num_of_elements++;
5335 if (visitor) {
5336 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
5337 }
5338 }
5339 }
5340 }
5341 break;
5342 }
5343 default:
5344 UNREACHABLE();
5345 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005346 }
5347
5348 return num_of_elements;
5349}
5350
5351
5352/**
5353 * A helper function that visits elements of an Array object, and elements
5354 * on its prototypes.
5355 *
5356 * Elements on prototypes are visited first, and only elements whose indices
5357 * less than Array length are visited.
5358 *
5359 * If a ArrayConcatVisitor object is given, the visitor is called with
5360 * parameters, element's index + visitor_index_offset and the element.
5361 */
5362static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5363 ArrayConcatVisitor* visitor) {
5364 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5365 Handle<Object> obj = array;
5366
5367 static const int kEstimatedPrototypes = 3;
5368 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5369
5370 // Visit prototype first. If an element on the prototype is shadowed by
5371 // the inheritor using the same index, the ArrayConcatVisitor visits
5372 // the prototype element before the shadowing element.
5373 // The visitor can simply overwrite the old value by new value using
5374 // the same index. This follows Array::concat semantics.
5375 while (!obj->IsNull()) {
5376 objects.Add(Handle<JSObject>::cast(obj));
5377 obj = Handle<Object>(obj->GetPrototype());
5378 }
5379
5380 uint32_t nof_elements = 0;
5381 for (int i = objects.length() - 1; i >= 0; i--) {
5382 Handle<JSObject> obj = objects[i];
5383 nof_elements +=
5384 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5385 }
5386
5387 return nof_elements;
5388}
5389
5390
5391/**
5392 * A helper function of Runtime_ArrayConcat.
5393 *
5394 * The first argument is an Array of arrays and objects. It is the
5395 * same as the arguments array of Array::concat JS function.
5396 *
5397 * If an argument is an Array object, the function visits array
5398 * elements. If an argument is not an Array object, the function
5399 * visits the object as if it is an one-element array.
5400 *
5401 * If the result array index overflows 32-bit integer, the rounded
5402 * non-negative number is used as new length. For example, if one
5403 * array length is 2^32 - 1, second array length is 1, the
5404 * concatenated array length is 0.
5405 */
5406static uint32_t IterateArguments(Handle<JSArray> arguments,
5407 ArrayConcatVisitor* visitor) {
5408 uint32_t visited_elements = 0;
5409 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5410
5411 for (uint32_t i = 0; i < num_of_args; i++) {
5412 Handle<Object> obj(arguments->GetElement(i));
5413 if (obj->IsJSArray()) {
5414 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5415 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5416 uint32_t nof_elements =
5417 IterateArrayAndPrototypeElements(array, visitor);
5418 // Total elements of array and its prototype chain can be more than
5419 // the array length, but ArrayConcat can only concatenate at most
5420 // the array length number of elements.
5421 visited_elements += (nof_elements > len) ? len : nof_elements;
5422 if (visitor) visitor->increase_index_offset(len);
5423
5424 } else {
5425 if (visitor) {
5426 visitor->visit(0, obj);
5427 visitor->increase_index_offset(1);
5428 }
5429 visited_elements++;
5430 }
5431 }
5432 return visited_elements;
5433}
5434
5435
5436/**
5437 * Array::concat implementation.
5438 * See ECMAScript 262, 15.4.4.4.
5439 */
5440static Object* Runtime_ArrayConcat(Arguments args) {
5441 ASSERT(args.length() == 1);
5442 HandleScope handle_scope;
5443
5444 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5445 Handle<JSArray> arguments(arg_arrays);
5446
5447 // Pass 1: estimate the number of elements of the result
5448 // (it could be more than real numbers if prototype has elements).
5449 uint32_t result_length = 0;
5450 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5451
5452 { AssertNoAllocation nogc;
5453 for (uint32_t i = 0; i < num_of_args; i++) {
5454 Object* obj = arguments->GetElement(i);
5455 if (obj->IsJSArray()) {
5456 result_length +=
5457 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5458 } else {
5459 result_length++;
5460 }
5461 }
5462 }
5463
5464 // Allocate an empty array, will set length and content later.
5465 Handle<JSArray> result = Factory::NewJSArray(0);
5466
5467 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5468 // If estimated number of elements is more than half of length, a
5469 // fixed array (fast case) is more time and space-efficient than a
5470 // dictionary.
5471 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5472
5473 Handle<FixedArray> storage;
5474 if (fast_case) {
5475 // The backing storage array must have non-existing elements to
5476 // preserve holes across concat operations.
5477 storage = Factory::NewFixedArrayWithHoles(result_length);
5478
5479 } else {
5480 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5481 uint32_t at_least_space_for = estimate_nof_elements +
5482 (estimate_nof_elements >> 2);
5483 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005484 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005485 }
5486
5487 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5488
5489 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5490
5491 IterateArguments(arguments, &visitor);
5492
5493 result->set_length(*len);
5494 result->set_elements(*storage);
5495
5496 return *result;
5497}
5498
5499
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005500// This will not allocate (flatten the string), but it may run
5501// very slowly for very deeply nested ConsStrings. For debugging use only.
5502static Object* Runtime_GlobalPrint(Arguments args) {
5503 NoHandleAllocation ha;
5504 ASSERT(args.length() == 1);
5505
5506 CONVERT_CHECKED(String, string, args[0]);
5507 StringInputBuffer buffer(string);
5508 while (buffer.has_more()) {
5509 uint16_t character = buffer.GetNext();
5510 PrintF("%c", character);
5511 }
5512 return string;
5513}
5514
ager@chromium.org5ec48922009-05-05 07:25:34 +00005515// Moves all own elements of an object, that are below a limit, to positions
5516// starting at zero. All undefined values are placed after non-undefined values,
5517// and are followed by non-existing element. Does not change the length
5518// property.
5519// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005520static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005521 ASSERT(args.length() == 2);
5522 CONVERT_CHECKED(JSObject, object, args[0]);
5523 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5524 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005525}
5526
5527
5528// Move contents of argument 0 (an array) to argument 1 (an array)
5529static Object* Runtime_MoveArrayContents(Arguments args) {
5530 ASSERT(args.length() == 2);
5531 CONVERT_CHECKED(JSArray, from, args[0]);
5532 CONVERT_CHECKED(JSArray, to, args[1]);
5533 to->SetContent(FixedArray::cast(from->elements()));
5534 to->set_length(from->length());
5535 from->SetContent(Heap::empty_fixed_array());
5536 from->set_length(0);
5537 return to;
5538}
5539
5540
5541// How many elements does this array have?
5542static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5543 ASSERT(args.length() == 1);
5544 CONVERT_CHECKED(JSArray, array, args[0]);
5545 HeapObject* elements = array->elements();
5546 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005547 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005548 } else {
5549 return array->length();
5550 }
5551}
5552
5553
5554// Returns an array that tells you where in the [0, length) interval an array
5555// might have elements. Can either return keys or intervals. Keys can have
5556// gaps in (undefined). Intervals can also span over some undefined keys.
5557static Object* Runtime_GetArrayKeys(Arguments args) {
5558 ASSERT(args.length() == 2);
5559 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005560 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005561 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005562 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005563 // Create an array and get all the keys into it, then remove all the
5564 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005565 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005566 int keys_length = keys->length();
5567 for (int i = 0; i < keys_length; i++) {
5568 Object* key = keys->get(i);
5569 uint32_t index;
5570 if (!Array::IndexFromObject(key, &index) || index >= length) {
5571 // Zap invalid keys.
5572 keys->set_undefined(i);
5573 }
5574 }
5575 return *Factory::NewJSArrayWithElements(keys);
5576 } else {
5577 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5578 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005579 single_interval->set(0,
5580 Smi::FromInt(-1),
5581 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005582 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5583 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005584 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005585 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005586 single_interval->set(1, *length_object);
5587 return *Factory::NewJSArrayWithElements(single_interval);
5588 }
5589}
5590
5591
5592// DefineAccessor takes an optional final argument which is the
5593// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5594// to the way accessors are implemented, it is set for both the getter
5595// and setter on the first call to DefineAccessor and ignored on
5596// subsequent calls.
5597static Object* Runtime_DefineAccessor(Arguments args) {
5598 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5599 // Compute attributes.
5600 PropertyAttributes attributes = NONE;
5601 if (args.length() == 5) {
5602 CONVERT_CHECKED(Smi, attrs, args[4]);
5603 int value = attrs->value();
5604 // Only attribute bits should be set.
5605 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5606 attributes = static_cast<PropertyAttributes>(value);
5607 }
5608
5609 CONVERT_CHECKED(JSObject, obj, args[0]);
5610 CONVERT_CHECKED(String, name, args[1]);
5611 CONVERT_CHECKED(Smi, flag, args[2]);
5612 CONVERT_CHECKED(JSFunction, fun, args[3]);
5613 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5614}
5615
5616
5617static Object* Runtime_LookupAccessor(Arguments args) {
5618 ASSERT(args.length() == 3);
5619 CONVERT_CHECKED(JSObject, obj, args[0]);
5620 CONVERT_CHECKED(String, name, args[1]);
5621 CONVERT_CHECKED(Smi, flag, args[2]);
5622 return obj->LookupAccessor(name, flag->value() == 0);
5623}
5624
5625
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005626#ifdef ENABLE_DEBUGGER_SUPPORT
5627static Object* Runtime_DebugBreak(Arguments args) {
5628 ASSERT(args.length() == 0);
5629 return Execution::DebugBreakHelper();
5630}
5631
5632
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005633// Helper functions for wrapping and unwrapping stack frame ids.
5634static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005635 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005636 return Smi::FromInt(id >> 2);
5637}
5638
5639
5640static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5641 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5642}
5643
5644
5645// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005646// args[0]: debug event listener function to set or null or undefined for
5647// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005648// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005649static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005650 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005651 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5652 args[0]->IsUndefined() ||
5653 args[0]->IsNull());
5654 Handle<Object> callback = args.at<Object>(0);
5655 Handle<Object> data = args.at<Object>(1);
5656 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005657
5658 return Heap::undefined_value();
5659}
5660
5661
5662static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005663 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005664 StackGuard::DebugBreak();
5665 return Heap::undefined_value();
5666}
5667
5668
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005669// Find the length of the prototype chain that is to to handled as one. If a
5670// prototype object is hidden it is to be viewed as part of the the object it
5671// is prototype for.
5672static int LocalPrototypeChainLength(JSObject* obj) {
5673 int count = 1;
5674 Object* proto = obj->GetPrototype();
5675 while (proto->IsJSObject() &&
5676 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5677 count++;
5678 proto = JSObject::cast(proto)->GetPrototype();
5679 }
5680 return count;
5681}
5682
5683
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005684static Object* DebugLookupResultValue(Object* receiver, String* name,
5685 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005686 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005687 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005688 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005689 case NORMAL:
5690 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005691 if (value->IsTheHole()) {
5692 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005693 }
5694 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005695 case FIELD:
5696 value =
5697 JSObject::cast(
5698 result->holder())->FastPropertyAt(result->GetFieldIndex());
5699 if (value->IsTheHole()) {
5700 return Heap::undefined_value();
5701 }
5702 return value;
5703 case CONSTANT_FUNCTION:
5704 return result->GetConstantFunction();
5705 case CALLBACKS: {
5706 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005707 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005708 value = receiver->GetPropertyWithCallback(
5709 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005710 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005711 value = Top::pending_exception();
5712 Top::clear_pending_exception();
5713 if (caught_exception != NULL) {
5714 *caught_exception = true;
5715 }
5716 }
5717 return value;
5718 } else {
5719 return Heap::undefined_value();
5720 }
5721 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005722 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005723 case MAP_TRANSITION:
5724 case CONSTANT_TRANSITION:
5725 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005726 return Heap::undefined_value();
5727 default:
5728 UNREACHABLE();
5729 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005730 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005731 return Heap::undefined_value();
5732}
5733
5734
ager@chromium.org32912102009-01-16 10:38:43 +00005735// Get debugger related details for an object property.
5736// args[0]: object holding property
5737// args[1]: name of the property
5738//
5739// The array returned contains the following information:
5740// 0: Property value
5741// 1: Property details
5742// 2: Property value is exception
5743// 3: Getter function if defined
5744// 4: Setter function if defined
5745// Items 2-4 are only filled if the property has either a getter or a setter
5746// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005747static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005748 HandleScope scope;
5749
5750 ASSERT(args.length() == 2);
5751
5752 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5753 CONVERT_ARG_CHECKED(String, name, 1);
5754
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005755 // Make sure to set the current context to the context before the debugger was
5756 // entered (if the debugger is entered). The reason for switching context here
5757 // is that for some property lookups (accessors and interceptors) callbacks
5758 // into the embedding application can occour, and the embedding application
5759 // could have the assumption that its own global context is the current
5760 // context and not some internal debugger context.
5761 SaveContext save;
5762 if (Debug::InDebugger()) {
5763 Top::set_context(*Debug::debugger_entry()->GetContext());
5764 }
5765
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005766 // Skip the global proxy as it has no properties and always delegates to the
5767 // real global object.
5768 if (obj->IsJSGlobalProxy()) {
5769 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5770 }
5771
5772
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005773 // Check if the name is trivially convertible to an index and get the element
5774 // if so.
5775 uint32_t index;
5776 if (name->AsArrayIndex(&index)) {
5777 Handle<FixedArray> details = Factory::NewFixedArray(2);
5778 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5779 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5780 return *Factory::NewJSArrayWithElements(details);
5781 }
5782
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005783 // Find the number of objects making up this.
5784 int length = LocalPrototypeChainLength(*obj);
5785
5786 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005787 Handle<JSObject> jsproto = obj;
5788 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005789 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005790 jsproto->LocalLookup(*name, &result);
5791 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005792 // LookupResult is not GC safe as it holds raw object pointers.
5793 // GC can happen later in this code so put the required fields into
5794 // local variables using handles when required for later use.
5795 PropertyType result_type = result.type();
5796 Handle<Object> result_callback_obj;
5797 if (result_type == CALLBACKS) {
5798 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5799 }
5800 Smi* property_details = result.GetPropertyDetails().AsSmi();
5801 // DebugLookupResultValue can cause GC so details from LookupResult needs
5802 // to be copied to handles before this.
5803 bool caught_exception = false;
5804 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
5805 &caught_exception);
5806 if (raw_value->IsFailure()) return raw_value;
5807 Handle<Object> value(raw_value);
5808
5809 // If the callback object is a fixed array then it contains JavaScript
5810 // getter and/or setter.
5811 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5812 result_callback_obj->IsFixedArray();
5813 Handle<FixedArray> details =
5814 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
5815 details->set(0, *value);
5816 details->set(1, property_details);
5817 if (hasJavaScriptAccessors) {
5818 details->set(2,
5819 caught_exception ? Heap::true_value()
5820 : Heap::false_value());
5821 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
5822 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
5823 }
5824
5825 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005826 }
5827 if (i < length - 1) {
5828 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5829 }
5830 }
5831
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005832 return Heap::undefined_value();
5833}
5834
5835
5836static Object* Runtime_DebugGetProperty(Arguments args) {
5837 HandleScope scope;
5838
5839 ASSERT(args.length() == 2);
5840
5841 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5842 CONVERT_ARG_CHECKED(String, name, 1);
5843
5844 LookupResult result;
5845 obj->Lookup(*name, &result);
5846 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005847 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005848 }
5849 return Heap::undefined_value();
5850}
5851
5852
5853// Return the names of the local named properties.
5854// args[0]: object
5855static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5856 HandleScope scope;
5857 ASSERT(args.length() == 1);
5858 if (!args[0]->IsJSObject()) {
5859 return Heap::undefined_value();
5860 }
5861 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5862
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005863 // Skip the global proxy as it has no properties and always delegates to the
5864 // real global object.
5865 if (obj->IsJSGlobalProxy()) {
5866 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5867 }
5868
5869 // Find the number of objects making up this.
5870 int length = LocalPrototypeChainLength(*obj);
5871
5872 // Find the number of local properties for each of the objects.
5873 int* local_property_count = NewArray<int>(length);
5874 int total_property_count = 0;
5875 Handle<JSObject> jsproto = obj;
5876 for (int i = 0; i < length; i++) {
5877 int n;
5878 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5879 local_property_count[i] = n;
5880 total_property_count += n;
5881 if (i < length - 1) {
5882 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5883 }
5884 }
5885
5886 // Allocate an array with storage for all the property names.
5887 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
5888
5889 // Get the property names.
5890 jsproto = obj;
5891 for (int i = 0; i < length; i++) {
5892 jsproto->GetLocalPropertyNames(*names,
5893 i == 0 ? 0 : local_property_count[i - 1]);
5894 if (i < length - 1) {
5895 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5896 }
5897 }
5898
5899 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005900 return *Factory::NewJSArrayWithElements(names);
5901}
5902
5903
5904// Return the names of the local indexed properties.
5905// args[0]: object
5906static Object* Runtime_DebugLocalElementNames(Arguments args) {
5907 HandleScope scope;
5908 ASSERT(args.length() == 1);
5909 if (!args[0]->IsJSObject()) {
5910 return Heap::undefined_value();
5911 }
5912 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5913
5914 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
5915 Handle<FixedArray> names = Factory::NewFixedArray(n);
5916 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
5917 return *Factory::NewJSArrayWithElements(names);
5918}
5919
5920
5921// Return the property type calculated from the property details.
5922// args[0]: smi with property details.
5923static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
5924 ASSERT(args.length() == 1);
5925 CONVERT_CHECKED(Smi, details, args[0]);
5926 PropertyType type = PropertyDetails(details).type();
5927 return Smi::FromInt(static_cast<int>(type));
5928}
5929
5930
5931// Return the property attribute calculated from the property details.
5932// args[0]: smi with property details.
5933static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
5934 ASSERT(args.length() == 1);
5935 CONVERT_CHECKED(Smi, details, args[0]);
5936 PropertyAttributes attributes = PropertyDetails(details).attributes();
5937 return Smi::FromInt(static_cast<int>(attributes));
5938}
5939
5940
5941// Return the property insertion index calculated from the property details.
5942// args[0]: smi with property details.
5943static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
5944 ASSERT(args.length() == 1);
5945 CONVERT_CHECKED(Smi, details, args[0]);
5946 int index = PropertyDetails(details).index();
5947 return Smi::FromInt(index);
5948}
5949
5950
5951// Return information on whether an object has a named or indexed interceptor.
5952// args[0]: object
5953static Object* Runtime_DebugInterceptorInfo(Arguments args) {
5954 HandleScope scope;
5955 ASSERT(args.length() == 1);
5956 if (!args[0]->IsJSObject()) {
5957 return Smi::FromInt(0);
5958 }
5959 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5960
5961 int result = 0;
5962 if (obj->HasNamedInterceptor()) result |= 2;
5963 if (obj->HasIndexedInterceptor()) result |= 1;
5964
5965 return Smi::FromInt(result);
5966}
5967
5968
5969// Return property names from named interceptor.
5970// args[0]: object
5971static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
5972 HandleScope scope;
5973 ASSERT(args.length() == 1);
5974 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005975
ager@chromium.org32912102009-01-16 10:38:43 +00005976 if (obj->HasNamedInterceptor()) {
5977 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
5978 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5979 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005980 return Heap::undefined_value();
5981}
5982
5983
5984// Return element names from indexed interceptor.
5985// args[0]: object
5986static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
5987 HandleScope scope;
5988 ASSERT(args.length() == 1);
5989 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005990
ager@chromium.org32912102009-01-16 10:38:43 +00005991 if (obj->HasIndexedInterceptor()) {
5992 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
5993 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
5994 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005995 return Heap::undefined_value();
5996}
5997
5998
5999// Return property value from named interceptor.
6000// args[0]: object
6001// args[1]: property name
6002static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
6003 HandleScope scope;
6004 ASSERT(args.length() == 2);
6005 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6006 RUNTIME_ASSERT(obj->HasNamedInterceptor());
6007 CONVERT_ARG_CHECKED(String, name, 1);
6008
6009 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006010 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006011}
6012
6013
6014// Return element value from indexed interceptor.
6015// args[0]: object
6016// args[1]: index
6017static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
6018 HandleScope scope;
6019 ASSERT(args.length() == 2);
6020 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6021 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
6022 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
6023
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006024 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006025}
6026
6027
6028static Object* Runtime_CheckExecutionState(Arguments args) {
6029 ASSERT(args.length() >= 1);
6030 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00006031 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006032 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006033 return Top::Throw(Heap::illegal_execution_state_symbol());
6034 }
6035
6036 return Heap::true_value();
6037}
6038
6039
6040static Object* Runtime_GetFrameCount(Arguments args) {
6041 HandleScope scope;
6042 ASSERT(args.length() == 1);
6043
6044 // Check arguments.
6045 Object* result = Runtime_CheckExecutionState(args);
6046 if (result->IsFailure()) return result;
6047
6048 // Count all frames which are relevant to debugging stack trace.
6049 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006050 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006051 if (id == StackFrame::NO_ID) {
6052 // If there is no JavaScript stack frame count is 0.
6053 return Smi::FromInt(0);
6054 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006055 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
6056 return Smi::FromInt(n);
6057}
6058
6059
6060static const int kFrameDetailsFrameIdIndex = 0;
6061static const int kFrameDetailsReceiverIndex = 1;
6062static const int kFrameDetailsFunctionIndex = 2;
6063static const int kFrameDetailsArgumentCountIndex = 3;
6064static const int kFrameDetailsLocalCountIndex = 4;
6065static const int kFrameDetailsSourcePositionIndex = 5;
6066static const int kFrameDetailsConstructCallIndex = 6;
6067static const int kFrameDetailsDebuggerFrameIndex = 7;
6068static const int kFrameDetailsFirstDynamicIndex = 8;
6069
6070// Return an array with frame details
6071// args[0]: number: break id
6072// args[1]: number: frame index
6073//
6074// The array returned contains the following information:
6075// 0: Frame id
6076// 1: Receiver
6077// 2: Function
6078// 3: Argument count
6079// 4: Local count
6080// 5: Source position
6081// 6: Constructor call
6082// 7: Debugger frame
6083// Arguments name, value
6084// Locals name, value
6085static Object* Runtime_GetFrameDetails(Arguments args) {
6086 HandleScope scope;
6087 ASSERT(args.length() == 2);
6088
6089 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006090 Object* check = Runtime_CheckExecutionState(args);
6091 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006092 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6093
6094 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006095 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006096 if (id == StackFrame::NO_ID) {
6097 // If there are no JavaScript stack frames return undefined.
6098 return Heap::undefined_value();
6099 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006100 int count = 0;
6101 JavaScriptFrameIterator it(id);
6102 for (; !it.done(); it.Advance()) {
6103 if (count == index) break;
6104 count++;
6105 }
6106 if (it.done()) return Heap::undefined_value();
6107
6108 // Traverse the saved contexts chain to find the active context for the
6109 // selected frame.
6110 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006111 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006112 save = save->prev();
6113 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006114 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006115
6116 // Get the frame id.
6117 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6118
6119 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006120 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006121
6122 // Check for constructor frame.
6123 bool constructor = it.frame()->IsConstructor();
6124
6125 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006126 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006127 ScopeInfo<> info(*code);
6128
6129 // Get the context.
6130 Handle<Context> context(Context::cast(it.frame()->context()));
6131
6132 // Get the locals names and values into a temporary array.
6133 //
6134 // TODO(1240907): Hide compiler-introduced stack variables
6135 // (e.g. .result)? For users of the debugger, they will probably be
6136 // confusing.
6137 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6138 for (int i = 0; i < info.NumberOfLocals(); i++) {
6139 // Name of the local.
6140 locals->set(i * 2, *info.LocalName(i));
6141
6142 // Fetch the value of the local - either from the stack or from a
6143 // heap-allocated context.
6144 if (i < info.number_of_stack_slots()) {
6145 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6146 } else {
6147 Handle<String> name = info.LocalName(i);
6148 // Traverse the context chain to the function context as all local
6149 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006150 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006151 context = Handle<Context>(context->previous());
6152 }
6153 ASSERT(context->is_function_context());
6154 locals->set(i * 2 + 1,
6155 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6156 NULL)));
6157 }
6158 }
6159
6160 // Now advance to the arguments adapter frame (if any). If contains all
6161 // the provided parameters and
6162
6163 // Now advance to the arguments adapter frame (if any). It contains all
6164 // the provided parameters whereas the function frame always have the number
6165 // of arguments matching the functions parameters. The rest of the
6166 // information (except for what is collected above) is the same.
6167 it.AdvanceToArgumentsFrame();
6168
6169 // Find the number of arguments to fill. At least fill the number of
6170 // parameters for the function and fill more if more parameters are provided.
6171 int argument_count = info.number_of_parameters();
6172 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6173 argument_count = it.frame()->GetProvidedParametersCount();
6174 }
6175
6176 // Calculate the size of the result.
6177 int details_size = kFrameDetailsFirstDynamicIndex +
6178 2 * (argument_count + info.NumberOfLocals());
6179 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6180
6181 // Add the frame id.
6182 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6183
6184 // Add the function (same as in function frame).
6185 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6186
6187 // Add the arguments count.
6188 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6189
6190 // Add the locals count
6191 details->set(kFrameDetailsLocalCountIndex,
6192 Smi::FromInt(info.NumberOfLocals()));
6193
6194 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006195 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006196 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6197 } else {
6198 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6199 }
6200
6201 // Add the constructor information.
6202 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6203
6204 // Add information on whether this frame is invoked in the debugger context.
6205 details->set(kFrameDetailsDebuggerFrameIndex,
6206 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6207
6208 // Fill the dynamic part.
6209 int details_index = kFrameDetailsFirstDynamicIndex;
6210
6211 // Add arguments name and value.
6212 for (int i = 0; i < argument_count; i++) {
6213 // Name of the argument.
6214 if (i < info.number_of_parameters()) {
6215 details->set(details_index++, *info.parameter_name(i));
6216 } else {
6217 details->set(details_index++, Heap::undefined_value());
6218 }
6219
6220 // Parameter value.
6221 if (i < it.frame()->GetProvidedParametersCount()) {
6222 details->set(details_index++, it.frame()->GetParameter(i));
6223 } else {
6224 details->set(details_index++, Heap::undefined_value());
6225 }
6226 }
6227
6228 // Add locals name and value from the temporary copy from the function frame.
6229 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6230 details->set(details_index++, locals->get(i));
6231 }
6232
6233 // Add the receiver (same as in function frame).
6234 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6235 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6236 Handle<Object> receiver(it.frame()->receiver());
6237 if (!receiver->IsJSObject()) {
6238 // If the receiver is NOT a JSObject we have hit an optimization
6239 // where a value object is not converted into a wrapped JS objects.
6240 // To hide this optimization from the debugger, we wrap the receiver
6241 // by creating correct wrapper object based on the calling frame's
6242 // global context.
6243 it.Advance();
6244 Handle<Context> calling_frames_global_context(
6245 Context::cast(Context::cast(it.frame()->context())->global_context()));
6246 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6247 }
6248 details->set(kFrameDetailsReceiverIndex, *receiver);
6249
6250 ASSERT_EQ(details_size, details_index);
6251 return *Factory::NewJSArrayWithElements(details);
6252}
6253
6254
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006255// Copy all the context locals into an object used to materialize a scope.
6256static void CopyContextLocalsToScopeObject(Handle<Code> code,
6257 ScopeInfo<>& scope_info,
6258 Handle<Context> context,
6259 Handle<JSObject> scope_object) {
6260 // Fill all context locals to the context extension.
6261 for (int i = Context::MIN_CONTEXT_SLOTS;
6262 i < scope_info.number_of_context_slots();
6263 i++) {
6264 int context_index =
6265 ScopeInfo<>::ContextSlotIndex(*code,
6266 *scope_info.context_slot_name(i),
6267 NULL);
6268
6269 // Don't include the arguments shadow (.arguments) context variable.
6270 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6271 SetProperty(scope_object,
6272 scope_info.context_slot_name(i),
6273 Handle<Object>(context->get(context_index)), NONE);
6274 }
6275 }
6276}
6277
6278
6279// Create a plain JSObject which materializes the local scope for the specified
6280// frame.
6281static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6282 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6283 Handle<Code> code(function->code());
6284 ScopeInfo<> scope_info(*code);
6285
6286 // Allocate and initialize a JSObject with all the arguments, stack locals
6287 // heap locals and extension properties of the debugged function.
6288 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6289
6290 // First fill all parameters.
6291 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6292 SetProperty(local_scope,
6293 scope_info.parameter_name(i),
6294 Handle<Object>(frame->GetParameter(i)), NONE);
6295 }
6296
6297 // Second fill all stack locals.
6298 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6299 SetProperty(local_scope,
6300 scope_info.stack_slot_name(i),
6301 Handle<Object>(frame->GetExpression(i)), NONE);
6302 }
6303
6304 // Third fill all context locals.
6305 Handle<Context> frame_context(Context::cast(frame->context()));
6306 Handle<Context> function_context(frame_context->fcontext());
6307 CopyContextLocalsToScopeObject(code, scope_info,
6308 function_context, local_scope);
6309
6310 // Finally copy any properties from the function context extension. This will
6311 // be variables introduced by eval.
6312 if (function_context->closure() == *function) {
6313 if (function_context->has_extension() &&
6314 !function_context->IsGlobalContext()) {
6315 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006316 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006317 for (int i = 0; i < keys->length(); i++) {
6318 // Names of variables introduced by eval are strings.
6319 ASSERT(keys->get(i)->IsString());
6320 Handle<String> key(String::cast(keys->get(i)));
6321 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6322 }
6323 }
6324 }
6325 return local_scope;
6326}
6327
6328
6329// Create a plain JSObject which materializes the closure content for the
6330// context.
6331static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6332 ASSERT(context->is_function_context());
6333
6334 Handle<Code> code(context->closure()->code());
6335 ScopeInfo<> scope_info(*code);
6336
6337 // Allocate and initialize a JSObject with all the content of theis function
6338 // closure.
6339 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6340
6341 // Check whether the arguments shadow object exists.
6342 int arguments_shadow_index =
6343 ScopeInfo<>::ContextSlotIndex(*code,
6344 Heap::arguments_shadow_symbol(),
6345 NULL);
6346 if (arguments_shadow_index >= 0) {
6347 // In this case all the arguments are available in the arguments shadow
6348 // object.
6349 Handle<JSObject> arguments_shadow(
6350 JSObject::cast(context->get(arguments_shadow_index)));
6351 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6352 SetProperty(closure_scope,
6353 scope_info.parameter_name(i),
6354 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6355 }
6356 }
6357
6358 // Fill all context locals to the context extension.
6359 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6360
6361 // Finally copy any properties from the function context extension. This will
6362 // be variables introduced by eval.
6363 if (context->has_extension()) {
6364 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006365 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006366 for (int i = 0; i < keys->length(); i++) {
6367 // Names of variables introduced by eval are strings.
6368 ASSERT(keys->get(i)->IsString());
6369 Handle<String> key(String::cast(keys->get(i)));
6370 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6371 }
6372 }
6373
6374 return closure_scope;
6375}
6376
6377
6378// Iterate over the actual scopes visible from a stack frame. All scopes are
6379// backed by an actual context except the local scope, which is inserted
6380// "artifically" in the context chain.
6381class ScopeIterator {
6382 public:
6383 enum ScopeType {
6384 ScopeTypeGlobal = 0,
6385 ScopeTypeLocal,
6386 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00006387 ScopeTypeClosure,
6388 // Every catch block contains an implicit with block (its parameter is
6389 // a JSContextExtensionObject) that extends current scope with a variable
6390 // holding exception object. Such with blocks are treated as scopes of their
6391 // own type.
6392 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006393 };
6394
6395 explicit ScopeIterator(JavaScriptFrame* frame)
6396 : frame_(frame),
6397 function_(JSFunction::cast(frame->function())),
6398 context_(Context::cast(frame->context())),
6399 local_done_(false),
6400 at_local_(false) {
6401
6402 // Check whether the first scope is actually a local scope.
6403 if (context_->IsGlobalContext()) {
6404 // If there is a stack slot for .result then this local scope has been
6405 // created for evaluating top level code and it is not a real local scope.
6406 // Checking for the existence of .result seems fragile, but the scope info
6407 // saved with the code object does not otherwise have that information.
6408 Handle<Code> code(function_->code());
6409 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6410 at_local_ = index < 0;
6411 } else if (context_->is_function_context()) {
6412 at_local_ = true;
6413 }
6414 }
6415
6416 // More scopes?
6417 bool Done() { return context_.is_null(); }
6418
6419 // Move to the next scope.
6420 void Next() {
6421 // If at a local scope mark the local scope as passed.
6422 if (at_local_) {
6423 at_local_ = false;
6424 local_done_ = true;
6425
6426 // If the current context is not associated with the local scope the
6427 // current context is the next real scope, so don't move to the next
6428 // context in this case.
6429 if (context_->closure() != *function_) {
6430 return;
6431 }
6432 }
6433
6434 // The global scope is always the last in the chain.
6435 if (context_->IsGlobalContext()) {
6436 context_ = Handle<Context>();
6437 return;
6438 }
6439
6440 // Move to the next context.
6441 if (context_->is_function_context()) {
6442 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6443 } else {
6444 context_ = Handle<Context>(context_->previous());
6445 }
6446
6447 // If passing the local scope indicate that the current scope is now the
6448 // local scope.
6449 if (!local_done_ &&
6450 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6451 at_local_ = true;
6452 }
6453 }
6454
6455 // Return the type of the current scope.
6456 int Type() {
6457 if (at_local_) {
6458 return ScopeTypeLocal;
6459 }
6460 if (context_->IsGlobalContext()) {
6461 ASSERT(context_->global()->IsGlobalObject());
6462 return ScopeTypeGlobal;
6463 }
6464 if (context_->is_function_context()) {
6465 return ScopeTypeClosure;
6466 }
6467 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00006468 // Current scope is either an explicit with statement or a with statement
6469 // implicitely generated for a catch block.
6470 // If the extension object here is a JSContextExtensionObject then
6471 // current with statement is one frome a catch block otherwise it's a
6472 // regular with statement.
6473 if (context_->extension()->IsJSContextExtensionObject()) {
6474 return ScopeTypeCatch;
6475 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006476 return ScopeTypeWith;
6477 }
6478
6479 // Return the JavaScript object with the content of the current scope.
6480 Handle<JSObject> ScopeObject() {
6481 switch (Type()) {
6482 case ScopeIterator::ScopeTypeGlobal:
6483 return Handle<JSObject>(CurrentContext()->global());
6484 break;
6485 case ScopeIterator::ScopeTypeLocal:
6486 // Materialize the content of the local scope into a JSObject.
6487 return MaterializeLocalScope(frame_);
6488 break;
6489 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00006490 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006491 // Return the with object.
6492 return Handle<JSObject>(CurrentContext()->extension());
6493 break;
6494 case ScopeIterator::ScopeTypeClosure:
6495 // Materialize the content of the closure scope into a JSObject.
6496 return MaterializeClosure(CurrentContext());
6497 break;
6498 }
6499 UNREACHABLE();
6500 return Handle<JSObject>();
6501 }
6502
6503 // Return the context for this scope. For the local context there might not
6504 // be an actual context.
6505 Handle<Context> CurrentContext() {
6506 if (at_local_ && context_->closure() != *function_) {
6507 return Handle<Context>();
6508 }
6509 return context_;
6510 }
6511
6512#ifdef DEBUG
6513 // Debug print of the content of the current scope.
6514 void DebugPrint() {
6515 switch (Type()) {
6516 case ScopeIterator::ScopeTypeGlobal:
6517 PrintF("Global:\n");
6518 CurrentContext()->Print();
6519 break;
6520
6521 case ScopeIterator::ScopeTypeLocal: {
6522 PrintF("Local:\n");
6523 Handle<Code> code(function_->code());
6524 ScopeInfo<> scope_info(*code);
6525 scope_info.Print();
6526 if (!CurrentContext().is_null()) {
6527 CurrentContext()->Print();
6528 if (CurrentContext()->has_extension()) {
6529 Handle<JSObject> extension =
6530 Handle<JSObject>(CurrentContext()->extension());
6531 if (extension->IsJSContextExtensionObject()) {
6532 extension->Print();
6533 }
6534 }
6535 }
6536 break;
6537 }
6538
6539 case ScopeIterator::ScopeTypeWith: {
6540 PrintF("With:\n");
6541 Handle<JSObject> extension =
6542 Handle<JSObject>(CurrentContext()->extension());
6543 extension->Print();
6544 break;
6545 }
6546
ager@chromium.orga1645e22009-09-09 19:27:10 +00006547 case ScopeIterator::ScopeTypeCatch: {
6548 PrintF("Catch:\n");
6549 Handle<JSObject> extension =
6550 Handle<JSObject>(CurrentContext()->extension());
6551 extension->Print();
6552 break;
6553 }
6554
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006555 case ScopeIterator::ScopeTypeClosure: {
6556 PrintF("Closure:\n");
6557 CurrentContext()->Print();
6558 if (CurrentContext()->has_extension()) {
6559 Handle<JSObject> extension =
6560 Handle<JSObject>(CurrentContext()->extension());
6561 if (extension->IsJSContextExtensionObject()) {
6562 extension->Print();
6563 }
6564 }
6565 break;
6566 }
6567
6568 default:
6569 UNREACHABLE();
6570 }
6571 PrintF("\n");
6572 }
6573#endif
6574
6575 private:
6576 JavaScriptFrame* frame_;
6577 Handle<JSFunction> function_;
6578 Handle<Context> context_;
6579 bool local_done_;
6580 bool at_local_;
6581
6582 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6583};
6584
6585
6586static Object* Runtime_GetScopeCount(Arguments args) {
6587 HandleScope scope;
6588 ASSERT(args.length() == 2);
6589
6590 // Check arguments.
6591 Object* check = Runtime_CheckExecutionState(args);
6592 if (check->IsFailure()) return check;
6593 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6594
6595 // Get the frame where the debugging is performed.
6596 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6597 JavaScriptFrameIterator it(id);
6598 JavaScriptFrame* frame = it.frame();
6599
6600 // Count the visible scopes.
6601 int n = 0;
6602 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6603 n++;
6604 }
6605
6606 return Smi::FromInt(n);
6607}
6608
6609
6610static const int kScopeDetailsTypeIndex = 0;
6611static const int kScopeDetailsObjectIndex = 1;
6612static const int kScopeDetailsSize = 2;
6613
6614// Return an array with scope details
6615// args[0]: number: break id
6616// args[1]: number: frame index
6617// args[2]: number: scope index
6618//
6619// The array returned contains the following information:
6620// 0: Scope type
6621// 1: Scope object
6622static Object* Runtime_GetScopeDetails(Arguments args) {
6623 HandleScope scope;
6624 ASSERT(args.length() == 3);
6625
6626 // Check arguments.
6627 Object* check = Runtime_CheckExecutionState(args);
6628 if (check->IsFailure()) return check;
6629 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6630 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6631
6632 // Get the frame where the debugging is performed.
6633 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6634 JavaScriptFrameIterator frame_it(id);
6635 JavaScriptFrame* frame = frame_it.frame();
6636
6637 // Find the requested scope.
6638 int n = 0;
6639 ScopeIterator it(frame);
6640 for (; !it.Done() && n < index; it.Next()) {
6641 n++;
6642 }
6643 if (it.Done()) {
6644 return Heap::undefined_value();
6645 }
6646
6647 // Calculate the size of the result.
6648 int details_size = kScopeDetailsSize;
6649 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6650
6651 // Fill in scope details.
6652 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6653 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6654
6655 return *Factory::NewJSArrayWithElements(details);
6656}
6657
6658
6659static Object* Runtime_DebugPrintScopes(Arguments args) {
6660 HandleScope scope;
6661 ASSERT(args.length() == 0);
6662
6663#ifdef DEBUG
6664 // Print the scopes for the top frame.
6665 StackFrameLocator locator;
6666 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6667 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6668 it.DebugPrint();
6669 }
6670#endif
6671 return Heap::undefined_value();
6672}
6673
6674
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006675static Object* Runtime_GetCFrames(Arguments args) {
6676 HandleScope scope;
6677 ASSERT(args.length() == 1);
6678 Object* result = Runtime_CheckExecutionState(args);
6679 if (result->IsFailure()) return result;
6680
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006681#if V8_HOST_ARCH_64_BIT
6682 UNIMPLEMENTED();
6683 return Heap::undefined_value();
6684#else
6685
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006686 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006687 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6688 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006689 if (frames_count == OS::kStackWalkError) {
6690 return Heap::undefined_value();
6691 }
6692
6693 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6694 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6695 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6696 for (int i = 0; i < frames_count; i++) {
6697 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6698 frame_value->SetProperty(
6699 *address_str,
6700 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6701 NONE);
6702
6703 // Get the stack walk text for this frame.
6704 Handle<String> frame_text;
6705 if (strlen(frames[i].text) > 0) {
6706 Vector<const char> str(frames[i].text, strlen(frames[i].text));
6707 frame_text = Factory::NewStringFromAscii(str);
6708 }
6709
6710 if (!frame_text.is_null()) {
6711 frame_value->SetProperty(*text_str, *frame_text, NONE);
6712 }
6713
6714 frames_array->set(i, *frame_value);
6715 }
6716 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006717#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006718}
6719
6720
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006721static Object* Runtime_GetThreadCount(Arguments args) {
6722 HandleScope scope;
6723 ASSERT(args.length() == 1);
6724
6725 // Check arguments.
6726 Object* result = Runtime_CheckExecutionState(args);
6727 if (result->IsFailure()) return result;
6728
6729 // Count all archived V8 threads.
6730 int n = 0;
6731 for (ThreadState* thread = ThreadState::FirstInUse();
6732 thread != NULL;
6733 thread = thread->Next()) {
6734 n++;
6735 }
6736
6737 // Total number of threads is current thread and archived threads.
6738 return Smi::FromInt(n + 1);
6739}
6740
6741
6742static const int kThreadDetailsCurrentThreadIndex = 0;
6743static const int kThreadDetailsThreadIdIndex = 1;
6744static const int kThreadDetailsSize = 2;
6745
6746// Return an array with thread details
6747// args[0]: number: break id
6748// args[1]: number: thread index
6749//
6750// The array returned contains the following information:
6751// 0: Is current thread?
6752// 1: Thread id
6753static Object* Runtime_GetThreadDetails(Arguments args) {
6754 HandleScope scope;
6755 ASSERT(args.length() == 2);
6756
6757 // Check arguments.
6758 Object* check = Runtime_CheckExecutionState(args);
6759 if (check->IsFailure()) return check;
6760 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6761
6762 // Allocate array for result.
6763 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6764
6765 // Thread index 0 is current thread.
6766 if (index == 0) {
6767 // Fill the details.
6768 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6769 details->set(kThreadDetailsThreadIdIndex,
6770 Smi::FromInt(ThreadManager::CurrentId()));
6771 } else {
6772 // Find the thread with the requested index.
6773 int n = 1;
6774 ThreadState* thread = ThreadState::FirstInUse();
6775 while (index != n && thread != NULL) {
6776 thread = thread->Next();
6777 n++;
6778 }
6779 if (thread == NULL) {
6780 return Heap::undefined_value();
6781 }
6782
6783 // Fill the details.
6784 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6785 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6786 }
6787
6788 // Convert to JS array and return.
6789 return *Factory::NewJSArrayWithElements(details);
6790}
6791
6792
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006793static Object* Runtime_GetBreakLocations(Arguments args) {
6794 HandleScope scope;
6795 ASSERT(args.length() == 1);
6796
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006797 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6798 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006799 // Find the number of break points
6800 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6801 if (break_locations->IsUndefined()) return Heap::undefined_value();
6802 // Return array as JS array
6803 return *Factory::NewJSArrayWithElements(
6804 Handle<FixedArray>::cast(break_locations));
6805}
6806
6807
6808// Set a break point in a function
6809// args[0]: function
6810// args[1]: number: break source position (within the function source)
6811// args[2]: number: break point object
6812static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6813 HandleScope scope;
6814 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006815 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6816 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006817 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6818 RUNTIME_ASSERT(source_position >= 0);
6819 Handle<Object> break_point_object_arg = args.at<Object>(2);
6820
6821 // Set break point.
6822 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6823
6824 return Heap::undefined_value();
6825}
6826
6827
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006828Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6829 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006830 // Iterate the heap looking for SharedFunctionInfo generated from the
6831 // script. The inner most SharedFunctionInfo containing the source position
6832 // for the requested break point is found.
6833 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6834 // which is found is not compiled it is compiled and the heap is iterated
6835 // again as the compilation might create inner functions from the newly
6836 // compiled function and the actual requested break point might be in one of
6837 // these functions.
6838 bool done = false;
6839 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006840 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006841 Handle<SharedFunctionInfo> target;
6842 // The current candidate for the last function in script:
6843 Handle<SharedFunctionInfo> last;
6844 while (!done) {
6845 HeapIterator iterator;
6846 while (iterator.has_next()) {
6847 HeapObject* obj = iterator.next();
6848 ASSERT(obj != NULL);
6849 if (obj->IsSharedFunctionInfo()) {
6850 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6851 if (shared->script() == *script) {
6852 // If the SharedFunctionInfo found has the requested script data and
6853 // contains the source position it is a candidate.
6854 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006855 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006856 start_position = shared->start_position();
6857 }
6858 if (start_position <= position &&
6859 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006860 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006861 // candidate this is the new candidate.
6862 if (target.is_null()) {
6863 target_start_position = start_position;
6864 target = shared;
6865 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00006866 if (target_start_position == start_position &&
6867 shared->end_position() == target->end_position()) {
6868 // If a top-level function contain only one function
6869 // declartion the source for the top-level and the function is
6870 // the same. In that case prefer the non top-level function.
6871 if (!shared->is_toplevel()) {
6872 target_start_position = start_position;
6873 target = shared;
6874 }
6875 } else if (target_start_position <= start_position &&
6876 shared->end_position() <= target->end_position()) {
6877 // This containment check includes equality as a function inside
6878 // a top-level function can share either start or end position
6879 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006880 target_start_position = start_position;
6881 target = shared;
6882 }
6883 }
6884 }
6885
6886 // Keep track of the last function in the script.
6887 if (last.is_null() ||
6888 shared->end_position() > last->start_position()) {
6889 last = shared;
6890 }
6891 }
6892 }
6893 }
6894
6895 // Make sure some candidate is selected.
6896 if (target.is_null()) {
6897 if (!last.is_null()) {
6898 // Position after the last function - use last.
6899 target = last;
6900 } else {
6901 // Unable to find function - possibly script without any function.
6902 return Heap::undefined_value();
6903 }
6904 }
6905
6906 // If the candidate found is compiled we are done. NOTE: when lazy
6907 // compilation of inner functions is introduced some additional checking
6908 // needs to be done here to compile inner functions.
6909 done = target->is_compiled();
6910 if (!done) {
6911 // If the candidate is not compiled compile it to reveal any inner
6912 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00006913 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006914 }
6915 }
6916
6917 return *target;
6918}
6919
6920
6921// Change the state of a break point in a script. NOTE: Regarding performance
6922// see the NOTE for GetScriptFromScriptData.
6923// args[0]: script to set break point in
6924// args[1]: number: break source position (within the script source)
6925// args[2]: number: break point object
6926static Object* Runtime_SetScriptBreakPoint(Arguments args) {
6927 HandleScope scope;
6928 ASSERT(args.length() == 3);
6929 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
6930 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6931 RUNTIME_ASSERT(source_position >= 0);
6932 Handle<Object> break_point_object_arg = args.at<Object>(2);
6933
6934 // Get the script from the script wrapper.
6935 RUNTIME_ASSERT(wrapper->value()->IsScript());
6936 Handle<Script> script(Script::cast(wrapper->value()));
6937
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006938 Object* result = Runtime::FindSharedFunctionInfoInScript(
6939 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006940 if (!result->IsUndefined()) {
6941 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
6942 // Find position within function. The script position might be before the
6943 // source position of the first function.
6944 int position;
6945 if (shared->start_position() > source_position) {
6946 position = 0;
6947 } else {
6948 position = source_position - shared->start_position();
6949 }
6950 Debug::SetBreakPoint(shared, position, break_point_object_arg);
6951 }
6952 return Heap::undefined_value();
6953}
6954
6955
6956// Clear a break point
6957// args[0]: number: break point object
6958static Object* Runtime_ClearBreakPoint(Arguments args) {
6959 HandleScope scope;
6960 ASSERT(args.length() == 1);
6961 Handle<Object> break_point_object_arg = args.at<Object>(0);
6962
6963 // Clear break point.
6964 Debug::ClearBreakPoint(break_point_object_arg);
6965
6966 return Heap::undefined_value();
6967}
6968
6969
6970// Change the state of break on exceptions
6971// args[0]: boolean indicating uncaught exceptions
6972// args[1]: boolean indicating on/off
6973static Object* Runtime_ChangeBreakOnException(Arguments args) {
6974 HandleScope scope;
6975 ASSERT(args.length() == 2);
6976 ASSERT(args[0]->IsNumber());
6977 ASSERT(args[1]->IsBoolean());
6978
6979 // Update break point state
6980 ExceptionBreakType type =
6981 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
6982 bool enable = args[1]->ToBoolean()->IsTrue();
6983 Debug::ChangeBreakOnException(type, enable);
6984 return Heap::undefined_value();
6985}
6986
6987
6988// Prepare for stepping
6989// args[0]: break id for checking execution state
6990// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00006991// args[2]: number of times to perform the step, for step out it is the number
6992// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006993static Object* Runtime_PrepareStep(Arguments args) {
6994 HandleScope scope;
6995 ASSERT(args.length() == 3);
6996 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006997 Object* check = Runtime_CheckExecutionState(args);
6998 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006999 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
7000 return Top::Throw(Heap::illegal_argument_symbol());
7001 }
7002
7003 // Get the step action and check validity.
7004 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
7005 if (step_action != StepIn &&
7006 step_action != StepNext &&
7007 step_action != StepOut &&
7008 step_action != StepInMin &&
7009 step_action != StepMin) {
7010 return Top::Throw(Heap::illegal_argument_symbol());
7011 }
7012
7013 // Get the number of steps.
7014 int step_count = NumberToInt32(args[2]);
7015 if (step_count < 1) {
7016 return Top::Throw(Heap::illegal_argument_symbol());
7017 }
7018
ager@chromium.orga1645e22009-09-09 19:27:10 +00007019 // Clear all current stepping setup.
7020 Debug::ClearStepping();
7021
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007022 // Prepare step.
7023 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
7024 return Heap::undefined_value();
7025}
7026
7027
7028// Clear all stepping set by PrepareStep.
7029static Object* Runtime_ClearStepping(Arguments args) {
7030 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007031 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007032 Debug::ClearStepping();
7033 return Heap::undefined_value();
7034}
7035
7036
7037// Creates a copy of the with context chain. The copy of the context chain is
7038// is linked to the function context supplied.
7039static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
7040 Handle<Context> function_context) {
7041 // At the bottom of the chain. Return the function context to link to.
7042 if (context_chain->is_function_context()) {
7043 return function_context;
7044 }
7045
7046 // Recursively copy the with contexts.
7047 Handle<Context> previous(context_chain->previous());
7048 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
7049 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007050 CopyWithContextChain(function_context, previous),
7051 extension,
7052 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007053}
7054
7055
7056// Helper function to find or create the arguments object for
7057// Runtime_DebugEvaluate.
7058static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
7059 Handle<JSFunction> function,
7060 Handle<Code> code,
7061 const ScopeInfo<>* sinfo,
7062 Handle<Context> function_context) {
7063 // Try to find the value of 'arguments' to pass as parameter. If it is not
7064 // found (that is the debugged function does not reference 'arguments' and
7065 // does not support eval) then create an 'arguments' object.
7066 int index;
7067 if (sinfo->number_of_stack_slots() > 0) {
7068 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
7069 if (index != -1) {
7070 return Handle<Object>(frame->GetExpression(index));
7071 }
7072 }
7073
7074 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
7075 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
7076 NULL);
7077 if (index != -1) {
7078 return Handle<Object>(function_context->get(index));
7079 }
7080 }
7081
7082 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007083 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
7084 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007085 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007086 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007087 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007088 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007089 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007090 return arguments;
7091}
7092
7093
7094// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00007095// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007096// extension part has all the parameters and locals of the function on the
7097// stack frame. A function which calls eval with the code to evaluate is then
7098// compiled in this context and called in this context. As this context
7099// replaces the context of the function on the stack frame a new (empty)
7100// function is created as well to be used as the closure for the context.
7101// This function and the context acts as replacements for the function on the
7102// stack frame presenting the same view of the values of parameters and
7103// local variables as if the piece of JavaScript was evaluated at the point
7104// where the function on the stack frame is currently stopped.
7105static Object* Runtime_DebugEvaluate(Arguments args) {
7106 HandleScope scope;
7107
7108 // Check the execution state and decode arguments frame and source to be
7109 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007110 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007111 Object* check_result = Runtime_CheckExecutionState(args);
7112 if (check_result->IsFailure()) return check_result;
7113 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7114 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007115 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
7116
7117 // Handle the processing of break.
7118 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007119
7120 // Get the frame where the debugging is performed.
7121 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7122 JavaScriptFrameIterator it(id);
7123 JavaScriptFrame* frame = it.frame();
7124 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7125 Handle<Code> code(function->code());
7126 ScopeInfo<> sinfo(*code);
7127
7128 // Traverse the saved contexts chain to find the active context for the
7129 // selected frame.
7130 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007131 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007132 save = save->prev();
7133 }
7134 ASSERT(save != NULL);
7135 SaveContext savex;
7136 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007137
7138 // Create the (empty) function replacing the function on the stack frame for
7139 // the purpose of evaluating in the context created below. It is important
7140 // that this function does not describe any parameters and local variables
7141 // in the context. If it does then this will cause problems with the lookup
7142 // in Context::Lookup, where context slots for parameters and local variables
7143 // are looked at before the extension object.
7144 Handle<JSFunction> go_between =
7145 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7146 go_between->set_context(function->context());
7147#ifdef DEBUG
7148 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7149 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7150 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7151#endif
7152
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007153 // Materialize the content of the local scope into a JSObject.
7154 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007155
7156 // Allocate a new context for the debug evaluation and set the extension
7157 // object build.
7158 Handle<Context> context =
7159 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007160 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007161 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007162 Handle<Context> frame_context(Context::cast(frame->context()));
7163 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007164 context = CopyWithContextChain(frame_context, context);
7165
7166 // Wrap the evaluation statement in a new function compiled in the newly
7167 // created context. The function has one parameter which has to be called
7168 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007169 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007170 // function(arguments,__source__) {return eval(__source__);}
7171 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00007172 "(function(arguments,__source__){return eval(__source__);})";
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007173 static const int source_str_length = strlen(source_str);
7174 Handle<String> function_source =
7175 Factory::NewStringFromAscii(Vector<const char>(source_str,
7176 source_str_length));
7177 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007178 Compiler::CompileEval(function_source,
7179 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007180 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007181 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007182 if (boilerplate.is_null()) return Failure::Exception();
7183 Handle<JSFunction> compiled_function =
7184 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7185
7186 // Invoke the result of the compilation to get the evaluation function.
7187 bool has_pending_exception;
7188 Handle<Object> receiver(frame->receiver());
7189 Handle<Object> evaluation_function =
7190 Execution::Call(compiled_function, receiver, 0, NULL,
7191 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007192 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007193
7194 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7195 function_context);
7196
7197 // Invoke the evaluation function and return the result.
7198 const int argc = 2;
7199 Object** argv[argc] = { arguments.location(),
7200 Handle<Object>::cast(source).location() };
7201 Handle<Object> result =
7202 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7203 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007204 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007205
7206 // Skip the global proxy as it has no properties and always delegates to the
7207 // real global object.
7208 if (result->IsJSGlobalProxy()) {
7209 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7210 }
7211
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007212 return *result;
7213}
7214
7215
7216static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7217 HandleScope scope;
7218
7219 // Check the execution state and decode arguments frame and source to be
7220 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007221 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007222 Object* check_result = Runtime_CheckExecutionState(args);
7223 if (check_result->IsFailure()) return check_result;
7224 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007225 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7226
7227 // Handle the processing of break.
7228 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007229
7230 // Enter the top context from before the debugger was invoked.
7231 SaveContext save;
7232 SaveContext* top = &save;
7233 while (top != NULL && *top->context() == *Debug::debug_context()) {
7234 top = top->prev();
7235 }
7236 if (top != NULL) {
7237 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007238 }
7239
7240 // Get the global context now set to the top context from before the
7241 // debugger was invoked.
7242 Handle<Context> context = Top::global_context();
7243
7244 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007245 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007246 Handle<JSFunction>(Compiler::CompileEval(source,
7247 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007248 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007249 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007250 if (boilerplate.is_null()) return Failure::Exception();
7251 Handle<JSFunction> compiled_function =
7252 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7253 context));
7254
7255 // Invoke the result of the compilation to get the evaluation function.
7256 bool has_pending_exception;
7257 Handle<Object> receiver = Top::global();
7258 Handle<Object> result =
7259 Execution::Call(compiled_function, receiver, 0, NULL,
7260 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007261 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007262 return *result;
7263}
7264
7265
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007266static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7267 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007268 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007269
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007270 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007271 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007272
7273 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007274 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007275 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7276 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7277 // because using
7278 // instances->set(i, *GetScriptWrapper(script))
7279 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7280 // already have deferenced the instances handle.
7281 Handle<JSValue> wrapper = GetScriptWrapper(script);
7282 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007283 }
7284
7285 // Return result as a JS array.
7286 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7287 Handle<JSArray>::cast(result)->SetContent(*instances);
7288 return *result;
7289}
7290
7291
7292// Helper function used by Runtime_DebugReferencedBy below.
7293static int DebugReferencedBy(JSObject* target,
7294 Object* instance_filter, int max_references,
7295 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007296 JSFunction* arguments_function) {
7297 NoHandleAllocation ha;
7298 AssertNoAllocation no_alloc;
7299
7300 // Iterate the heap.
7301 int count = 0;
7302 JSObject* last = NULL;
7303 HeapIterator iterator;
7304 while (iterator.has_next() &&
7305 (max_references == 0 || count < max_references)) {
7306 // Only look at all JSObjects.
7307 HeapObject* heap_obj = iterator.next();
7308 if (heap_obj->IsJSObject()) {
7309 // Skip context extension objects and argument arrays as these are
7310 // checked in the context of functions using them.
7311 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007312 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007313 obj->map()->constructor() == arguments_function) {
7314 continue;
7315 }
7316
7317 // Check if the JS object has a reference to the object looked for.
7318 if (obj->ReferencesObject(target)) {
7319 // Check instance filter if supplied. This is normally used to avoid
7320 // references from mirror objects (see Runtime_IsInPrototypeChain).
7321 if (!instance_filter->IsUndefined()) {
7322 Object* V = obj;
7323 while (true) {
7324 Object* prototype = V->GetPrototype();
7325 if (prototype->IsNull()) {
7326 break;
7327 }
7328 if (instance_filter == prototype) {
7329 obj = NULL; // Don't add this object.
7330 break;
7331 }
7332 V = prototype;
7333 }
7334 }
7335
7336 if (obj != NULL) {
7337 // Valid reference found add to instance array if supplied an update
7338 // count.
7339 if (instances != NULL && count < instances_size) {
7340 instances->set(count, obj);
7341 }
7342 last = obj;
7343 count++;
7344 }
7345 }
7346 }
7347 }
7348
7349 // Check for circular reference only. This can happen when the object is only
7350 // referenced from mirrors and has a circular reference in which case the
7351 // object is not really alive and would have been garbage collected if not
7352 // referenced from the mirror.
7353 if (count == 1 && last == target) {
7354 count = 0;
7355 }
7356
7357 // Return the number of referencing objects found.
7358 return count;
7359}
7360
7361
7362// Scan the heap for objects with direct references to an object
7363// args[0]: the object to find references to
7364// args[1]: constructor function for instances to exclude (Mirror)
7365// args[2]: the the maximum number of objects to return
7366static Object* Runtime_DebugReferencedBy(Arguments args) {
7367 ASSERT(args.length() == 3);
7368
7369 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007370 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007371
7372 // Check parameters.
7373 CONVERT_CHECKED(JSObject, target, args[0]);
7374 Object* instance_filter = args[1];
7375 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7376 instance_filter->IsJSObject());
7377 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7378 RUNTIME_ASSERT(max_references >= 0);
7379
7380 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007381 JSObject* arguments_boilerplate =
7382 Top::context()->global_context()->arguments_boilerplate();
7383 JSFunction* arguments_function =
7384 JSFunction::cast(arguments_boilerplate->map()->constructor());
7385
7386 // Get the number of referencing objects.
7387 int count;
7388 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007389 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007390
7391 // Allocate an array to hold the result.
7392 Object* object = Heap::AllocateFixedArray(count);
7393 if (object->IsFailure()) return object;
7394 FixedArray* instances = FixedArray::cast(object);
7395
7396 // Fill the referencing objects.
7397 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007398 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007399
7400 // Return result as JS array.
7401 Object* result =
7402 Heap::AllocateJSObject(
7403 Top::context()->global_context()->array_function());
7404 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7405 return result;
7406}
7407
7408
7409// Helper function used by Runtime_DebugConstructedBy below.
7410static int DebugConstructedBy(JSFunction* constructor, int max_references,
7411 FixedArray* instances, int instances_size) {
7412 AssertNoAllocation no_alloc;
7413
7414 // Iterate the heap.
7415 int count = 0;
7416 HeapIterator iterator;
7417 while (iterator.has_next() &&
7418 (max_references == 0 || count < max_references)) {
7419 // Only look at all JSObjects.
7420 HeapObject* heap_obj = iterator.next();
7421 if (heap_obj->IsJSObject()) {
7422 JSObject* obj = JSObject::cast(heap_obj);
7423 if (obj->map()->constructor() == constructor) {
7424 // Valid reference found add to instance array if supplied an update
7425 // count.
7426 if (instances != NULL && count < instances_size) {
7427 instances->set(count, obj);
7428 }
7429 count++;
7430 }
7431 }
7432 }
7433
7434 // Return the number of referencing objects found.
7435 return count;
7436}
7437
7438
7439// Scan the heap for objects constructed by a specific function.
7440// args[0]: the constructor to find instances of
7441// args[1]: the the maximum number of objects to return
7442static Object* Runtime_DebugConstructedBy(Arguments args) {
7443 ASSERT(args.length() == 2);
7444
7445 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007446 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007447
7448 // Check parameters.
7449 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7450 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7451 RUNTIME_ASSERT(max_references >= 0);
7452
7453 // Get the number of referencing objects.
7454 int count;
7455 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7456
7457 // Allocate an array to hold the result.
7458 Object* object = Heap::AllocateFixedArray(count);
7459 if (object->IsFailure()) return object;
7460 FixedArray* instances = FixedArray::cast(object);
7461
7462 // Fill the referencing objects.
7463 count = DebugConstructedBy(constructor, max_references, instances, count);
7464
7465 // Return result as JS array.
7466 Object* result =
7467 Heap::AllocateJSObject(
7468 Top::context()->global_context()->array_function());
7469 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7470 return result;
7471}
7472
7473
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007474// Find the effective prototype object as returned by __proto__.
7475// args[0]: the object to find the prototype for.
7476static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007477 ASSERT(args.length() == 1);
7478
7479 CONVERT_CHECKED(JSObject, obj, args[0]);
7480
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007481 // Use the __proto__ accessor.
7482 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007483}
7484
7485
7486static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007487 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007488 CPU::DebugBreak();
7489 return Heap::undefined_value();
7490}
7491
7492
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007493static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007494#ifdef DEBUG
7495 HandleScope scope;
7496 ASSERT(args.length() == 1);
7497 // Get the function and make sure it is compiled.
7498 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7499 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7500 return Failure::Exception();
7501 }
7502 func->code()->PrintLn();
7503#endif // DEBUG
7504 return Heap::undefined_value();
7505}
ager@chromium.org9085a012009-05-11 19:22:57 +00007506
7507
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007508static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
7509#ifdef DEBUG
7510 HandleScope scope;
7511 ASSERT(args.length() == 1);
7512 // Get the function and make sure it is compiled.
7513 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7514 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7515 return Failure::Exception();
7516 }
7517 func->shared()->construct_stub()->PrintLn();
7518#endif // DEBUG
7519 return Heap::undefined_value();
7520}
7521
7522
ager@chromium.org9085a012009-05-11 19:22:57 +00007523static Object* Runtime_FunctionGetInferredName(Arguments args) {
7524 NoHandleAllocation ha;
7525 ASSERT(args.length() == 1);
7526
7527 CONVERT_CHECKED(JSFunction, f, args[0]);
7528 return f->shared()->inferred_name();
7529}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007530#endif // ENABLE_DEBUGGER_SUPPORT
7531
7532
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007533// Finds the script object from the script data. NOTE: This operation uses
7534// heap traversal to find the function generated for the source position
7535// for the requested break point. For lazily compiled functions several heap
7536// traversals might be required rendering this operation as a rather slow
7537// operation. However for setting break points which is normally done through
7538// some kind of user interaction the performance is not crucial.
7539static Handle<Object> Runtime_GetScriptFromScriptName(
7540 Handle<String> script_name) {
7541 // Scan the heap for Script objects to find the script with the requested
7542 // script data.
7543 Handle<Script> script;
7544 HeapIterator iterator;
7545 while (script.is_null() && iterator.has_next()) {
7546 HeapObject* obj = iterator.next();
7547 // If a script is found check if it has the script data requested.
7548 if (obj->IsScript()) {
7549 if (Script::cast(obj)->name()->IsString()) {
7550 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7551 script = Handle<Script>(Script::cast(obj));
7552 }
7553 }
7554 }
7555 }
7556
7557 // If no script with the requested script data is found return undefined.
7558 if (script.is_null()) return Factory::undefined_value();
7559
7560 // Return the script found.
7561 return GetScriptWrapper(script);
7562}
7563
7564
7565// Get the script object from script data. NOTE: Regarding performance
7566// see the NOTE for GetScriptFromScriptData.
7567// args[0]: script data for the script to find the source for
7568static Object* Runtime_GetScript(Arguments args) {
7569 HandleScope scope;
7570
7571 ASSERT(args.length() == 1);
7572
7573 CONVERT_CHECKED(String, script_name, args[0]);
7574
7575 // Find the requested script.
7576 Handle<Object> result =
7577 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7578 return *result;
7579}
7580
7581
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007582// Determines whether the given stack frame should be displayed in
7583// a stack trace. The caller is the error constructor that asked
7584// for the stack trace to be collected. The first time a construct
7585// call to this function is encountered it is skipped. The seen_caller
7586// in/out parameter is used to remember if the caller has been seen
7587// yet.
7588static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
7589 bool* seen_caller) {
7590 // Only display JS frames.
7591 if (!raw_frame->is_java_script())
7592 return false;
7593 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7594 Object* raw_fun = frame->function();
7595 // Not sure when this can happen but skip it just in case.
7596 if (!raw_fun->IsJSFunction())
7597 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007598 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007599 *seen_caller = true;
7600 return false;
7601 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007602 // Skip all frames until we've seen the caller. Also, skip the most
7603 // obvious builtin calls. Some builtin calls (such as Number.ADD
7604 // which is invoked using 'call') are very difficult to recognize
7605 // so we're leaving them in for now.
7606 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007607}
7608
7609
7610// Collect the raw data for a stack trace. Returns an array of three
7611// element segments each containing a receiver, function and native
7612// code offset.
7613static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007614 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007615 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007616 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
7617
7618 HandleScope scope;
7619
7620 int initial_size = limit < 10 ? limit : 10;
7621 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007622
7623 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007624 // If the caller parameter is a function we skip frames until we're
7625 // under it before starting to collect.
7626 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007627 int cursor = 0;
7628 int frames_seen = 0;
7629 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007630 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007631 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007632 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007633 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007634 Object* recv = frame->receiver();
7635 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007636 Address pc = frame->pc();
7637 Address start = frame->code()->address();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007638 Smi* offset = Smi::FromInt(pc - start);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007639 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007640 if (cursor + 2 < elements->length()) {
7641 elements->set(cursor++, recv);
7642 elements->set(cursor++, fun);
7643 elements->set(cursor++, offset, SKIP_WRITE_BARRIER);
7644 } else {
7645 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007646 Handle<Object> recv_handle(recv);
7647 Handle<Object> fun_handle(fun);
7648 SetElement(result, cursor++, recv_handle);
7649 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007650 SetElement(result, cursor++, Handle<Smi>(offset));
7651 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007652 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007653 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007654 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007655
7656 result->set_length(Smi::FromInt(cursor), SKIP_WRITE_BARRIER);
7657
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007658 return *result;
7659}
7660
7661
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007662static Object* Runtime_Abort(Arguments args) {
7663 ASSERT(args.length() == 2);
7664 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7665 Smi::cast(args[1])->value());
7666 Top::PrintStack();
7667 OS::Abort();
7668 UNREACHABLE();
7669 return NULL;
7670}
7671
7672
kasper.lund44510672008-07-25 07:37:58 +00007673#ifdef DEBUG
7674// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7675// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007676static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007677 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007678 HandleScope scope;
7679 Handle<JSArray> result = Factory::NewJSArray(0);
7680 int index = 0;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007681#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007682 { \
7683 HandleScope inner; \
7684 Handle<String> name = \
7685 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
7686 Handle<JSArray> pair = Factory::NewJSArray(0); \
7687 SetElement(pair, 0, name); \
7688 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7689 SetElement(result, index++, pair); \
7690 }
7691 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7692#undef ADD_ENTRY
7693 return *result;
7694}
kasper.lund44510672008-07-25 07:37:58 +00007695#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007696
7697
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007698static Object* Runtime_Log(Arguments args) {
7699 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007700 CONVERT_CHECKED(String, format, args[0]);
7701 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007702 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007703 Logger::LogRuntime(chars, elms);
7704 return Heap::undefined_value();
7705}
7706
7707
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007708static Object* Runtime_IS_VAR(Arguments args) {
7709 UNREACHABLE(); // implemented as macro in the parser
7710 return NULL;
7711}
7712
7713
7714// ----------------------------------------------------------------------------
7715// Implementation of Runtime
7716
ager@chromium.orga1645e22009-09-09 19:27:10 +00007717#define F(name, nargs, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007718 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +00007719 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007720
7721static Runtime::Function Runtime_functions[] = {
7722 RUNTIME_FUNCTION_LIST(F)
ager@chromium.orga1645e22009-09-09 19:27:10 +00007723 { NULL, NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007724};
7725
7726#undef F
7727
7728
7729Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7730 ASSERT(0 <= fid && fid < kNofFunctions);
7731 return &Runtime_functions[fid];
7732}
7733
7734
7735Runtime::Function* Runtime::FunctionForName(const char* name) {
7736 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7737 if (strcmp(f->name, name) == 0) {
7738 return f;
7739 }
7740 }
7741 return NULL;
7742}
7743
7744
7745void Runtime::PerformGC(Object* result) {
7746 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007747 if (failure->IsRetryAfterGC()) {
7748 // Try to do a garbage collection; ignore it if it fails. The C
7749 // entry stub will throw an out-of-memory exception in that case.
7750 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7751 } else {
7752 // Handle last resort GC and make sure to allow future allocations
7753 // to grow the heap without causing GCs (if possible).
7754 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007755 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007756 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007757}
7758
7759
7760} } // namespace v8::internal