blob: 65dfd1326dbd2cce55b5d308b4e37583f187a992 [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.
ager@chromium.org3811b432009-10-28 14:53:37 +0000159 ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000160 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
ager@chromium.org3811b432009-10-28 14:53:37 +0000580 Handle<Context> context = args.at<Context>(0);
581 CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000582 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.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000791 // Note that objects can have hidden prototypes, so we need to traverse
792 // the whole chain of hidden prototypes to do a 'local' lookup.
793 JSObject* real_holder = global;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000794 LookupResult lookup;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000795 while (true) {
796 real_holder->LocalLookup(*name, &lookup);
797 if (lookup.IsProperty()) {
798 // Determine if this is a redeclaration of something read-only.
799 if (lookup.IsReadOnly()) {
800 // If we found readonly property on one of hidden prototypes,
801 // just shadow it.
802 if (real_holder != Top::context()->global()) break;
803 return ThrowRedeclarationError("const", name);
804 }
805
806 // Determine if this is a redeclaration of an intercepted read-only
807 // property and figure out if the property exists at all.
808 bool found = true;
809 PropertyType type = lookup.type();
810 if (type == INTERCEPTOR) {
811 HandleScope handle_scope;
812 Handle<JSObject> holder(real_holder);
813 PropertyAttributes intercepted = holder->GetPropertyAttribute(*name);
814 real_holder = *holder;
815 if (intercepted == ABSENT) {
816 // The interceptor claims the property isn't there. We need to
817 // make sure to introduce it.
818 found = false;
819 } else if ((intercepted & READ_ONLY) != 0) {
820 // The property is present, but read-only. Since we're trying to
821 // overwrite it with a variable declaration we must throw a
822 // re-declaration error. However if we found readonly property
823 // on one of hidden prototypes, just shadow it.
824 if (real_holder != Top::context()->global()) break;
825 return ThrowRedeclarationError("const", name);
826 }
827 }
828
829 if (found && !assign) {
830 // The global property is there and we're not assigning any value
831 // to it. Just return.
832 return Heap::undefined_value();
833 }
834
835 // Assign the value (or undefined) to the property.
836 Object* value = (assign) ? args[1] : Heap::undefined_value();
837 return real_holder->SetProperty(&lookup, *name, value, attributes);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000838 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000839
840 Object* proto = real_holder->GetPrototype();
841 if (!proto->IsJSObject())
842 break;
843
844 if (!JSObject::cast(proto)->map()->is_hidden_prototype())
845 break;
846
847 real_holder = JSObject::cast(proto);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000848 }
849
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000850 global = Top::context()->global();
851 if (assign) {
852 return global->IgnoreAttributesAndSetLocalProperty(*name,
853 args[1],
854 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000855 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000856 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000857}
858
859
860static Object* Runtime_InitializeConstGlobal(Arguments args) {
861 // All constants are declared with an initial value. The name
862 // of the constant is the first argument and the initial value
863 // is the second.
864 RUNTIME_ASSERT(args.length() == 2);
865 CONVERT_ARG_CHECKED(String, name, 0);
866 Handle<Object> value = args.at<Object>(1);
867
868 // Get the current global object from top.
869 GlobalObject* global = Top::context()->global();
870
871 // According to ECMA-262, section 12.2, page 62, the property must
872 // not be deletable. Since it's a const, it must be READ_ONLY too.
873 PropertyAttributes attributes =
874 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
875
876 // Lookup the property locally in the global object. If it isn't
877 // there, we add the property and take special precautions to always
878 // add it as a local property even in case of callbacks in the
879 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000880 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000881 LookupResult lookup;
882 global->LocalLookup(*name, &lookup);
883 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000884 return global->IgnoreAttributesAndSetLocalProperty(*name,
885 *value,
886 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000887 }
888
889 // Determine if this is a redeclaration of something not
890 // read-only. In case the result is hidden behind an interceptor we
891 // need to ask it for the property attributes.
892 if (!lookup.IsReadOnly()) {
893 if (lookup.type() != INTERCEPTOR) {
894 return ThrowRedeclarationError("var", name);
895 }
896
897 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
898
899 // Throw re-declaration error if the intercepted property is present
900 // but not read-only.
901 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
902 return ThrowRedeclarationError("var", name);
903 }
904
905 // Restore global object from context (in case of GC) and continue
906 // with setting the value because the property is either absent or
907 // read-only. We also have to do redo the lookup.
908 global = Top::context()->global();
909
910 // BUG 1213579: Handle the case where we have to set a read-only
911 // property through an interceptor and only do it if it's
912 // uninitialized, e.g. the hole. Nirk...
913 global->SetProperty(*name, *value, attributes);
914 return *value;
915 }
916
917 // Set the value, but only we're assigning the initial value to a
918 // constant. For now, we determine this by checking if the
919 // current value is the hole.
920 PropertyType type = lookup.type();
921 if (type == FIELD) {
922 FixedArray* properties = global->properties();
923 int index = lookup.GetFieldIndex();
924 if (properties->get(index)->IsTheHole()) {
925 properties->set(index, *value);
926 }
927 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000928 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
929 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000930 }
931 } else {
932 // Ignore re-initialization of constants that have already been
933 // assigned a function value.
934 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
935 }
936
937 // Use the set value as the result of the operation.
938 return *value;
939}
940
941
942static Object* Runtime_InitializeConstContextSlot(Arguments args) {
943 HandleScope scope;
944 ASSERT(args.length() == 3);
945
946 Handle<Object> value(args[0]);
947 ASSERT(!value->IsTheHole());
948 CONVERT_ARG_CHECKED(Context, context, 1);
949 Handle<String> name(String::cast(args[2]));
950
951 // Initializations are always done in the function context.
952 context = Handle<Context>(context->fcontext());
953
954 int index;
955 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000956 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000957 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000958 context->Lookup(name, flags, &index, &attributes);
959
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000960 // In most situations, the property introduced by the const
961 // declaration should be present in the context extension object.
962 // However, because declaration and initialization are separate, the
963 // property might have been deleted (if it was introduced by eval)
964 // before we reach the initialization point.
965 //
966 // Example:
967 //
968 // function f() { eval("delete x; const x;"); }
969 //
970 // In that case, the initialization behaves like a normal assignment
971 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000972 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000973 // Property was found in a context.
974 if (holder->IsContext()) {
975 // The holder cannot be the function context. If it is, there
976 // should have been a const redeclaration error when declaring
977 // the const property.
978 ASSERT(!holder.is_identical_to(context));
979 if ((attributes & READ_ONLY) == 0) {
980 Handle<Context>::cast(holder)->set(index, *value);
981 }
982 } else {
983 // The holder is an arguments object.
984 ASSERT((attributes & READ_ONLY) == 0);
985 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000986 }
987 return *value;
988 }
989
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000990 // The property could not be found, we introduce it in the global
991 // context.
992 if (attributes == ABSENT) {
993 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
994 SetProperty(global, name, value, NONE);
995 return *value;
996 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000997
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000998 // The property was present in a context extension object.
999 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001000
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001001 if (*context_ext == context->extension()) {
1002 // This is the property that was introduced by the const
1003 // declaration. Set it if it hasn't been set before. NOTE: We
1004 // cannot use GetProperty() to get the current value as it
1005 // 'unholes' the value.
1006 LookupResult lookup;
1007 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
1008 ASSERT(lookup.IsProperty()); // the property was declared
1009 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
1010
1011 PropertyType type = lookup.type();
1012 if (type == FIELD) {
1013 FixedArray* properties = context_ext->properties();
1014 int index = lookup.GetFieldIndex();
1015 if (properties->get(index)->IsTheHole()) {
1016 properties->set(index, *value);
1017 }
1018 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001019 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
1020 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001021 }
1022 } else {
1023 // We should not reach here. Any real, named property should be
1024 // either a field or a dictionary slot.
1025 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001026 }
1027 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001028 // The property was found in a different context extension object.
1029 // Set it if it is not a read-only property.
1030 if ((attributes & READ_ONLY) == 0) {
1031 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1032 // Setting a property might throw an exception. Exceptions
1033 // are converted to empty handles in handle operations. We
1034 // need to convert back to exceptions here.
1035 if (set.is_null()) {
1036 ASSERT(Top::has_pending_exception());
1037 return Failure::Exception();
1038 }
1039 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001040 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001041
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001042 return *value;
1043}
1044
1045
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001046static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1047 Arguments args) {
1048 HandleScope scope;
1049 ASSERT(args.length() == 2);
1050 CONVERT_ARG_CHECKED(JSObject, object, 0);
1051 CONVERT_SMI_CHECKED(properties, args[1]);
1052 if (object->HasFastProperties()) {
1053 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1054 }
1055 return *object;
1056}
1057
1058
1059static Object* Runtime_TransformToFastProperties(Arguments args) {
1060 HandleScope scope;
1061 ASSERT(args.length() == 1);
1062 CONVERT_ARG_CHECKED(JSObject, object, 0);
1063 if (!object->HasFastProperties() && !object->IsGlobalObject()) {
1064 TransformToFastProperties(object, 0);
1065 }
1066 return *object;
1067}
1068
1069
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001070static Object* Runtime_RegExpExec(Arguments args) {
1071 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001072 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001073 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1074 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001075 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001076 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001077 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001078 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001079 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001080 RUNTIME_ASSERT(index >= 0);
1081 RUNTIME_ASSERT(index <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001082 Handle<Object> result = RegExpImpl::Exec(regexp,
1083 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001084 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001085 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001086 if (result.is_null()) return Failure::Exception();
1087 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001088}
1089
1090
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001091static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1092 HandleScope scope;
1093 ASSERT(args.length() == 4);
1094 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1095 int index = Smi::cast(args[1])->value();
1096 Handle<String> pattern = args.at<String>(2);
1097 Handle<String> flags = args.at<String>(3);
1098
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001099 // Get the RegExp function from the context in the literals array.
1100 // This is the RegExp function from the context in which the
1101 // function was created. We do not use the RegExp function from the
1102 // current global context because this might be the RegExp function
1103 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001104 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001105 Handle<JSFunction>(
1106 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001107 // Compute the regular expression literal.
1108 bool has_pending_exception;
1109 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001110 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1111 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001112 if (has_pending_exception) {
1113 ASSERT(Top::has_pending_exception());
1114 return Failure::Exception();
1115 }
1116 literals->set(index, *regexp);
1117 return *regexp;
1118}
1119
1120
1121static Object* Runtime_FunctionGetName(Arguments args) {
1122 NoHandleAllocation ha;
1123 ASSERT(args.length() == 1);
1124
1125 CONVERT_CHECKED(JSFunction, f, args[0]);
1126 return f->shared()->name();
1127}
1128
1129
ager@chromium.org236ad962008-09-25 09:45:57 +00001130static Object* Runtime_FunctionSetName(Arguments args) {
1131 NoHandleAllocation ha;
1132 ASSERT(args.length() == 2);
1133
1134 CONVERT_CHECKED(JSFunction, f, args[0]);
1135 CONVERT_CHECKED(String, name, args[1]);
1136 f->shared()->set_name(name);
1137 return Heap::undefined_value();
1138}
1139
1140
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001141static Object* Runtime_FunctionGetScript(Arguments args) {
1142 HandleScope scope;
1143 ASSERT(args.length() == 1);
1144
1145 CONVERT_CHECKED(JSFunction, fun, args[0]);
1146 Handle<Object> script = Handle<Object>(fun->shared()->script());
1147 if (!script->IsScript()) return Heap::undefined_value();
1148
1149 return *GetScriptWrapper(Handle<Script>::cast(script));
1150}
1151
1152
1153static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1154 NoHandleAllocation ha;
1155 ASSERT(args.length() == 1);
1156
1157 CONVERT_CHECKED(JSFunction, f, args[0]);
1158 return f->shared()->GetSourceCode();
1159}
1160
1161
1162static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1163 NoHandleAllocation ha;
1164 ASSERT(args.length() == 1);
1165
1166 CONVERT_CHECKED(JSFunction, fun, args[0]);
1167 int pos = fun->shared()->start_position();
1168 return Smi::FromInt(pos);
1169}
1170
1171
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001172static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1173 ASSERT(args.length() == 2);
1174
1175 CONVERT_CHECKED(JSFunction, fun, args[0]);
1176 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1177
1178 Code* code = fun->code();
1179 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1180
1181 Address pc = code->address() + offset;
1182 return Smi::FromInt(fun->code()->SourcePosition(pc));
1183}
1184
1185
1186
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001187static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1188 NoHandleAllocation ha;
1189 ASSERT(args.length() == 2);
1190
1191 CONVERT_CHECKED(JSFunction, fun, args[0]);
1192 CONVERT_CHECKED(String, name, args[1]);
1193 fun->SetInstanceClassName(name);
1194 return Heap::undefined_value();
1195}
1196
1197
1198static Object* Runtime_FunctionSetLength(Arguments args) {
1199 NoHandleAllocation ha;
1200 ASSERT(args.length() == 2);
1201
1202 CONVERT_CHECKED(JSFunction, fun, args[0]);
1203 CONVERT_CHECKED(Smi, length, args[1]);
1204 fun->shared()->set_length(length->value());
1205 return length;
1206}
1207
1208
1209static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001210 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001211 ASSERT(args.length() == 2);
1212
1213 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001214 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1215 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001216 return args[0]; // return TOS
1217}
1218
1219
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001220static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1221 NoHandleAllocation ha;
1222 ASSERT(args.length() == 1);
1223
1224 CONVERT_CHECKED(JSFunction, f, args[0]);
1225 // The function_data field of the shared function info is used exclusively by
1226 // the API.
1227 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1228 : Heap::false_value();
1229}
1230
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001231static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1232 NoHandleAllocation ha;
1233 ASSERT(args.length() == 1);
1234
1235 CONVERT_CHECKED(JSFunction, f, args[0]);
1236 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1237}
1238
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001239
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001240static Object* Runtime_SetCode(Arguments args) {
1241 HandleScope scope;
1242 ASSERT(args.length() == 2);
1243
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001244 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001245 Handle<Object> code = args.at<Object>(1);
1246
1247 Handle<Context> context(target->context());
1248
1249 if (!code->IsNull()) {
1250 RUNTIME_ASSERT(code->IsJSFunction());
1251 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1252 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1253 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1254 return Failure::Exception();
1255 }
1256 // Set the code, formal parameter count, and the length of the target
1257 // function.
1258 target->set_code(fun->code());
1259 target->shared()->set_length(fun->shared()->length());
1260 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001261 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001262 // Set the source code of the target function to undefined.
1263 // SetCode is only used for built-in constructors like String,
1264 // Array, and Object, and some web code
1265 // doesn't like seeing source code for constructors.
1266 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001267 // Clear the optimization hints related to the compiled code as these are no
1268 // longer valid when the code is overwritten.
1269 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001270 context = Handle<Context>(fun->context());
1271
1272 // Make sure we get a fresh copy of the literal vector to avoid
1273 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001274 int number_of_literals = fun->NumberOfLiterals();
1275 Handle<FixedArray> literals =
1276 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001277 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001278 // Insert the object, regexp and array functions in the literals
1279 // array prefix. These are the functions that will be used when
1280 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001281 literals->set(JSFunction::kLiteralGlobalContextIndex,
1282 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001283 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001284 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001285 }
1286
1287 target->set_context(*context);
1288 return *target;
1289}
1290
1291
1292static Object* CharCodeAt(String* subject, Object* index) {
1293 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001294 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001295 // Flatten the string. If someone wants to get a char at an index
1296 // in a cons string, it is likely that more indices will be
1297 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001298 Object* flat = subject->TryFlatten();
1299 if (flat->IsFailure()) return flat;
1300 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001301 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001302 return Heap::nan_value();
1303 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001304 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001305}
1306
1307
1308static Object* Runtime_StringCharCodeAt(Arguments args) {
1309 NoHandleAllocation ha;
1310 ASSERT(args.length() == 2);
1311
1312 CONVERT_CHECKED(String, subject, args[0]);
1313 Object* index = args[1];
1314 return CharCodeAt(subject, index);
1315}
1316
1317
1318static Object* Runtime_CharFromCode(Arguments args) {
1319 NoHandleAllocation ha;
1320 ASSERT(args.length() == 1);
1321 uint32_t code;
1322 if (Array::IndexFromObject(args[0], &code)) {
1323 if (code <= 0xffff) {
1324 return Heap::LookupSingleCharacterStringFromCode(code);
1325 }
1326 }
1327 return Heap::empty_string();
1328}
1329
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001330// Forward declarations.
1331static const int kStringBuilderConcatHelperLengthBits = 11;
1332static const int kStringBuilderConcatHelperPositionBits = 19;
1333
1334template <typename schar>
1335static inline void StringBuilderConcatHelper(String*,
1336 schar*,
1337 FixedArray*,
1338 int);
1339
1340typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1341typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1342
1343class ReplacementStringBuilder {
1344 public:
1345 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1346 : subject_(subject),
1347 parts_(Factory::NewFixedArray(estimated_part_count)),
1348 part_count_(0),
1349 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001350 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001351 // Require a non-zero initial size. Ensures that doubling the size to
1352 // extend the array will work.
1353 ASSERT(estimated_part_count > 0);
1354 }
1355
1356 void EnsureCapacity(int elements) {
1357 int length = parts_->length();
1358 int required_length = part_count_ + elements;
1359 if (length < required_length) {
1360 int new_length = length;
1361 do {
1362 new_length *= 2;
1363 } while (new_length < required_length);
1364 Handle<FixedArray> extended_array =
1365 Factory::NewFixedArray(new_length);
1366 parts_->CopyTo(0, *extended_array, 0, part_count_);
1367 parts_ = extended_array;
1368 }
1369 }
1370
1371 void AddSubjectSlice(int from, int to) {
1372 ASSERT(from >= 0);
1373 int length = to - from;
1374 ASSERT(length > 0);
1375 // Can we encode the slice in 11 bits for length and 19 bits for
1376 // start position - as used by StringBuilderConcatHelper?
1377 if (StringBuilderSubstringLength::is_valid(length) &&
1378 StringBuilderSubstringPosition::is_valid(from)) {
1379 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1380 StringBuilderSubstringPosition::encode(from);
1381 AddElement(Smi::FromInt(encoded_slice));
1382 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001383 // Otherwise encode as two smis.
1384 AddElement(Smi::FromInt(-length));
1385 AddElement(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001386 }
1387 IncrementCharacterCount(length);
1388 }
1389
1390
1391 void AddString(Handle<String> string) {
1392 int length = string->length();
1393 ASSERT(length > 0);
1394 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001395 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001396 is_ascii_ = false;
1397 }
1398 IncrementCharacterCount(length);
1399 }
1400
1401
1402 Handle<String> ToString() {
1403 if (part_count_ == 0) {
1404 return Factory::empty_string();
1405 }
1406
1407 Handle<String> joined_string;
1408 if (is_ascii_) {
1409 joined_string = NewRawAsciiString(character_count_);
1410 AssertNoAllocation no_alloc;
1411 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1412 char* char_buffer = seq->GetChars();
1413 StringBuilderConcatHelper(*subject_,
1414 char_buffer,
1415 *parts_,
1416 part_count_);
1417 } else {
1418 // Non-ASCII.
1419 joined_string = NewRawTwoByteString(character_count_);
1420 AssertNoAllocation no_alloc;
1421 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1422 uc16* char_buffer = seq->GetChars();
1423 StringBuilderConcatHelper(*subject_,
1424 char_buffer,
1425 *parts_,
1426 part_count_);
1427 }
1428 return joined_string;
1429 }
1430
1431
1432 void IncrementCharacterCount(int by) {
1433 if (character_count_ > Smi::kMaxValue - by) {
1434 V8::FatalProcessOutOfMemory("String.replace result too large.");
1435 }
1436 character_count_ += by;
1437 }
1438
1439 private:
1440
1441 Handle<String> NewRawAsciiString(int size) {
1442 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1443 }
1444
1445
1446 Handle<String> NewRawTwoByteString(int size) {
1447 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1448 }
1449
1450
1451 void AddElement(Object* element) {
1452 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001453 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001454 parts_->set(part_count_, element);
1455 part_count_++;
1456 }
1457
1458 Handle<String> subject_;
1459 Handle<FixedArray> parts_;
1460 int part_count_;
1461 int character_count_;
1462 bool is_ascii_;
1463};
1464
1465
1466class CompiledReplacement {
1467 public:
1468 CompiledReplacement()
1469 : parts_(1), replacement_substrings_(0) {}
1470
1471 void Compile(Handle<String> replacement,
1472 int capture_count,
1473 int subject_length);
1474
1475 void Apply(ReplacementStringBuilder* builder,
1476 int match_from,
1477 int match_to,
1478 Handle<JSArray> last_match_info);
1479
1480 // Number of distinct parts of the replacement pattern.
1481 int parts() {
1482 return parts_.length();
1483 }
1484 private:
1485 enum PartType {
1486 SUBJECT_PREFIX = 1,
1487 SUBJECT_SUFFIX,
1488 SUBJECT_CAPTURE,
1489 REPLACEMENT_SUBSTRING,
1490 REPLACEMENT_STRING,
1491
1492 NUMBER_OF_PART_TYPES
1493 };
1494
1495 struct ReplacementPart {
1496 static inline ReplacementPart SubjectMatch() {
1497 return ReplacementPart(SUBJECT_CAPTURE, 0);
1498 }
1499 static inline ReplacementPart SubjectCapture(int capture_index) {
1500 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1501 }
1502 static inline ReplacementPart SubjectPrefix() {
1503 return ReplacementPart(SUBJECT_PREFIX, 0);
1504 }
1505 static inline ReplacementPart SubjectSuffix(int subject_length) {
1506 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1507 }
1508 static inline ReplacementPart ReplacementString() {
1509 return ReplacementPart(REPLACEMENT_STRING, 0);
1510 }
1511 static inline ReplacementPart ReplacementSubString(int from, int to) {
1512 ASSERT(from >= 0);
1513 ASSERT(to > from);
1514 return ReplacementPart(-from, to);
1515 }
1516
1517 // If tag <= 0 then it is the negation of a start index of a substring of
1518 // the replacement pattern, otherwise it's a value from PartType.
1519 ReplacementPart(int tag, int data)
1520 : tag(tag), data(data) {
1521 // Must be non-positive or a PartType value.
1522 ASSERT(tag < NUMBER_OF_PART_TYPES);
1523 }
1524 // Either a value of PartType or a non-positive number that is
1525 // the negation of an index into the replacement string.
1526 int tag;
1527 // The data value's interpretation depends on the value of tag:
1528 // tag == SUBJECT_PREFIX ||
1529 // tag == SUBJECT_SUFFIX: data is unused.
1530 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1531 // tag == REPLACEMENT_SUBSTRING ||
1532 // tag == REPLACEMENT_STRING: data is index into array of substrings
1533 // of the replacement string.
1534 // tag <= 0: Temporary representation of the substring of the replacement
1535 // string ranging over -tag .. data.
1536 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1537 // substring objects.
1538 int data;
1539 };
1540
1541 template<typename Char>
1542 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1543 Vector<Char> characters,
1544 int capture_count,
1545 int subject_length) {
1546 int length = characters.length();
1547 int last = 0;
1548 for (int i = 0; i < length; i++) {
1549 Char c = characters[i];
1550 if (c == '$') {
1551 int next_index = i + 1;
1552 if (next_index == length) { // No next character!
1553 break;
1554 }
1555 Char c2 = characters[next_index];
1556 switch (c2) {
1557 case '$':
1558 if (i > last) {
1559 // There is a substring before. Include the first "$".
1560 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1561 last = next_index + 1; // Continue after the second "$".
1562 } else {
1563 // Let the next substring start with the second "$".
1564 last = next_index;
1565 }
1566 i = next_index;
1567 break;
1568 case '`':
1569 if (i > last) {
1570 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1571 }
1572 parts->Add(ReplacementPart::SubjectPrefix());
1573 i = next_index;
1574 last = i + 1;
1575 break;
1576 case '\'':
1577 if (i > last) {
1578 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1579 }
1580 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1581 i = next_index;
1582 last = i + 1;
1583 break;
1584 case '&':
1585 if (i > last) {
1586 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1587 }
1588 parts->Add(ReplacementPart::SubjectMatch());
1589 i = next_index;
1590 last = i + 1;
1591 break;
1592 case '0':
1593 case '1':
1594 case '2':
1595 case '3':
1596 case '4':
1597 case '5':
1598 case '6':
1599 case '7':
1600 case '8':
1601 case '9': {
1602 int capture_ref = c2 - '0';
1603 if (capture_ref > capture_count) {
1604 i = next_index;
1605 continue;
1606 }
1607 int second_digit_index = next_index + 1;
1608 if (second_digit_index < length) {
1609 // Peek ahead to see if we have two digits.
1610 Char c3 = characters[second_digit_index];
1611 if ('0' <= c3 && c3 <= '9') { // Double digits.
1612 int double_digit_ref = capture_ref * 10 + c3 - '0';
1613 if (double_digit_ref <= capture_count) {
1614 next_index = second_digit_index;
1615 capture_ref = double_digit_ref;
1616 }
1617 }
1618 }
1619 if (capture_ref > 0) {
1620 if (i > last) {
1621 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1622 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001623 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001624 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1625 last = next_index + 1;
1626 }
1627 i = next_index;
1628 break;
1629 }
1630 default:
1631 i = next_index;
1632 break;
1633 }
1634 }
1635 }
1636 if (length > last) {
1637 if (last == 0) {
1638 parts->Add(ReplacementPart::ReplacementString());
1639 } else {
1640 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1641 }
1642 }
1643 }
1644
1645 ZoneList<ReplacementPart> parts_;
1646 ZoneList<Handle<String> > replacement_substrings_;
1647};
1648
1649
1650void CompiledReplacement::Compile(Handle<String> replacement,
1651 int capture_count,
1652 int subject_length) {
1653 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001654 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001655 AssertNoAllocation no_alloc;
1656 ParseReplacementPattern(&parts_,
1657 replacement->ToAsciiVector(),
1658 capture_count,
1659 subject_length);
1660 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001661 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001662 AssertNoAllocation no_alloc;
1663
1664 ParseReplacementPattern(&parts_,
1665 replacement->ToUC16Vector(),
1666 capture_count,
1667 subject_length);
1668 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001669 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001670 int substring_index = 0;
1671 for (int i = 0, n = parts_.length(); i < n; i++) {
1672 int tag = parts_[i].tag;
1673 if (tag <= 0) { // A replacement string slice.
1674 int from = -tag;
1675 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001676 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001677 parts_[i].tag = REPLACEMENT_SUBSTRING;
1678 parts_[i].data = substring_index;
1679 substring_index++;
1680 } else if (tag == REPLACEMENT_STRING) {
1681 replacement_substrings_.Add(replacement);
1682 parts_[i].data = substring_index;
1683 substring_index++;
1684 }
1685 }
1686}
1687
1688
1689void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1690 int match_from,
1691 int match_to,
1692 Handle<JSArray> last_match_info) {
1693 for (int i = 0, n = parts_.length(); i < n; i++) {
1694 ReplacementPart part = parts_[i];
1695 switch (part.tag) {
1696 case SUBJECT_PREFIX:
1697 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1698 break;
1699 case SUBJECT_SUFFIX: {
1700 int subject_length = part.data;
1701 if (match_to < subject_length) {
1702 builder->AddSubjectSlice(match_to, subject_length);
1703 }
1704 break;
1705 }
1706 case SUBJECT_CAPTURE: {
1707 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001708 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001709 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1710 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1711 if (from >= 0 && to > from) {
1712 builder->AddSubjectSlice(from, to);
1713 }
1714 break;
1715 }
1716 case REPLACEMENT_SUBSTRING:
1717 case REPLACEMENT_STRING:
1718 builder->AddString(replacement_substrings_[part.data]);
1719 break;
1720 default:
1721 UNREACHABLE();
1722 }
1723 }
1724}
1725
1726
1727
1728static Object* StringReplaceRegExpWithString(String* subject,
1729 JSRegExp* regexp,
1730 String* replacement,
1731 JSArray* last_match_info) {
1732 ASSERT(subject->IsFlat());
1733 ASSERT(replacement->IsFlat());
1734
1735 HandleScope handles;
1736
1737 int length = subject->length();
1738 Handle<String> subject_handle(subject);
1739 Handle<JSRegExp> regexp_handle(regexp);
1740 Handle<String> replacement_handle(replacement);
1741 Handle<JSArray> last_match_info_handle(last_match_info);
1742 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1743 subject_handle,
1744 0,
1745 last_match_info_handle);
1746 if (match.is_null()) {
1747 return Failure::Exception();
1748 }
1749 if (match->IsNull()) {
1750 return *subject_handle;
1751 }
1752
1753 int capture_count = regexp_handle->CaptureCount();
1754
1755 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001756 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001757 CompiledReplacement compiled_replacement;
1758 compiled_replacement.Compile(replacement_handle,
1759 capture_count,
1760 length);
1761
1762 bool is_global = regexp_handle->GetFlags().is_global();
1763
1764 // Guessing the number of parts that the final result string is built
1765 // from. Global regexps can match any number of times, so we guess
1766 // conservatively.
1767 int expected_parts =
1768 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1769 ReplacementStringBuilder builder(subject_handle, expected_parts);
1770
1771 // Index of end of last match.
1772 int prev = 0;
1773
ager@chromium.org6141cbe2009-11-20 12:14:52 +00001774 // Number of parts added by compiled replacement plus preceeding
1775 // string and possibly suffix after last match. It is possible for
1776 // all components to use two elements when encoded as two smis.
1777 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001778 bool matched = true;
1779 do {
1780 ASSERT(last_match_info_handle->HasFastElements());
1781 // Increase the capacity of the builder before entering local handle-scope,
1782 // so its internal buffer can safely allocate a new handle if it grows.
1783 builder.EnsureCapacity(parts_added_per_loop);
1784
1785 HandleScope loop_scope;
1786 int start, end;
1787 {
1788 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001789 FixedArray* match_info_array =
1790 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001791
1792 ASSERT_EQ(capture_count * 2 + 2,
1793 RegExpImpl::GetLastCaptureCount(match_info_array));
1794 start = RegExpImpl::GetCapture(match_info_array, 0);
1795 end = RegExpImpl::GetCapture(match_info_array, 1);
1796 }
1797
1798 if (prev < start) {
1799 builder.AddSubjectSlice(prev, start);
1800 }
1801 compiled_replacement.Apply(&builder,
1802 start,
1803 end,
1804 last_match_info_handle);
1805 prev = end;
1806
1807 // Only continue checking for global regexps.
1808 if (!is_global) break;
1809
1810 // Continue from where the match ended, unless it was an empty match.
1811 int next = end;
1812 if (start == end) {
1813 next = end + 1;
1814 if (next > length) break;
1815 }
1816
1817 match = RegExpImpl::Exec(regexp_handle,
1818 subject_handle,
1819 next,
1820 last_match_info_handle);
1821 if (match.is_null()) {
1822 return Failure::Exception();
1823 }
1824 matched = !match->IsNull();
1825 } while (matched);
1826
1827 if (prev < length) {
1828 builder.AddSubjectSlice(prev, length);
1829 }
1830
1831 return *(builder.ToString());
1832}
1833
1834
1835static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1836 ASSERT(args.length() == 4);
1837
1838 CONVERT_CHECKED(String, subject, args[0]);
1839 if (!subject->IsFlat()) {
1840 Object* flat_subject = subject->TryFlatten();
1841 if (flat_subject->IsFailure()) {
1842 return flat_subject;
1843 }
1844 subject = String::cast(flat_subject);
1845 }
1846
1847 CONVERT_CHECKED(String, replacement, args[2]);
1848 if (!replacement->IsFlat()) {
1849 Object* flat_replacement = replacement->TryFlatten();
1850 if (flat_replacement->IsFailure()) {
1851 return flat_replacement;
1852 }
1853 replacement = String::cast(flat_replacement);
1854 }
1855
1856 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1857 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1858
1859 ASSERT(last_match_info->HasFastElements());
1860
1861 return StringReplaceRegExpWithString(subject,
1862 regexp,
1863 replacement,
1864 last_match_info);
1865}
1866
1867
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001868
ager@chromium.org7c537e22008-10-16 08:43:32 +00001869// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1870// limit, we can fix the size of tables.
1871static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001872// Reduce alphabet to this size.
1873static const int kBMAlphabetSize = 0x100;
1874// For patterns below this length, the skip length of Boyer-Moore is too short
1875// to compensate for the algorithmic overhead compared to simple brute force.
1876static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001877
ager@chromium.org7c537e22008-10-16 08:43:32 +00001878// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1879// shift. Only allows the last kBMMaxShift characters of the needle
1880// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001881class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001882 public:
1883 BMGoodSuffixBuffers() {}
1884 inline void init(int needle_length) {
1885 ASSERT(needle_length > 1);
1886 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1887 int len = needle_length - start;
1888 biased_suffixes_ = suffixes_ - start;
1889 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1890 for (int i = 0; i <= len; i++) {
1891 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001892 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001893 }
1894 inline int& suffix(int index) {
1895 ASSERT(biased_suffixes_ + index >= suffixes_);
1896 return biased_suffixes_[index];
1897 }
1898 inline int& shift(int index) {
1899 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1900 return biased_good_suffix_shift_[index];
1901 }
1902 private:
1903 int suffixes_[kBMMaxShift + 1];
1904 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001905 int* biased_suffixes_;
1906 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001907 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1908};
1909
1910// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001911static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001912static BMGoodSuffixBuffers bmgs_buffers;
1913
1914// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001915template <typename pchar>
1916static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1917 int start) {
1918 // Run forwards to populate bad_char_table, so that *last* instance
1919 // of character equivalence class is the one registered.
1920 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001921 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1922 : kBMAlphabetSize;
1923 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001924 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001925 } else {
1926 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001927 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001928 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001929 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001930 for (int i = start; i < pattern.length() - 1; i++) {
1931 pchar c = pattern[i];
1932 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001933 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001934 }
1935}
1936
1937template <typename pchar>
1938static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001939 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001940 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001941 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001942 // Compute Good Suffix tables.
1943 bmgs_buffers.init(m);
1944
1945 bmgs_buffers.shift(m-1) = 1;
1946 bmgs_buffers.suffix(m) = m + 1;
1947 pchar last_char = pattern[m - 1];
1948 int suffix = m + 1;
1949 for (int i = m; i > start;) {
1950 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1951 if (bmgs_buffers.shift(suffix) == len) {
1952 bmgs_buffers.shift(suffix) = suffix - i;
1953 }
1954 suffix = bmgs_buffers.suffix(suffix);
1955 }
1956 i--;
1957 suffix--;
1958 bmgs_buffers.suffix(i) = suffix;
1959 if (suffix == m) {
1960 // No suffix to extend, so we check against last_char only.
1961 while (i > start && pattern[i - 1] != last_char) {
1962 if (bmgs_buffers.shift(m) == len) {
1963 bmgs_buffers.shift(m) = m - i;
1964 }
1965 i--;
1966 bmgs_buffers.suffix(i) = m;
1967 }
1968 if (i > start) {
1969 i--;
1970 suffix--;
1971 bmgs_buffers.suffix(i) = suffix;
1972 }
1973 }
1974 }
1975 if (suffix < m) {
1976 for (int i = start; i <= m; i++) {
1977 if (bmgs_buffers.shift(i) == len) {
1978 bmgs_buffers.shift(i) = suffix - start;
1979 }
1980 if (i == suffix) {
1981 suffix = bmgs_buffers.suffix(suffix);
1982 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001983 }
1984 }
1985}
1986
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001987template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001988static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001989 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001990 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001991 }
1992 if (sizeof(pchar) == 1) {
1993 if (char_code > String::kMaxAsciiCharCode) {
1994 return -1;
1995 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001996 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001997 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001998 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001999}
2000
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002001// Restricted simplified Boyer-Moore string matching.
2002// Uses only the bad-shift table of Boyer-Moore and only uses it
2003// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002004template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002005static int BoyerMooreHorspool(Vector<const schar> subject,
2006 Vector<const pchar> pattern,
2007 int start_index,
2008 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002009 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002010 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002011 // Only preprocess at most kBMMaxShift last characters of pattern.
2012 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002013
ager@chromium.org7c537e22008-10-16 08:43:32 +00002014 BoyerMoorePopulateBadCharTable(pattern, start);
2015
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002016 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002017 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002018 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002019 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002020 // Perform search
2021 for (idx = start_index; idx <= n - m;) {
2022 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002023 int c;
2024 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002025 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002026 int shift = j - bc_occ;
2027 idx += shift;
2028 badness += 1 - shift; // at most zero, so badness cannot increase.
2029 if (idx > n - m) {
2030 *complete = true;
2031 return -1;
2032 }
2033 }
2034 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002035 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002036 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002037 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002038 return idx;
2039 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002040 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002041 // Badness increases by the number of characters we have
2042 // checked, and decreases by the number of characters we
2043 // can skip by shifting. It's a measure of how we are doing
2044 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002045 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002046 if (badness > 0) {
2047 *complete = false;
2048 return idx;
2049 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002050 }
2051 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002052 *complete = true;
2053 return -1;
2054}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002055
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002056
2057template <typename schar, typename pchar>
2058static int BoyerMooreIndexOf(Vector<const schar> subject,
2059 Vector<const pchar> pattern,
2060 int idx) {
2061 int n = subject.length();
2062 int m = pattern.length();
2063 // Only preprocess at most kBMMaxShift last characters of pattern.
2064 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2065
2066 // Build the Good Suffix table and continue searching.
2067 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2068 pchar last_char = pattern[m - 1];
2069 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002070 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002071 int j = m - 1;
2072 schar c;
2073 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002074 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002075 idx += shift;
2076 if (idx > n - m) {
2077 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002078 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002079 }
2080 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2081 if (j < 0) {
2082 return idx;
2083 } else if (j < start) {
2084 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002085 // Fall back on BMH shift.
2086 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002087 } else {
2088 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002089 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002090 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002091 if (gs_shift > shift) {
2092 shift = gs_shift;
2093 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002094 idx += shift;
2095 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002096 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002097
2098 return -1;
2099}
2100
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002101
2102template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002103static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002104 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002105 int start_index) {
2106 for (int i = start_index, n = string.length(); i < n; i++) {
2107 if (pattern_char == string[i]) {
2108 return i;
2109 }
2110 }
2111 return -1;
2112}
2113
2114// Trivial string search for shorter strings.
2115// On return, if "complete" is set to true, the return value is the
2116// final result of searching for the patter in the subject.
2117// If "complete" is set to false, the return value is the index where
2118// further checking should start, i.e., it's guaranteed that the pattern
2119// does not occur at a position prior to the returned index.
2120template <typename pchar, typename schar>
2121static int SimpleIndexOf(Vector<const schar> subject,
2122 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002123 int idx,
2124 bool* complete) {
2125 // Badness is a count of how much work we have done. When we have
2126 // done enough work we decide it's probably worth switching to a better
2127 // algorithm.
2128 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002129 // We know our pattern is at least 2 characters, we cache the first so
2130 // the common case of the first character not matching is faster.
2131 pchar pattern_first_char = pattern[0];
2132
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002133 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2134 badness++;
2135 if (badness > 0) {
2136 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002137 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002138 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002139 if (subject[i] != pattern_first_char) continue;
2140 int j = 1;
2141 do {
2142 if (pattern[j] != subject[i+j]) {
2143 break;
2144 }
2145 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002146 } while (j < pattern.length());
2147 if (j == pattern.length()) {
2148 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002149 return i;
2150 }
2151 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002152 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002153 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002154 return -1;
2155}
2156
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002157// Simple indexOf that never bails out. For short patterns only.
2158template <typename pchar, typename schar>
2159static int SimpleIndexOf(Vector<const schar> subject,
2160 Vector<const pchar> pattern,
2161 int idx) {
2162 pchar pattern_first_char = pattern[0];
2163 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2164 if (subject[i] != pattern_first_char) continue;
2165 int j = 1;
2166 do {
2167 if (pattern[j] != subject[i+j]) {
2168 break;
2169 }
2170 j++;
2171 } while (j < pattern.length());
2172 if (j == pattern.length()) {
2173 return i;
2174 }
2175 }
2176 return -1;
2177}
2178
2179
2180// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002181template <typename schar, typename pchar>
2182static int StringMatchStrategy(Vector<const schar> sub,
2183 Vector<const pchar> pat,
2184 int start_index) {
2185 ASSERT(pat.length() > 1);
2186
2187 // We have an ASCII haystack and a non-ASCII needle. Check if there
2188 // really is a non-ASCII character in the needle and bail out if there
2189 // is.
2190 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2191 for (int i = 0; i < pat.length(); i++) {
2192 uc16 c = pat[i];
2193 if (c > String::kMaxAsciiCharCode) {
2194 return -1;
2195 }
2196 }
2197 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002198 if (pat.length() < kBMMinPatternLength) {
2199 // We don't believe fancy searching can ever be more efficient.
2200 // The max shift of Boyer-Moore on a pattern of this length does
2201 // not compensate for the overhead.
2202 return SimpleIndexOf(sub, pat, start_index);
2203 }
2204 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002205 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002206 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2207 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002208 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002209 if (complete) return idx;
2210 return BoyerMooreIndexOf(sub, pat, idx);
2211}
2212
2213// Perform string match of pattern on subject, starting at start index.
2214// Caller must ensure that 0 <= start_index <= sub->length(),
2215// and should check that pat->length() + start_index <= sub->length()
2216int Runtime::StringMatch(Handle<String> sub,
2217 Handle<String> pat,
2218 int start_index) {
2219 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002220 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002221
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002222 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002223 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002224
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002225 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002226 if (start_index + pattern_length > subject_length) return -1;
2227
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002228 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002229 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002230 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002231 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002232 // character patterns linear search is necessary, so any smart
2233 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002234 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002235 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002236 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002237 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002238 if (pchar > String::kMaxAsciiCharCode) {
2239 return -1;
2240 }
2241 Vector<const char> ascii_vector =
2242 sub->ToAsciiVector().SubVector(start_index, subject_length);
2243 const void* pos = memchr(ascii_vector.start(),
2244 static_cast<const char>(pchar),
2245 static_cast<size_t>(ascii_vector.length()));
2246 if (pos == NULL) {
2247 return -1;
2248 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002249 return static_cast<int>(reinterpret_cast<const char*>(pos)
2250 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002251 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002252 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002253 }
2254
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002255 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002256 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002257 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002258
ager@chromium.org7c537e22008-10-16 08:43:32 +00002259 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2260 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002261 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002262 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002263 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002264 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002265 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002266 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002267 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002268 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002269 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002270 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002271 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002272 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002273}
2274
2275
2276static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002277 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002278 ASSERT(args.length() == 3);
2279
ager@chromium.org7c537e22008-10-16 08:43:32 +00002280 CONVERT_ARG_CHECKED(String, sub, 0);
2281 CONVERT_ARG_CHECKED(String, pat, 1);
2282
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002283 Object* index = args[2];
2284 uint32_t start_index;
2285 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2286
ager@chromium.org870a0b62008-11-04 11:43:05 +00002287 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002288 int position = Runtime::StringMatch(sub, pat, start_index);
2289 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002290}
2291
2292
2293static Object* Runtime_StringLastIndexOf(Arguments args) {
2294 NoHandleAllocation ha;
2295 ASSERT(args.length() == 3);
2296
2297 CONVERT_CHECKED(String, sub, args[0]);
2298 CONVERT_CHECKED(String, pat, args[1]);
2299 Object* index = args[2];
2300
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002301 sub->TryFlattenIfNotFlat();
2302 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002303
2304 uint32_t start_index;
2305 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2306
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002307 uint32_t pattern_length = pat->length();
2308 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002309
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002310 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002311 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002312 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002313
2314 for (int i = start_index; i >= 0; i--) {
2315 bool found = true;
2316 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002317 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002318 found = false;
2319 break;
2320 }
2321 }
2322 if (found) return Smi::FromInt(i);
2323 }
2324
2325 return Smi::FromInt(-1);
2326}
2327
2328
2329static Object* Runtime_StringLocaleCompare(Arguments args) {
2330 NoHandleAllocation ha;
2331 ASSERT(args.length() == 2);
2332
2333 CONVERT_CHECKED(String, str1, args[0]);
2334 CONVERT_CHECKED(String, str2, args[1]);
2335
2336 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002337 int str1_length = str1->length();
2338 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002339
2340 // Decide trivial cases without flattening.
2341 if (str1_length == 0) {
2342 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2343 return Smi::FromInt(-str2_length);
2344 } else {
2345 if (str2_length == 0) return Smi::FromInt(str1_length);
2346 }
2347
2348 int end = str1_length < str2_length ? str1_length : str2_length;
2349
2350 // No need to flatten if we are going to find the answer on the first
2351 // character. At this point we know there is at least one character
2352 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002353 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002354 if (d != 0) return Smi::FromInt(d);
2355
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002356 str1->TryFlattenIfNotFlat();
2357 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002358
2359 static StringInputBuffer buf1;
2360 static StringInputBuffer buf2;
2361
2362 buf1.Reset(str1);
2363 buf2.Reset(str2);
2364
2365 for (int i = 0; i < end; i++) {
2366 uint16_t char1 = buf1.GetNext();
2367 uint16_t char2 = buf2.GetNext();
2368 if (char1 != char2) return Smi::FromInt(char1 - char2);
2369 }
2370
2371 return Smi::FromInt(str1_length - str2_length);
2372}
2373
2374
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002375static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002376 NoHandleAllocation ha;
2377 ASSERT(args.length() == 3);
2378
2379 CONVERT_CHECKED(String, value, args[0]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002380 Object* from = args[1];
2381 Object* to = args[2];
2382 int start, end;
2383 // We have a fast integer-only case here to avoid a conversion to double in
2384 // the common case where from and to are Smis.
2385 if (from->IsSmi() && to->IsSmi()) {
2386 start = Smi::cast(from)->value();
2387 end = Smi::cast(to)->value();
2388 } else {
2389 CONVERT_DOUBLE_CHECKED(from_number, from);
2390 CONVERT_DOUBLE_CHECKED(to_number, to);
2391 start = FastD2I(from_number);
2392 end = FastD2I(to_number);
2393 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002394 RUNTIME_ASSERT(end >= start);
2395 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002396 RUNTIME_ASSERT(end <= value->length());
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002397 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002398}
2399
2400
ager@chromium.org41826e72009-03-30 13:30:57 +00002401static Object* Runtime_StringMatch(Arguments args) {
2402 ASSERT_EQ(3, args.length());
2403
2404 CONVERT_ARG_CHECKED(String, subject, 0);
2405 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2406 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2407 HandleScope handles;
2408
2409 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2410
2411 if (match.is_null()) {
2412 return Failure::Exception();
2413 }
2414 if (match->IsNull()) {
2415 return Heap::null_value();
2416 }
2417 int length = subject->length();
2418
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002419 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002420 ZoneList<int> offsets(8);
2421 do {
2422 int start;
2423 int end;
2424 {
2425 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002426 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002427 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2428 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2429 }
2430 offsets.Add(start);
2431 offsets.Add(end);
2432 int index = start < end ? end : end + 1;
2433 if (index > length) break;
2434 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2435 if (match.is_null()) {
2436 return Failure::Exception();
2437 }
2438 } while (!match->IsNull());
2439 int matches = offsets.length() / 2;
2440 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2441 for (int i = 0; i < matches ; i++) {
2442 int from = offsets.at(i * 2);
2443 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002444 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00002445 }
2446 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2447 result->set_length(Smi::FromInt(matches));
2448 return *result;
2449}
2450
2451
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002452static Object* Runtime_NumberToRadixString(Arguments args) {
2453 NoHandleAllocation ha;
2454 ASSERT(args.length() == 2);
2455
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002456 // Fast case where the result is a one character string.
2457 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2458 int value = Smi::cast(args[0])->value();
2459 int radix = Smi::cast(args[1])->value();
2460 if (value >= 0 && value < radix) {
2461 RUNTIME_ASSERT(radix <= 36);
2462 // Character array used for conversion.
2463 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2464 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2465 }
2466 }
2467
2468 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002469 CONVERT_DOUBLE_CHECKED(value, args[0]);
2470 if (isnan(value)) {
2471 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2472 }
2473 if (isinf(value)) {
2474 if (value < 0) {
2475 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2476 }
2477 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2478 }
2479 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2480 int radix = FastD2I(radix_number);
2481 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2482 char* str = DoubleToRadixCString(value, radix);
2483 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2484 DeleteArray(str);
2485 return result;
2486}
2487
2488
2489static Object* Runtime_NumberToFixed(Arguments args) {
2490 NoHandleAllocation ha;
2491 ASSERT(args.length() == 2);
2492
2493 CONVERT_DOUBLE_CHECKED(value, args[0]);
2494 if (isnan(value)) {
2495 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2496 }
2497 if (isinf(value)) {
2498 if (value < 0) {
2499 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2500 }
2501 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2502 }
2503 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2504 int f = FastD2I(f_number);
2505 RUNTIME_ASSERT(f >= 0);
2506 char* str = DoubleToFixedCString(value, f);
2507 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2508 DeleteArray(str);
2509 return res;
2510}
2511
2512
2513static Object* Runtime_NumberToExponential(Arguments args) {
2514 NoHandleAllocation ha;
2515 ASSERT(args.length() == 2);
2516
2517 CONVERT_DOUBLE_CHECKED(value, args[0]);
2518 if (isnan(value)) {
2519 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2520 }
2521 if (isinf(value)) {
2522 if (value < 0) {
2523 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2524 }
2525 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2526 }
2527 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2528 int f = FastD2I(f_number);
2529 RUNTIME_ASSERT(f >= -1 && f <= 20);
2530 char* str = DoubleToExponentialCString(value, f);
2531 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2532 DeleteArray(str);
2533 return res;
2534}
2535
2536
2537static Object* Runtime_NumberToPrecision(Arguments args) {
2538 NoHandleAllocation ha;
2539 ASSERT(args.length() == 2);
2540
2541 CONVERT_DOUBLE_CHECKED(value, args[0]);
2542 if (isnan(value)) {
2543 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2544 }
2545 if (isinf(value)) {
2546 if (value < 0) {
2547 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2548 }
2549 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2550 }
2551 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2552 int f = FastD2I(f_number);
2553 RUNTIME_ASSERT(f >= 1 && f <= 21);
2554 char* str = DoubleToPrecisionCString(value, f);
2555 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2556 DeleteArray(str);
2557 return res;
2558}
2559
2560
2561// Returns a single character string where first character equals
2562// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002563static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002564 if (index < static_cast<uint32_t>(string->length())) {
2565 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002566 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002567 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002568 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002569 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002570}
2571
2572
2573Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2574 // Handle [] indexing on Strings
2575 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002576 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2577 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002578 }
2579
2580 // Handle [] indexing on String objects
2581 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002582 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2583 Handle<Object> result =
2584 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2585 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002586 }
2587
2588 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002589 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002590 return prototype->GetElement(index);
2591 }
2592
2593 return object->GetElement(index);
2594}
2595
2596
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002597Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2598 HandleScope scope;
2599
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002600 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002601 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002602 Handle<Object> error =
2603 Factory::NewTypeError("non_object_property_load",
2604 HandleVector(args, 2));
2605 return Top::Throw(*error);
2606 }
2607
2608 // Check if the given key is an array index.
2609 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002610 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002611 return GetElementOrCharAt(object, index);
2612 }
2613
2614 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002615 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002616 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002617 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002618 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002619 bool has_pending_exception = false;
2620 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002621 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002622 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002623 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002624 }
2625
ager@chromium.org32912102009-01-16 10:38:43 +00002626 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002627 // the element if so.
2628 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002629 return GetElementOrCharAt(object, index);
2630 } else {
2631 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002632 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002633 }
2634}
2635
2636
2637static Object* Runtime_GetProperty(Arguments args) {
2638 NoHandleAllocation ha;
2639 ASSERT(args.length() == 2);
2640
2641 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002642 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002643
2644 return Runtime::GetObjectProperty(object, key);
2645}
2646
2647
ager@chromium.org7c537e22008-10-16 08:43:32 +00002648
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002649// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002650static Object* Runtime_KeyedGetProperty(Arguments args) {
2651 NoHandleAllocation ha;
2652 ASSERT(args.length() == 2);
2653
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002654 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002655 // itself.
2656 //
2657 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002658 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002659 // global proxy object never has properties. This is the case
2660 // because the global proxy object forwards everything to its hidden
2661 // prototype including local lookups.
2662 //
2663 // Additionally, we need to make sure that we do not cache results
2664 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002665 if (args[0]->IsJSObject() &&
2666 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002667 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002668 args[1]->IsString()) {
2669 JSObject* receiver = JSObject::cast(args[0]);
2670 String* key = String::cast(args[1]);
2671 if (receiver->HasFastProperties()) {
2672 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002673 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002674 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2675 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002676 Object* value = receiver->FastPropertyAt(offset);
2677 return value->IsTheHole() ? Heap::undefined_value() : value;
2678 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002679 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002680 LookupResult result;
2681 receiver->LocalLookup(key, &result);
2682 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2683 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002684 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002685 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002686 }
2687 } else {
2688 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002689 StringDictionary* dictionary = receiver->property_dictionary();
2690 int entry = dictionary->FindEntry(key);
2691 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002692 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002693 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002694 if (!receiver->IsGlobalObject()) return value;
2695 value = JSGlobalPropertyCell::cast(value)->value();
2696 if (!value->IsTheHole()) return value;
2697 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002698 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002699 }
2700 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002701
2702 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002703 return Runtime::GetObjectProperty(args.at<Object>(0),
2704 args.at<Object>(1));
2705}
2706
2707
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002708Object* Runtime::SetObjectProperty(Handle<Object> object,
2709 Handle<Object> key,
2710 Handle<Object> value,
2711 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002712 HandleScope scope;
2713
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002714 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002715 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002716 Handle<Object> error =
2717 Factory::NewTypeError("non_object_property_store",
2718 HandleVector(args, 2));
2719 return Top::Throw(*error);
2720 }
2721
2722 // If the object isn't a JavaScript object, we ignore the store.
2723 if (!object->IsJSObject()) return *value;
2724
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002725 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2726
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002727 // Check if the given key is an array index.
2728 uint32_t index;
2729 if (Array::IndexFromObject(*key, &index)) {
2730 ASSERT(attr == NONE);
2731
2732 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2733 // of a string using [] notation. We need to support this too in
2734 // JavaScript.
2735 // In the case of a String object we just need to redirect the assignment to
2736 // the underlying string if the index is in range. Since the underlying
2737 // string does nothing with the assignment then we can ignore such
2738 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002739 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002740 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002741 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002742
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002743 Handle<Object> result = SetElement(js_object, index, value);
2744 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002745 return *value;
2746 }
2747
2748 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002749 Handle<Object> result;
2750 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002751 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002752 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002753 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002754 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002755 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002756 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002757 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002758 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002759 return *value;
2760 }
2761
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002762 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002763 bool has_pending_exception = false;
2764 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2765 if (has_pending_exception) return Failure::Exception();
2766 Handle<String> name = Handle<String>::cast(converted);
2767
2768 if (name->AsArrayIndex(&index)) {
2769 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002770 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002771 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002772 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002773 }
2774}
2775
2776
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002777Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2778 Handle<Object> key,
2779 Handle<Object> value,
2780 PropertyAttributes attr) {
2781 HandleScope scope;
2782
2783 // Check if the given key is an array index.
2784 uint32_t index;
2785 if (Array::IndexFromObject(*key, &index)) {
2786 ASSERT(attr == NONE);
2787
2788 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2789 // of a string using [] notation. We need to support this too in
2790 // JavaScript.
2791 // In the case of a String object we just need to redirect the assignment to
2792 // the underlying string if the index is in range. Since the underlying
2793 // string does nothing with the assignment then we can ignore such
2794 // assignments.
2795 if (js_object->IsStringObjectWithCharacterAt(index)) {
2796 return *value;
2797 }
2798
2799 return js_object->SetElement(index, *value);
2800 }
2801
2802 if (key->IsString()) {
2803 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2804 ASSERT(attr == NONE);
2805 return js_object->SetElement(index, *value);
2806 } else {
2807 Handle<String> key_string = Handle<String>::cast(key);
2808 key_string->TryFlattenIfNotFlat();
2809 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2810 *value,
2811 attr);
2812 }
2813 }
2814
2815 // Call-back into JavaScript to convert the key to a string.
2816 bool has_pending_exception = false;
2817 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2818 if (has_pending_exception) return Failure::Exception();
2819 Handle<String> name = Handle<String>::cast(converted);
2820
2821 if (name->AsArrayIndex(&index)) {
2822 ASSERT(attr == NONE);
2823 return js_object->SetElement(index, *value);
2824 } else {
2825 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2826 }
2827}
2828
2829
ager@chromium.orge2902be2009-06-08 12:21:35 +00002830Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2831 Handle<Object> key) {
2832 HandleScope scope;
2833
2834 // Check if the given key is an array index.
2835 uint32_t index;
2836 if (Array::IndexFromObject(*key, &index)) {
2837 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2838 // characters of a string using [] notation. In the case of a
2839 // String object we just need to redirect the deletion to the
2840 // underlying string if the index is in range. Since the
2841 // underlying string does nothing with the deletion, we can ignore
2842 // such deletions.
2843 if (js_object->IsStringObjectWithCharacterAt(index)) {
2844 return Heap::true_value();
2845 }
2846
2847 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2848 }
2849
2850 Handle<String> key_string;
2851 if (key->IsString()) {
2852 key_string = Handle<String>::cast(key);
2853 } else {
2854 // Call-back into JavaScript to convert the key to a string.
2855 bool has_pending_exception = false;
2856 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2857 if (has_pending_exception) return Failure::Exception();
2858 key_string = Handle<String>::cast(converted);
2859 }
2860
2861 key_string->TryFlattenIfNotFlat();
2862 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2863}
2864
2865
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002866static Object* Runtime_SetProperty(Arguments args) {
2867 NoHandleAllocation ha;
2868 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2869
2870 Handle<Object> object = args.at<Object>(0);
2871 Handle<Object> key = args.at<Object>(1);
2872 Handle<Object> value = args.at<Object>(2);
2873
2874 // Compute attributes.
2875 PropertyAttributes attributes = NONE;
2876 if (args.length() == 4) {
2877 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002878 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002879 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002880 RUNTIME_ASSERT(
2881 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2882 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002883 }
2884 return Runtime::SetObjectProperty(object, key, value, attributes);
2885}
2886
2887
2888// Set a local property, even if it is READ_ONLY. If the property does not
2889// exist, it will be added with attributes NONE.
2890static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2891 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002892 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002893 CONVERT_CHECKED(JSObject, object, args[0]);
2894 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002895 // Compute attributes.
2896 PropertyAttributes attributes = NONE;
2897 if (args.length() == 4) {
2898 CONVERT_CHECKED(Smi, value_obj, args[3]);
2899 int unchecked_value = value_obj->value();
2900 // Only attribute bits should be set.
2901 RUNTIME_ASSERT(
2902 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2903 attributes = static_cast<PropertyAttributes>(unchecked_value);
2904 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002905
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002906 return object->
2907 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002908}
2909
2910
2911static Object* Runtime_DeleteProperty(Arguments args) {
2912 NoHandleAllocation ha;
2913 ASSERT(args.length() == 2);
2914
2915 CONVERT_CHECKED(JSObject, object, args[0]);
2916 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002917 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002918}
2919
2920
ager@chromium.org9085a012009-05-11 19:22:57 +00002921static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2922 Handle<String> key) {
2923 if (object->HasLocalProperty(*key)) return Heap::true_value();
2924 // Handle hidden prototypes. If there's a hidden prototype above this thing
2925 // then we have to check it for properties, because they are supposed to
2926 // look like they are on this object.
2927 Handle<Object> proto(object->GetPrototype());
2928 if (proto->IsJSObject() &&
2929 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2930 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2931 }
2932 return Heap::false_value();
2933}
2934
2935
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002936static Object* Runtime_HasLocalProperty(Arguments args) {
2937 NoHandleAllocation ha;
2938 ASSERT(args.length() == 2);
2939 CONVERT_CHECKED(String, key, args[1]);
2940
ager@chromium.org9085a012009-05-11 19:22:57 +00002941 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002942 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002943 if (obj->IsJSObject()) {
2944 JSObject* object = JSObject::cast(obj);
2945 // Fast case - no interceptors.
2946 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2947 // Slow case. Either it's not there or we have an interceptor. We should
2948 // have handles for this kind of deal.
2949 HandleScope scope;
2950 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2951 Handle<String>(key));
2952 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002953 // Well, there is one exception: Handle [] on strings.
2954 uint32_t index;
2955 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002956 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002957 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002958 return Heap::true_value();
2959 }
2960 }
2961 return Heap::false_value();
2962}
2963
2964
2965static Object* Runtime_HasProperty(Arguments args) {
2966 NoHandleAllocation na;
2967 ASSERT(args.length() == 2);
2968
2969 // Only JS objects can have properties.
2970 if (args[0]->IsJSObject()) {
2971 JSObject* object = JSObject::cast(args[0]);
2972 CONVERT_CHECKED(String, key, args[1]);
2973 if (object->HasProperty(key)) return Heap::true_value();
2974 }
2975 return Heap::false_value();
2976}
2977
2978
2979static Object* Runtime_HasElement(Arguments args) {
2980 NoHandleAllocation na;
2981 ASSERT(args.length() == 2);
2982
2983 // Only JS objects can have elements.
2984 if (args[0]->IsJSObject()) {
2985 JSObject* object = JSObject::cast(args[0]);
2986 CONVERT_CHECKED(Smi, index_obj, args[1]);
2987 uint32_t index = index_obj->value();
2988 if (object->HasElement(index)) return Heap::true_value();
2989 }
2990 return Heap::false_value();
2991}
2992
2993
2994static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2995 NoHandleAllocation ha;
2996 ASSERT(args.length() == 2);
2997
2998 CONVERT_CHECKED(JSObject, object, args[0]);
2999 CONVERT_CHECKED(String, key, args[1]);
3000
3001 uint32_t index;
3002 if (key->AsArrayIndex(&index)) {
3003 return Heap::ToBoolean(object->HasElement(index));
3004 }
3005
ager@chromium.org870a0b62008-11-04 11:43:05 +00003006 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
3007 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003008}
3009
3010
3011static Object* Runtime_GetPropertyNames(Arguments args) {
3012 HandleScope scope;
3013 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00003014 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003015 return *GetKeysFor(object);
3016}
3017
3018
3019// Returns either a FixedArray as Runtime_GetPropertyNames,
3020// or, if the given object has an enum cache that contains
3021// all enumerable properties of the object and its prototypes
3022// have none, the map of the object. This is used to speed up
3023// the check for deletions during a for-in.
3024static Object* Runtime_GetPropertyNamesFast(Arguments args) {
3025 ASSERT(args.length() == 1);
3026
3027 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3028
3029 if (raw_object->IsSimpleEnum()) return raw_object->map();
3030
3031 HandleScope scope;
3032 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003033 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
3034 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003035
3036 // Test again, since cache may have been built by preceding call.
3037 if (object->IsSimpleEnum()) return object->map();
3038
3039 return *content;
3040}
3041
3042
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003043static Object* Runtime_LocalKeys(Arguments args) {
3044 ASSERT_EQ(args.length(), 1);
3045 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3046 HandleScope scope;
3047 Handle<JSObject> object(raw_object);
3048 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
3049 LOCAL_ONLY);
3050 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
3051 // property array and since the result is mutable we have to create
3052 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003053 int length = contents->length();
3054 Handle<FixedArray> copy = Factory::NewFixedArray(length);
3055 for (int i = 0; i < length; i++) {
3056 Object* entry = contents->get(i);
3057 if (entry->IsString()) {
3058 copy->set(i, entry);
3059 } else {
3060 ASSERT(entry->IsNumber());
3061 HandleScope scope;
3062 Handle<Object> entry_handle(entry);
3063 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
3064 copy->set(i, *entry_str);
3065 }
3066 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003067 return *Factory::NewJSArrayWithElements(copy);
3068}
3069
3070
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003071static Object* Runtime_GetArgumentsProperty(Arguments args) {
3072 NoHandleAllocation ha;
3073 ASSERT(args.length() == 1);
3074
3075 // Compute the frame holding the arguments.
3076 JavaScriptFrameIterator it;
3077 it.AdvanceToArgumentsFrame();
3078 JavaScriptFrame* frame = it.frame();
3079
3080 // Get the actual number of provided arguments.
3081 const uint32_t n = frame->GetProvidedParametersCount();
3082
3083 // Try to convert the key to an index. If successful and within
3084 // index return the the argument from the frame.
3085 uint32_t index;
3086 if (Array::IndexFromObject(args[0], &index) && index < n) {
3087 return frame->GetParameter(index);
3088 }
3089
3090 // Convert the key to a string.
3091 HandleScope scope;
3092 bool exception = false;
3093 Handle<Object> converted =
3094 Execution::ToString(args.at<Object>(0), &exception);
3095 if (exception) return Failure::Exception();
3096 Handle<String> key = Handle<String>::cast(converted);
3097
3098 // Try to convert the string key into an array index.
3099 if (key->AsArrayIndex(&index)) {
3100 if (index < n) {
3101 return frame->GetParameter(index);
3102 } else {
3103 return Top::initial_object_prototype()->GetElement(index);
3104 }
3105 }
3106
3107 // Handle special arguments properties.
3108 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3109 if (key->Equals(Heap::callee_symbol())) return frame->function();
3110
3111 // Lookup in the initial Object.prototype object.
3112 return Top::initial_object_prototype()->GetProperty(*key);
3113}
3114
3115
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003116static Object* Runtime_ToFastProperties(Arguments args) {
3117 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003118 Handle<Object> object = args.at<Object>(0);
3119 if (object->IsJSObject()) {
3120 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3121 js_object->TransformToFastProperties(0);
3122 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003123 return *object;
3124}
3125
3126
3127static Object* Runtime_ToSlowProperties(Arguments args) {
3128 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003129 Handle<Object> object = args.at<Object>(0);
3130 if (object->IsJSObject()) {
3131 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003132 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003133 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003134 return *object;
3135}
3136
3137
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003138static Object* Runtime_ToBool(Arguments args) {
3139 NoHandleAllocation ha;
3140 ASSERT(args.length() == 1);
3141
3142 return args[0]->ToBoolean();
3143}
3144
3145
3146// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3147// Possible optimizations: put the type string into the oddballs.
3148static Object* Runtime_Typeof(Arguments args) {
3149 NoHandleAllocation ha;
3150
3151 Object* obj = args[0];
3152 if (obj->IsNumber()) return Heap::number_symbol();
3153 HeapObject* heap_obj = HeapObject::cast(obj);
3154
3155 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003156 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003157
3158 InstanceType instance_type = heap_obj->map()->instance_type();
3159 if (instance_type < FIRST_NONSTRING_TYPE) {
3160 return Heap::string_symbol();
3161 }
3162
3163 switch (instance_type) {
3164 case ODDBALL_TYPE:
3165 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3166 return Heap::boolean_symbol();
3167 }
3168 if (heap_obj->IsNull()) {
3169 return Heap::object_symbol();
3170 }
3171 ASSERT(heap_obj->IsUndefined());
3172 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00003173 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003174 return Heap::function_symbol();
3175 default:
3176 // For any kind of object not handled above, the spec rule for
3177 // host objects gives that it is okay to return "object"
3178 return Heap::object_symbol();
3179 }
3180}
3181
3182
3183static Object* Runtime_StringToNumber(Arguments args) {
3184 NoHandleAllocation ha;
3185 ASSERT(args.length() == 1);
3186 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003187 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003188 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3189}
3190
3191
3192static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3193 NoHandleAllocation ha;
3194 ASSERT(args.length() == 1);
3195
3196 CONVERT_CHECKED(JSArray, codes, args[0]);
3197 int length = Smi::cast(codes->length())->value();
3198
3199 // Check if the string can be ASCII.
3200 int i;
3201 for (i = 0; i < length; i++) {
3202 Object* element = codes->GetElement(i);
3203 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3204 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3205 break;
3206 }
3207
3208 Object* object = NULL;
3209 if (i == length) { // The string is ASCII.
3210 object = Heap::AllocateRawAsciiString(length);
3211 } else { // The string is not ASCII.
3212 object = Heap::AllocateRawTwoByteString(length);
3213 }
3214
3215 if (object->IsFailure()) return object;
3216 String* result = String::cast(object);
3217 for (int i = 0; i < length; i++) {
3218 Object* element = codes->GetElement(i);
3219 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003220 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003221 }
3222 return result;
3223}
3224
3225
3226// kNotEscaped is generated by the following:
3227//
3228// #!/bin/perl
3229// for (my $i = 0; $i < 256; $i++) {
3230// print "\n" if $i % 16 == 0;
3231// my $c = chr($i);
3232// my $escaped = 1;
3233// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3234// print $escaped ? "0, " : "1, ";
3235// }
3236
3237
3238static bool IsNotEscaped(uint16_t character) {
3239 // Only for 8 bit characters, the rest are always escaped (in a different way)
3240 ASSERT(character < 256);
3241 static const char kNotEscaped[256] = {
3242 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3243 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3244 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3245 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3246 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3247 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3248 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3249 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3250 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3251 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3252 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3253 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3254 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3255 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3256 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3257 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3258 };
3259 return kNotEscaped[character] != 0;
3260}
3261
3262
3263static Object* Runtime_URIEscape(Arguments args) {
3264 const char hex_chars[] = "0123456789ABCDEF";
3265 NoHandleAllocation ha;
3266 ASSERT(args.length() == 1);
3267 CONVERT_CHECKED(String, source, args[0]);
3268
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003269 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003270
3271 int escaped_length = 0;
3272 int length = source->length();
3273 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003274 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003275 buffer->Reset(source);
3276 while (buffer->has_more()) {
3277 uint16_t character = buffer->GetNext();
3278 if (character >= 256) {
3279 escaped_length += 6;
3280 } else if (IsNotEscaped(character)) {
3281 escaped_length++;
3282 } else {
3283 escaped_length += 3;
3284 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003285 // We don't allow strings that are longer than a maximal length.
3286 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003287 Top::context()->mark_out_of_memory();
3288 return Failure::OutOfMemoryException();
3289 }
3290 }
3291 }
3292 // No length change implies no change. Return original string if no change.
3293 if (escaped_length == length) {
3294 return source;
3295 }
3296 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3297 if (o->IsFailure()) return o;
3298 String* destination = String::cast(o);
3299 int dest_position = 0;
3300
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003301 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003302 buffer->Rewind();
3303 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003304 uint16_t chr = buffer->GetNext();
3305 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003306 destination->Set(dest_position, '%');
3307 destination->Set(dest_position+1, 'u');
3308 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3309 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3310 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3311 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003312 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003313 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003314 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003315 dest_position++;
3316 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003317 destination->Set(dest_position, '%');
3318 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3319 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003320 dest_position += 3;
3321 }
3322 }
3323 return destination;
3324}
3325
3326
3327static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3328 static const signed char kHexValue['g'] = {
3329 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3330 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3331 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3332 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3333 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3334 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3335 -1, 10, 11, 12, 13, 14, 15 };
3336
3337 if (character1 > 'f') return -1;
3338 int hi = kHexValue[character1];
3339 if (hi == -1) return -1;
3340 if (character2 > 'f') return -1;
3341 int lo = kHexValue[character2];
3342 if (lo == -1) return -1;
3343 return (hi << 4) + lo;
3344}
3345
3346
ager@chromium.org870a0b62008-11-04 11:43:05 +00003347static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003348 int i,
3349 int length,
3350 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003351 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003352 int32_t hi = 0;
3353 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003354 if (character == '%' &&
3355 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003356 source->Get(i + 1) == 'u' &&
3357 (hi = TwoDigitHex(source->Get(i + 2),
3358 source->Get(i + 3))) != -1 &&
3359 (lo = TwoDigitHex(source->Get(i + 4),
3360 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003361 *step = 6;
3362 return (hi << 8) + lo;
3363 } else if (character == '%' &&
3364 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003365 (lo = TwoDigitHex(source->Get(i + 1),
3366 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003367 *step = 3;
3368 return lo;
3369 } else {
3370 *step = 1;
3371 return character;
3372 }
3373}
3374
3375
3376static Object* Runtime_URIUnescape(Arguments args) {
3377 NoHandleAllocation ha;
3378 ASSERT(args.length() == 1);
3379 CONVERT_CHECKED(String, source, args[0]);
3380
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003381 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003382
3383 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003384 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003385
3386 int unescaped_length = 0;
3387 for (int i = 0; i < length; unescaped_length++) {
3388 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003389 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003390 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003391 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003392 i += step;
3393 }
3394
3395 // No length change implies no change. Return original string if no change.
3396 if (unescaped_length == length)
3397 return source;
3398
3399 Object* o = ascii ?
3400 Heap::AllocateRawAsciiString(unescaped_length) :
3401 Heap::AllocateRawTwoByteString(unescaped_length);
3402 if (o->IsFailure()) return o;
3403 String* destination = String::cast(o);
3404
3405 int dest_position = 0;
3406 for (int i = 0; i < length; dest_position++) {
3407 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003408 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003409 i += step;
3410 }
3411 return destination;
3412}
3413
3414
3415static Object* Runtime_StringParseInt(Arguments args) {
3416 NoHandleAllocation ha;
3417
3418 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003419 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003420
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003421 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003422
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003423 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003424 int i;
3425
3426 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003427 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003428 if (i == len) return Heap::nan_value();
3429
3430 // Compute the sign (default to +).
3431 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003432 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003433 sign = -1;
3434 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003435 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003436 i++;
3437 }
3438
3439 // Compute the radix if 0.
3440 if (radix == 0) {
3441 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003442 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003443 radix = 8;
3444 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003445 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003446 if (c == 'x' || c == 'X') {
3447 radix = 16;
3448 i += 2;
3449 }
3450 }
3451 }
3452 } else if (radix == 16) {
3453 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003454 if (i + 1 < len && s->Get(i) == '0') {
3455 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003456 if (c == 'x' || c == 'X') i += 2;
3457 }
3458 }
3459
3460 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3461 double value;
3462 int end_index = StringToInt(s, i, radix, &value);
3463 if (end_index != i) {
3464 return Heap::NumberFromDouble(sign * value);
3465 }
3466 return Heap::nan_value();
3467}
3468
3469
3470static Object* Runtime_StringParseFloat(Arguments args) {
3471 NoHandleAllocation ha;
3472 CONVERT_CHECKED(String, str, args[0]);
3473
3474 // ECMA-262 section 15.1.2.3, empty string is NaN
3475 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3476
3477 // Create a number object from the value.
3478 return Heap::NumberFromDouble(value);
3479}
3480
3481
3482static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3483static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3484
3485
3486template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003487static Object* ConvertCaseHelper(String* s,
3488 int length,
3489 int input_string_length,
3490 unibrow::Mapping<Converter, 128>* mapping) {
3491 // We try this twice, once with the assumption that the result is no longer
3492 // than the input and, if that assumption breaks, again with the exact
3493 // length. This may not be pretty, but it is nicer than what was here before
3494 // and I hereby claim my vaffel-is.
3495 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003496 // Allocate the resulting string.
3497 //
3498 // NOTE: This assumes that the upper/lower case of an ascii
3499 // character is also ascii. This is currently the case, but it
3500 // might break in the future if we implement more context and locale
3501 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003502 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003503 ? Heap::AllocateRawAsciiString(length)
3504 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003505 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003506 String* result = String::cast(o);
3507 bool has_changed_character = false;
3508
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003509 // Convert all characters to upper case, assuming that they will fit
3510 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003511 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003512 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003513 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003514 // We can assume that the string is not empty
3515 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003516 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003517 bool has_next = buffer->has_more();
3518 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003519 int char_length = mapping->get(current, next, chars);
3520 if (char_length == 0) {
3521 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003522 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003523 i++;
3524 } else if (char_length == 1) {
3525 // Common case: converting the letter resulted in one character.
3526 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003527 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003528 has_changed_character = true;
3529 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003530 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003531 // We've assumed that the result would be as long as the
3532 // input but here is a character that converts to several
3533 // characters. No matter, we calculate the exact length
3534 // of the result and try the whole thing again.
3535 //
3536 // Note that this leaves room for optimization. We could just
3537 // memcpy what we already have to the result string. Also,
3538 // the result string is the last object allocated we could
3539 // "realloc" it and probably, in the vast majority of cases,
3540 // extend the existing string to be able to hold the full
3541 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003542 int next_length = 0;
3543 if (has_next) {
3544 next_length = mapping->get(next, 0, chars);
3545 if (next_length == 0) next_length = 1;
3546 }
3547 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003548 while (buffer->has_more()) {
3549 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003550 // NOTE: we use 0 as the next character here because, while
3551 // the next character may affect what a character converts to,
3552 // it does not in any case affect the length of what it convert
3553 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003554 int char_length = mapping->get(current, 0, chars);
3555 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003556 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003557 if (current_length > Smi::kMaxValue) {
3558 Top::context()->mark_out_of_memory();
3559 return Failure::OutOfMemoryException();
3560 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003561 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003562 // Try again with the real length.
3563 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003564 } else {
3565 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003566 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003567 i++;
3568 }
3569 has_changed_character = true;
3570 }
3571 current = next;
3572 }
3573 if (has_changed_character) {
3574 return result;
3575 } else {
3576 // If we didn't actually change anything in doing the conversion
3577 // we simple return the result and let the converted string
3578 // become garbage; there is no reason to keep two identical strings
3579 // alive.
3580 return s;
3581 }
3582}
3583
3584
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003585template <class Converter>
3586static Object* ConvertCase(Arguments args,
3587 unibrow::Mapping<Converter, 128>* mapping) {
3588 NoHandleAllocation ha;
3589
3590 CONVERT_CHECKED(String, s, args[0]);
3591 s->TryFlattenIfNotFlat();
3592
3593 int input_string_length = s->length();
3594 // Assume that the string is not empty; we need this assumption later
3595 if (input_string_length == 0) return s;
3596 int length = input_string_length;
3597
3598 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3599 if (answer->IsSmi()) {
3600 // Retry with correct length.
3601 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3602 }
3603 return answer; // This may be a failure.
3604}
3605
3606
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003607static Object* Runtime_StringToLowerCase(Arguments args) {
3608 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3609}
3610
3611
3612static Object* Runtime_StringToUpperCase(Arguments args) {
3613 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3614}
3615
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003616static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
3617 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
3618}
3619
3620static Object* Runtime_StringTrim(Arguments args) {
3621 NoHandleAllocation ha;
3622 ASSERT(args.length() == 3);
3623
3624 CONVERT_CHECKED(String, s, args[0]);
3625 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
3626 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
3627
3628 s->TryFlattenIfNotFlat();
3629 int length = s->length();
3630
3631 int left = 0;
3632 if (trimLeft) {
3633 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
3634 left++;
3635 }
3636 }
3637
3638 int right = length;
3639 if (trimRight) {
3640 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
3641 right--;
3642 }
3643 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003644 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003645}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003646
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003647bool Runtime::IsUpperCaseChar(uint16_t ch) {
3648 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3649 int char_length = to_upper_mapping.get(ch, 0, chars);
3650 return char_length == 0;
3651}
3652
3653
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003654static Object* Runtime_NumberToString(Arguments args) {
3655 NoHandleAllocation ha;
3656 ASSERT(args.length() == 1);
3657
3658 Object* number = args[0];
3659 RUNTIME_ASSERT(number->IsNumber());
3660
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003661 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003662}
3663
3664
3665static Object* Runtime_NumberToInteger(Arguments args) {
3666 NoHandleAllocation ha;
3667 ASSERT(args.length() == 1);
3668
3669 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003670 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003671 CONVERT_DOUBLE_CHECKED(number, obj);
3672 return Heap::NumberFromDouble(DoubleToInteger(number));
3673}
3674
3675
3676static Object* Runtime_NumberToJSUint32(Arguments args) {
3677 NoHandleAllocation ha;
3678 ASSERT(args.length() == 1);
3679
3680 Object* obj = args[0];
3681 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3682 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3683 return Heap::NumberFromUint32(number);
3684}
3685
3686
3687static Object* Runtime_NumberToJSInt32(Arguments args) {
3688 NoHandleAllocation ha;
3689 ASSERT(args.length() == 1);
3690
3691 Object* obj = args[0];
3692 if (obj->IsSmi()) return obj;
3693 CONVERT_DOUBLE_CHECKED(number, obj);
3694 return Heap::NumberFromInt32(DoubleToInt32(number));
3695}
3696
3697
ager@chromium.org870a0b62008-11-04 11:43:05 +00003698// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3699// a small integer.
3700static Object* Runtime_NumberToSmi(Arguments args) {
3701 NoHandleAllocation ha;
3702 ASSERT(args.length() == 1);
3703
3704 Object* obj = args[0];
3705 if (obj->IsSmi()) {
3706 return obj;
3707 }
3708 if (obj->IsHeapNumber()) {
3709 double value = HeapNumber::cast(obj)->value();
3710 int int_value = FastD2I(value);
3711 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3712 return Smi::FromInt(int_value);
3713 }
3714 }
3715 return Heap::nan_value();
3716}
3717
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003718
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003719static Object* Runtime_NumberAdd(Arguments args) {
3720 NoHandleAllocation ha;
3721 ASSERT(args.length() == 2);
3722
3723 CONVERT_DOUBLE_CHECKED(x, args[0]);
3724 CONVERT_DOUBLE_CHECKED(y, args[1]);
3725 return Heap::AllocateHeapNumber(x + y);
3726}
3727
3728
3729static Object* Runtime_NumberSub(Arguments args) {
3730 NoHandleAllocation ha;
3731 ASSERT(args.length() == 2);
3732
3733 CONVERT_DOUBLE_CHECKED(x, args[0]);
3734 CONVERT_DOUBLE_CHECKED(y, args[1]);
3735 return Heap::AllocateHeapNumber(x - y);
3736}
3737
3738
3739static Object* Runtime_NumberMul(Arguments args) {
3740 NoHandleAllocation ha;
3741 ASSERT(args.length() == 2);
3742
3743 CONVERT_DOUBLE_CHECKED(x, args[0]);
3744 CONVERT_DOUBLE_CHECKED(y, args[1]);
3745 return Heap::AllocateHeapNumber(x * y);
3746}
3747
3748
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003749static Object* Runtime_NumberUnaryMinus(Arguments args) {
3750 NoHandleAllocation ha;
3751 ASSERT(args.length() == 1);
3752
3753 CONVERT_DOUBLE_CHECKED(x, args[0]);
3754 return Heap::AllocateHeapNumber(-x);
3755}
3756
3757
3758static Object* Runtime_NumberDiv(Arguments args) {
3759 NoHandleAllocation ha;
3760 ASSERT(args.length() == 2);
3761
3762 CONVERT_DOUBLE_CHECKED(x, args[0]);
3763 CONVERT_DOUBLE_CHECKED(y, args[1]);
3764 return Heap::NewNumberFromDouble(x / y);
3765}
3766
3767
3768static Object* Runtime_NumberMod(Arguments args) {
3769 NoHandleAllocation ha;
3770 ASSERT(args.length() == 2);
3771
3772 CONVERT_DOUBLE_CHECKED(x, args[0]);
3773 CONVERT_DOUBLE_CHECKED(y, args[1]);
3774
ager@chromium.org3811b432009-10-28 14:53:37 +00003775 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003776 // NewNumberFromDouble may return a Smi instead of a Number object
3777 return Heap::NewNumberFromDouble(x);
3778}
3779
3780
3781static Object* Runtime_StringAdd(Arguments args) {
3782 NoHandleAllocation ha;
3783 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003784 CONVERT_CHECKED(String, str1, args[0]);
3785 CONVERT_CHECKED(String, str2, args[1]);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00003786 Counters::string_add_runtime.Increment();
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003787 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003788}
3789
3790
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003791template<typename sinkchar>
3792static inline void StringBuilderConcatHelper(String* special,
3793 sinkchar* sink,
3794 FixedArray* fixed_array,
3795 int array_length) {
3796 int position = 0;
3797 for (int i = 0; i < array_length; i++) {
3798 Object* element = fixed_array->get(i);
3799 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003800 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003801 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003802 int pos;
3803 int len;
3804 if (encoded_slice > 0) {
3805 // Position and length encoded in one smi.
3806 pos = StringBuilderSubstringPosition::decode(encoded_slice);
3807 len = StringBuilderSubstringLength::decode(encoded_slice);
3808 } else {
3809 // Position and length encoded in two smis.
3810 Object* obj = fixed_array->get(++i);
3811 ASSERT(obj->IsSmi());
3812 pos = Smi::cast(obj)->value();
3813 len = -encoded_slice;
3814 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00003815 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003816 sink + position,
3817 pos,
3818 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003819 position += len;
3820 } else {
3821 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003822 int element_length = string->length();
3823 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003824 position += element_length;
3825 }
3826 }
3827}
3828
3829
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003830static Object* Runtime_StringBuilderConcat(Arguments args) {
3831 NoHandleAllocation ha;
3832 ASSERT(args.length() == 2);
3833 CONVERT_CHECKED(JSArray, array, args[0]);
3834 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003835
3836 // This assumption is used by the slice encoding in one or two smis.
3837 ASSERT(Smi::kMaxValue >= String::kMaxLength);
3838
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003839 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003840 Object* smi_array_length = array->length();
3841 if (!smi_array_length->IsSmi()) {
3842 Top::context()->mark_out_of_memory();
3843 return Failure::OutOfMemoryException();
3844 }
3845 int array_length = Smi::cast(smi_array_length)->value();
3846 if (!array->HasFastElements()) {
3847 return Top::Throw(Heap::illegal_argument_symbol());
3848 }
3849 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003850 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003851 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003852 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003853
3854 if (array_length == 0) {
3855 return Heap::empty_string();
3856 } else if (array_length == 1) {
3857 Object* first = fixed_array->get(0);
3858 if (first->IsString()) return first;
3859 }
3860
ager@chromium.org5ec48922009-05-05 07:25:34 +00003861 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003862 int position = 0;
3863 for (int i = 0; i < array_length; i++) {
3864 Object* elt = fixed_array->get(i);
3865 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003866 // Smi encoding of position and length.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003867 int len = Smi::cast(elt)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003868 if (len > 0) {
3869 // Position and length encoded in one smi.
3870 int pos = len >> 11;
3871 len &= 0x7ff;
3872 if (pos + len > special_length) {
3873 return Top::Throw(Heap::illegal_argument_symbol());
3874 }
3875 position += len;
3876 } else {
3877 // Position and length encoded in two smis.
3878 position += (-len);
3879 // Get the position and check that it is also a smi.
3880 i++;
3881 if (i >= array_length) {
3882 return Top::Throw(Heap::illegal_argument_symbol());
3883 }
3884 Object* pos = fixed_array->get(i);
3885 if (!pos->IsSmi()) {
3886 return Top::Throw(Heap::illegal_argument_symbol());
3887 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003888 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003889 } else if (elt->IsString()) {
3890 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003891 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003892 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003893 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003894 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003895 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003896 } else {
3897 return Top::Throw(Heap::illegal_argument_symbol());
3898 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003899 if (position > String::kMaxLength) {
3900 Top::context()->mark_out_of_memory();
3901 return Failure::OutOfMemoryException();
3902 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003903 }
3904
3905 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003906 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003907
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003908 if (ascii) {
3909 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003910 if (object->IsFailure()) return object;
3911 SeqAsciiString* answer = SeqAsciiString::cast(object);
3912 StringBuilderConcatHelper(special,
3913 answer->GetChars(),
3914 fixed_array,
3915 array_length);
3916 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003917 } else {
3918 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003919 if (object->IsFailure()) return object;
3920 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3921 StringBuilderConcatHelper(special,
3922 answer->GetChars(),
3923 fixed_array,
3924 array_length);
3925 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003926 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003927}
3928
3929
3930static Object* Runtime_NumberOr(Arguments args) {
3931 NoHandleAllocation ha;
3932 ASSERT(args.length() == 2);
3933
3934 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3935 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3936 return Heap::NumberFromInt32(x | y);
3937}
3938
3939
3940static Object* Runtime_NumberAnd(Arguments args) {
3941 NoHandleAllocation ha;
3942 ASSERT(args.length() == 2);
3943
3944 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3945 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3946 return Heap::NumberFromInt32(x & y);
3947}
3948
3949
3950static Object* Runtime_NumberXor(Arguments args) {
3951 NoHandleAllocation ha;
3952 ASSERT(args.length() == 2);
3953
3954 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3955 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3956 return Heap::NumberFromInt32(x ^ y);
3957}
3958
3959
3960static Object* Runtime_NumberNot(Arguments args) {
3961 NoHandleAllocation ha;
3962 ASSERT(args.length() == 1);
3963
3964 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3965 return Heap::NumberFromInt32(~x);
3966}
3967
3968
3969static Object* Runtime_NumberShl(Arguments args) {
3970 NoHandleAllocation ha;
3971 ASSERT(args.length() == 2);
3972
3973 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3974 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3975 return Heap::NumberFromInt32(x << (y & 0x1f));
3976}
3977
3978
3979static Object* Runtime_NumberShr(Arguments args) {
3980 NoHandleAllocation ha;
3981 ASSERT(args.length() == 2);
3982
3983 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3984 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3985 return Heap::NumberFromUint32(x >> (y & 0x1f));
3986}
3987
3988
3989static Object* Runtime_NumberSar(Arguments args) {
3990 NoHandleAllocation ha;
3991 ASSERT(args.length() == 2);
3992
3993 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3994 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3995 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3996}
3997
3998
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003999static Object* Runtime_NumberEquals(Arguments args) {
4000 NoHandleAllocation ha;
4001 ASSERT(args.length() == 2);
4002
4003 CONVERT_DOUBLE_CHECKED(x, args[0]);
4004 CONVERT_DOUBLE_CHECKED(y, args[1]);
4005 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
4006 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
4007 if (x == y) return Smi::FromInt(EQUAL);
4008 Object* result;
4009 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
4010 result = Smi::FromInt(EQUAL);
4011 } else {
4012 result = Smi::FromInt(NOT_EQUAL);
4013 }
4014 return result;
4015}
4016
4017
4018static Object* Runtime_StringEquals(Arguments args) {
4019 NoHandleAllocation ha;
4020 ASSERT(args.length() == 2);
4021
4022 CONVERT_CHECKED(String, x, args[0]);
4023 CONVERT_CHECKED(String, y, args[1]);
4024
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004025 bool not_equal = !x->Equals(y);
4026 // This is slightly convoluted because the value that signifies
4027 // equality is 0 and inequality is 1 so we have to negate the result
4028 // from String::Equals.
4029 ASSERT(not_equal == 0 || not_equal == 1);
4030 STATIC_CHECK(EQUAL == 0);
4031 STATIC_CHECK(NOT_EQUAL == 1);
4032 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004033}
4034
4035
4036static Object* Runtime_NumberCompare(Arguments args) {
4037 NoHandleAllocation ha;
4038 ASSERT(args.length() == 3);
4039
4040 CONVERT_DOUBLE_CHECKED(x, args[0]);
4041 CONVERT_DOUBLE_CHECKED(y, args[1]);
4042 if (isnan(x) || isnan(y)) return args[2];
4043 if (x == y) return Smi::FromInt(EQUAL);
4044 if (isless(x, y)) return Smi::FromInt(LESS);
4045 return Smi::FromInt(GREATER);
4046}
4047
4048
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004049// Compare two Smis as if they were converted to strings and then
4050// compared lexicographically.
4051static Object* Runtime_SmiLexicographicCompare(Arguments args) {
4052 NoHandleAllocation ha;
4053 ASSERT(args.length() == 2);
4054
4055 // Arrays for the individual characters of the two Smis. Smis are
4056 // 31 bit integers and 10 decimal digits are therefore enough.
4057 static int x_elms[10];
4058 static int y_elms[10];
4059
4060 // Extract the integer values from the Smis.
4061 CONVERT_CHECKED(Smi, x, args[0]);
4062 CONVERT_CHECKED(Smi, y, args[1]);
4063 int x_value = x->value();
4064 int y_value = y->value();
4065
4066 // If the integers are equal so are the string representations.
4067 if (x_value == y_value) return Smi::FromInt(EQUAL);
4068
4069 // If one of the integers are zero the normal integer order is the
4070 // same as the lexicographic order of the string representations.
4071 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
4072
ager@chromium.org32912102009-01-16 10:38:43 +00004073 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004074 // smallest because the char code of '-' is less than the char code
4075 // of any digit. Otherwise, we make both values positive.
4076 if (x_value < 0 || y_value < 0) {
4077 if (y_value >= 0) return Smi::FromInt(LESS);
4078 if (x_value >= 0) return Smi::FromInt(GREATER);
4079 x_value = -x_value;
4080 y_value = -y_value;
4081 }
4082
4083 // Convert the integers to arrays of their decimal digits.
4084 int x_index = 0;
4085 int y_index = 0;
4086 while (x_value > 0) {
4087 x_elms[x_index++] = x_value % 10;
4088 x_value /= 10;
4089 }
4090 while (y_value > 0) {
4091 y_elms[y_index++] = y_value % 10;
4092 y_value /= 10;
4093 }
4094
4095 // Loop through the arrays of decimal digits finding the first place
4096 // where they differ.
4097 while (--x_index >= 0 && --y_index >= 0) {
4098 int diff = x_elms[x_index] - y_elms[y_index];
4099 if (diff != 0) return Smi::FromInt(diff);
4100 }
4101
4102 // If one array is a suffix of the other array, the longest array is
4103 // the representation of the largest of the Smis in the
4104 // lexicographic ordering.
4105 return Smi::FromInt(x_index - y_index);
4106}
4107
4108
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004109static Object* Runtime_StringCompare(Arguments args) {
4110 NoHandleAllocation ha;
4111 ASSERT(args.length() == 2);
4112
4113 CONVERT_CHECKED(String, x, args[0]);
4114 CONVERT_CHECKED(String, y, args[1]);
4115
4116 // A few fast case tests before we flatten.
4117 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004118 if (y->length() == 0) {
4119 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004120 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004121 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004122 return Smi::FromInt(LESS);
4123 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004124
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004125 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004126 if (d < 0) return Smi::FromInt(LESS);
4127 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004128
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004129 x->TryFlattenIfNotFlat();
4130 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004131
4132 static StringInputBuffer bufx;
4133 static StringInputBuffer bufy;
4134 bufx.Reset(x);
4135 bufy.Reset(y);
4136 while (bufx.has_more() && bufy.has_more()) {
4137 int d = bufx.GetNext() - bufy.GetNext();
4138 if (d < 0) return Smi::FromInt(LESS);
4139 else if (d > 0) return Smi::FromInt(GREATER);
4140 }
4141
4142 // x is (non-trivial) prefix of y:
4143 if (bufy.has_more()) return Smi::FromInt(LESS);
4144 // y is prefix of x:
4145 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4146}
4147
4148
4149static Object* Runtime_Math_abs(Arguments args) {
4150 NoHandleAllocation ha;
4151 ASSERT(args.length() == 1);
4152
4153 CONVERT_DOUBLE_CHECKED(x, args[0]);
4154 return Heap::AllocateHeapNumber(fabs(x));
4155}
4156
4157
4158static Object* Runtime_Math_acos(Arguments args) {
4159 NoHandleAllocation ha;
4160 ASSERT(args.length() == 1);
4161
4162 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004163 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004164}
4165
4166
4167static Object* Runtime_Math_asin(Arguments args) {
4168 NoHandleAllocation ha;
4169 ASSERT(args.length() == 1);
4170
4171 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004172 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004173}
4174
4175
4176static Object* Runtime_Math_atan(Arguments args) {
4177 NoHandleAllocation ha;
4178 ASSERT(args.length() == 1);
4179
4180 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004181 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004182}
4183
4184
4185static Object* Runtime_Math_atan2(Arguments args) {
4186 NoHandleAllocation ha;
4187 ASSERT(args.length() == 2);
4188
4189 CONVERT_DOUBLE_CHECKED(x, args[0]);
4190 CONVERT_DOUBLE_CHECKED(y, args[1]);
4191 double result;
4192 if (isinf(x) && isinf(y)) {
4193 // Make sure that the result in case of two infinite arguments
4194 // is a multiple of Pi / 4. The sign of the result is determined
4195 // by the first argument (x) and the sign of the second argument
4196 // determines the multiplier: one or three.
4197 static double kPiDividedBy4 = 0.78539816339744830962;
4198 int multiplier = (x < 0) ? -1 : 1;
4199 if (y < 0) multiplier *= 3;
4200 result = multiplier * kPiDividedBy4;
4201 } else {
4202 result = atan2(x, y);
4203 }
4204 return Heap::AllocateHeapNumber(result);
4205}
4206
4207
4208static Object* Runtime_Math_ceil(Arguments args) {
4209 NoHandleAllocation ha;
4210 ASSERT(args.length() == 1);
4211
4212 CONVERT_DOUBLE_CHECKED(x, args[0]);
4213 return Heap::NumberFromDouble(ceiling(x));
4214}
4215
4216
4217static Object* Runtime_Math_cos(Arguments args) {
4218 NoHandleAllocation ha;
4219 ASSERT(args.length() == 1);
4220
4221 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004222 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004223}
4224
4225
4226static Object* Runtime_Math_exp(Arguments args) {
4227 NoHandleAllocation ha;
4228 ASSERT(args.length() == 1);
4229
4230 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004231 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004232}
4233
4234
4235static Object* Runtime_Math_floor(Arguments args) {
4236 NoHandleAllocation ha;
4237 ASSERT(args.length() == 1);
4238
4239 CONVERT_DOUBLE_CHECKED(x, args[0]);
4240 return Heap::NumberFromDouble(floor(x));
4241}
4242
4243
4244static Object* Runtime_Math_log(Arguments args) {
4245 NoHandleAllocation ha;
4246 ASSERT(args.length() == 1);
4247
4248 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004249 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004250}
4251
4252
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004253// Helper function to compute x^y, where y is known to be an
4254// integer. Uses binary decomposition to limit the number of
4255// multiplications; see the discussion in "Hacker's Delight" by Henry
4256// S. Warren, Jr., figure 11-6, page 213.
4257static double powi(double x, int y) {
4258 ASSERT(y != kMinInt);
4259 unsigned n = (y < 0) ? -y : y;
4260 double m = x;
4261 double p = 1;
4262 while (true) {
4263 if ((n & 1) != 0) p *= m;
4264 n >>= 1;
4265 if (n == 0) {
4266 if (y < 0) {
4267 // Unfortunately, we have to be careful when p has reached
4268 // infinity in the computation, because sometimes the higher
4269 // internal precision in the pow() implementation would have
4270 // given us a finite p. This happens very rarely.
4271 double result = 1.0 / p;
4272 return (result == 0 && isinf(p))
4273 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4274 : result;
4275 } else {
4276 return p;
4277 }
4278 }
4279 m *= m;
4280 }
4281}
4282
4283
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004284static Object* Runtime_Math_pow(Arguments args) {
4285 NoHandleAllocation ha;
4286 ASSERT(args.length() == 2);
4287
4288 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004289
4290 // If the second argument is a smi, it is much faster to call the
4291 // custom powi() function than the generic pow().
4292 if (args[1]->IsSmi()) {
4293 int y = Smi::cast(args[1])->value();
4294 return Heap::AllocateHeapNumber(powi(x, y));
4295 }
4296
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004297 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004298
4299 if (!isinf(x)) {
4300 if (y == 0.5) {
4301 // It's not uncommon to use Math.pow(x, 0.5) to compute the
4302 // square root of a number. To speed up such computations, we
4303 // explictly check for this case and use the sqrt() function
4304 // which is faster than pow().
4305 return Heap::AllocateHeapNumber(sqrt(x));
4306 } else if (y == -0.5) {
4307 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4308 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
4309 }
4310 }
4311
4312 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004313 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004314 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4315 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004316 } else {
4317 return Heap::AllocateHeapNumber(pow(x, y));
4318 }
4319}
4320
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004321
4322static Object* Runtime_Math_round(Arguments args) {
4323 NoHandleAllocation ha;
4324 ASSERT(args.length() == 1);
4325
4326 CONVERT_DOUBLE_CHECKED(x, args[0]);
4327 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4328 return Heap::NumberFromDouble(floor(x + 0.5));
4329}
4330
4331
4332static Object* Runtime_Math_sin(Arguments args) {
4333 NoHandleAllocation ha;
4334 ASSERT(args.length() == 1);
4335
4336 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004337 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004338}
4339
4340
4341static Object* Runtime_Math_sqrt(Arguments args) {
4342 NoHandleAllocation ha;
4343 ASSERT(args.length() == 1);
4344
4345 CONVERT_DOUBLE_CHECKED(x, args[0]);
4346 return Heap::AllocateHeapNumber(sqrt(x));
4347}
4348
4349
4350static Object* Runtime_Math_tan(Arguments args) {
4351 NoHandleAllocation ha;
4352 ASSERT(args.length() == 1);
4353
4354 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004355 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004356}
4357
4358
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004359// The NewArguments function is only used when constructing the
4360// arguments array when calling non-functions from JavaScript in
4361// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004362static Object* Runtime_NewArguments(Arguments args) {
4363 NoHandleAllocation ha;
4364 ASSERT(args.length() == 1);
4365
4366 // ECMA-262, 3rd., 10.1.8, p.39
4367 CONVERT_CHECKED(JSFunction, callee, args[0]);
4368
4369 // Compute the frame holding the arguments.
4370 JavaScriptFrameIterator it;
4371 it.AdvanceToArgumentsFrame();
4372 JavaScriptFrame* frame = it.frame();
4373
4374 const int length = frame->GetProvidedParametersCount();
4375 Object* result = Heap::AllocateArgumentsObject(callee, length);
4376 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004377 if (length > 0) {
4378 Object* obj = Heap::AllocateFixedArray(length);
4379 if (obj->IsFailure()) return obj;
4380 FixedArray* array = FixedArray::cast(obj);
4381 ASSERT(array->length() == length);
4382 WriteBarrierMode mode = array->GetWriteBarrierMode();
4383 for (int i = 0; i < length; i++) {
4384 array->set(i, frame->GetParameter(i), mode);
4385 }
4386 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004387 }
4388 return result;
4389}
4390
4391
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004392static Object* Runtime_NewArgumentsFast(Arguments args) {
4393 NoHandleAllocation ha;
4394 ASSERT(args.length() == 3);
4395
4396 JSFunction* callee = JSFunction::cast(args[0]);
4397 Object** parameters = reinterpret_cast<Object**>(args[1]);
4398 const int length = Smi::cast(args[2])->value();
4399
4400 Object* result = Heap::AllocateArgumentsObject(callee, length);
4401 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004402 // Allocate the elements if needed.
4403 if (length > 0) {
4404 // Allocate the fixed array.
4405 Object* obj = Heap::AllocateRawFixedArray(length);
4406 if (obj->IsFailure()) return obj;
4407 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4408 FixedArray* array = FixedArray::cast(obj);
4409 array->set_length(length);
4410 WriteBarrierMode mode = array->GetWriteBarrierMode();
4411 for (int i = 0; i < length; i++) {
4412 array->set(i, *--parameters, mode);
4413 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004414 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004415 }
4416 return result;
4417}
4418
4419
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004420static Object* Runtime_NewClosure(Arguments args) {
4421 HandleScope scope;
4422 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00004423 CONVERT_ARG_CHECKED(Context, context, 0);
4424 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004425
4426 Handle<JSFunction> result =
4427 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4428 return *result;
4429}
4430
4431
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004432static Code* ComputeConstructStub(Handle<SharedFunctionInfo> shared) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004433 // TODO(385): Change this to create a construct stub specialized for
4434 // the given map to make allocation of simple objects - and maybe
4435 // arrays - much faster.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004436 if (FLAG_inline_new
4437 && shared->has_only_simple_this_property_assignments()) {
4438 ConstructStubCompiler compiler;
4439 Object* code = compiler.CompileConstructStub(*shared);
4440 if (code->IsFailure()) {
4441 return Builtins::builtin(Builtins::JSConstructStubGeneric);
4442 }
4443 return Code::cast(code);
4444 }
4445
4446 return Builtins::builtin(Builtins::JSConstructStubGeneric);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004447}
4448
4449
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004450static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004451 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004452 ASSERT(args.length() == 1);
4453
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004454 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004455
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004456 // If the constructor isn't a proper function we throw a type error.
4457 if (!constructor->IsJSFunction()) {
4458 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4459 Handle<Object> type_error =
4460 Factory::NewTypeError("not_constructor", arguments);
4461 return Top::Throw(*type_error);
4462 }
4463
4464 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004465#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004466 // Handle stepping into constructors if step into is active.
4467 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004468 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004469 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004470#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004471
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004472 if (function->has_initial_map()) {
4473 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004474 // The 'Function' function ignores the receiver object when
4475 // called using 'new' and creates a new JSFunction object that
4476 // is returned. The receiver object is only used for error
4477 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004478 // JSFunction. Factory::NewJSObject() should not be used to
4479 // allocate JSFunctions since it does not properly initialize
4480 // the shared part of the function. Since the receiver is
4481 // ignored anyway, we use the global object as the receiver
4482 // instead of a new JSFunction object. This way, errors are
4483 // reported the same way whether or not 'Function' is called
4484 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004485 return Top::context()->global();
4486 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004487 }
4488
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004489 // The function should be compiled for the optimization hints to be available.
4490 if (!function->shared()->is_compiled()) {
4491 CompileLazyShared(Handle<SharedFunctionInfo>(function->shared()),
4492 CLEAR_EXCEPTION,
4493 0);
4494 }
4495
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004496 bool first_allocation = !function->has_initial_map();
4497 Handle<JSObject> result = Factory::NewJSObject(function);
4498 if (first_allocation) {
4499 Handle<Map> map = Handle<Map>(function->initial_map());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004500 Handle<Code> stub = Handle<Code>(
4501 ComputeConstructStub(Handle<SharedFunctionInfo>(function->shared())));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004502 function->shared()->set_construct_stub(*stub);
4503 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004504
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004505 Counters::constructed_objects.Increment();
4506 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004507
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004508 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004509}
4510
4511
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004512static Object* Runtime_LazyCompile(Arguments args) {
4513 HandleScope scope;
4514 ASSERT(args.length() == 1);
4515
4516 Handle<JSFunction> function = args.at<JSFunction>(0);
4517#ifdef DEBUG
4518 if (FLAG_trace_lazy) {
4519 PrintF("[lazy: ");
4520 function->shared()->name()->Print();
4521 PrintF("]\n");
4522 }
4523#endif
4524
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004525 // Compile the target function. Here we compile using CompileLazyInLoop in
4526 // order to get the optimized version. This helps code like delta-blue
4527 // that calls performance-critical routines through constructors. A
4528 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4529 // direct call. Since the in-loop tracking takes place through CallICs
4530 // this means that things called through constructors are never known to
4531 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004532 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004533 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004534 return Failure::Exception();
4535 }
4536
4537 return function->code();
4538}
4539
4540
4541static Object* Runtime_GetCalledFunction(Arguments args) {
4542 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004543 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004544 StackFrameIterator it;
4545 // Get past the JS-to-C exit frame.
4546 ASSERT(it.frame()->is_exit());
4547 it.Advance();
4548 // Get past the CALL_NON_FUNCTION activation frame.
4549 ASSERT(it.frame()->is_java_script());
4550 it.Advance();
4551 // Argument adaptor frames do not copy the function; we have to skip
4552 // past them to get to the real calling frame.
4553 if (it.frame()->is_arguments_adaptor()) it.Advance();
4554 // Get the function from the top of the expression stack of the
4555 // calling frame.
4556 StandardFrame* frame = StandardFrame::cast(it.frame());
4557 int index = frame->ComputeExpressionsCount() - 1;
4558 Object* result = frame->GetExpression(index);
4559 return result;
4560}
4561
4562
4563static Object* Runtime_GetFunctionDelegate(Arguments args) {
4564 HandleScope scope;
4565 ASSERT(args.length() == 1);
4566 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4567 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4568}
4569
4570
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004571static Object* Runtime_GetConstructorDelegate(Arguments args) {
4572 HandleScope scope;
4573 ASSERT(args.length() == 1);
4574 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4575 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4576}
4577
4578
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004579static Object* Runtime_NewContext(Arguments args) {
4580 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004581 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004582
kasper.lund7276f142008-07-30 08:49:36 +00004583 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004584 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4585 Object* result = Heap::AllocateFunctionContext(length, function);
4586 if (result->IsFailure()) return result;
4587
4588 Top::set_context(Context::cast(result));
4589
kasper.lund7276f142008-07-30 08:49:36 +00004590 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004591}
4592
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004593static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004594 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004595 Object* js_object = object;
4596 if (!js_object->IsJSObject()) {
4597 js_object = js_object->ToObject();
4598 if (js_object->IsFailure()) {
4599 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004600 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004601 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004602 Handle<Object> result =
4603 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4604 return Top::Throw(*result);
4605 }
4606 }
4607
4608 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004609 Heap::AllocateWithContext(Top::context(),
4610 JSObject::cast(js_object),
4611 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004612 if (result->IsFailure()) return result;
4613
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004614 Context* context = Context::cast(result);
4615 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004616
kasper.lund7276f142008-07-30 08:49:36 +00004617 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004618}
4619
4620
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004621static Object* Runtime_PushContext(Arguments args) {
4622 NoHandleAllocation ha;
4623 ASSERT(args.length() == 1);
4624 return PushContextHelper(args[0], false);
4625}
4626
4627
4628static Object* Runtime_PushCatchContext(Arguments args) {
4629 NoHandleAllocation ha;
4630 ASSERT(args.length() == 1);
4631 return PushContextHelper(args[0], true);
4632}
4633
4634
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004635static Object* Runtime_LookupContext(Arguments args) {
4636 HandleScope scope;
4637 ASSERT(args.length() == 2);
4638
4639 CONVERT_ARG_CHECKED(Context, context, 0);
4640 CONVERT_ARG_CHECKED(String, name, 1);
4641
4642 int index;
4643 PropertyAttributes attributes;
4644 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004645 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004646 context->Lookup(name, flags, &index, &attributes);
4647
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004648 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004649 ASSERT(holder->IsJSObject());
4650 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004651 }
4652
4653 // No intermediate context found. Use global object by default.
4654 return Top::context()->global();
4655}
4656
4657
ager@chromium.orga1645e22009-09-09 19:27:10 +00004658// A mechanism to return a pair of Object pointers in registers (if possible).
4659// How this is achieved is calling convention-dependent.
4660// All currently supported x86 compiles uses calling conventions that are cdecl
4661// variants where a 64-bit value is returned in two 32-bit registers
4662// (edx:eax on ia32, r1:r0 on ARM).
4663// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
4664// In Win64 calling convention, a struct of two pointers is returned in memory,
4665// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004666#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004667struct ObjectPair {
4668 Object* x;
4669 Object* y;
4670};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004671
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004672static inline ObjectPair MakePair(Object* x, Object* y) {
4673 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004674 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
4675 // In Win64 they are assigned to a hidden first argument.
4676 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004677}
4678#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004679typedef uint64_t ObjectPair;
4680static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004681 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004682 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004683}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004684#endif
4685
4686
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004687static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004688 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4689 USE(attributes);
4690 return x->IsTheHole() ? Heap::undefined_value() : x;
4691}
4692
4693
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004694static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4695 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004696 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004697 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004698 JSFunction* context_extension_function =
4699 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004700 // If the holder isn't a context extension object, we just return it
4701 // as the receiver. This allows arguments objects to be used as
4702 // receivers, but only if they are put in the context scope chain
4703 // explicitly via a with-statement.
4704 Object* constructor = holder->map()->constructor();
4705 if (constructor != context_extension_function) return holder;
4706 // Fall back to using the global object as the receiver if the
4707 // property turns out to be a local variable allocated in a context
4708 // extension object - introduced via eval.
4709 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004710}
4711
4712
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004713static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004714 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00004715 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004716
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004717 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004718 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004719 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004720 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004721 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004722
4723 int index;
4724 PropertyAttributes attributes;
4725 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004726 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004727 context->Lookup(name, flags, &index, &attributes);
4728
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004729 // If the index is non-negative, the slot has been found in a local
4730 // variable or a parameter. Read it from the context object or the
4731 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004732 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004733 // If the "property" we were looking for is a local variable or an
4734 // argument in a context, the receiver is the global object; see
4735 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4736 JSObject* receiver = Top::context()->global()->global_receiver();
4737 Object* value = (holder->IsContext())
4738 ? Context::cast(*holder)->get(index)
4739 : JSObject::cast(*holder)->GetElement(index);
4740 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004741 }
4742
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004743 // If the holder is found, we read the property from it.
4744 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004745 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004746 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004747 JSObject* receiver;
4748 if (object->IsGlobalObject()) {
4749 receiver = GlobalObject::cast(object)->global_receiver();
4750 } else if (context->is_exception_holder(*holder)) {
4751 receiver = Top::context()->global()->global_receiver();
4752 } else {
4753 receiver = ComputeReceiverForNonGlobal(object);
4754 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004755 // No need to unhole the value here. This is taken care of by the
4756 // GetProperty function.
4757 Object* value = object->GetProperty(*name);
4758 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004759 }
4760
4761 if (throw_error) {
4762 // The property doesn't exist - throw exception.
4763 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004764 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004765 return MakePair(Top::Throw(*reference_error), NULL);
4766 } else {
4767 // The property doesn't exist - return undefined
4768 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4769 }
4770}
4771
4772
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004773static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004774 return LoadContextSlotHelper(args, true);
4775}
4776
4777
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004778static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004779 return LoadContextSlotHelper(args, false);
4780}
4781
4782
4783static Object* Runtime_StoreContextSlot(Arguments args) {
4784 HandleScope scope;
4785 ASSERT(args.length() == 3);
4786
4787 Handle<Object> value(args[0]);
4788 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004789 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004790
4791 int index;
4792 PropertyAttributes attributes;
4793 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004794 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004795 context->Lookup(name, flags, &index, &attributes);
4796
4797 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004798 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004799 // Ignore if read_only variable.
4800 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004801 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004802 }
4803 } else {
4804 ASSERT((attributes & READ_ONLY) == 0);
4805 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004806 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004807 USE(result);
4808 ASSERT(!result->IsFailure());
4809 }
4810 return *value;
4811 }
4812
4813 // Slow case: The property is not in a FixedArray context.
4814 // It is either in an JSObject extension context or it was not found.
4815 Handle<JSObject> context_ext;
4816
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004817 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004818 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004819 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004820 } else {
4821 // The property was not found. It needs to be stored in the global context.
4822 ASSERT(attributes == ABSENT);
4823 attributes = NONE;
4824 context_ext = Handle<JSObject>(Top::context()->global());
4825 }
4826
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004827 // Set the property, but ignore if read_only variable on the context
4828 // extension object itself.
4829 if ((attributes & READ_ONLY) == 0 ||
4830 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004831 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4832 if (set.is_null()) {
4833 // Failure::Exception is converted to a null handle in the
4834 // handle-based methods such as SetProperty. We therefore need
4835 // to convert null handles back to exceptions.
4836 ASSERT(Top::has_pending_exception());
4837 return Failure::Exception();
4838 }
4839 }
4840 return *value;
4841}
4842
4843
4844static Object* Runtime_Throw(Arguments args) {
4845 HandleScope scope;
4846 ASSERT(args.length() == 1);
4847
4848 return Top::Throw(args[0]);
4849}
4850
4851
4852static Object* Runtime_ReThrow(Arguments args) {
4853 HandleScope scope;
4854 ASSERT(args.length() == 1);
4855
4856 return Top::ReThrow(args[0]);
4857}
4858
4859
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004860static Object* Runtime_PromoteScheduledException(Arguments args) {
4861 ASSERT_EQ(0, args.length());
4862 return Top::PromoteScheduledException();
4863}
4864
4865
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004866static Object* Runtime_ThrowReferenceError(Arguments args) {
4867 HandleScope scope;
4868 ASSERT(args.length() == 1);
4869
4870 Handle<Object> name(args[0]);
4871 Handle<Object> reference_error =
4872 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4873 return Top::Throw(*reference_error);
4874}
4875
4876
4877static Object* Runtime_StackOverflow(Arguments args) {
4878 NoHandleAllocation na;
4879 return Top::StackOverflow();
4880}
4881
4882
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004883static Object* Runtime_StackGuard(Arguments args) {
4884 ASSERT(args.length() == 1);
4885
4886 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004887 if (StackGuard::IsStackOverflow()) {
4888 return Runtime_StackOverflow(args);
4889 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004890
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004891 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004892}
4893
4894
4895// NOTE: These PrintXXX functions are defined for all builds (not just
4896// DEBUG builds) because we may want to be able to trace function
4897// calls in all modes.
4898static void PrintString(String* str) {
4899 // not uncommon to have empty strings
4900 if (str->length() > 0) {
4901 SmartPointer<char> s =
4902 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4903 PrintF("%s", *s);
4904 }
4905}
4906
4907
4908static void PrintObject(Object* obj) {
4909 if (obj->IsSmi()) {
4910 PrintF("%d", Smi::cast(obj)->value());
4911 } else if (obj->IsString() || obj->IsSymbol()) {
4912 PrintString(String::cast(obj));
4913 } else if (obj->IsNumber()) {
4914 PrintF("%g", obj->Number());
4915 } else if (obj->IsFailure()) {
4916 PrintF("<failure>");
4917 } else if (obj->IsUndefined()) {
4918 PrintF("<undefined>");
4919 } else if (obj->IsNull()) {
4920 PrintF("<null>");
4921 } else if (obj->IsTrue()) {
4922 PrintF("<true>");
4923 } else if (obj->IsFalse()) {
4924 PrintF("<false>");
4925 } else {
4926 PrintF("%p", obj);
4927 }
4928}
4929
4930
4931static int StackSize() {
4932 int n = 0;
4933 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4934 return n;
4935}
4936
4937
4938static void PrintTransition(Object* result) {
4939 // indentation
4940 { const int nmax = 80;
4941 int n = StackSize();
4942 if (n <= nmax)
4943 PrintF("%4d:%*s", n, n, "");
4944 else
4945 PrintF("%4d:%*s", n, nmax, "...");
4946 }
4947
4948 if (result == NULL) {
4949 // constructor calls
4950 JavaScriptFrameIterator it;
4951 JavaScriptFrame* frame = it.frame();
4952 if (frame->IsConstructor()) PrintF("new ");
4953 // function name
4954 Object* fun = frame->function();
4955 if (fun->IsJSFunction()) {
4956 PrintObject(JSFunction::cast(fun)->shared()->name());
4957 } else {
4958 PrintObject(fun);
4959 }
4960 // function arguments
4961 // (we are intentionally only printing the actually
4962 // supplied parameters, not all parameters required)
4963 PrintF("(this=");
4964 PrintObject(frame->receiver());
4965 const int length = frame->GetProvidedParametersCount();
4966 for (int i = 0; i < length; i++) {
4967 PrintF(", ");
4968 PrintObject(frame->GetParameter(i));
4969 }
4970 PrintF(") {\n");
4971
4972 } else {
4973 // function result
4974 PrintF("} -> ");
4975 PrintObject(result);
4976 PrintF("\n");
4977 }
4978}
4979
4980
4981static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004982 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004983 NoHandleAllocation ha;
4984 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004985 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004986}
4987
4988
4989static Object* Runtime_TraceExit(Arguments args) {
4990 NoHandleAllocation ha;
4991 PrintTransition(args[0]);
4992 return args[0]; // return TOS
4993}
4994
4995
4996static Object* Runtime_DebugPrint(Arguments args) {
4997 NoHandleAllocation ha;
4998 ASSERT(args.length() == 1);
4999
5000#ifdef DEBUG
5001 if (args[0]->IsString()) {
5002 // If we have a string, assume it's a code "marker"
5003 // and print some interesting cpu debugging info.
5004 JavaScriptFrameIterator it;
5005 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00005006 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
5007 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005008 } else {
5009 PrintF("DebugPrint: ");
5010 }
5011 args[0]->Print();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00005012 if (args[0]->IsHeapObject()) {
5013 HeapObject::cast(args[0])->map()->Print();
5014 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005015#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005016 // ShortPrint is available in release mode. Print is not.
5017 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005018#endif
5019 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00005020 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005021
5022 return args[0]; // return TOS
5023}
5024
5025
5026static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005027 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005028 NoHandleAllocation ha;
5029 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005030 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005031}
5032
5033
mads.s.ager31e71382008-08-13 09:32:07 +00005034static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005035 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005036 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005037
5038 // According to ECMA-262, section 15.9.1, page 117, the precision of
5039 // the number in a Date object representing a particular instant in
5040 // time is milliseconds. Therefore, we floor the result of getting
5041 // the OS time.
5042 double millis = floor(OS::TimeCurrentMillis());
5043 return Heap::NumberFromDouble(millis);
5044}
5045
5046
5047static Object* Runtime_DateParseString(Arguments args) {
5048 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005049 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005050
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005051 CONVERT_ARG_CHECKED(String, str, 0);
5052 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005053
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005054 CONVERT_ARG_CHECKED(JSArray, output, 1);
5055 RUNTIME_ASSERT(output->HasFastElements());
5056
5057 AssertNoAllocation no_allocation;
5058
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005059 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005060 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
5061 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005062 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005063 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005064 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005065 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005066 result = DateParser::Parse(str->ToUC16Vector(), output_array);
5067 }
5068
5069 if (result) {
5070 return *output;
5071 } else {
5072 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005073 }
5074}
5075
5076
5077static Object* Runtime_DateLocalTimezone(Arguments args) {
5078 NoHandleAllocation ha;
5079 ASSERT(args.length() == 1);
5080
5081 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00005082 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005083 return Heap::AllocateStringFromUtf8(CStrVector(zone));
5084}
5085
5086
5087static Object* Runtime_DateLocalTimeOffset(Arguments args) {
5088 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005089 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005090
5091 return Heap::NumberFromDouble(OS::LocalTimeOffset());
5092}
5093
5094
5095static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
5096 NoHandleAllocation ha;
5097 ASSERT(args.length() == 1);
5098
5099 CONVERT_DOUBLE_CHECKED(x, args[0]);
5100 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
5101}
5102
5103
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005104static Object* Runtime_NumberIsFinite(Arguments args) {
5105 NoHandleAllocation ha;
5106 ASSERT(args.length() == 1);
5107
5108 CONVERT_DOUBLE_CHECKED(value, args[0]);
5109 Object* result;
5110 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
5111 result = Heap::false_value();
5112 } else {
5113 result = Heap::true_value();
5114 }
5115 return result;
5116}
5117
5118
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005119static Object* Runtime_GlobalReceiver(Arguments args) {
5120 ASSERT(args.length() == 1);
5121 Object* global = args[0];
5122 if (!global->IsJSGlobalObject()) return Heap::null_value();
5123 return JSGlobalObject::cast(global)->global_receiver();
5124}
5125
5126
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005127static Object* Runtime_CompileString(Arguments args) {
5128 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005129 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005130 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005131 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005132
ager@chromium.org381abbb2009-02-25 13:23:22 +00005133 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005134 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005135 Compiler::ValidationState validate = (is_json->IsTrue())
5136 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005137 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
5138 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005139 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005140 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005141 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005142 Handle<JSFunction> fun =
5143 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5144 return *fun;
5145}
5146
5147
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005148static Handle<JSFunction> GetBuiltinFunction(String* name) {
5149 LookupResult result;
5150 Top::global_context()->builtins()->LocalLookup(name, &result);
5151 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
5152}
5153
5154
5155static Object* CompileDirectEval(Handle<String> source) {
5156 // Compute the eval context.
5157 HandleScope scope;
5158 StackFrameLocator locator;
5159 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5160 Handle<Context> context(Context::cast(frame->context()));
5161 bool is_global = context->IsGlobalContext();
5162
ager@chromium.org381abbb2009-02-25 13:23:22 +00005163 // Compile source string in the current context.
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005164 Handle<JSFunction> boilerplate = Compiler::CompileEval(
5165 source,
5166 context,
5167 is_global,
5168 Compiler::DONT_VALIDATE_JSON);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005169 if (boilerplate.is_null()) return Failure::Exception();
5170 Handle<JSFunction> fun =
5171 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5172 return *fun;
5173}
5174
5175
5176static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
5177 ASSERT(args.length() == 2);
5178
5179 HandleScope scope;
5180
5181 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5182
5183 Handle<Object> receiver;
5184
5185 // Find where the 'eval' symbol is bound. It is unaliased only if
5186 // it is bound in the global context.
5187 StackFrameLocator locator;
5188 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5189 Handle<Context> context(Context::cast(frame->context()));
5190 int index;
5191 PropertyAttributes attributes;
5192 while (!context.is_null()) {
5193 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5194 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005195 // Stop search when eval is found or when the global context is
5196 // reached.
5197 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005198 if (context->is_function_context()) {
5199 context = Handle<Context>(Context::cast(context->closure()->context()));
5200 } else {
5201 context = Handle<Context>(context->previous());
5202 }
5203 }
5204
iposva@chromium.org245aa852009-02-10 00:49:54 +00005205 // If eval could not be resolved, it has been deleted and we need to
5206 // throw a reference error.
5207 if (attributes == ABSENT) {
5208 Handle<Object> name = Factory::eval_symbol();
5209 Handle<Object> reference_error =
5210 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5211 return Top::Throw(*reference_error);
5212 }
5213
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005214 if (context->IsGlobalContext()) {
5215 // 'eval' is bound in the global context, but it may have been overwritten.
5216 // Compare it to the builtin 'GlobalEval' function to make sure.
5217 Handle<JSFunction> global_eval =
5218 GetBuiltinFunction(Heap::global_eval_symbol());
5219 if (global_eval.is_identical_to(callee)) {
5220 // A direct eval call.
5221 if (args[1]->IsString()) {
5222 CONVERT_ARG_CHECKED(String, source, 1);
5223 // A normal eval call on a string. Compile it and return the
5224 // compiled function bound in the local context.
5225 Object* compiled_source = CompileDirectEval(source);
5226 if (compiled_source->IsFailure()) return compiled_source;
5227 receiver = Handle<Object>(frame->receiver());
5228 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5229 } else {
5230 // An eval call that is not called on a string. Global eval
5231 // deals better with this.
5232 receiver = Handle<Object>(Top::global_context()->global());
5233 }
5234 } else {
5235 // 'eval' is overwritten. Just call the function with the given arguments.
5236 receiver = Handle<Object>(Top::global_context()->global());
5237 }
5238 } else {
5239 // 'eval' is not bound in the global context. Just call the function
5240 // with the given arguments. This is not necessarily the global eval.
5241 if (receiver->IsContext()) {
5242 context = Handle<Context>::cast(receiver);
5243 receiver = Handle<Object>(context->get(index));
5244 }
5245 }
5246
5247 Handle<FixedArray> call = Factory::NewFixedArray(2);
5248 call->set(0, *callee);
5249 call->set(1, *receiver);
5250 return *call;
5251}
5252
5253
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005254static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5255 // This utility adjusts the property attributes for newly created Function
5256 // object ("new Function(...)") by changing the map.
5257 // All it does is changing the prototype property to enumerable
5258 // as specified in ECMA262, 15.3.5.2.
5259 HandleScope scope;
5260 ASSERT(args.length() == 1);
5261 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5262 ASSERT(func->map()->instance_type() ==
5263 Top::function_instance_map()->instance_type());
5264 ASSERT(func->map()->instance_size() ==
5265 Top::function_instance_map()->instance_size());
5266 func->set_map(*Top::function_instance_map());
5267 return *func;
5268}
5269
5270
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005271// Push an array unto an array of arrays if it is not already in the
5272// array. Returns true if the element was pushed on the stack and
5273// false otherwise.
5274static Object* Runtime_PushIfAbsent(Arguments args) {
5275 ASSERT(args.length() == 2);
5276 CONVERT_CHECKED(JSArray, array, args[0]);
5277 CONVERT_CHECKED(JSArray, element, args[1]);
5278 RUNTIME_ASSERT(array->HasFastElements());
5279 int length = Smi::cast(array->length())->value();
5280 FixedArray* elements = FixedArray::cast(array->elements());
5281 for (int i = 0; i < length; i++) {
5282 if (elements->get(i) == element) return Heap::false_value();
5283 }
5284 Object* obj = array->SetFastElement(length, element);
5285 if (obj->IsFailure()) return obj;
5286 return Heap::true_value();
5287}
5288
5289
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005290/**
5291 * A simple visitor visits every element of Array's.
5292 * The backend storage can be a fixed array for fast elements case,
5293 * or a dictionary for sparse array. Since Dictionary is a subtype
5294 * of FixedArray, the class can be used by both fast and slow cases.
5295 * The second parameter of the constructor, fast_elements, specifies
5296 * whether the storage is a FixedArray or Dictionary.
5297 *
5298 * An index limit is used to deal with the situation that a result array
5299 * length overflows 32-bit non-negative integer.
5300 */
5301class ArrayConcatVisitor {
5302 public:
5303 ArrayConcatVisitor(Handle<FixedArray> storage,
5304 uint32_t index_limit,
5305 bool fast_elements) :
5306 storage_(storage), index_limit_(index_limit),
5307 fast_elements_(fast_elements), index_offset_(0) { }
5308
5309 void visit(uint32_t i, Handle<Object> elm) {
5310 uint32_t index = i + index_offset_;
5311 if (index >= index_limit_) return;
5312
5313 if (fast_elements_) {
5314 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5315 storage_->set(index, *elm);
5316
5317 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005318 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5319 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005320 Factory::DictionaryAtNumberPut(dict, index, elm);
5321 if (!result.is_identical_to(dict))
5322 storage_ = result;
5323 }
5324 }
5325
5326 void increase_index_offset(uint32_t delta) {
5327 index_offset_ += delta;
5328 }
5329
5330 private:
5331 Handle<FixedArray> storage_;
5332 uint32_t index_limit_;
5333 bool fast_elements_;
5334 uint32_t index_offset_;
5335};
5336
5337
ager@chromium.org3811b432009-10-28 14:53:37 +00005338template<class ExternalArrayClass, class ElementType>
5339static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
5340 bool elements_are_ints,
5341 bool elements_are_guaranteed_smis,
5342 uint32_t range,
5343 ArrayConcatVisitor* visitor) {
5344 Handle<ExternalArrayClass> array(
5345 ExternalArrayClass::cast(receiver->elements()));
5346 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
5347
5348 if (visitor != NULL) {
5349 if (elements_are_ints) {
5350 if (elements_are_guaranteed_smis) {
5351 for (uint32_t j = 0; j < len; j++) {
5352 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
5353 visitor->visit(j, e);
5354 }
5355 } else {
5356 for (uint32_t j = 0; j < len; j++) {
5357 int64_t val = static_cast<int64_t>(array->get(j));
5358 if (Smi::IsValid(static_cast<intptr_t>(val))) {
5359 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
5360 visitor->visit(j, e);
5361 } else {
5362 Handle<Object> e(
5363 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
5364 visitor->visit(j, e);
5365 }
5366 }
5367 }
5368 } else {
5369 for (uint32_t j = 0; j < len; j++) {
5370 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
5371 visitor->visit(j, e);
5372 }
5373 }
5374 }
5375
5376 return len;
5377}
5378
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005379/**
5380 * A helper function that visits elements of a JSObject. Only elements
5381 * whose index between 0 and range (exclusive) are visited.
5382 *
5383 * If the third parameter, visitor, is not NULL, the visitor is called
5384 * with parameters, 'visitor_index_offset + element index' and the element.
5385 *
5386 * It returns the number of visisted elements.
5387 */
5388static uint32_t IterateElements(Handle<JSObject> receiver,
5389 uint32_t range,
5390 ArrayConcatVisitor* visitor) {
5391 uint32_t num_of_elements = 0;
5392
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005393 switch (receiver->GetElementsKind()) {
5394 case JSObject::FAST_ELEMENTS: {
5395 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5396 uint32_t len = elements->length();
5397 if (range < len) {
5398 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005399 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005400
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005401 for (uint32_t j = 0; j < len; j++) {
5402 Handle<Object> e(elements->get(j));
5403 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005404 num_of_elements++;
5405 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005406 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005407 }
5408 }
5409 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005410 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005411 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005412 case JSObject::PIXEL_ELEMENTS: {
5413 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
5414 uint32_t len = pixels->length();
5415 if (range < len) {
5416 len = range;
5417 }
5418
5419 for (uint32_t j = 0; j < len; j++) {
5420 num_of_elements++;
5421 if (visitor != NULL) {
5422 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
5423 visitor->visit(j, e);
5424 }
5425 }
5426 break;
5427 }
ager@chromium.org3811b432009-10-28 14:53:37 +00005428 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
5429 num_of_elements =
5430 IterateExternalArrayElements<ExternalByteArray, int8_t>(
5431 receiver, true, true, range, visitor);
5432 break;
5433 }
5434 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
5435 num_of_elements =
5436 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
5437 receiver, true, true, range, visitor);
5438 break;
5439 }
5440 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
5441 num_of_elements =
5442 IterateExternalArrayElements<ExternalShortArray, int16_t>(
5443 receiver, true, true, range, visitor);
5444 break;
5445 }
5446 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
5447 num_of_elements =
5448 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
5449 receiver, true, true, range, visitor);
5450 break;
5451 }
5452 case JSObject::EXTERNAL_INT_ELEMENTS: {
5453 num_of_elements =
5454 IterateExternalArrayElements<ExternalIntArray, int32_t>(
5455 receiver, true, false, range, visitor);
5456 break;
5457 }
5458 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
5459 num_of_elements =
5460 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
5461 receiver, true, false, range, visitor);
5462 break;
5463 }
5464 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
5465 num_of_elements =
5466 IterateExternalArrayElements<ExternalFloatArray, float>(
5467 receiver, false, false, range, visitor);
5468 break;
5469 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005470 case JSObject::DICTIONARY_ELEMENTS: {
5471 Handle<NumberDictionary> dict(receiver->element_dictionary());
5472 uint32_t capacity = dict->Capacity();
5473 for (uint32_t j = 0; j < capacity; j++) {
5474 Handle<Object> k(dict->KeyAt(j));
5475 if (dict->IsKey(*k)) {
5476 ASSERT(k->IsNumber());
5477 uint32_t index = static_cast<uint32_t>(k->Number());
5478 if (index < range) {
5479 num_of_elements++;
5480 if (visitor) {
5481 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
5482 }
5483 }
5484 }
5485 }
5486 break;
5487 }
5488 default:
5489 UNREACHABLE();
5490 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005491 }
5492
5493 return num_of_elements;
5494}
5495
5496
5497/**
5498 * A helper function that visits elements of an Array object, and elements
5499 * on its prototypes.
5500 *
5501 * Elements on prototypes are visited first, and only elements whose indices
5502 * less than Array length are visited.
5503 *
5504 * If a ArrayConcatVisitor object is given, the visitor is called with
5505 * parameters, element's index + visitor_index_offset and the element.
5506 */
5507static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5508 ArrayConcatVisitor* visitor) {
5509 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5510 Handle<Object> obj = array;
5511
5512 static const int kEstimatedPrototypes = 3;
5513 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5514
5515 // Visit prototype first. If an element on the prototype is shadowed by
5516 // the inheritor using the same index, the ArrayConcatVisitor visits
5517 // the prototype element before the shadowing element.
5518 // The visitor can simply overwrite the old value by new value using
5519 // the same index. This follows Array::concat semantics.
5520 while (!obj->IsNull()) {
5521 objects.Add(Handle<JSObject>::cast(obj));
5522 obj = Handle<Object>(obj->GetPrototype());
5523 }
5524
5525 uint32_t nof_elements = 0;
5526 for (int i = objects.length() - 1; i >= 0; i--) {
5527 Handle<JSObject> obj = objects[i];
5528 nof_elements +=
5529 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5530 }
5531
5532 return nof_elements;
5533}
5534
5535
5536/**
5537 * A helper function of Runtime_ArrayConcat.
5538 *
5539 * The first argument is an Array of arrays and objects. It is the
5540 * same as the arguments array of Array::concat JS function.
5541 *
5542 * If an argument is an Array object, the function visits array
5543 * elements. If an argument is not an Array object, the function
5544 * visits the object as if it is an one-element array.
5545 *
5546 * If the result array index overflows 32-bit integer, the rounded
5547 * non-negative number is used as new length. For example, if one
5548 * array length is 2^32 - 1, second array length is 1, the
5549 * concatenated array length is 0.
5550 */
5551static uint32_t IterateArguments(Handle<JSArray> arguments,
5552 ArrayConcatVisitor* visitor) {
5553 uint32_t visited_elements = 0;
5554 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5555
5556 for (uint32_t i = 0; i < num_of_args; i++) {
5557 Handle<Object> obj(arguments->GetElement(i));
5558 if (obj->IsJSArray()) {
5559 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5560 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5561 uint32_t nof_elements =
5562 IterateArrayAndPrototypeElements(array, visitor);
5563 // Total elements of array and its prototype chain can be more than
5564 // the array length, but ArrayConcat can only concatenate at most
5565 // the array length number of elements.
5566 visited_elements += (nof_elements > len) ? len : nof_elements;
5567 if (visitor) visitor->increase_index_offset(len);
5568
5569 } else {
5570 if (visitor) {
5571 visitor->visit(0, obj);
5572 visitor->increase_index_offset(1);
5573 }
5574 visited_elements++;
5575 }
5576 }
5577 return visited_elements;
5578}
5579
5580
5581/**
5582 * Array::concat implementation.
5583 * See ECMAScript 262, 15.4.4.4.
5584 */
5585static Object* Runtime_ArrayConcat(Arguments args) {
5586 ASSERT(args.length() == 1);
5587 HandleScope handle_scope;
5588
5589 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5590 Handle<JSArray> arguments(arg_arrays);
5591
5592 // Pass 1: estimate the number of elements of the result
5593 // (it could be more than real numbers if prototype has elements).
5594 uint32_t result_length = 0;
5595 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5596
5597 { AssertNoAllocation nogc;
5598 for (uint32_t i = 0; i < num_of_args; i++) {
5599 Object* obj = arguments->GetElement(i);
5600 if (obj->IsJSArray()) {
5601 result_length +=
5602 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5603 } else {
5604 result_length++;
5605 }
5606 }
5607 }
5608
5609 // Allocate an empty array, will set length and content later.
5610 Handle<JSArray> result = Factory::NewJSArray(0);
5611
5612 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5613 // If estimated number of elements is more than half of length, a
5614 // fixed array (fast case) is more time and space-efficient than a
5615 // dictionary.
5616 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5617
5618 Handle<FixedArray> storage;
5619 if (fast_case) {
5620 // The backing storage array must have non-existing elements to
5621 // preserve holes across concat operations.
5622 storage = Factory::NewFixedArrayWithHoles(result_length);
5623
5624 } else {
5625 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5626 uint32_t at_least_space_for = estimate_nof_elements +
5627 (estimate_nof_elements >> 2);
5628 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005629 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005630 }
5631
5632 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5633
5634 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5635
5636 IterateArguments(arguments, &visitor);
5637
5638 result->set_length(*len);
5639 result->set_elements(*storage);
5640
5641 return *result;
5642}
5643
5644
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005645// This will not allocate (flatten the string), but it may run
5646// very slowly for very deeply nested ConsStrings. For debugging use only.
5647static Object* Runtime_GlobalPrint(Arguments args) {
5648 NoHandleAllocation ha;
5649 ASSERT(args.length() == 1);
5650
5651 CONVERT_CHECKED(String, string, args[0]);
5652 StringInputBuffer buffer(string);
5653 while (buffer.has_more()) {
5654 uint16_t character = buffer.GetNext();
5655 PrintF("%c", character);
5656 }
5657 return string;
5658}
5659
ager@chromium.org5ec48922009-05-05 07:25:34 +00005660// Moves all own elements of an object, that are below a limit, to positions
5661// starting at zero. All undefined values are placed after non-undefined values,
5662// and are followed by non-existing element. Does not change the length
5663// property.
5664// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005665static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005666 ASSERT(args.length() == 2);
5667 CONVERT_CHECKED(JSObject, object, args[0]);
5668 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5669 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005670}
5671
5672
5673// Move contents of argument 0 (an array) to argument 1 (an array)
5674static Object* Runtime_MoveArrayContents(Arguments args) {
5675 ASSERT(args.length() == 2);
5676 CONVERT_CHECKED(JSArray, from, args[0]);
5677 CONVERT_CHECKED(JSArray, to, args[1]);
5678 to->SetContent(FixedArray::cast(from->elements()));
5679 to->set_length(from->length());
5680 from->SetContent(Heap::empty_fixed_array());
5681 from->set_length(0);
5682 return to;
5683}
5684
5685
5686// How many elements does this array have?
5687static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5688 ASSERT(args.length() == 1);
5689 CONVERT_CHECKED(JSArray, array, args[0]);
5690 HeapObject* elements = array->elements();
5691 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005692 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005693 } else {
5694 return array->length();
5695 }
5696}
5697
5698
5699// Returns an array that tells you where in the [0, length) interval an array
5700// might have elements. Can either return keys or intervals. Keys can have
5701// gaps in (undefined). Intervals can also span over some undefined keys.
5702static Object* Runtime_GetArrayKeys(Arguments args) {
5703 ASSERT(args.length() == 2);
5704 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005705 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005706 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005707 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005708 // Create an array and get all the keys into it, then remove all the
5709 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005710 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005711 int keys_length = keys->length();
5712 for (int i = 0; i < keys_length; i++) {
5713 Object* key = keys->get(i);
5714 uint32_t index;
5715 if (!Array::IndexFromObject(key, &index) || index >= length) {
5716 // Zap invalid keys.
5717 keys->set_undefined(i);
5718 }
5719 }
5720 return *Factory::NewJSArrayWithElements(keys);
5721 } else {
5722 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5723 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005724 single_interval->set(0,
5725 Smi::FromInt(-1),
5726 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005727 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5728 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005729 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005730 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005731 single_interval->set(1, *length_object);
5732 return *Factory::NewJSArrayWithElements(single_interval);
5733 }
5734}
5735
5736
5737// DefineAccessor takes an optional final argument which is the
5738// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5739// to the way accessors are implemented, it is set for both the getter
5740// and setter on the first call to DefineAccessor and ignored on
5741// subsequent calls.
5742static Object* Runtime_DefineAccessor(Arguments args) {
5743 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5744 // Compute attributes.
5745 PropertyAttributes attributes = NONE;
5746 if (args.length() == 5) {
5747 CONVERT_CHECKED(Smi, attrs, args[4]);
5748 int value = attrs->value();
5749 // Only attribute bits should be set.
5750 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5751 attributes = static_cast<PropertyAttributes>(value);
5752 }
5753
5754 CONVERT_CHECKED(JSObject, obj, args[0]);
5755 CONVERT_CHECKED(String, name, args[1]);
5756 CONVERT_CHECKED(Smi, flag, args[2]);
5757 CONVERT_CHECKED(JSFunction, fun, args[3]);
5758 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5759}
5760
5761
5762static Object* Runtime_LookupAccessor(Arguments args) {
5763 ASSERT(args.length() == 3);
5764 CONVERT_CHECKED(JSObject, obj, args[0]);
5765 CONVERT_CHECKED(String, name, args[1]);
5766 CONVERT_CHECKED(Smi, flag, args[2]);
5767 return obj->LookupAccessor(name, flag->value() == 0);
5768}
5769
5770
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005771#ifdef ENABLE_DEBUGGER_SUPPORT
5772static Object* Runtime_DebugBreak(Arguments args) {
5773 ASSERT(args.length() == 0);
5774 return Execution::DebugBreakHelper();
5775}
5776
5777
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005778// Helper functions for wrapping and unwrapping stack frame ids.
5779static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005780 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005781 return Smi::FromInt(id >> 2);
5782}
5783
5784
5785static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5786 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5787}
5788
5789
5790// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005791// args[0]: debug event listener function to set or null or undefined for
5792// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005793// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005794static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005795 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005796 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5797 args[0]->IsUndefined() ||
5798 args[0]->IsNull());
5799 Handle<Object> callback = args.at<Object>(0);
5800 Handle<Object> data = args.at<Object>(1);
5801 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005802
5803 return Heap::undefined_value();
5804}
5805
5806
5807static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005808 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005809 StackGuard::DebugBreak();
5810 return Heap::undefined_value();
5811}
5812
5813
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005814// Find the length of the prototype chain that is to to handled as one. If a
5815// prototype object is hidden it is to be viewed as part of the the object it
5816// is prototype for.
5817static int LocalPrototypeChainLength(JSObject* obj) {
5818 int count = 1;
5819 Object* proto = obj->GetPrototype();
5820 while (proto->IsJSObject() &&
5821 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5822 count++;
5823 proto = JSObject::cast(proto)->GetPrototype();
5824 }
5825 return count;
5826}
5827
5828
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005829static Object* DebugLookupResultValue(Object* receiver, String* name,
5830 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005831 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005832 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005833 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005834 case NORMAL:
5835 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005836 if (value->IsTheHole()) {
5837 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005838 }
5839 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005840 case FIELD:
5841 value =
5842 JSObject::cast(
5843 result->holder())->FastPropertyAt(result->GetFieldIndex());
5844 if (value->IsTheHole()) {
5845 return Heap::undefined_value();
5846 }
5847 return value;
5848 case CONSTANT_FUNCTION:
5849 return result->GetConstantFunction();
5850 case CALLBACKS: {
5851 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005852 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005853 value = receiver->GetPropertyWithCallback(
5854 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005855 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005856 value = Top::pending_exception();
5857 Top::clear_pending_exception();
5858 if (caught_exception != NULL) {
5859 *caught_exception = true;
5860 }
5861 }
5862 return value;
5863 } else {
5864 return Heap::undefined_value();
5865 }
5866 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005867 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005868 case MAP_TRANSITION:
5869 case CONSTANT_TRANSITION:
5870 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005871 return Heap::undefined_value();
5872 default:
5873 UNREACHABLE();
5874 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005875 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005876 return Heap::undefined_value();
5877}
5878
5879
ager@chromium.org32912102009-01-16 10:38:43 +00005880// Get debugger related details for an object property.
5881// args[0]: object holding property
5882// args[1]: name of the property
5883//
5884// The array returned contains the following information:
5885// 0: Property value
5886// 1: Property details
5887// 2: Property value is exception
5888// 3: Getter function if defined
5889// 4: Setter function if defined
5890// Items 2-4 are only filled if the property has either a getter or a setter
5891// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005892static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005893 HandleScope scope;
5894
5895 ASSERT(args.length() == 2);
5896
5897 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5898 CONVERT_ARG_CHECKED(String, name, 1);
5899
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005900 // Make sure to set the current context to the context before the debugger was
5901 // entered (if the debugger is entered). The reason for switching context here
5902 // is that for some property lookups (accessors and interceptors) callbacks
5903 // into the embedding application can occour, and the embedding application
5904 // could have the assumption that its own global context is the current
5905 // context and not some internal debugger context.
5906 SaveContext save;
5907 if (Debug::InDebugger()) {
5908 Top::set_context(*Debug::debugger_entry()->GetContext());
5909 }
5910
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005911 // Skip the global proxy as it has no properties and always delegates to the
5912 // real global object.
5913 if (obj->IsJSGlobalProxy()) {
5914 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5915 }
5916
5917
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005918 // Check if the name is trivially convertible to an index and get the element
5919 // if so.
5920 uint32_t index;
5921 if (name->AsArrayIndex(&index)) {
5922 Handle<FixedArray> details = Factory::NewFixedArray(2);
5923 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5924 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5925 return *Factory::NewJSArrayWithElements(details);
5926 }
5927
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005928 // Find the number of objects making up this.
5929 int length = LocalPrototypeChainLength(*obj);
5930
5931 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005932 Handle<JSObject> jsproto = obj;
5933 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005934 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005935 jsproto->LocalLookup(*name, &result);
5936 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005937 // LookupResult is not GC safe as it holds raw object pointers.
5938 // GC can happen later in this code so put the required fields into
5939 // local variables using handles when required for later use.
5940 PropertyType result_type = result.type();
5941 Handle<Object> result_callback_obj;
5942 if (result_type == CALLBACKS) {
5943 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5944 }
5945 Smi* property_details = result.GetPropertyDetails().AsSmi();
5946 // DebugLookupResultValue can cause GC so details from LookupResult needs
5947 // to be copied to handles before this.
5948 bool caught_exception = false;
5949 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
5950 &caught_exception);
5951 if (raw_value->IsFailure()) return raw_value;
5952 Handle<Object> value(raw_value);
5953
5954 // If the callback object is a fixed array then it contains JavaScript
5955 // getter and/or setter.
5956 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5957 result_callback_obj->IsFixedArray();
5958 Handle<FixedArray> details =
5959 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
5960 details->set(0, *value);
5961 details->set(1, property_details);
5962 if (hasJavaScriptAccessors) {
5963 details->set(2,
5964 caught_exception ? Heap::true_value()
5965 : Heap::false_value());
5966 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
5967 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
5968 }
5969
5970 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005971 }
5972 if (i < length - 1) {
5973 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5974 }
5975 }
5976
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005977 return Heap::undefined_value();
5978}
5979
5980
5981static Object* Runtime_DebugGetProperty(Arguments args) {
5982 HandleScope scope;
5983
5984 ASSERT(args.length() == 2);
5985
5986 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5987 CONVERT_ARG_CHECKED(String, name, 1);
5988
5989 LookupResult result;
5990 obj->Lookup(*name, &result);
5991 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005992 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005993 }
5994 return Heap::undefined_value();
5995}
5996
5997
5998// Return the names of the local named properties.
5999// args[0]: object
6000static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
6001 HandleScope scope;
6002 ASSERT(args.length() == 1);
6003 if (!args[0]->IsJSObject()) {
6004 return Heap::undefined_value();
6005 }
6006 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6007
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006008 // Skip the global proxy as it has no properties and always delegates to the
6009 // real global object.
6010 if (obj->IsJSGlobalProxy()) {
6011 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
6012 }
6013
6014 // Find the number of objects making up this.
6015 int length = LocalPrototypeChainLength(*obj);
6016
6017 // Find the number of local properties for each of the objects.
6018 int* local_property_count = NewArray<int>(length);
6019 int total_property_count = 0;
6020 Handle<JSObject> jsproto = obj;
6021 for (int i = 0; i < length; i++) {
6022 int n;
6023 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
6024 local_property_count[i] = n;
6025 total_property_count += n;
6026 if (i < length - 1) {
6027 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6028 }
6029 }
6030
6031 // Allocate an array with storage for all the property names.
6032 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
6033
6034 // Get the property names.
6035 jsproto = obj;
ager@chromium.orgc730f772009-11-11 10:11:16 +00006036 int proto_with_hidden_properties = 0;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006037 for (int i = 0; i < length; i++) {
6038 jsproto->GetLocalPropertyNames(*names,
6039 i == 0 ? 0 : local_property_count[i - 1]);
ager@chromium.orgc730f772009-11-11 10:11:16 +00006040 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
6041 proto_with_hidden_properties++;
6042 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006043 if (i < length - 1) {
6044 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6045 }
6046 }
6047
ager@chromium.orgc730f772009-11-11 10:11:16 +00006048 // Filter out name of hidden propeties object.
6049 if (proto_with_hidden_properties > 0) {
6050 Handle<FixedArray> old_names = names;
6051 names = Factory::NewFixedArray(
6052 names->length() - proto_with_hidden_properties);
6053 int dest_pos = 0;
6054 for (int i = 0; i < total_property_count; i++) {
6055 Object* name = old_names->get(i);
6056 if (name == Heap::hidden_symbol()) {
6057 continue;
6058 }
6059 names->set(dest_pos++, name);
6060 }
6061 }
6062
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006063 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006064 return *Factory::NewJSArrayWithElements(names);
6065}
6066
6067
6068// Return the names of the local indexed properties.
6069// args[0]: object
6070static Object* Runtime_DebugLocalElementNames(Arguments args) {
6071 HandleScope scope;
6072 ASSERT(args.length() == 1);
6073 if (!args[0]->IsJSObject()) {
6074 return Heap::undefined_value();
6075 }
6076 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6077
6078 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
6079 Handle<FixedArray> names = Factory::NewFixedArray(n);
6080 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
6081 return *Factory::NewJSArrayWithElements(names);
6082}
6083
6084
6085// Return the property type calculated from the property details.
6086// args[0]: smi with property details.
6087static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
6088 ASSERT(args.length() == 1);
6089 CONVERT_CHECKED(Smi, details, args[0]);
6090 PropertyType type = PropertyDetails(details).type();
6091 return Smi::FromInt(static_cast<int>(type));
6092}
6093
6094
6095// Return the property attribute calculated from the property details.
6096// args[0]: smi with property details.
6097static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
6098 ASSERT(args.length() == 1);
6099 CONVERT_CHECKED(Smi, details, args[0]);
6100 PropertyAttributes attributes = PropertyDetails(details).attributes();
6101 return Smi::FromInt(static_cast<int>(attributes));
6102}
6103
6104
6105// Return the property insertion index calculated from the property details.
6106// args[0]: smi with property details.
6107static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
6108 ASSERT(args.length() == 1);
6109 CONVERT_CHECKED(Smi, details, args[0]);
6110 int index = PropertyDetails(details).index();
6111 return Smi::FromInt(index);
6112}
6113
6114
6115// Return information on whether an object has a named or indexed interceptor.
6116// args[0]: object
6117static Object* Runtime_DebugInterceptorInfo(Arguments args) {
6118 HandleScope scope;
6119 ASSERT(args.length() == 1);
6120 if (!args[0]->IsJSObject()) {
6121 return Smi::FromInt(0);
6122 }
6123 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6124
6125 int result = 0;
6126 if (obj->HasNamedInterceptor()) result |= 2;
6127 if (obj->HasIndexedInterceptor()) result |= 1;
6128
6129 return Smi::FromInt(result);
6130}
6131
6132
6133// Return property names from named interceptor.
6134// args[0]: object
6135static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
6136 HandleScope scope;
6137 ASSERT(args.length() == 1);
6138 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006139
ager@chromium.org32912102009-01-16 10:38:43 +00006140 if (obj->HasNamedInterceptor()) {
6141 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
6142 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6143 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006144 return Heap::undefined_value();
6145}
6146
6147
6148// Return element names from indexed interceptor.
6149// args[0]: object
6150static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
6151 HandleScope scope;
6152 ASSERT(args.length() == 1);
6153 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006154
ager@chromium.org32912102009-01-16 10:38:43 +00006155 if (obj->HasIndexedInterceptor()) {
6156 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
6157 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6158 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006159 return Heap::undefined_value();
6160}
6161
6162
6163// Return property value from named interceptor.
6164// args[0]: object
6165// args[1]: property name
6166static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
6167 HandleScope scope;
6168 ASSERT(args.length() == 2);
6169 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6170 RUNTIME_ASSERT(obj->HasNamedInterceptor());
6171 CONVERT_ARG_CHECKED(String, name, 1);
6172
6173 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006174 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006175}
6176
6177
6178// Return element value from indexed interceptor.
6179// args[0]: object
6180// args[1]: index
6181static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
6182 HandleScope scope;
6183 ASSERT(args.length() == 2);
6184 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6185 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
6186 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
6187
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006188 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006189}
6190
6191
6192static Object* Runtime_CheckExecutionState(Arguments args) {
6193 ASSERT(args.length() >= 1);
6194 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00006195 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006196 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006197 return Top::Throw(Heap::illegal_execution_state_symbol());
6198 }
6199
6200 return Heap::true_value();
6201}
6202
6203
6204static Object* Runtime_GetFrameCount(Arguments args) {
6205 HandleScope scope;
6206 ASSERT(args.length() == 1);
6207
6208 // Check arguments.
6209 Object* result = Runtime_CheckExecutionState(args);
6210 if (result->IsFailure()) return result;
6211
6212 // Count all frames which are relevant to debugging stack trace.
6213 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006214 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006215 if (id == StackFrame::NO_ID) {
6216 // If there is no JavaScript stack frame count is 0.
6217 return Smi::FromInt(0);
6218 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006219 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
6220 return Smi::FromInt(n);
6221}
6222
6223
6224static const int kFrameDetailsFrameIdIndex = 0;
6225static const int kFrameDetailsReceiverIndex = 1;
6226static const int kFrameDetailsFunctionIndex = 2;
6227static const int kFrameDetailsArgumentCountIndex = 3;
6228static const int kFrameDetailsLocalCountIndex = 4;
6229static const int kFrameDetailsSourcePositionIndex = 5;
6230static const int kFrameDetailsConstructCallIndex = 6;
6231static const int kFrameDetailsDebuggerFrameIndex = 7;
6232static const int kFrameDetailsFirstDynamicIndex = 8;
6233
6234// Return an array with frame details
6235// args[0]: number: break id
6236// args[1]: number: frame index
6237//
6238// The array returned contains the following information:
6239// 0: Frame id
6240// 1: Receiver
6241// 2: Function
6242// 3: Argument count
6243// 4: Local count
6244// 5: Source position
6245// 6: Constructor call
6246// 7: Debugger frame
6247// Arguments name, value
6248// Locals name, value
6249static Object* Runtime_GetFrameDetails(Arguments args) {
6250 HandleScope scope;
6251 ASSERT(args.length() == 2);
6252
6253 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006254 Object* check = Runtime_CheckExecutionState(args);
6255 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006256 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6257
6258 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006259 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006260 if (id == StackFrame::NO_ID) {
6261 // If there are no JavaScript stack frames return undefined.
6262 return Heap::undefined_value();
6263 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006264 int count = 0;
6265 JavaScriptFrameIterator it(id);
6266 for (; !it.done(); it.Advance()) {
6267 if (count == index) break;
6268 count++;
6269 }
6270 if (it.done()) return Heap::undefined_value();
6271
6272 // Traverse the saved contexts chain to find the active context for the
6273 // selected frame.
6274 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006275 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006276 save = save->prev();
6277 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006278 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006279
6280 // Get the frame id.
6281 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6282
6283 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006284 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006285
6286 // Check for constructor frame.
6287 bool constructor = it.frame()->IsConstructor();
6288
6289 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006290 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006291 ScopeInfo<> info(*code);
6292
6293 // Get the context.
6294 Handle<Context> context(Context::cast(it.frame()->context()));
6295
6296 // Get the locals names and values into a temporary array.
6297 //
6298 // TODO(1240907): Hide compiler-introduced stack variables
6299 // (e.g. .result)? For users of the debugger, they will probably be
6300 // confusing.
6301 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6302 for (int i = 0; i < info.NumberOfLocals(); i++) {
6303 // Name of the local.
6304 locals->set(i * 2, *info.LocalName(i));
6305
6306 // Fetch the value of the local - either from the stack or from a
6307 // heap-allocated context.
6308 if (i < info.number_of_stack_slots()) {
6309 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6310 } else {
6311 Handle<String> name = info.LocalName(i);
6312 // Traverse the context chain to the function context as all local
6313 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006314 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006315 context = Handle<Context>(context->previous());
6316 }
6317 ASSERT(context->is_function_context());
6318 locals->set(i * 2 + 1,
6319 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6320 NULL)));
6321 }
6322 }
6323
6324 // Now advance to the arguments adapter frame (if any). If contains all
6325 // the provided parameters and
6326
6327 // Now advance to the arguments adapter frame (if any). It contains all
6328 // the provided parameters whereas the function frame always have the number
6329 // of arguments matching the functions parameters. The rest of the
6330 // information (except for what is collected above) is the same.
6331 it.AdvanceToArgumentsFrame();
6332
6333 // Find the number of arguments to fill. At least fill the number of
6334 // parameters for the function and fill more if more parameters are provided.
6335 int argument_count = info.number_of_parameters();
6336 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6337 argument_count = it.frame()->GetProvidedParametersCount();
6338 }
6339
6340 // Calculate the size of the result.
6341 int details_size = kFrameDetailsFirstDynamicIndex +
6342 2 * (argument_count + info.NumberOfLocals());
6343 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6344
6345 // Add the frame id.
6346 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6347
6348 // Add the function (same as in function frame).
6349 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6350
6351 // Add the arguments count.
6352 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6353
6354 // Add the locals count
6355 details->set(kFrameDetailsLocalCountIndex,
6356 Smi::FromInt(info.NumberOfLocals()));
6357
6358 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006359 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006360 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6361 } else {
6362 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6363 }
6364
6365 // Add the constructor information.
6366 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6367
6368 // Add information on whether this frame is invoked in the debugger context.
6369 details->set(kFrameDetailsDebuggerFrameIndex,
6370 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6371
6372 // Fill the dynamic part.
6373 int details_index = kFrameDetailsFirstDynamicIndex;
6374
6375 // Add arguments name and value.
6376 for (int i = 0; i < argument_count; i++) {
6377 // Name of the argument.
6378 if (i < info.number_of_parameters()) {
6379 details->set(details_index++, *info.parameter_name(i));
6380 } else {
6381 details->set(details_index++, Heap::undefined_value());
6382 }
6383
6384 // Parameter value.
6385 if (i < it.frame()->GetProvidedParametersCount()) {
6386 details->set(details_index++, it.frame()->GetParameter(i));
6387 } else {
6388 details->set(details_index++, Heap::undefined_value());
6389 }
6390 }
6391
6392 // Add locals name and value from the temporary copy from the function frame.
6393 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6394 details->set(details_index++, locals->get(i));
6395 }
6396
6397 // Add the receiver (same as in function frame).
6398 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6399 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6400 Handle<Object> receiver(it.frame()->receiver());
6401 if (!receiver->IsJSObject()) {
6402 // If the receiver is NOT a JSObject we have hit an optimization
6403 // where a value object is not converted into a wrapped JS objects.
6404 // To hide this optimization from the debugger, we wrap the receiver
6405 // by creating correct wrapper object based on the calling frame's
6406 // global context.
6407 it.Advance();
6408 Handle<Context> calling_frames_global_context(
6409 Context::cast(Context::cast(it.frame()->context())->global_context()));
6410 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6411 }
6412 details->set(kFrameDetailsReceiverIndex, *receiver);
6413
6414 ASSERT_EQ(details_size, details_index);
6415 return *Factory::NewJSArrayWithElements(details);
6416}
6417
6418
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006419// Copy all the context locals into an object used to materialize a scope.
6420static void CopyContextLocalsToScopeObject(Handle<Code> code,
6421 ScopeInfo<>& scope_info,
6422 Handle<Context> context,
6423 Handle<JSObject> scope_object) {
6424 // Fill all context locals to the context extension.
6425 for (int i = Context::MIN_CONTEXT_SLOTS;
6426 i < scope_info.number_of_context_slots();
6427 i++) {
6428 int context_index =
6429 ScopeInfo<>::ContextSlotIndex(*code,
6430 *scope_info.context_slot_name(i),
6431 NULL);
6432
6433 // Don't include the arguments shadow (.arguments) context variable.
6434 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6435 SetProperty(scope_object,
6436 scope_info.context_slot_name(i),
6437 Handle<Object>(context->get(context_index)), NONE);
6438 }
6439 }
6440}
6441
6442
6443// Create a plain JSObject which materializes the local scope for the specified
6444// frame.
6445static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6446 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6447 Handle<Code> code(function->code());
6448 ScopeInfo<> scope_info(*code);
6449
6450 // Allocate and initialize a JSObject with all the arguments, stack locals
6451 // heap locals and extension properties of the debugged function.
6452 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6453
6454 // First fill all parameters.
6455 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6456 SetProperty(local_scope,
6457 scope_info.parameter_name(i),
6458 Handle<Object>(frame->GetParameter(i)), NONE);
6459 }
6460
6461 // Second fill all stack locals.
6462 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6463 SetProperty(local_scope,
6464 scope_info.stack_slot_name(i),
6465 Handle<Object>(frame->GetExpression(i)), NONE);
6466 }
6467
6468 // Third fill all context locals.
6469 Handle<Context> frame_context(Context::cast(frame->context()));
6470 Handle<Context> function_context(frame_context->fcontext());
6471 CopyContextLocalsToScopeObject(code, scope_info,
6472 function_context, local_scope);
6473
6474 // Finally copy any properties from the function context extension. This will
6475 // be variables introduced by eval.
6476 if (function_context->closure() == *function) {
6477 if (function_context->has_extension() &&
6478 !function_context->IsGlobalContext()) {
6479 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006480 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006481 for (int i = 0; i < keys->length(); i++) {
6482 // Names of variables introduced by eval are strings.
6483 ASSERT(keys->get(i)->IsString());
6484 Handle<String> key(String::cast(keys->get(i)));
6485 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6486 }
6487 }
6488 }
6489 return local_scope;
6490}
6491
6492
6493// Create a plain JSObject which materializes the closure content for the
6494// context.
6495static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6496 ASSERT(context->is_function_context());
6497
6498 Handle<Code> code(context->closure()->code());
6499 ScopeInfo<> scope_info(*code);
6500
6501 // Allocate and initialize a JSObject with all the content of theis function
6502 // closure.
6503 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6504
6505 // Check whether the arguments shadow object exists.
6506 int arguments_shadow_index =
6507 ScopeInfo<>::ContextSlotIndex(*code,
6508 Heap::arguments_shadow_symbol(),
6509 NULL);
6510 if (arguments_shadow_index >= 0) {
6511 // In this case all the arguments are available in the arguments shadow
6512 // object.
6513 Handle<JSObject> arguments_shadow(
6514 JSObject::cast(context->get(arguments_shadow_index)));
6515 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6516 SetProperty(closure_scope,
6517 scope_info.parameter_name(i),
6518 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6519 }
6520 }
6521
6522 // Fill all context locals to the context extension.
6523 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6524
6525 // Finally copy any properties from the function context extension. This will
6526 // be variables introduced by eval.
6527 if (context->has_extension()) {
6528 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006529 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006530 for (int i = 0; i < keys->length(); i++) {
6531 // Names of variables introduced by eval are strings.
6532 ASSERT(keys->get(i)->IsString());
6533 Handle<String> key(String::cast(keys->get(i)));
6534 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6535 }
6536 }
6537
6538 return closure_scope;
6539}
6540
6541
6542// Iterate over the actual scopes visible from a stack frame. All scopes are
6543// backed by an actual context except the local scope, which is inserted
6544// "artifically" in the context chain.
6545class ScopeIterator {
6546 public:
6547 enum ScopeType {
6548 ScopeTypeGlobal = 0,
6549 ScopeTypeLocal,
6550 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00006551 ScopeTypeClosure,
6552 // Every catch block contains an implicit with block (its parameter is
6553 // a JSContextExtensionObject) that extends current scope with a variable
6554 // holding exception object. Such with blocks are treated as scopes of their
6555 // own type.
6556 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006557 };
6558
6559 explicit ScopeIterator(JavaScriptFrame* frame)
6560 : frame_(frame),
6561 function_(JSFunction::cast(frame->function())),
6562 context_(Context::cast(frame->context())),
6563 local_done_(false),
6564 at_local_(false) {
6565
6566 // Check whether the first scope is actually a local scope.
6567 if (context_->IsGlobalContext()) {
6568 // If there is a stack slot for .result then this local scope has been
6569 // created for evaluating top level code and it is not a real local scope.
6570 // Checking for the existence of .result seems fragile, but the scope info
6571 // saved with the code object does not otherwise have that information.
6572 Handle<Code> code(function_->code());
6573 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6574 at_local_ = index < 0;
6575 } else if (context_->is_function_context()) {
6576 at_local_ = true;
6577 }
6578 }
6579
6580 // More scopes?
6581 bool Done() { return context_.is_null(); }
6582
6583 // Move to the next scope.
6584 void Next() {
6585 // If at a local scope mark the local scope as passed.
6586 if (at_local_) {
6587 at_local_ = false;
6588 local_done_ = true;
6589
6590 // If the current context is not associated with the local scope the
6591 // current context is the next real scope, so don't move to the next
6592 // context in this case.
6593 if (context_->closure() != *function_) {
6594 return;
6595 }
6596 }
6597
6598 // The global scope is always the last in the chain.
6599 if (context_->IsGlobalContext()) {
6600 context_ = Handle<Context>();
6601 return;
6602 }
6603
6604 // Move to the next context.
6605 if (context_->is_function_context()) {
6606 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6607 } else {
6608 context_ = Handle<Context>(context_->previous());
6609 }
6610
6611 // If passing the local scope indicate that the current scope is now the
6612 // local scope.
6613 if (!local_done_ &&
6614 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6615 at_local_ = true;
6616 }
6617 }
6618
6619 // Return the type of the current scope.
6620 int Type() {
6621 if (at_local_) {
6622 return ScopeTypeLocal;
6623 }
6624 if (context_->IsGlobalContext()) {
6625 ASSERT(context_->global()->IsGlobalObject());
6626 return ScopeTypeGlobal;
6627 }
6628 if (context_->is_function_context()) {
6629 return ScopeTypeClosure;
6630 }
6631 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00006632 // Current scope is either an explicit with statement or a with statement
6633 // implicitely generated for a catch block.
6634 // If the extension object here is a JSContextExtensionObject then
6635 // current with statement is one frome a catch block otherwise it's a
6636 // regular with statement.
6637 if (context_->extension()->IsJSContextExtensionObject()) {
6638 return ScopeTypeCatch;
6639 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006640 return ScopeTypeWith;
6641 }
6642
6643 // Return the JavaScript object with the content of the current scope.
6644 Handle<JSObject> ScopeObject() {
6645 switch (Type()) {
6646 case ScopeIterator::ScopeTypeGlobal:
6647 return Handle<JSObject>(CurrentContext()->global());
6648 break;
6649 case ScopeIterator::ScopeTypeLocal:
6650 // Materialize the content of the local scope into a JSObject.
6651 return MaterializeLocalScope(frame_);
6652 break;
6653 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00006654 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006655 // Return the with object.
6656 return Handle<JSObject>(CurrentContext()->extension());
6657 break;
6658 case ScopeIterator::ScopeTypeClosure:
6659 // Materialize the content of the closure scope into a JSObject.
6660 return MaterializeClosure(CurrentContext());
6661 break;
6662 }
6663 UNREACHABLE();
6664 return Handle<JSObject>();
6665 }
6666
6667 // Return the context for this scope. For the local context there might not
6668 // be an actual context.
6669 Handle<Context> CurrentContext() {
6670 if (at_local_ && context_->closure() != *function_) {
6671 return Handle<Context>();
6672 }
6673 return context_;
6674 }
6675
6676#ifdef DEBUG
6677 // Debug print of the content of the current scope.
6678 void DebugPrint() {
6679 switch (Type()) {
6680 case ScopeIterator::ScopeTypeGlobal:
6681 PrintF("Global:\n");
6682 CurrentContext()->Print();
6683 break;
6684
6685 case ScopeIterator::ScopeTypeLocal: {
6686 PrintF("Local:\n");
6687 Handle<Code> code(function_->code());
6688 ScopeInfo<> scope_info(*code);
6689 scope_info.Print();
6690 if (!CurrentContext().is_null()) {
6691 CurrentContext()->Print();
6692 if (CurrentContext()->has_extension()) {
6693 Handle<JSObject> extension =
6694 Handle<JSObject>(CurrentContext()->extension());
6695 if (extension->IsJSContextExtensionObject()) {
6696 extension->Print();
6697 }
6698 }
6699 }
6700 break;
6701 }
6702
6703 case ScopeIterator::ScopeTypeWith: {
6704 PrintF("With:\n");
6705 Handle<JSObject> extension =
6706 Handle<JSObject>(CurrentContext()->extension());
6707 extension->Print();
6708 break;
6709 }
6710
ager@chromium.orga1645e22009-09-09 19:27:10 +00006711 case ScopeIterator::ScopeTypeCatch: {
6712 PrintF("Catch:\n");
6713 Handle<JSObject> extension =
6714 Handle<JSObject>(CurrentContext()->extension());
6715 extension->Print();
6716 break;
6717 }
6718
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006719 case ScopeIterator::ScopeTypeClosure: {
6720 PrintF("Closure:\n");
6721 CurrentContext()->Print();
6722 if (CurrentContext()->has_extension()) {
6723 Handle<JSObject> extension =
6724 Handle<JSObject>(CurrentContext()->extension());
6725 if (extension->IsJSContextExtensionObject()) {
6726 extension->Print();
6727 }
6728 }
6729 break;
6730 }
6731
6732 default:
6733 UNREACHABLE();
6734 }
6735 PrintF("\n");
6736 }
6737#endif
6738
6739 private:
6740 JavaScriptFrame* frame_;
6741 Handle<JSFunction> function_;
6742 Handle<Context> context_;
6743 bool local_done_;
6744 bool at_local_;
6745
6746 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6747};
6748
6749
6750static Object* Runtime_GetScopeCount(Arguments args) {
6751 HandleScope scope;
6752 ASSERT(args.length() == 2);
6753
6754 // Check arguments.
6755 Object* check = Runtime_CheckExecutionState(args);
6756 if (check->IsFailure()) return check;
6757 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6758
6759 // Get the frame where the debugging is performed.
6760 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6761 JavaScriptFrameIterator it(id);
6762 JavaScriptFrame* frame = it.frame();
6763
6764 // Count the visible scopes.
6765 int n = 0;
6766 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6767 n++;
6768 }
6769
6770 return Smi::FromInt(n);
6771}
6772
6773
6774static const int kScopeDetailsTypeIndex = 0;
6775static const int kScopeDetailsObjectIndex = 1;
6776static const int kScopeDetailsSize = 2;
6777
6778// Return an array with scope details
6779// args[0]: number: break id
6780// args[1]: number: frame index
6781// args[2]: number: scope index
6782//
6783// The array returned contains the following information:
6784// 0: Scope type
6785// 1: Scope object
6786static Object* Runtime_GetScopeDetails(Arguments args) {
6787 HandleScope scope;
6788 ASSERT(args.length() == 3);
6789
6790 // Check arguments.
6791 Object* check = Runtime_CheckExecutionState(args);
6792 if (check->IsFailure()) return check;
6793 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6794 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6795
6796 // Get the frame where the debugging is performed.
6797 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6798 JavaScriptFrameIterator frame_it(id);
6799 JavaScriptFrame* frame = frame_it.frame();
6800
6801 // Find the requested scope.
6802 int n = 0;
6803 ScopeIterator it(frame);
6804 for (; !it.Done() && n < index; it.Next()) {
6805 n++;
6806 }
6807 if (it.Done()) {
6808 return Heap::undefined_value();
6809 }
6810
6811 // Calculate the size of the result.
6812 int details_size = kScopeDetailsSize;
6813 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6814
6815 // Fill in scope details.
6816 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6817 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6818
6819 return *Factory::NewJSArrayWithElements(details);
6820}
6821
6822
6823static Object* Runtime_DebugPrintScopes(Arguments args) {
6824 HandleScope scope;
6825 ASSERT(args.length() == 0);
6826
6827#ifdef DEBUG
6828 // Print the scopes for the top frame.
6829 StackFrameLocator locator;
6830 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6831 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6832 it.DebugPrint();
6833 }
6834#endif
6835 return Heap::undefined_value();
6836}
6837
6838
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006839static Object* Runtime_GetCFrames(Arguments args) {
6840 HandleScope scope;
6841 ASSERT(args.length() == 1);
6842 Object* result = Runtime_CheckExecutionState(args);
6843 if (result->IsFailure()) return result;
6844
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006845#if V8_HOST_ARCH_64_BIT
6846 UNIMPLEMENTED();
6847 return Heap::undefined_value();
6848#else
6849
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006850 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006851 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6852 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006853 if (frames_count == OS::kStackWalkError) {
6854 return Heap::undefined_value();
6855 }
6856
6857 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6858 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6859 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6860 for (int i = 0; i < frames_count; i++) {
6861 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6862 frame_value->SetProperty(
6863 *address_str,
6864 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6865 NONE);
6866
6867 // Get the stack walk text for this frame.
6868 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006869 int frame_text_length = StrLength(frames[i].text);
6870 if (frame_text_length > 0) {
6871 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006872 frame_text = Factory::NewStringFromAscii(str);
6873 }
6874
6875 if (!frame_text.is_null()) {
6876 frame_value->SetProperty(*text_str, *frame_text, NONE);
6877 }
6878
6879 frames_array->set(i, *frame_value);
6880 }
6881 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006882#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006883}
6884
6885
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006886static Object* Runtime_GetThreadCount(Arguments args) {
6887 HandleScope scope;
6888 ASSERT(args.length() == 1);
6889
6890 // Check arguments.
6891 Object* result = Runtime_CheckExecutionState(args);
6892 if (result->IsFailure()) return result;
6893
6894 // Count all archived V8 threads.
6895 int n = 0;
6896 for (ThreadState* thread = ThreadState::FirstInUse();
6897 thread != NULL;
6898 thread = thread->Next()) {
6899 n++;
6900 }
6901
6902 // Total number of threads is current thread and archived threads.
6903 return Smi::FromInt(n + 1);
6904}
6905
6906
6907static const int kThreadDetailsCurrentThreadIndex = 0;
6908static const int kThreadDetailsThreadIdIndex = 1;
6909static const int kThreadDetailsSize = 2;
6910
6911// Return an array with thread details
6912// args[0]: number: break id
6913// args[1]: number: thread index
6914//
6915// The array returned contains the following information:
6916// 0: Is current thread?
6917// 1: Thread id
6918static Object* Runtime_GetThreadDetails(Arguments args) {
6919 HandleScope scope;
6920 ASSERT(args.length() == 2);
6921
6922 // Check arguments.
6923 Object* check = Runtime_CheckExecutionState(args);
6924 if (check->IsFailure()) return check;
6925 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6926
6927 // Allocate array for result.
6928 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6929
6930 // Thread index 0 is current thread.
6931 if (index == 0) {
6932 // Fill the details.
6933 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6934 details->set(kThreadDetailsThreadIdIndex,
6935 Smi::FromInt(ThreadManager::CurrentId()));
6936 } else {
6937 // Find the thread with the requested index.
6938 int n = 1;
6939 ThreadState* thread = ThreadState::FirstInUse();
6940 while (index != n && thread != NULL) {
6941 thread = thread->Next();
6942 n++;
6943 }
6944 if (thread == NULL) {
6945 return Heap::undefined_value();
6946 }
6947
6948 // Fill the details.
6949 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6950 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6951 }
6952
6953 // Convert to JS array and return.
6954 return *Factory::NewJSArrayWithElements(details);
6955}
6956
6957
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006958static Object* Runtime_GetBreakLocations(Arguments args) {
6959 HandleScope scope;
6960 ASSERT(args.length() == 1);
6961
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006962 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6963 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006964 // Find the number of break points
6965 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6966 if (break_locations->IsUndefined()) return Heap::undefined_value();
6967 // Return array as JS array
6968 return *Factory::NewJSArrayWithElements(
6969 Handle<FixedArray>::cast(break_locations));
6970}
6971
6972
6973// Set a break point in a function
6974// args[0]: function
6975// args[1]: number: break source position (within the function source)
6976// args[2]: number: break point object
6977static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6978 HandleScope scope;
6979 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006980 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6981 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006982 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6983 RUNTIME_ASSERT(source_position >= 0);
6984 Handle<Object> break_point_object_arg = args.at<Object>(2);
6985
6986 // Set break point.
6987 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6988
6989 return Heap::undefined_value();
6990}
6991
6992
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006993Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6994 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006995 // Iterate the heap looking for SharedFunctionInfo generated from the
6996 // script. The inner most SharedFunctionInfo containing the source position
6997 // for the requested break point is found.
6998 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6999 // which is found is not compiled it is compiled and the heap is iterated
7000 // again as the compilation might create inner functions from the newly
7001 // compiled function and the actual requested break point might be in one of
7002 // these functions.
7003 bool done = false;
7004 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00007005 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007006 Handle<SharedFunctionInfo> target;
7007 // The current candidate for the last function in script:
7008 Handle<SharedFunctionInfo> last;
7009 while (!done) {
7010 HeapIterator iterator;
7011 while (iterator.has_next()) {
7012 HeapObject* obj = iterator.next();
7013 ASSERT(obj != NULL);
7014 if (obj->IsSharedFunctionInfo()) {
7015 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
7016 if (shared->script() == *script) {
7017 // If the SharedFunctionInfo found has the requested script data and
7018 // contains the source position it is a candidate.
7019 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00007020 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007021 start_position = shared->start_position();
7022 }
7023 if (start_position <= position &&
7024 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00007025 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007026 // candidate this is the new candidate.
7027 if (target.is_null()) {
7028 target_start_position = start_position;
7029 target = shared;
7030 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00007031 if (target_start_position == start_position &&
7032 shared->end_position() == target->end_position()) {
7033 // If a top-level function contain only one function
7034 // declartion the source for the top-level and the function is
7035 // the same. In that case prefer the non top-level function.
7036 if (!shared->is_toplevel()) {
7037 target_start_position = start_position;
7038 target = shared;
7039 }
7040 } else if (target_start_position <= start_position &&
7041 shared->end_position() <= target->end_position()) {
7042 // This containment check includes equality as a function inside
7043 // a top-level function can share either start or end position
7044 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007045 target_start_position = start_position;
7046 target = shared;
7047 }
7048 }
7049 }
7050
7051 // Keep track of the last function in the script.
7052 if (last.is_null() ||
7053 shared->end_position() > last->start_position()) {
7054 last = shared;
7055 }
7056 }
7057 }
7058 }
7059
7060 // Make sure some candidate is selected.
7061 if (target.is_null()) {
7062 if (!last.is_null()) {
7063 // Position after the last function - use last.
7064 target = last;
7065 } else {
7066 // Unable to find function - possibly script without any function.
7067 return Heap::undefined_value();
7068 }
7069 }
7070
7071 // If the candidate found is compiled we are done. NOTE: when lazy
7072 // compilation of inner functions is introduced some additional checking
7073 // needs to be done here to compile inner functions.
7074 done = target->is_compiled();
7075 if (!done) {
7076 // If the candidate is not compiled compile it to reveal any inner
7077 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007078 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007079 }
7080 }
7081
7082 return *target;
7083}
7084
7085
7086// Change the state of a break point in a script. NOTE: Regarding performance
7087// see the NOTE for GetScriptFromScriptData.
7088// args[0]: script to set break point in
7089// args[1]: number: break source position (within the script source)
7090// args[2]: number: break point object
7091static Object* Runtime_SetScriptBreakPoint(Arguments args) {
7092 HandleScope scope;
7093 ASSERT(args.length() == 3);
7094 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
7095 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7096 RUNTIME_ASSERT(source_position >= 0);
7097 Handle<Object> break_point_object_arg = args.at<Object>(2);
7098
7099 // Get the script from the script wrapper.
7100 RUNTIME_ASSERT(wrapper->value()->IsScript());
7101 Handle<Script> script(Script::cast(wrapper->value()));
7102
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007103 Object* result = Runtime::FindSharedFunctionInfoInScript(
7104 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007105 if (!result->IsUndefined()) {
7106 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
7107 // Find position within function. The script position might be before the
7108 // source position of the first function.
7109 int position;
7110 if (shared->start_position() > source_position) {
7111 position = 0;
7112 } else {
7113 position = source_position - shared->start_position();
7114 }
7115 Debug::SetBreakPoint(shared, position, break_point_object_arg);
7116 }
7117 return Heap::undefined_value();
7118}
7119
7120
7121// Clear a break point
7122// args[0]: number: break point object
7123static Object* Runtime_ClearBreakPoint(Arguments args) {
7124 HandleScope scope;
7125 ASSERT(args.length() == 1);
7126 Handle<Object> break_point_object_arg = args.at<Object>(0);
7127
7128 // Clear break point.
7129 Debug::ClearBreakPoint(break_point_object_arg);
7130
7131 return Heap::undefined_value();
7132}
7133
7134
7135// Change the state of break on exceptions
7136// args[0]: boolean indicating uncaught exceptions
7137// args[1]: boolean indicating on/off
7138static Object* Runtime_ChangeBreakOnException(Arguments args) {
7139 HandleScope scope;
7140 ASSERT(args.length() == 2);
7141 ASSERT(args[0]->IsNumber());
7142 ASSERT(args[1]->IsBoolean());
7143
7144 // Update break point state
7145 ExceptionBreakType type =
7146 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
7147 bool enable = args[1]->ToBoolean()->IsTrue();
7148 Debug::ChangeBreakOnException(type, enable);
7149 return Heap::undefined_value();
7150}
7151
7152
7153// Prepare for stepping
7154// args[0]: break id for checking execution state
7155// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00007156// args[2]: number of times to perform the step, for step out it is the number
7157// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007158static Object* Runtime_PrepareStep(Arguments args) {
7159 HandleScope scope;
7160 ASSERT(args.length() == 3);
7161 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007162 Object* check = Runtime_CheckExecutionState(args);
7163 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007164 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
7165 return Top::Throw(Heap::illegal_argument_symbol());
7166 }
7167
7168 // Get the step action and check validity.
7169 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
7170 if (step_action != StepIn &&
7171 step_action != StepNext &&
7172 step_action != StepOut &&
7173 step_action != StepInMin &&
7174 step_action != StepMin) {
7175 return Top::Throw(Heap::illegal_argument_symbol());
7176 }
7177
7178 // Get the number of steps.
7179 int step_count = NumberToInt32(args[2]);
7180 if (step_count < 1) {
7181 return Top::Throw(Heap::illegal_argument_symbol());
7182 }
7183
ager@chromium.orga1645e22009-09-09 19:27:10 +00007184 // Clear all current stepping setup.
7185 Debug::ClearStepping();
7186
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007187 // Prepare step.
7188 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
7189 return Heap::undefined_value();
7190}
7191
7192
7193// Clear all stepping set by PrepareStep.
7194static Object* Runtime_ClearStepping(Arguments args) {
7195 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007196 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007197 Debug::ClearStepping();
7198 return Heap::undefined_value();
7199}
7200
7201
7202// Creates a copy of the with context chain. The copy of the context chain is
7203// is linked to the function context supplied.
7204static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
7205 Handle<Context> function_context) {
7206 // At the bottom of the chain. Return the function context to link to.
7207 if (context_chain->is_function_context()) {
7208 return function_context;
7209 }
7210
7211 // Recursively copy the with contexts.
7212 Handle<Context> previous(context_chain->previous());
7213 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
7214 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007215 CopyWithContextChain(function_context, previous),
7216 extension,
7217 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007218}
7219
7220
7221// Helper function to find or create the arguments object for
7222// Runtime_DebugEvaluate.
7223static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
7224 Handle<JSFunction> function,
7225 Handle<Code> code,
7226 const ScopeInfo<>* sinfo,
7227 Handle<Context> function_context) {
7228 // Try to find the value of 'arguments' to pass as parameter. If it is not
7229 // found (that is the debugged function does not reference 'arguments' and
7230 // does not support eval) then create an 'arguments' object.
7231 int index;
7232 if (sinfo->number_of_stack_slots() > 0) {
7233 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
7234 if (index != -1) {
7235 return Handle<Object>(frame->GetExpression(index));
7236 }
7237 }
7238
7239 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
7240 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
7241 NULL);
7242 if (index != -1) {
7243 return Handle<Object>(function_context->get(index));
7244 }
7245 }
7246
7247 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007248 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
7249 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007250 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007251 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007252 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007253 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007254 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007255 return arguments;
7256}
7257
7258
7259// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00007260// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007261// extension part has all the parameters and locals of the function on the
7262// stack frame. A function which calls eval with the code to evaluate is then
7263// compiled in this context and called in this context. As this context
7264// replaces the context of the function on the stack frame a new (empty)
7265// function is created as well to be used as the closure for the context.
7266// This function and the context acts as replacements for the function on the
7267// stack frame presenting the same view of the values of parameters and
7268// local variables as if the piece of JavaScript was evaluated at the point
7269// where the function on the stack frame is currently stopped.
7270static Object* Runtime_DebugEvaluate(Arguments args) {
7271 HandleScope scope;
7272
7273 // Check the execution state and decode arguments frame and source to be
7274 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007275 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007276 Object* check_result = Runtime_CheckExecutionState(args);
7277 if (check_result->IsFailure()) return check_result;
7278 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7279 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007280 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
7281
7282 // Handle the processing of break.
7283 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007284
7285 // Get the frame where the debugging is performed.
7286 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7287 JavaScriptFrameIterator it(id);
7288 JavaScriptFrame* frame = it.frame();
7289 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7290 Handle<Code> code(function->code());
7291 ScopeInfo<> sinfo(*code);
7292
7293 // Traverse the saved contexts chain to find the active context for the
7294 // selected frame.
7295 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007296 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007297 save = save->prev();
7298 }
7299 ASSERT(save != NULL);
7300 SaveContext savex;
7301 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007302
7303 // Create the (empty) function replacing the function on the stack frame for
7304 // the purpose of evaluating in the context created below. It is important
7305 // that this function does not describe any parameters and local variables
7306 // in the context. If it does then this will cause problems with the lookup
7307 // in Context::Lookup, where context slots for parameters and local variables
7308 // are looked at before the extension object.
7309 Handle<JSFunction> go_between =
7310 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7311 go_between->set_context(function->context());
7312#ifdef DEBUG
7313 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7314 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7315 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7316#endif
7317
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007318 // Materialize the content of the local scope into a JSObject.
7319 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007320
7321 // Allocate a new context for the debug evaluation and set the extension
7322 // object build.
7323 Handle<Context> context =
7324 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007325 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007326 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007327 Handle<Context> frame_context(Context::cast(frame->context()));
7328 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007329 context = CopyWithContextChain(frame_context, context);
7330
7331 // Wrap the evaluation statement in a new function compiled in the newly
7332 // created context. The function has one parameter which has to be called
7333 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007334 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007335 // function(arguments,__source__) {return eval(__source__);}
7336 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00007337 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007338 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007339 Handle<String> function_source =
7340 Factory::NewStringFromAscii(Vector<const char>(source_str,
7341 source_str_length));
7342 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007343 Compiler::CompileEval(function_source,
7344 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007345 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007346 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007347 if (boilerplate.is_null()) return Failure::Exception();
7348 Handle<JSFunction> compiled_function =
7349 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7350
7351 // Invoke the result of the compilation to get the evaluation function.
7352 bool has_pending_exception;
7353 Handle<Object> receiver(frame->receiver());
7354 Handle<Object> evaluation_function =
7355 Execution::Call(compiled_function, receiver, 0, NULL,
7356 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007357 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007358
7359 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7360 function_context);
7361
7362 // Invoke the evaluation function and return the result.
7363 const int argc = 2;
7364 Object** argv[argc] = { arguments.location(),
7365 Handle<Object>::cast(source).location() };
7366 Handle<Object> result =
7367 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7368 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007369 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007370
7371 // Skip the global proxy as it has no properties and always delegates to the
7372 // real global object.
7373 if (result->IsJSGlobalProxy()) {
7374 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7375 }
7376
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007377 return *result;
7378}
7379
7380
7381static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7382 HandleScope scope;
7383
7384 // Check the execution state and decode arguments frame and source to be
7385 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007386 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007387 Object* check_result = Runtime_CheckExecutionState(args);
7388 if (check_result->IsFailure()) return check_result;
7389 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007390 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7391
7392 // Handle the processing of break.
7393 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007394
7395 // Enter the top context from before the debugger was invoked.
7396 SaveContext save;
7397 SaveContext* top = &save;
7398 while (top != NULL && *top->context() == *Debug::debug_context()) {
7399 top = top->prev();
7400 }
7401 if (top != NULL) {
7402 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007403 }
7404
7405 // Get the global context now set to the top context from before the
7406 // debugger was invoked.
7407 Handle<Context> context = Top::global_context();
7408
7409 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007410 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007411 Handle<JSFunction>(Compiler::CompileEval(source,
7412 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007413 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007414 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007415 if (boilerplate.is_null()) return Failure::Exception();
7416 Handle<JSFunction> compiled_function =
7417 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7418 context));
7419
7420 // Invoke the result of the compilation to get the evaluation function.
7421 bool has_pending_exception;
7422 Handle<Object> receiver = Top::global();
7423 Handle<Object> result =
7424 Execution::Call(compiled_function, receiver, 0, NULL,
7425 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007426 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007427 return *result;
7428}
7429
7430
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007431static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7432 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007433 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007434
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007435 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007436 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007437
7438 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007439 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007440 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7441 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7442 // because using
7443 // instances->set(i, *GetScriptWrapper(script))
7444 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7445 // already have deferenced the instances handle.
7446 Handle<JSValue> wrapper = GetScriptWrapper(script);
7447 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007448 }
7449
7450 // Return result as a JS array.
7451 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7452 Handle<JSArray>::cast(result)->SetContent(*instances);
7453 return *result;
7454}
7455
7456
7457// Helper function used by Runtime_DebugReferencedBy below.
7458static int DebugReferencedBy(JSObject* target,
7459 Object* instance_filter, int max_references,
7460 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007461 JSFunction* arguments_function) {
7462 NoHandleAllocation ha;
7463 AssertNoAllocation no_alloc;
7464
7465 // Iterate the heap.
7466 int count = 0;
7467 JSObject* last = NULL;
7468 HeapIterator iterator;
7469 while (iterator.has_next() &&
7470 (max_references == 0 || count < max_references)) {
7471 // Only look at all JSObjects.
7472 HeapObject* heap_obj = iterator.next();
7473 if (heap_obj->IsJSObject()) {
7474 // Skip context extension objects and argument arrays as these are
7475 // checked in the context of functions using them.
7476 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007477 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007478 obj->map()->constructor() == arguments_function) {
7479 continue;
7480 }
7481
7482 // Check if the JS object has a reference to the object looked for.
7483 if (obj->ReferencesObject(target)) {
7484 // Check instance filter if supplied. This is normally used to avoid
7485 // references from mirror objects (see Runtime_IsInPrototypeChain).
7486 if (!instance_filter->IsUndefined()) {
7487 Object* V = obj;
7488 while (true) {
7489 Object* prototype = V->GetPrototype();
7490 if (prototype->IsNull()) {
7491 break;
7492 }
7493 if (instance_filter == prototype) {
7494 obj = NULL; // Don't add this object.
7495 break;
7496 }
7497 V = prototype;
7498 }
7499 }
7500
7501 if (obj != NULL) {
7502 // Valid reference found add to instance array if supplied an update
7503 // count.
7504 if (instances != NULL && count < instances_size) {
7505 instances->set(count, obj);
7506 }
7507 last = obj;
7508 count++;
7509 }
7510 }
7511 }
7512 }
7513
7514 // Check for circular reference only. This can happen when the object is only
7515 // referenced from mirrors and has a circular reference in which case the
7516 // object is not really alive and would have been garbage collected if not
7517 // referenced from the mirror.
7518 if (count == 1 && last == target) {
7519 count = 0;
7520 }
7521
7522 // Return the number of referencing objects found.
7523 return count;
7524}
7525
7526
7527// Scan the heap for objects with direct references to an object
7528// args[0]: the object to find references to
7529// args[1]: constructor function for instances to exclude (Mirror)
7530// args[2]: the the maximum number of objects to return
7531static Object* Runtime_DebugReferencedBy(Arguments args) {
7532 ASSERT(args.length() == 3);
7533
7534 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007535 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007536
7537 // Check parameters.
7538 CONVERT_CHECKED(JSObject, target, args[0]);
7539 Object* instance_filter = args[1];
7540 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7541 instance_filter->IsJSObject());
7542 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7543 RUNTIME_ASSERT(max_references >= 0);
7544
7545 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007546 JSObject* arguments_boilerplate =
7547 Top::context()->global_context()->arguments_boilerplate();
7548 JSFunction* arguments_function =
7549 JSFunction::cast(arguments_boilerplate->map()->constructor());
7550
7551 // Get the number of referencing objects.
7552 int count;
7553 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007554 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007555
7556 // Allocate an array to hold the result.
7557 Object* object = Heap::AllocateFixedArray(count);
7558 if (object->IsFailure()) return object;
7559 FixedArray* instances = FixedArray::cast(object);
7560
7561 // Fill the referencing objects.
7562 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007563 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007564
7565 // Return result as JS array.
7566 Object* result =
7567 Heap::AllocateJSObject(
7568 Top::context()->global_context()->array_function());
7569 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7570 return result;
7571}
7572
7573
7574// Helper function used by Runtime_DebugConstructedBy below.
7575static int DebugConstructedBy(JSFunction* constructor, int max_references,
7576 FixedArray* instances, int instances_size) {
7577 AssertNoAllocation no_alloc;
7578
7579 // Iterate the heap.
7580 int count = 0;
7581 HeapIterator iterator;
7582 while (iterator.has_next() &&
7583 (max_references == 0 || count < max_references)) {
7584 // Only look at all JSObjects.
7585 HeapObject* heap_obj = iterator.next();
7586 if (heap_obj->IsJSObject()) {
7587 JSObject* obj = JSObject::cast(heap_obj);
7588 if (obj->map()->constructor() == constructor) {
7589 // Valid reference found add to instance array if supplied an update
7590 // count.
7591 if (instances != NULL && count < instances_size) {
7592 instances->set(count, obj);
7593 }
7594 count++;
7595 }
7596 }
7597 }
7598
7599 // Return the number of referencing objects found.
7600 return count;
7601}
7602
7603
7604// Scan the heap for objects constructed by a specific function.
7605// args[0]: the constructor to find instances of
7606// args[1]: the the maximum number of objects to return
7607static Object* Runtime_DebugConstructedBy(Arguments args) {
7608 ASSERT(args.length() == 2);
7609
7610 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007611 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007612
7613 // Check parameters.
7614 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7615 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7616 RUNTIME_ASSERT(max_references >= 0);
7617
7618 // Get the number of referencing objects.
7619 int count;
7620 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7621
7622 // Allocate an array to hold the result.
7623 Object* object = Heap::AllocateFixedArray(count);
7624 if (object->IsFailure()) return object;
7625 FixedArray* instances = FixedArray::cast(object);
7626
7627 // Fill the referencing objects.
7628 count = DebugConstructedBy(constructor, max_references, instances, count);
7629
7630 // Return result as JS array.
7631 Object* result =
7632 Heap::AllocateJSObject(
7633 Top::context()->global_context()->array_function());
7634 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7635 return result;
7636}
7637
7638
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007639// Find the effective prototype object as returned by __proto__.
7640// args[0]: the object to find the prototype for.
7641static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007642 ASSERT(args.length() == 1);
7643
7644 CONVERT_CHECKED(JSObject, obj, args[0]);
7645
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007646 // Use the __proto__ accessor.
7647 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007648}
7649
7650
7651static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007652 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007653 CPU::DebugBreak();
7654 return Heap::undefined_value();
7655}
7656
7657
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007658static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007659#ifdef DEBUG
7660 HandleScope scope;
7661 ASSERT(args.length() == 1);
7662 // Get the function and make sure it is compiled.
7663 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7664 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7665 return Failure::Exception();
7666 }
7667 func->code()->PrintLn();
7668#endif // DEBUG
7669 return Heap::undefined_value();
7670}
ager@chromium.org9085a012009-05-11 19:22:57 +00007671
7672
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007673static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
7674#ifdef DEBUG
7675 HandleScope scope;
7676 ASSERT(args.length() == 1);
7677 // Get the function and make sure it is compiled.
7678 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7679 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7680 return Failure::Exception();
7681 }
7682 func->shared()->construct_stub()->PrintLn();
7683#endif // DEBUG
7684 return Heap::undefined_value();
7685}
7686
7687
ager@chromium.org9085a012009-05-11 19:22:57 +00007688static Object* Runtime_FunctionGetInferredName(Arguments args) {
7689 NoHandleAllocation ha;
7690 ASSERT(args.length() == 1);
7691
7692 CONVERT_CHECKED(JSFunction, f, args[0]);
7693 return f->shared()->inferred_name();
7694}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007695
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007696#endif // ENABLE_DEBUGGER_SUPPORT
7697
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00007698#ifdef ENABLE_LOGGING_AND_PROFILING
7699
7700static Object* Runtime_ProfilerResume(Arguments args) {
7701 NoHandleAllocation ha;
7702 ASSERT(args.length() == 1);
7703
7704 CONVERT_CHECKED(Smi, smi_modules, args[0]);
7705 v8::V8::ResumeProfilerEx(smi_modules->value());
7706 return Heap::undefined_value();
7707}
7708
7709
7710static Object* Runtime_ProfilerPause(Arguments args) {
7711 NoHandleAllocation ha;
7712 ASSERT(args.length() == 1);
7713
7714 CONVERT_CHECKED(Smi, smi_modules, args[0]);
7715 v8::V8::PauseProfilerEx(smi_modules->value());
7716 return Heap::undefined_value();
7717}
7718
7719#endif // ENABLE_LOGGING_AND_PROFILING
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007720
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007721// Finds the script object from the script data. NOTE: This operation uses
7722// heap traversal to find the function generated for the source position
7723// for the requested break point. For lazily compiled functions several heap
7724// traversals might be required rendering this operation as a rather slow
7725// operation. However for setting break points which is normally done through
7726// some kind of user interaction the performance is not crucial.
7727static Handle<Object> Runtime_GetScriptFromScriptName(
7728 Handle<String> script_name) {
7729 // Scan the heap for Script objects to find the script with the requested
7730 // script data.
7731 Handle<Script> script;
7732 HeapIterator iterator;
7733 while (script.is_null() && iterator.has_next()) {
7734 HeapObject* obj = iterator.next();
7735 // If a script is found check if it has the script data requested.
7736 if (obj->IsScript()) {
7737 if (Script::cast(obj)->name()->IsString()) {
7738 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7739 script = Handle<Script>(Script::cast(obj));
7740 }
7741 }
7742 }
7743 }
7744
7745 // If no script with the requested script data is found return undefined.
7746 if (script.is_null()) return Factory::undefined_value();
7747
7748 // Return the script found.
7749 return GetScriptWrapper(script);
7750}
7751
7752
7753// Get the script object from script data. NOTE: Regarding performance
7754// see the NOTE for GetScriptFromScriptData.
7755// args[0]: script data for the script to find the source for
7756static Object* Runtime_GetScript(Arguments args) {
7757 HandleScope scope;
7758
7759 ASSERT(args.length() == 1);
7760
7761 CONVERT_CHECKED(String, script_name, args[0]);
7762
7763 // Find the requested script.
7764 Handle<Object> result =
7765 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7766 return *result;
7767}
7768
7769
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007770// Determines whether the given stack frame should be displayed in
7771// a stack trace. The caller is the error constructor that asked
7772// for the stack trace to be collected. The first time a construct
7773// call to this function is encountered it is skipped. The seen_caller
7774// in/out parameter is used to remember if the caller has been seen
7775// yet.
7776static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
7777 bool* seen_caller) {
7778 // Only display JS frames.
7779 if (!raw_frame->is_java_script())
7780 return false;
7781 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7782 Object* raw_fun = frame->function();
7783 // Not sure when this can happen but skip it just in case.
7784 if (!raw_fun->IsJSFunction())
7785 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007786 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007787 *seen_caller = true;
7788 return false;
7789 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007790 // Skip all frames until we've seen the caller. Also, skip the most
7791 // obvious builtin calls. Some builtin calls (such as Number.ADD
7792 // which is invoked using 'call') are very difficult to recognize
7793 // so we're leaving them in for now.
7794 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007795}
7796
7797
7798// Collect the raw data for a stack trace. Returns an array of three
7799// element segments each containing a receiver, function and native
7800// code offset.
7801static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007802 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007803 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007804 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
7805
7806 HandleScope scope;
7807
7808 int initial_size = limit < 10 ? limit : 10;
7809 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007810
7811 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007812 // If the caller parameter is a function we skip frames until we're
7813 // under it before starting to collect.
7814 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007815 int cursor = 0;
7816 int frames_seen = 0;
7817 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007818 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007819 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007820 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007821 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007822 Object* recv = frame->receiver();
7823 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007824 Address pc = frame->pc();
7825 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007826 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007827 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007828 if (cursor + 2 < elements->length()) {
7829 elements->set(cursor++, recv);
7830 elements->set(cursor++, fun);
7831 elements->set(cursor++, offset, SKIP_WRITE_BARRIER);
7832 } else {
7833 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007834 Handle<Object> recv_handle(recv);
7835 Handle<Object> fun_handle(fun);
7836 SetElement(result, cursor++, recv_handle);
7837 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007838 SetElement(result, cursor++, Handle<Smi>(offset));
7839 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007840 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007841 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007842 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007843
7844 result->set_length(Smi::FromInt(cursor), SKIP_WRITE_BARRIER);
7845
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007846 return *result;
7847}
7848
7849
ager@chromium.org3811b432009-10-28 14:53:37 +00007850// Returns V8 version as a string.
7851static Object* Runtime_GetV8Version(Arguments args) {
7852 ASSERT_EQ(args.length(), 0);
7853
7854 NoHandleAllocation ha;
7855
7856 const char* version_string = v8::V8::GetVersion();
7857
7858 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
7859}
7860
7861
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007862static Object* Runtime_Abort(Arguments args) {
7863 ASSERT(args.length() == 2);
7864 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7865 Smi::cast(args[1])->value());
7866 Top::PrintStack();
7867 OS::Abort();
7868 UNREACHABLE();
7869 return NULL;
7870}
7871
7872
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007873static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
7874 ASSERT(args.length() == 0);
7875 HandleScope::DeleteExtensions();
7876 return Heap::undefined_value();
7877}
7878
7879
kasper.lund44510672008-07-25 07:37:58 +00007880#ifdef DEBUG
7881// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7882// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007883static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007884 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007885 HandleScope scope;
7886 Handle<JSArray> result = Factory::NewJSArray(0);
7887 int index = 0;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007888#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007889 { \
7890 HandleScope inner; \
7891 Handle<String> name = \
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007892 Factory::NewStringFromAscii( \
7893 Vector<const char>(#Name, StrLength(#Name))); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007894 Handle<JSArray> pair = Factory::NewJSArray(0); \
7895 SetElement(pair, 0, name); \
7896 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7897 SetElement(result, index++, pair); \
7898 }
7899 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7900#undef ADD_ENTRY
7901 return *result;
7902}
kasper.lund44510672008-07-25 07:37:58 +00007903#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007904
7905
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007906static Object* Runtime_Log(Arguments args) {
7907 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007908 CONVERT_CHECKED(String, format, args[0]);
7909 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007910 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007911 Logger::LogRuntime(chars, elms);
7912 return Heap::undefined_value();
7913}
7914
7915
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007916static Object* Runtime_IS_VAR(Arguments args) {
7917 UNREACHABLE(); // implemented as macro in the parser
7918 return NULL;
7919}
7920
7921
7922// ----------------------------------------------------------------------------
7923// Implementation of Runtime
7924
ager@chromium.orga1645e22009-09-09 19:27:10 +00007925#define F(name, nargs, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007926 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +00007927 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007928
7929static Runtime::Function Runtime_functions[] = {
7930 RUNTIME_FUNCTION_LIST(F)
ager@chromium.orga1645e22009-09-09 19:27:10 +00007931 { NULL, NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007932};
7933
7934#undef F
7935
7936
7937Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7938 ASSERT(0 <= fid && fid < kNofFunctions);
7939 return &Runtime_functions[fid];
7940}
7941
7942
7943Runtime::Function* Runtime::FunctionForName(const char* name) {
7944 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7945 if (strcmp(f->name, name) == 0) {
7946 return f;
7947 }
7948 }
7949 return NULL;
7950}
7951
7952
7953void Runtime::PerformGC(Object* result) {
7954 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007955 if (failure->IsRetryAfterGC()) {
7956 // Try to do a garbage collection; ignore it if it fails. The C
7957 // entry stub will throw an out-of-memory exception in that case.
7958 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7959 } else {
7960 // Handle last resort GC and make sure to allow future allocations
7961 // to grow the heap without causing GCs (if possible).
7962 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007963 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007964 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007965}
7966
7967
7968} } // namespace v8::internal