blob: ccb88851b8e5fb513e82d76751edcac94312c552 [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.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000791 LookupResult lookup;
792 global->LocalLookup(*name, &lookup);
793 if (!lookup.IsProperty()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000794 if (assign) {
795 return global->IgnoreAttributesAndSetLocalProperty(*name,
796 args[1],
797 attributes);
798 }
799 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000800 }
801
802 // Determine if this is a redeclaration of something read-only.
803 if (lookup.IsReadOnly()) {
804 return ThrowRedeclarationError("const", name);
805 }
806
807 // Determine if this is a redeclaration of an intercepted read-only
808 // property and figure out if the property exists at all.
809 bool found = true;
810 PropertyType type = lookup.type();
811 if (type == INTERCEPTOR) {
812 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
813 if (intercepted == ABSENT) {
814 // The interceptor claims the property isn't there. We need to
815 // make sure to introduce it.
816 found = false;
817 } else if ((intercepted & READ_ONLY) != 0) {
818 // The property is present, but read-only. Since we're trying to
819 // overwrite it with a variable declaration we must throw a
820 // re-declaration error.
821 return ThrowRedeclarationError("const", name);
822 }
823 // Restore global object from context (in case of GC).
824 global = Top::context()->global();
825 }
826
827 if (found && !assign) {
828 // The global property is there and we're not assigning any value
829 // to it. Just return.
830 return Heap::undefined_value();
831 }
832
833 // Assign the value (or undefined) to the property.
834 Object* value = (assign) ? args[1] : Heap::undefined_value();
835 return global->SetProperty(&lookup, *name, value, attributes);
836}
837
838
839static Object* Runtime_InitializeConstGlobal(Arguments args) {
840 // All constants are declared with an initial value. The name
841 // of the constant is the first argument and the initial value
842 // is the second.
843 RUNTIME_ASSERT(args.length() == 2);
844 CONVERT_ARG_CHECKED(String, name, 0);
845 Handle<Object> value = args.at<Object>(1);
846
847 // Get the current global object from top.
848 GlobalObject* global = Top::context()->global();
849
850 // According to ECMA-262, section 12.2, page 62, the property must
851 // not be deletable. Since it's a const, it must be READ_ONLY too.
852 PropertyAttributes attributes =
853 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
854
855 // Lookup the property locally in the global object. If it isn't
856 // there, we add the property and take special precautions to always
857 // add it as a local property even in case of callbacks in the
858 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000859 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000860 LookupResult lookup;
861 global->LocalLookup(*name, &lookup);
862 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000863 return global->IgnoreAttributesAndSetLocalProperty(*name,
864 *value,
865 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000866 }
867
868 // Determine if this is a redeclaration of something not
869 // read-only. In case the result is hidden behind an interceptor we
870 // need to ask it for the property attributes.
871 if (!lookup.IsReadOnly()) {
872 if (lookup.type() != INTERCEPTOR) {
873 return ThrowRedeclarationError("var", name);
874 }
875
876 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
877
878 // Throw re-declaration error if the intercepted property is present
879 // but not read-only.
880 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
881 return ThrowRedeclarationError("var", name);
882 }
883
884 // Restore global object from context (in case of GC) and continue
885 // with setting the value because the property is either absent or
886 // read-only. We also have to do redo the lookup.
887 global = Top::context()->global();
888
889 // BUG 1213579: Handle the case where we have to set a read-only
890 // property through an interceptor and only do it if it's
891 // uninitialized, e.g. the hole. Nirk...
892 global->SetProperty(*name, *value, attributes);
893 return *value;
894 }
895
896 // Set the value, but only we're assigning the initial value to a
897 // constant. For now, we determine this by checking if the
898 // current value is the hole.
899 PropertyType type = lookup.type();
900 if (type == FIELD) {
901 FixedArray* properties = global->properties();
902 int index = lookup.GetFieldIndex();
903 if (properties->get(index)->IsTheHole()) {
904 properties->set(index, *value);
905 }
906 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000907 if (global->GetNormalizedProperty(&lookup)->IsTheHole()) {
908 global->SetNormalizedProperty(&lookup, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000909 }
910 } else {
911 // Ignore re-initialization of constants that have already been
912 // assigned a function value.
913 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
914 }
915
916 // Use the set value as the result of the operation.
917 return *value;
918}
919
920
921static Object* Runtime_InitializeConstContextSlot(Arguments args) {
922 HandleScope scope;
923 ASSERT(args.length() == 3);
924
925 Handle<Object> value(args[0]);
926 ASSERT(!value->IsTheHole());
927 CONVERT_ARG_CHECKED(Context, context, 1);
928 Handle<String> name(String::cast(args[2]));
929
930 // Initializations are always done in the function context.
931 context = Handle<Context>(context->fcontext());
932
933 int index;
934 PropertyAttributes attributes;
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000935 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000936 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000937 context->Lookup(name, flags, &index, &attributes);
938
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000939 // In most situations, the property introduced by the const
940 // declaration should be present in the context extension object.
941 // However, because declaration and initialization are separate, the
942 // property might have been deleted (if it was introduced by eval)
943 // before we reach the initialization point.
944 //
945 // Example:
946 //
947 // function f() { eval("delete x; const x;"); }
948 //
949 // In that case, the initialization behaves like a normal assignment
950 // to property 'x'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000951 if (index >= 0) {
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000952 // Property was found in a context.
953 if (holder->IsContext()) {
954 // The holder cannot be the function context. If it is, there
955 // should have been a const redeclaration error when declaring
956 // the const property.
957 ASSERT(!holder.is_identical_to(context));
958 if ((attributes & READ_ONLY) == 0) {
959 Handle<Context>::cast(holder)->set(index, *value);
960 }
961 } else {
962 // The holder is an arguments object.
963 ASSERT((attributes & READ_ONLY) == 0);
964 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000965 }
966 return *value;
967 }
968
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000969 // The property could not be found, we introduce it in the global
970 // context.
971 if (attributes == ABSENT) {
972 Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
973 SetProperty(global, name, value, NONE);
974 return *value;
975 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000976
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000977 // The property was present in a context extension object.
978 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000979
ager@chromium.orgddb913d2009-01-27 10:01:48 +0000980 if (*context_ext == context->extension()) {
981 // This is the property that was introduced by the const
982 // declaration. Set it if it hasn't been set before. NOTE: We
983 // cannot use GetProperty() to get the current value as it
984 // 'unholes' the value.
985 LookupResult lookup;
986 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
987 ASSERT(lookup.IsProperty()); // the property was declared
988 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
989
990 PropertyType type = lookup.type();
991 if (type == FIELD) {
992 FixedArray* properties = context_ext->properties();
993 int index = lookup.GetFieldIndex();
994 if (properties->get(index)->IsTheHole()) {
995 properties->set(index, *value);
996 }
997 } else if (type == NORMAL) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000998 if (context_ext->GetNormalizedProperty(&lookup)->IsTheHole()) {
999 context_ext->SetNormalizedProperty(&lookup, *value);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001000 }
1001 } else {
1002 // We should not reach here. Any real, named property should be
1003 // either a field or a dictionary slot.
1004 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001005 }
1006 } else {
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001007 // The property was found in a different context extension object.
1008 // Set it if it is not a read-only property.
1009 if ((attributes & READ_ONLY) == 0) {
1010 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
1011 // Setting a property might throw an exception. Exceptions
1012 // are converted to empty handles in handle operations. We
1013 // need to convert back to exceptions here.
1014 if (set.is_null()) {
1015 ASSERT(Top::has_pending_exception());
1016 return Failure::Exception();
1017 }
1018 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001019 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00001020
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001021 return *value;
1022}
1023
1024
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001025static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
1026 Arguments args) {
1027 HandleScope scope;
1028 ASSERT(args.length() == 2);
1029 CONVERT_ARG_CHECKED(JSObject, object, 0);
1030 CONVERT_SMI_CHECKED(properties, args[1]);
1031 if (object->HasFastProperties()) {
1032 NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties);
1033 }
1034 return *object;
1035}
1036
1037
1038static Object* Runtime_TransformToFastProperties(Arguments args) {
1039 HandleScope scope;
1040 ASSERT(args.length() == 1);
1041 CONVERT_ARG_CHECKED(JSObject, object, 0);
1042 if (!object->HasFastProperties() && !object->IsGlobalObject()) {
1043 TransformToFastProperties(object, 0);
1044 }
1045 return *object;
1046}
1047
1048
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001049static Object* Runtime_RegExpExec(Arguments args) {
1050 HandleScope scope;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001051 ASSERT(args.length() == 4);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001052 CONVERT_ARG_CHECKED(JSRegExp, regexp, 0);
1053 CONVERT_ARG_CHECKED(String, subject, 1);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001054 // Due to the way the JS calls are constructed this must be less than the
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001055 // length of a string, i.e. it is always a Smi. We check anyway for security.
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001056 CONVERT_SMI_CHECKED(index, args[2]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001057 CONVERT_ARG_CHECKED(JSArray, last_match_info, 3);
ager@chromium.org41826e72009-03-30 13:30:57 +00001058 RUNTIME_ASSERT(last_match_info->HasFastElements());
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001059 RUNTIME_ASSERT(index >= 0);
1060 RUNTIME_ASSERT(index <= subject->length());
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001061 Handle<Object> result = RegExpImpl::Exec(regexp,
1062 subject,
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001063 index,
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00001064 last_match_info);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001065 if (result.is_null()) return Failure::Exception();
1066 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001067}
1068
1069
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001070static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
1071 HandleScope scope;
1072 ASSERT(args.length() == 4);
1073 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
1074 int index = Smi::cast(args[1])->value();
1075 Handle<String> pattern = args.at<String>(2);
1076 Handle<String> flags = args.at<String>(3);
1077
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001078 // Get the RegExp function from the context in the literals array.
1079 // This is the RegExp function from the context in which the
1080 // function was created. We do not use the RegExp function from the
1081 // current global context because this might be the RegExp function
1082 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001083 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +00001084 Handle<JSFunction>(
1085 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001086 // Compute the regular expression literal.
1087 bool has_pending_exception;
1088 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001089 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
1090 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001091 if (has_pending_exception) {
1092 ASSERT(Top::has_pending_exception());
1093 return Failure::Exception();
1094 }
1095 literals->set(index, *regexp);
1096 return *regexp;
1097}
1098
1099
1100static Object* Runtime_FunctionGetName(Arguments args) {
1101 NoHandleAllocation ha;
1102 ASSERT(args.length() == 1);
1103
1104 CONVERT_CHECKED(JSFunction, f, args[0]);
1105 return f->shared()->name();
1106}
1107
1108
ager@chromium.org236ad962008-09-25 09:45:57 +00001109static Object* Runtime_FunctionSetName(Arguments args) {
1110 NoHandleAllocation ha;
1111 ASSERT(args.length() == 2);
1112
1113 CONVERT_CHECKED(JSFunction, f, args[0]);
1114 CONVERT_CHECKED(String, name, args[1]);
1115 f->shared()->set_name(name);
1116 return Heap::undefined_value();
1117}
1118
1119
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001120static Object* Runtime_FunctionGetScript(Arguments args) {
1121 HandleScope scope;
1122 ASSERT(args.length() == 1);
1123
1124 CONVERT_CHECKED(JSFunction, fun, args[0]);
1125 Handle<Object> script = Handle<Object>(fun->shared()->script());
1126 if (!script->IsScript()) return Heap::undefined_value();
1127
1128 return *GetScriptWrapper(Handle<Script>::cast(script));
1129}
1130
1131
1132static Object* Runtime_FunctionGetSourceCode(Arguments args) {
1133 NoHandleAllocation ha;
1134 ASSERT(args.length() == 1);
1135
1136 CONVERT_CHECKED(JSFunction, f, args[0]);
1137 return f->shared()->GetSourceCode();
1138}
1139
1140
1141static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
1142 NoHandleAllocation ha;
1143 ASSERT(args.length() == 1);
1144
1145 CONVERT_CHECKED(JSFunction, fun, args[0]);
1146 int pos = fun->shared()->start_position();
1147 return Smi::FromInt(pos);
1148}
1149
1150
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001151static Object* Runtime_FunctionGetPositionForOffset(Arguments args) {
1152 ASSERT(args.length() == 2);
1153
1154 CONVERT_CHECKED(JSFunction, fun, args[0]);
1155 CONVERT_NUMBER_CHECKED(int, offset, Int32, args[1]);
1156
1157 Code* code = fun->code();
1158 RUNTIME_ASSERT(0 <= offset && offset < code->Size());
1159
1160 Address pc = code->address() + offset;
1161 return Smi::FromInt(fun->code()->SourcePosition(pc));
1162}
1163
1164
1165
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001166static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
1167 NoHandleAllocation ha;
1168 ASSERT(args.length() == 2);
1169
1170 CONVERT_CHECKED(JSFunction, fun, args[0]);
1171 CONVERT_CHECKED(String, name, args[1]);
1172 fun->SetInstanceClassName(name);
1173 return Heap::undefined_value();
1174}
1175
1176
1177static Object* Runtime_FunctionSetLength(Arguments args) {
1178 NoHandleAllocation ha;
1179 ASSERT(args.length() == 2);
1180
1181 CONVERT_CHECKED(JSFunction, fun, args[0]);
1182 CONVERT_CHECKED(Smi, length, args[1]);
1183 fun->shared()->set_length(length->value());
1184 return length;
1185}
1186
1187
1188static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001189 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001190 ASSERT(args.length() == 2);
1191
1192 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001193 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
1194 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001195 return args[0]; // return TOS
1196}
1197
1198
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001199static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
1200 NoHandleAllocation ha;
1201 ASSERT(args.length() == 1);
1202
1203 CONVERT_CHECKED(JSFunction, f, args[0]);
1204 // The function_data field of the shared function info is used exclusively by
1205 // the API.
1206 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
1207 : Heap::false_value();
1208}
1209
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00001210static Object* Runtime_FunctionIsBuiltin(Arguments args) {
1211 NoHandleAllocation ha;
1212 ASSERT(args.length() == 1);
1213
1214 CONVERT_CHECKED(JSFunction, f, args[0]);
1215 return f->IsBuiltin() ? Heap::true_value() : Heap::false_value();
1216}
1217
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001218
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001219static Object* Runtime_SetCode(Arguments args) {
1220 HandleScope scope;
1221 ASSERT(args.length() == 2);
1222
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001223 CONVERT_ARG_CHECKED(JSFunction, target, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001224 Handle<Object> code = args.at<Object>(1);
1225
1226 Handle<Context> context(target->context());
1227
1228 if (!code->IsNull()) {
1229 RUNTIME_ASSERT(code->IsJSFunction());
1230 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
1231 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
1232 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
1233 return Failure::Exception();
1234 }
1235 // Set the code, formal parameter count, and the length of the target
1236 // function.
1237 target->set_code(fun->code());
1238 target->shared()->set_length(fun->shared()->length());
1239 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001240 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +00001241 // Set the source code of the target function to undefined.
1242 // SetCode is only used for built-in constructors like String,
1243 // Array, and Object, and some web code
1244 // doesn't like seeing source code for constructors.
1245 target->shared()->set_script(Heap::undefined_value());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00001246 // Clear the optimization hints related to the compiled code as these are no
1247 // longer valid when the code is overwritten.
1248 target->shared()->ClearThisPropertyAssignmentsInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001249 context = Handle<Context>(fun->context());
1250
1251 // Make sure we get a fresh copy of the literal vector to avoid
1252 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001253 int number_of_literals = fun->NumberOfLiterals();
1254 Handle<FixedArray> literals =
1255 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001256 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001257 // Insert the object, regexp and array functions in the literals
1258 // array prefix. These are the functions that will be used when
1259 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001260 literals->set(JSFunction::kLiteralGlobalContextIndex,
1261 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001262 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001263 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001264 }
1265
1266 target->set_context(*context);
1267 return *target;
1268}
1269
1270
1271static Object* CharCodeAt(String* subject, Object* index) {
1272 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001273 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001274 // Flatten the string. If someone wants to get a char at an index
1275 // in a cons string, it is likely that more indices will be
1276 // accessed.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001277 Object* flat = subject->TryFlatten();
1278 if (flat->IsFailure()) return flat;
1279 subject = String::cast(flat);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001280 if (i >= static_cast<uint32_t>(subject->length())) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001281 return Heap::nan_value();
1282 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001283 return Smi::FromInt(subject->Get(i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001284}
1285
1286
1287static Object* Runtime_StringCharCodeAt(Arguments args) {
1288 NoHandleAllocation ha;
1289 ASSERT(args.length() == 2);
1290
1291 CONVERT_CHECKED(String, subject, args[0]);
1292 Object* index = args[1];
1293 return CharCodeAt(subject, index);
1294}
1295
1296
1297static Object* Runtime_CharFromCode(Arguments args) {
1298 NoHandleAllocation ha;
1299 ASSERT(args.length() == 1);
1300 uint32_t code;
1301 if (Array::IndexFromObject(args[0], &code)) {
1302 if (code <= 0xffff) {
1303 return Heap::LookupSingleCharacterStringFromCode(code);
1304 }
1305 }
1306 return Heap::empty_string();
1307}
1308
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001309// Forward declarations.
1310static const int kStringBuilderConcatHelperLengthBits = 11;
1311static const int kStringBuilderConcatHelperPositionBits = 19;
1312
1313template <typename schar>
1314static inline void StringBuilderConcatHelper(String*,
1315 schar*,
1316 FixedArray*,
1317 int);
1318
1319typedef BitField<int, 0, 11> StringBuilderSubstringLength;
1320typedef BitField<int, 11, 19> StringBuilderSubstringPosition;
1321
1322class ReplacementStringBuilder {
1323 public:
1324 ReplacementStringBuilder(Handle<String> subject, int estimated_part_count)
1325 : subject_(subject),
1326 parts_(Factory::NewFixedArray(estimated_part_count)),
1327 part_count_(0),
1328 character_count_(0),
ager@chromium.org5ec48922009-05-05 07:25:34 +00001329 is_ascii_(subject->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001330 // Require a non-zero initial size. Ensures that doubling the size to
1331 // extend the array will work.
1332 ASSERT(estimated_part_count > 0);
1333 }
1334
1335 void EnsureCapacity(int elements) {
1336 int length = parts_->length();
1337 int required_length = part_count_ + elements;
1338 if (length < required_length) {
1339 int new_length = length;
1340 do {
1341 new_length *= 2;
1342 } while (new_length < required_length);
1343 Handle<FixedArray> extended_array =
1344 Factory::NewFixedArray(new_length);
1345 parts_->CopyTo(0, *extended_array, 0, part_count_);
1346 parts_ = extended_array;
1347 }
1348 }
1349
1350 void AddSubjectSlice(int from, int to) {
1351 ASSERT(from >= 0);
1352 int length = to - from;
1353 ASSERT(length > 0);
1354 // Can we encode the slice in 11 bits for length and 19 bits for
1355 // start position - as used by StringBuilderConcatHelper?
1356 if (StringBuilderSubstringLength::is_valid(length) &&
1357 StringBuilderSubstringPosition::is_valid(from)) {
1358 int encoded_slice = StringBuilderSubstringLength::encode(length) |
1359 StringBuilderSubstringPosition::encode(from);
1360 AddElement(Smi::FromInt(encoded_slice));
1361 } else {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001362 // Otherwise encode as two smis.
1363 AddElement(Smi::FromInt(-length));
1364 AddElement(Smi::FromInt(from));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001365 }
1366 IncrementCharacterCount(length);
1367 }
1368
1369
1370 void AddString(Handle<String> string) {
1371 int length = string->length();
1372 ASSERT(length > 0);
1373 AddElement(*string);
ager@chromium.org5ec48922009-05-05 07:25:34 +00001374 if (!string->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001375 is_ascii_ = false;
1376 }
1377 IncrementCharacterCount(length);
1378 }
1379
1380
1381 Handle<String> ToString() {
1382 if (part_count_ == 0) {
1383 return Factory::empty_string();
1384 }
1385
1386 Handle<String> joined_string;
1387 if (is_ascii_) {
1388 joined_string = NewRawAsciiString(character_count_);
1389 AssertNoAllocation no_alloc;
1390 SeqAsciiString* seq = SeqAsciiString::cast(*joined_string);
1391 char* char_buffer = seq->GetChars();
1392 StringBuilderConcatHelper(*subject_,
1393 char_buffer,
1394 *parts_,
1395 part_count_);
1396 } else {
1397 // Non-ASCII.
1398 joined_string = NewRawTwoByteString(character_count_);
1399 AssertNoAllocation no_alloc;
1400 SeqTwoByteString* seq = SeqTwoByteString::cast(*joined_string);
1401 uc16* char_buffer = seq->GetChars();
1402 StringBuilderConcatHelper(*subject_,
1403 char_buffer,
1404 *parts_,
1405 part_count_);
1406 }
1407 return joined_string;
1408 }
1409
1410
1411 void IncrementCharacterCount(int by) {
1412 if (character_count_ > Smi::kMaxValue - by) {
1413 V8::FatalProcessOutOfMemory("String.replace result too large.");
1414 }
1415 character_count_ += by;
1416 }
1417
1418 private:
1419
1420 Handle<String> NewRawAsciiString(int size) {
1421 CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(size), String);
1422 }
1423
1424
1425 Handle<String> NewRawTwoByteString(int size) {
1426 CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(size), String);
1427 }
1428
1429
1430 void AddElement(Object* element) {
1431 ASSERT(element->IsSmi() || element->IsString());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001432 ASSERT(parts_->length() > part_count_);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001433 parts_->set(part_count_, element);
1434 part_count_++;
1435 }
1436
1437 Handle<String> subject_;
1438 Handle<FixedArray> parts_;
1439 int part_count_;
1440 int character_count_;
1441 bool is_ascii_;
1442};
1443
1444
1445class CompiledReplacement {
1446 public:
1447 CompiledReplacement()
1448 : parts_(1), replacement_substrings_(0) {}
1449
1450 void Compile(Handle<String> replacement,
1451 int capture_count,
1452 int subject_length);
1453
1454 void Apply(ReplacementStringBuilder* builder,
1455 int match_from,
1456 int match_to,
1457 Handle<JSArray> last_match_info);
1458
1459 // Number of distinct parts of the replacement pattern.
1460 int parts() {
1461 return parts_.length();
1462 }
1463 private:
1464 enum PartType {
1465 SUBJECT_PREFIX = 1,
1466 SUBJECT_SUFFIX,
1467 SUBJECT_CAPTURE,
1468 REPLACEMENT_SUBSTRING,
1469 REPLACEMENT_STRING,
1470
1471 NUMBER_OF_PART_TYPES
1472 };
1473
1474 struct ReplacementPart {
1475 static inline ReplacementPart SubjectMatch() {
1476 return ReplacementPart(SUBJECT_CAPTURE, 0);
1477 }
1478 static inline ReplacementPart SubjectCapture(int capture_index) {
1479 return ReplacementPart(SUBJECT_CAPTURE, capture_index);
1480 }
1481 static inline ReplacementPart SubjectPrefix() {
1482 return ReplacementPart(SUBJECT_PREFIX, 0);
1483 }
1484 static inline ReplacementPart SubjectSuffix(int subject_length) {
1485 return ReplacementPart(SUBJECT_SUFFIX, subject_length);
1486 }
1487 static inline ReplacementPart ReplacementString() {
1488 return ReplacementPart(REPLACEMENT_STRING, 0);
1489 }
1490 static inline ReplacementPart ReplacementSubString(int from, int to) {
1491 ASSERT(from >= 0);
1492 ASSERT(to > from);
1493 return ReplacementPart(-from, to);
1494 }
1495
1496 // If tag <= 0 then it is the negation of a start index of a substring of
1497 // the replacement pattern, otherwise it's a value from PartType.
1498 ReplacementPart(int tag, int data)
1499 : tag(tag), data(data) {
1500 // Must be non-positive or a PartType value.
1501 ASSERT(tag < NUMBER_OF_PART_TYPES);
1502 }
1503 // Either a value of PartType or a non-positive number that is
1504 // the negation of an index into the replacement string.
1505 int tag;
1506 // The data value's interpretation depends on the value of tag:
1507 // tag == SUBJECT_PREFIX ||
1508 // tag == SUBJECT_SUFFIX: data is unused.
1509 // tag == SUBJECT_CAPTURE: data is the number of the capture.
1510 // tag == REPLACEMENT_SUBSTRING ||
1511 // tag == REPLACEMENT_STRING: data is index into array of substrings
1512 // of the replacement string.
1513 // tag <= 0: Temporary representation of the substring of the replacement
1514 // string ranging over -tag .. data.
1515 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the
1516 // substring objects.
1517 int data;
1518 };
1519
1520 template<typename Char>
1521 static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts,
1522 Vector<Char> characters,
1523 int capture_count,
1524 int subject_length) {
1525 int length = characters.length();
1526 int last = 0;
1527 for (int i = 0; i < length; i++) {
1528 Char c = characters[i];
1529 if (c == '$') {
1530 int next_index = i + 1;
1531 if (next_index == length) { // No next character!
1532 break;
1533 }
1534 Char c2 = characters[next_index];
1535 switch (c2) {
1536 case '$':
1537 if (i > last) {
1538 // There is a substring before. Include the first "$".
1539 parts->Add(ReplacementPart::ReplacementSubString(last, next_index));
1540 last = next_index + 1; // Continue after the second "$".
1541 } else {
1542 // Let the next substring start with the second "$".
1543 last = next_index;
1544 }
1545 i = next_index;
1546 break;
1547 case '`':
1548 if (i > last) {
1549 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1550 }
1551 parts->Add(ReplacementPart::SubjectPrefix());
1552 i = next_index;
1553 last = i + 1;
1554 break;
1555 case '\'':
1556 if (i > last) {
1557 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1558 }
1559 parts->Add(ReplacementPart::SubjectSuffix(subject_length));
1560 i = next_index;
1561 last = i + 1;
1562 break;
1563 case '&':
1564 if (i > last) {
1565 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1566 }
1567 parts->Add(ReplacementPart::SubjectMatch());
1568 i = next_index;
1569 last = i + 1;
1570 break;
1571 case '0':
1572 case '1':
1573 case '2':
1574 case '3':
1575 case '4':
1576 case '5':
1577 case '6':
1578 case '7':
1579 case '8':
1580 case '9': {
1581 int capture_ref = c2 - '0';
1582 if (capture_ref > capture_count) {
1583 i = next_index;
1584 continue;
1585 }
1586 int second_digit_index = next_index + 1;
1587 if (second_digit_index < length) {
1588 // Peek ahead to see if we have two digits.
1589 Char c3 = characters[second_digit_index];
1590 if ('0' <= c3 && c3 <= '9') { // Double digits.
1591 int double_digit_ref = capture_ref * 10 + c3 - '0';
1592 if (double_digit_ref <= capture_count) {
1593 next_index = second_digit_index;
1594 capture_ref = double_digit_ref;
1595 }
1596 }
1597 }
1598 if (capture_ref > 0) {
1599 if (i > last) {
1600 parts->Add(ReplacementPart::ReplacementSubString(last, i));
1601 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001602 ASSERT(capture_ref <= capture_count);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001603 parts->Add(ReplacementPart::SubjectCapture(capture_ref));
1604 last = next_index + 1;
1605 }
1606 i = next_index;
1607 break;
1608 }
1609 default:
1610 i = next_index;
1611 break;
1612 }
1613 }
1614 }
1615 if (length > last) {
1616 if (last == 0) {
1617 parts->Add(ReplacementPart::ReplacementString());
1618 } else {
1619 parts->Add(ReplacementPart::ReplacementSubString(last, length));
1620 }
1621 }
1622 }
1623
1624 ZoneList<ReplacementPart> parts_;
1625 ZoneList<Handle<String> > replacement_substrings_;
1626};
1627
1628
1629void CompiledReplacement::Compile(Handle<String> replacement,
1630 int capture_count,
1631 int subject_length) {
1632 ASSERT(replacement->IsFlat());
ager@chromium.org5ec48922009-05-05 07:25:34 +00001633 if (replacement->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001634 AssertNoAllocation no_alloc;
1635 ParseReplacementPattern(&parts_,
1636 replacement->ToAsciiVector(),
1637 capture_count,
1638 subject_length);
1639 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00001640 ASSERT(replacement->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001641 AssertNoAllocation no_alloc;
1642
1643 ParseReplacementPattern(&parts_,
1644 replacement->ToUC16Vector(),
1645 capture_count,
1646 subject_length);
1647 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001648 // Find substrings of replacement string and create them as String objects.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001649 int substring_index = 0;
1650 for (int i = 0, n = parts_.length(); i < n; i++) {
1651 int tag = parts_[i].tag;
1652 if (tag <= 0) { // A replacement string slice.
1653 int from = -tag;
1654 int to = parts_[i].data;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001655 replacement_substrings_.Add(Factory::NewSubString(replacement, from, to));
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001656 parts_[i].tag = REPLACEMENT_SUBSTRING;
1657 parts_[i].data = substring_index;
1658 substring_index++;
1659 } else if (tag == REPLACEMENT_STRING) {
1660 replacement_substrings_.Add(replacement);
1661 parts_[i].data = substring_index;
1662 substring_index++;
1663 }
1664 }
1665}
1666
1667
1668void CompiledReplacement::Apply(ReplacementStringBuilder* builder,
1669 int match_from,
1670 int match_to,
1671 Handle<JSArray> last_match_info) {
1672 for (int i = 0, n = parts_.length(); i < n; i++) {
1673 ReplacementPart part = parts_[i];
1674 switch (part.tag) {
1675 case SUBJECT_PREFIX:
1676 if (match_from > 0) builder->AddSubjectSlice(0, match_from);
1677 break;
1678 case SUBJECT_SUFFIX: {
1679 int subject_length = part.data;
1680 if (match_to < subject_length) {
1681 builder->AddSubjectSlice(match_to, subject_length);
1682 }
1683 break;
1684 }
1685 case SUBJECT_CAPTURE: {
1686 int capture = part.data;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001687 FixedArray* match_info = FixedArray::cast(last_match_info->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001688 int from = RegExpImpl::GetCapture(match_info, capture * 2);
1689 int to = RegExpImpl::GetCapture(match_info, capture * 2 + 1);
1690 if (from >= 0 && to > from) {
1691 builder->AddSubjectSlice(from, to);
1692 }
1693 break;
1694 }
1695 case REPLACEMENT_SUBSTRING:
1696 case REPLACEMENT_STRING:
1697 builder->AddString(replacement_substrings_[part.data]);
1698 break;
1699 default:
1700 UNREACHABLE();
1701 }
1702 }
1703}
1704
1705
1706
1707static Object* StringReplaceRegExpWithString(String* subject,
1708 JSRegExp* regexp,
1709 String* replacement,
1710 JSArray* last_match_info) {
1711 ASSERT(subject->IsFlat());
1712 ASSERT(replacement->IsFlat());
1713
1714 HandleScope handles;
1715
1716 int length = subject->length();
1717 Handle<String> subject_handle(subject);
1718 Handle<JSRegExp> regexp_handle(regexp);
1719 Handle<String> replacement_handle(replacement);
1720 Handle<JSArray> last_match_info_handle(last_match_info);
1721 Handle<Object> match = RegExpImpl::Exec(regexp_handle,
1722 subject_handle,
1723 0,
1724 last_match_info_handle);
1725 if (match.is_null()) {
1726 return Failure::Exception();
1727 }
1728 if (match->IsNull()) {
1729 return *subject_handle;
1730 }
1731
1732 int capture_count = regexp_handle->CaptureCount();
1733
1734 // CompiledReplacement uses zone allocation.
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001735 CompilationZoneScope zone(DELETE_ON_EXIT);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001736 CompiledReplacement compiled_replacement;
1737 compiled_replacement.Compile(replacement_handle,
1738 capture_count,
1739 length);
1740
1741 bool is_global = regexp_handle->GetFlags().is_global();
1742
1743 // Guessing the number of parts that the final result string is built
1744 // from. Global regexps can match any number of times, so we guess
1745 // conservatively.
1746 int expected_parts =
1747 (compiled_replacement.parts() + 1) * (is_global ? 4 : 1) + 1;
1748 ReplacementStringBuilder builder(subject_handle, expected_parts);
1749
1750 // Index of end of last match.
1751 int prev = 0;
1752
1753 // Number of parts added by compiled replacement plus preceeding string
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001754 // and possibly suffix after last match. It is possible for compiled
1755 // replacements to use two elements when encoded as two smis.
1756 const int parts_added_per_loop = compiled_replacement.parts() * 2 + 2;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001757 bool matched = true;
1758 do {
1759 ASSERT(last_match_info_handle->HasFastElements());
1760 // Increase the capacity of the builder before entering local handle-scope,
1761 // so its internal buffer can safely allocate a new handle if it grows.
1762 builder.EnsureCapacity(parts_added_per_loop);
1763
1764 HandleScope loop_scope;
1765 int start, end;
1766 {
1767 AssertNoAllocation match_info_array_is_not_in_a_handle;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00001768 FixedArray* match_info_array =
1769 FixedArray::cast(last_match_info_handle->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001770
1771 ASSERT_EQ(capture_count * 2 + 2,
1772 RegExpImpl::GetLastCaptureCount(match_info_array));
1773 start = RegExpImpl::GetCapture(match_info_array, 0);
1774 end = RegExpImpl::GetCapture(match_info_array, 1);
1775 }
1776
1777 if (prev < start) {
1778 builder.AddSubjectSlice(prev, start);
1779 }
1780 compiled_replacement.Apply(&builder,
1781 start,
1782 end,
1783 last_match_info_handle);
1784 prev = end;
1785
1786 // Only continue checking for global regexps.
1787 if (!is_global) break;
1788
1789 // Continue from where the match ended, unless it was an empty match.
1790 int next = end;
1791 if (start == end) {
1792 next = end + 1;
1793 if (next > length) break;
1794 }
1795
1796 match = RegExpImpl::Exec(regexp_handle,
1797 subject_handle,
1798 next,
1799 last_match_info_handle);
1800 if (match.is_null()) {
1801 return Failure::Exception();
1802 }
1803 matched = !match->IsNull();
1804 } while (matched);
1805
1806 if (prev < length) {
1807 builder.AddSubjectSlice(prev, length);
1808 }
1809
1810 return *(builder.ToString());
1811}
1812
1813
1814static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
1815 ASSERT(args.length() == 4);
1816
1817 CONVERT_CHECKED(String, subject, args[0]);
1818 if (!subject->IsFlat()) {
1819 Object* flat_subject = subject->TryFlatten();
1820 if (flat_subject->IsFailure()) {
1821 return flat_subject;
1822 }
1823 subject = String::cast(flat_subject);
1824 }
1825
1826 CONVERT_CHECKED(String, replacement, args[2]);
1827 if (!replacement->IsFlat()) {
1828 Object* flat_replacement = replacement->TryFlatten();
1829 if (flat_replacement->IsFailure()) {
1830 return flat_replacement;
1831 }
1832 replacement = String::cast(flat_replacement);
1833 }
1834
1835 CONVERT_CHECKED(JSRegExp, regexp, args[1]);
1836 CONVERT_CHECKED(JSArray, last_match_info, args[3]);
1837
1838 ASSERT(last_match_info->HasFastElements());
1839
1840 return StringReplaceRegExpWithString(subject,
1841 regexp,
1842 replacement,
1843 last_match_info);
1844}
1845
1846
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001847
ager@chromium.org7c537e22008-10-16 08:43:32 +00001848// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1849// limit, we can fix the size of tables.
1850static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001851// Reduce alphabet to this size.
1852static const int kBMAlphabetSize = 0x100;
1853// For patterns below this length, the skip length of Boyer-Moore is too short
1854// to compensate for the algorithmic overhead compared to simple brute force.
1855static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001856
ager@chromium.org7c537e22008-10-16 08:43:32 +00001857// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1858// shift. Only allows the last kBMMaxShift characters of the needle
1859// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001860class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001861 public:
1862 BMGoodSuffixBuffers() {}
1863 inline void init(int needle_length) {
1864 ASSERT(needle_length > 1);
1865 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1866 int len = needle_length - start;
1867 biased_suffixes_ = suffixes_ - start;
1868 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1869 for (int i = 0; i <= len; i++) {
1870 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001871 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001872 }
1873 inline int& suffix(int index) {
1874 ASSERT(biased_suffixes_ + index >= suffixes_);
1875 return biased_suffixes_[index];
1876 }
1877 inline int& shift(int index) {
1878 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1879 return biased_good_suffix_shift_[index];
1880 }
1881 private:
1882 int suffixes_[kBMMaxShift + 1];
1883 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001884 int* biased_suffixes_;
1885 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001886 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1887};
1888
1889// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001890static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001891static BMGoodSuffixBuffers bmgs_buffers;
1892
1893// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001894template <typename pchar>
1895static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1896 int start) {
1897 // Run forwards to populate bad_char_table, so that *last* instance
1898 // of character equivalence class is the one registered.
1899 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001900 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1901 : kBMAlphabetSize;
1902 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001903 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001904 } else {
1905 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001906 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001907 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001908 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001909 for (int i = start; i < pattern.length() - 1; i++) {
1910 pchar c = pattern[i];
1911 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001912 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001913 }
1914}
1915
1916template <typename pchar>
1917static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001918 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001919 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001920 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001921 // Compute Good Suffix tables.
1922 bmgs_buffers.init(m);
1923
1924 bmgs_buffers.shift(m-1) = 1;
1925 bmgs_buffers.suffix(m) = m + 1;
1926 pchar last_char = pattern[m - 1];
1927 int suffix = m + 1;
1928 for (int i = m; i > start;) {
1929 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1930 if (bmgs_buffers.shift(suffix) == len) {
1931 bmgs_buffers.shift(suffix) = suffix - i;
1932 }
1933 suffix = bmgs_buffers.suffix(suffix);
1934 }
1935 i--;
1936 suffix--;
1937 bmgs_buffers.suffix(i) = suffix;
1938 if (suffix == m) {
1939 // No suffix to extend, so we check against last_char only.
1940 while (i > start && pattern[i - 1] != last_char) {
1941 if (bmgs_buffers.shift(m) == len) {
1942 bmgs_buffers.shift(m) = m - i;
1943 }
1944 i--;
1945 bmgs_buffers.suffix(i) = m;
1946 }
1947 if (i > start) {
1948 i--;
1949 suffix--;
1950 bmgs_buffers.suffix(i) = suffix;
1951 }
1952 }
1953 }
1954 if (suffix < m) {
1955 for (int i = start; i <= m; i++) {
1956 if (bmgs_buffers.shift(i) == len) {
1957 bmgs_buffers.shift(i) = suffix - start;
1958 }
1959 if (i == suffix) {
1960 suffix = bmgs_buffers.suffix(suffix);
1961 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001962 }
1963 }
1964}
1965
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001966template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001967static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001968 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001969 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001970 }
1971 if (sizeof(pchar) == 1) {
1972 if (char_code > String::kMaxAsciiCharCode) {
1973 return -1;
1974 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001975 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001976 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001977 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001978}
1979
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001980// Restricted simplified Boyer-Moore string matching.
1981// Uses only the bad-shift table of Boyer-Moore and only uses it
1982// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001983template <typename schar, typename pchar>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00001984static int BoyerMooreHorspool(Vector<const schar> subject,
1985 Vector<const pchar> pattern,
1986 int start_index,
1987 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001988 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001989 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001990 // Only preprocess at most kBMMaxShift last characters of pattern.
1991 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001992
ager@chromium.org7c537e22008-10-16 08:43:32 +00001993 BoyerMoorePopulateBadCharTable(pattern, start);
1994
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001995 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001996 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001997 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001998 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001999 // Perform search
2000 for (idx = start_index; idx <= n - m;) {
2001 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002002 int c;
2003 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002004 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002005 int shift = j - bc_occ;
2006 idx += shift;
2007 badness += 1 - shift; // at most zero, so badness cannot increase.
2008 if (idx > n - m) {
2009 *complete = true;
2010 return -1;
2011 }
2012 }
2013 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002014 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002015 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002016 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002017 return idx;
2018 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002019 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002020 // Badness increases by the number of characters we have
2021 // checked, and decreases by the number of characters we
2022 // can skip by shifting. It's a measure of how we are doing
2023 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002024 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002025 if (badness > 0) {
2026 *complete = false;
2027 return idx;
2028 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002029 }
2030 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002031 *complete = true;
2032 return -1;
2033}
ager@chromium.org7c537e22008-10-16 08:43:32 +00002034
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002035
2036template <typename schar, typename pchar>
2037static int BoyerMooreIndexOf(Vector<const schar> subject,
2038 Vector<const pchar> pattern,
2039 int idx) {
2040 int n = subject.length();
2041 int m = pattern.length();
2042 // Only preprocess at most kBMMaxShift last characters of pattern.
2043 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
2044
2045 // Build the Good Suffix table and continue searching.
2046 BoyerMoorePopulateGoodSuffixTable(pattern, start);
2047 pchar last_char = pattern[m - 1];
2048 // Continue search from i.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002049 while (idx <= n - m) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002050 int j = m - 1;
2051 schar c;
2052 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002053 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002054 idx += shift;
2055 if (idx > n - m) {
2056 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002057 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002058 }
2059 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
2060 if (j < 0) {
2061 return idx;
2062 } else if (j < start) {
2063 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002064 // Fall back on BMH shift.
2065 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002066 } else {
2067 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002068 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002069 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002070 if (gs_shift > shift) {
2071 shift = gs_shift;
2072 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002073 idx += shift;
2074 }
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002075 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002076
2077 return -1;
2078}
2079
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002080
2081template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00002082static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002083 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00002084 int start_index) {
2085 for (int i = start_index, n = string.length(); i < n; i++) {
2086 if (pattern_char == string[i]) {
2087 return i;
2088 }
2089 }
2090 return -1;
2091}
2092
2093// Trivial string search for shorter strings.
2094// On return, if "complete" is set to true, the return value is the
2095// final result of searching for the patter in the subject.
2096// If "complete" is set to false, the return value is the index where
2097// further checking should start, i.e., it's guaranteed that the pattern
2098// does not occur at a position prior to the returned index.
2099template <typename pchar, typename schar>
2100static int SimpleIndexOf(Vector<const schar> subject,
2101 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002102 int idx,
2103 bool* complete) {
2104 // Badness is a count of how much work we have done. When we have
2105 // done enough work we decide it's probably worth switching to a better
2106 // algorithm.
2107 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002108 // We know our pattern is at least 2 characters, we cache the first so
2109 // the common case of the first character not matching is faster.
2110 pchar pattern_first_char = pattern[0];
2111
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002112 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2113 badness++;
2114 if (badness > 0) {
2115 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00002116 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002117 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002118 if (subject[i] != pattern_first_char) continue;
2119 int j = 1;
2120 do {
2121 if (pattern[j] != subject[i+j]) {
2122 break;
2123 }
2124 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002125 } while (j < pattern.length());
2126 if (j == pattern.length()) {
2127 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002128 return i;
2129 }
2130 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002131 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002132 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002133 return -1;
2134}
2135
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002136// Simple indexOf that never bails out. For short patterns only.
2137template <typename pchar, typename schar>
2138static int SimpleIndexOf(Vector<const schar> subject,
2139 Vector<const pchar> pattern,
2140 int idx) {
2141 pchar pattern_first_char = pattern[0];
2142 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
2143 if (subject[i] != pattern_first_char) continue;
2144 int j = 1;
2145 do {
2146 if (pattern[j] != subject[i+j]) {
2147 break;
2148 }
2149 j++;
2150 } while (j < pattern.length());
2151 if (j == pattern.length()) {
2152 return i;
2153 }
2154 }
2155 return -1;
2156}
2157
2158
2159// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002160template <typename schar, typename pchar>
2161static int StringMatchStrategy(Vector<const schar> sub,
2162 Vector<const pchar> pat,
2163 int start_index) {
2164 ASSERT(pat.length() > 1);
2165
2166 // We have an ASCII haystack and a non-ASCII needle. Check if there
2167 // really is a non-ASCII character in the needle and bail out if there
2168 // is.
2169 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
2170 for (int i = 0; i < pat.length(); i++) {
2171 uc16 c = pat[i];
2172 if (c > String::kMaxAsciiCharCode) {
2173 return -1;
2174 }
2175 }
2176 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002177 if (pat.length() < kBMMinPatternLength) {
2178 // We don't believe fancy searching can ever be more efficient.
2179 // The max shift of Boyer-Moore on a pattern of this length does
2180 // not compensate for the overhead.
2181 return SimpleIndexOf(sub, pat, start_index);
2182 }
2183 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002184 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002185 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
2186 if (complete) return idx;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002187 idx = BoyerMooreHorspool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00002188 if (complete) return idx;
2189 return BoyerMooreIndexOf(sub, pat, idx);
2190}
2191
2192// Perform string match of pattern on subject, starting at start index.
2193// Caller must ensure that 0 <= start_index <= sub->length(),
2194// and should check that pat->length() + start_index <= sub->length()
2195int Runtime::StringMatch(Handle<String> sub,
2196 Handle<String> pat,
2197 int start_index) {
2198 ASSERT(0 <= start_index);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002199 ASSERT(start_index <= sub->length());
ager@chromium.org7c537e22008-10-16 08:43:32 +00002200
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002201 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002202 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002203
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002204 int subject_length = sub->length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002205 if (start_index + pattern_length > subject_length) return -1;
2206
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002207 if (!sub->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002208 FlattenString(sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002209 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002210 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00002211 // character patterns linear search is necessary, so any smart
2212 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002213 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002214 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org5ec48922009-05-05 07:25:34 +00002215 if (sub->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002216 uc16 pchar = pat->Get(0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002217 if (pchar > String::kMaxAsciiCharCode) {
2218 return -1;
2219 }
2220 Vector<const char> ascii_vector =
2221 sub->ToAsciiVector().SubVector(start_index, subject_length);
2222 const void* pos = memchr(ascii_vector.start(),
2223 static_cast<const char>(pchar),
2224 static_cast<size_t>(ascii_vector.length()));
2225 if (pos == NULL) {
2226 return -1;
2227 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002228 return static_cast<int>(reinterpret_cast<const char*>(pos)
2229 - ascii_vector.start() + start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002230 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002231 return SingleCharIndexOf(sub->ToUC16Vector(), pat->Get(0), start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002232 }
2233
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002234 if (!pat->IsFlat()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002235 FlattenString(pat);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002236 }
ager@chromium.org236ad962008-09-25 09:45:57 +00002237
ager@chromium.org7c537e22008-10-16 08:43:32 +00002238 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
2239 // dispatch on type of strings
ager@chromium.org5ec48922009-05-05 07:25:34 +00002240 if (pat->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002241 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002242 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002243 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002244 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002245 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00002246 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002247 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org5ec48922009-05-05 07:25:34 +00002248 if (sub->IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002249 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002250 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002251 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002252}
2253
2254
2255static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002256 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002257 ASSERT(args.length() == 3);
2258
ager@chromium.org7c537e22008-10-16 08:43:32 +00002259 CONVERT_ARG_CHECKED(String, sub, 0);
2260 CONVERT_ARG_CHECKED(String, pat, 1);
2261
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002262 Object* index = args[2];
2263 uint32_t start_index;
2264 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2265
ager@chromium.org870a0b62008-11-04 11:43:05 +00002266 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00002267 int position = Runtime::StringMatch(sub, pat, start_index);
2268 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002269}
2270
2271
2272static Object* Runtime_StringLastIndexOf(Arguments args) {
2273 NoHandleAllocation ha;
2274 ASSERT(args.length() == 3);
2275
2276 CONVERT_CHECKED(String, sub, args[0]);
2277 CONVERT_CHECKED(String, pat, args[1]);
2278 Object* index = args[2];
2279
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002280 sub->TryFlattenIfNotFlat();
2281 pat->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002282
2283 uint32_t start_index;
2284 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
2285
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002286 uint32_t pattern_length = pat->length();
2287 uint32_t sub_length = sub->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002288
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002289 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002290 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00002291 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002292
2293 for (int i = start_index; i >= 0; i--) {
2294 bool found = true;
2295 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002296 if (sub->Get(i + j) != pat->Get(j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002297 found = false;
2298 break;
2299 }
2300 }
2301 if (found) return Smi::FromInt(i);
2302 }
2303
2304 return Smi::FromInt(-1);
2305}
2306
2307
2308static Object* Runtime_StringLocaleCompare(Arguments args) {
2309 NoHandleAllocation ha;
2310 ASSERT(args.length() == 2);
2311
2312 CONVERT_CHECKED(String, str1, args[0]);
2313 CONVERT_CHECKED(String, str2, args[1]);
2314
2315 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002316 int str1_length = str1->length();
2317 int str2_length = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002318
2319 // Decide trivial cases without flattening.
2320 if (str1_length == 0) {
2321 if (str2_length == 0) return Smi::FromInt(0); // Equal.
2322 return Smi::FromInt(-str2_length);
2323 } else {
2324 if (str2_length == 0) return Smi::FromInt(str1_length);
2325 }
2326
2327 int end = str1_length < str2_length ? str1_length : str2_length;
2328
2329 // No need to flatten if we are going to find the answer on the first
2330 // character. At this point we know there is at least one character
2331 // in each string, due to the trivial case handling above.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002332 int d = str1->Get(0) - str2->Get(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002333 if (d != 0) return Smi::FromInt(d);
2334
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002335 str1->TryFlattenIfNotFlat();
2336 str2->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002337
2338 static StringInputBuffer buf1;
2339 static StringInputBuffer buf2;
2340
2341 buf1.Reset(str1);
2342 buf2.Reset(str2);
2343
2344 for (int i = 0; i < end; i++) {
2345 uint16_t char1 = buf1.GetNext();
2346 uint16_t char2 = buf2.GetNext();
2347 if (char1 != char2) return Smi::FromInt(char1 - char2);
2348 }
2349
2350 return Smi::FromInt(str1_length - str2_length);
2351}
2352
2353
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002354static Object* Runtime_SubString(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002355 NoHandleAllocation ha;
2356 ASSERT(args.length() == 3);
2357
2358 CONVERT_CHECKED(String, value, args[0]);
2359 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
2360 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
2361
2362 int start = FastD2I(from_number);
2363 int end = FastD2I(to_number);
2364
2365 RUNTIME_ASSERT(end >= start);
2366 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002367 RUNTIME_ASSERT(end <= value->length());
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002368 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002369}
2370
2371
ager@chromium.org41826e72009-03-30 13:30:57 +00002372static Object* Runtime_StringMatch(Arguments args) {
2373 ASSERT_EQ(3, args.length());
2374
2375 CONVERT_ARG_CHECKED(String, subject, 0);
2376 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2377 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2378 HandleScope handles;
2379
2380 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2381
2382 if (match.is_null()) {
2383 return Failure::Exception();
2384 }
2385 if (match->IsNull()) {
2386 return Heap::null_value();
2387 }
2388 int length = subject->length();
2389
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002390 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002391 ZoneList<int> offsets(8);
2392 do {
2393 int start;
2394 int end;
2395 {
2396 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002397 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002398 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2399 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2400 }
2401 offsets.Add(start);
2402 offsets.Add(end);
2403 int index = start < end ? end : end + 1;
2404 if (index > length) break;
2405 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2406 if (match.is_null()) {
2407 return Failure::Exception();
2408 }
2409 } while (!match->IsNull());
2410 int matches = offsets.length() / 2;
2411 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2412 for (int i = 0; i < matches ; i++) {
2413 int from = offsets.at(i * 2);
2414 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002415 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00002416 }
2417 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2418 result->set_length(Smi::FromInt(matches));
2419 return *result;
2420}
2421
2422
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002423static Object* Runtime_NumberToRadixString(Arguments args) {
2424 NoHandleAllocation ha;
2425 ASSERT(args.length() == 2);
2426
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002427 // Fast case where the result is a one character string.
2428 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2429 int value = Smi::cast(args[0])->value();
2430 int radix = Smi::cast(args[1])->value();
2431 if (value >= 0 && value < radix) {
2432 RUNTIME_ASSERT(radix <= 36);
2433 // Character array used for conversion.
2434 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2435 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2436 }
2437 }
2438
2439 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002440 CONVERT_DOUBLE_CHECKED(value, args[0]);
2441 if (isnan(value)) {
2442 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2443 }
2444 if (isinf(value)) {
2445 if (value < 0) {
2446 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2447 }
2448 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2449 }
2450 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2451 int radix = FastD2I(radix_number);
2452 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2453 char* str = DoubleToRadixCString(value, radix);
2454 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2455 DeleteArray(str);
2456 return result;
2457}
2458
2459
2460static Object* Runtime_NumberToFixed(Arguments args) {
2461 NoHandleAllocation ha;
2462 ASSERT(args.length() == 2);
2463
2464 CONVERT_DOUBLE_CHECKED(value, args[0]);
2465 if (isnan(value)) {
2466 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2467 }
2468 if (isinf(value)) {
2469 if (value < 0) {
2470 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2471 }
2472 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2473 }
2474 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2475 int f = FastD2I(f_number);
2476 RUNTIME_ASSERT(f >= 0);
2477 char* str = DoubleToFixedCString(value, f);
2478 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2479 DeleteArray(str);
2480 return res;
2481}
2482
2483
2484static Object* Runtime_NumberToExponential(Arguments args) {
2485 NoHandleAllocation ha;
2486 ASSERT(args.length() == 2);
2487
2488 CONVERT_DOUBLE_CHECKED(value, args[0]);
2489 if (isnan(value)) {
2490 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2491 }
2492 if (isinf(value)) {
2493 if (value < 0) {
2494 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2495 }
2496 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2497 }
2498 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2499 int f = FastD2I(f_number);
2500 RUNTIME_ASSERT(f >= -1 && f <= 20);
2501 char* str = DoubleToExponentialCString(value, f);
2502 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2503 DeleteArray(str);
2504 return res;
2505}
2506
2507
2508static Object* Runtime_NumberToPrecision(Arguments args) {
2509 NoHandleAllocation ha;
2510 ASSERT(args.length() == 2);
2511
2512 CONVERT_DOUBLE_CHECKED(value, args[0]);
2513 if (isnan(value)) {
2514 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2515 }
2516 if (isinf(value)) {
2517 if (value < 0) {
2518 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2519 }
2520 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2521 }
2522 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2523 int f = FastD2I(f_number);
2524 RUNTIME_ASSERT(f >= 1 && f <= 21);
2525 char* str = DoubleToPrecisionCString(value, f);
2526 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2527 DeleteArray(str);
2528 return res;
2529}
2530
2531
2532// Returns a single character string where first character equals
2533// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002534static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002535 if (index < static_cast<uint32_t>(string->length())) {
2536 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002537 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002538 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002539 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002540 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002541}
2542
2543
2544Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2545 // Handle [] indexing on Strings
2546 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002547 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2548 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002549 }
2550
2551 // Handle [] indexing on String objects
2552 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002553 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2554 Handle<Object> result =
2555 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2556 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002557 }
2558
2559 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002560 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002561 return prototype->GetElement(index);
2562 }
2563
2564 return object->GetElement(index);
2565}
2566
2567
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002568Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2569 HandleScope scope;
2570
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002571 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002572 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002573 Handle<Object> error =
2574 Factory::NewTypeError("non_object_property_load",
2575 HandleVector(args, 2));
2576 return Top::Throw(*error);
2577 }
2578
2579 // Check if the given key is an array index.
2580 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002581 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002582 return GetElementOrCharAt(object, index);
2583 }
2584
2585 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002586 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002587 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002588 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002589 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002590 bool has_pending_exception = false;
2591 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002592 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002593 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002594 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002595 }
2596
ager@chromium.org32912102009-01-16 10:38:43 +00002597 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002598 // the element if so.
2599 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002600 return GetElementOrCharAt(object, index);
2601 } else {
2602 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002603 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002604 }
2605}
2606
2607
2608static Object* Runtime_GetProperty(Arguments args) {
2609 NoHandleAllocation ha;
2610 ASSERT(args.length() == 2);
2611
2612 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002613 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002614
2615 return Runtime::GetObjectProperty(object, key);
2616}
2617
2618
ager@chromium.org7c537e22008-10-16 08:43:32 +00002619
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002620// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002621static Object* Runtime_KeyedGetProperty(Arguments args) {
2622 NoHandleAllocation ha;
2623 ASSERT(args.length() == 2);
2624
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002625 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002626 // itself.
2627 //
2628 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002629 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002630 // global proxy object never has properties. This is the case
2631 // because the global proxy object forwards everything to its hidden
2632 // prototype including local lookups.
2633 //
2634 // Additionally, we need to make sure that we do not cache results
2635 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002636 if (args[0]->IsJSObject() &&
2637 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002638 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002639 args[1]->IsString()) {
2640 JSObject* receiver = JSObject::cast(args[0]);
2641 String* key = String::cast(args[1]);
2642 if (receiver->HasFastProperties()) {
2643 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002644 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002645 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2646 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002647 Object* value = receiver->FastPropertyAt(offset);
2648 return value->IsTheHole() ? Heap::undefined_value() : value;
2649 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002650 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002651 LookupResult result;
2652 receiver->LocalLookup(key, &result);
2653 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2654 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002655 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002656 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002657 }
2658 } else {
2659 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002660 StringDictionary* dictionary = receiver->property_dictionary();
2661 int entry = dictionary->FindEntry(key);
2662 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002663 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002664 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002665 if (!receiver->IsGlobalObject()) return value;
2666 value = JSGlobalPropertyCell::cast(value)->value();
2667 if (!value->IsTheHole()) return value;
2668 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002669 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002670 }
2671 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002672
2673 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002674 return Runtime::GetObjectProperty(args.at<Object>(0),
2675 args.at<Object>(1));
2676}
2677
2678
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002679Object* Runtime::SetObjectProperty(Handle<Object> object,
2680 Handle<Object> key,
2681 Handle<Object> value,
2682 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002683 HandleScope scope;
2684
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002685 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002686 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002687 Handle<Object> error =
2688 Factory::NewTypeError("non_object_property_store",
2689 HandleVector(args, 2));
2690 return Top::Throw(*error);
2691 }
2692
2693 // If the object isn't a JavaScript object, we ignore the store.
2694 if (!object->IsJSObject()) return *value;
2695
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002696 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2697
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002698 // Check if the given key is an array index.
2699 uint32_t index;
2700 if (Array::IndexFromObject(*key, &index)) {
2701 ASSERT(attr == NONE);
2702
2703 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2704 // of a string using [] notation. We need to support this too in
2705 // JavaScript.
2706 // In the case of a String object we just need to redirect the assignment to
2707 // the underlying string if the index is in range. Since the underlying
2708 // string does nothing with the assignment then we can ignore such
2709 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002710 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002711 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002712 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002713
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002714 Handle<Object> result = SetElement(js_object, index, value);
2715 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002716 return *value;
2717 }
2718
2719 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002720 Handle<Object> result;
2721 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002722 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002723 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002724 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002725 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002726 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002727 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002728 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002729 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002730 return *value;
2731 }
2732
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002733 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002734 bool has_pending_exception = false;
2735 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2736 if (has_pending_exception) return Failure::Exception();
2737 Handle<String> name = Handle<String>::cast(converted);
2738
2739 if (name->AsArrayIndex(&index)) {
2740 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002741 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002742 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002743 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002744 }
2745}
2746
2747
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002748Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2749 Handle<Object> key,
2750 Handle<Object> value,
2751 PropertyAttributes attr) {
2752 HandleScope scope;
2753
2754 // Check if the given key is an array index.
2755 uint32_t index;
2756 if (Array::IndexFromObject(*key, &index)) {
2757 ASSERT(attr == NONE);
2758
2759 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2760 // of a string using [] notation. We need to support this too in
2761 // JavaScript.
2762 // In the case of a String object we just need to redirect the assignment to
2763 // the underlying string if the index is in range. Since the underlying
2764 // string does nothing with the assignment then we can ignore such
2765 // assignments.
2766 if (js_object->IsStringObjectWithCharacterAt(index)) {
2767 return *value;
2768 }
2769
2770 return js_object->SetElement(index, *value);
2771 }
2772
2773 if (key->IsString()) {
2774 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2775 ASSERT(attr == NONE);
2776 return js_object->SetElement(index, *value);
2777 } else {
2778 Handle<String> key_string = Handle<String>::cast(key);
2779 key_string->TryFlattenIfNotFlat();
2780 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2781 *value,
2782 attr);
2783 }
2784 }
2785
2786 // Call-back into JavaScript to convert the key to a string.
2787 bool has_pending_exception = false;
2788 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2789 if (has_pending_exception) return Failure::Exception();
2790 Handle<String> name = Handle<String>::cast(converted);
2791
2792 if (name->AsArrayIndex(&index)) {
2793 ASSERT(attr == NONE);
2794 return js_object->SetElement(index, *value);
2795 } else {
2796 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2797 }
2798}
2799
2800
ager@chromium.orge2902be2009-06-08 12:21:35 +00002801Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2802 Handle<Object> key) {
2803 HandleScope scope;
2804
2805 // Check if the given key is an array index.
2806 uint32_t index;
2807 if (Array::IndexFromObject(*key, &index)) {
2808 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2809 // characters of a string using [] notation. In the case of a
2810 // String object we just need to redirect the deletion to the
2811 // underlying string if the index is in range. Since the
2812 // underlying string does nothing with the deletion, we can ignore
2813 // such deletions.
2814 if (js_object->IsStringObjectWithCharacterAt(index)) {
2815 return Heap::true_value();
2816 }
2817
2818 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2819 }
2820
2821 Handle<String> key_string;
2822 if (key->IsString()) {
2823 key_string = Handle<String>::cast(key);
2824 } else {
2825 // Call-back into JavaScript to convert the key to a string.
2826 bool has_pending_exception = false;
2827 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2828 if (has_pending_exception) return Failure::Exception();
2829 key_string = Handle<String>::cast(converted);
2830 }
2831
2832 key_string->TryFlattenIfNotFlat();
2833 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2834}
2835
2836
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002837static Object* Runtime_SetProperty(Arguments args) {
2838 NoHandleAllocation ha;
2839 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2840
2841 Handle<Object> object = args.at<Object>(0);
2842 Handle<Object> key = args.at<Object>(1);
2843 Handle<Object> value = args.at<Object>(2);
2844
2845 // Compute attributes.
2846 PropertyAttributes attributes = NONE;
2847 if (args.length() == 4) {
2848 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002849 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002850 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002851 RUNTIME_ASSERT(
2852 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2853 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002854 }
2855 return Runtime::SetObjectProperty(object, key, value, attributes);
2856}
2857
2858
2859// Set a local property, even if it is READ_ONLY. If the property does not
2860// exist, it will be added with attributes NONE.
2861static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2862 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002863 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002864 CONVERT_CHECKED(JSObject, object, args[0]);
2865 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002866 // Compute attributes.
2867 PropertyAttributes attributes = NONE;
2868 if (args.length() == 4) {
2869 CONVERT_CHECKED(Smi, value_obj, args[3]);
2870 int unchecked_value = value_obj->value();
2871 // Only attribute bits should be set.
2872 RUNTIME_ASSERT(
2873 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2874 attributes = static_cast<PropertyAttributes>(unchecked_value);
2875 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002876
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002877 return object->
2878 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002879}
2880
2881
2882static Object* Runtime_DeleteProperty(Arguments args) {
2883 NoHandleAllocation ha;
2884 ASSERT(args.length() == 2);
2885
2886 CONVERT_CHECKED(JSObject, object, args[0]);
2887 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002888 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002889}
2890
2891
ager@chromium.org9085a012009-05-11 19:22:57 +00002892static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2893 Handle<String> key) {
2894 if (object->HasLocalProperty(*key)) return Heap::true_value();
2895 // Handle hidden prototypes. If there's a hidden prototype above this thing
2896 // then we have to check it for properties, because they are supposed to
2897 // look like they are on this object.
2898 Handle<Object> proto(object->GetPrototype());
2899 if (proto->IsJSObject() &&
2900 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2901 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2902 }
2903 return Heap::false_value();
2904}
2905
2906
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002907static Object* Runtime_HasLocalProperty(Arguments args) {
2908 NoHandleAllocation ha;
2909 ASSERT(args.length() == 2);
2910 CONVERT_CHECKED(String, key, args[1]);
2911
ager@chromium.org9085a012009-05-11 19:22:57 +00002912 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002913 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002914 if (obj->IsJSObject()) {
2915 JSObject* object = JSObject::cast(obj);
2916 // Fast case - no interceptors.
2917 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2918 // Slow case. Either it's not there or we have an interceptor. We should
2919 // have handles for this kind of deal.
2920 HandleScope scope;
2921 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2922 Handle<String>(key));
2923 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002924 // Well, there is one exception: Handle [] on strings.
2925 uint32_t index;
2926 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002927 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002928 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002929 return Heap::true_value();
2930 }
2931 }
2932 return Heap::false_value();
2933}
2934
2935
2936static Object* Runtime_HasProperty(Arguments args) {
2937 NoHandleAllocation na;
2938 ASSERT(args.length() == 2);
2939
2940 // Only JS objects can have properties.
2941 if (args[0]->IsJSObject()) {
2942 JSObject* object = JSObject::cast(args[0]);
2943 CONVERT_CHECKED(String, key, args[1]);
2944 if (object->HasProperty(key)) return Heap::true_value();
2945 }
2946 return Heap::false_value();
2947}
2948
2949
2950static Object* Runtime_HasElement(Arguments args) {
2951 NoHandleAllocation na;
2952 ASSERT(args.length() == 2);
2953
2954 // Only JS objects can have elements.
2955 if (args[0]->IsJSObject()) {
2956 JSObject* object = JSObject::cast(args[0]);
2957 CONVERT_CHECKED(Smi, index_obj, args[1]);
2958 uint32_t index = index_obj->value();
2959 if (object->HasElement(index)) return Heap::true_value();
2960 }
2961 return Heap::false_value();
2962}
2963
2964
2965static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2966 NoHandleAllocation ha;
2967 ASSERT(args.length() == 2);
2968
2969 CONVERT_CHECKED(JSObject, object, args[0]);
2970 CONVERT_CHECKED(String, key, args[1]);
2971
2972 uint32_t index;
2973 if (key->AsArrayIndex(&index)) {
2974 return Heap::ToBoolean(object->HasElement(index));
2975 }
2976
ager@chromium.org870a0b62008-11-04 11:43:05 +00002977 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2978 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002979}
2980
2981
2982static Object* Runtime_GetPropertyNames(Arguments args) {
2983 HandleScope scope;
2984 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002985 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002986 return *GetKeysFor(object);
2987}
2988
2989
2990// Returns either a FixedArray as Runtime_GetPropertyNames,
2991// or, if the given object has an enum cache that contains
2992// all enumerable properties of the object and its prototypes
2993// have none, the map of the object. This is used to speed up
2994// the check for deletions during a for-in.
2995static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2996 ASSERT(args.length() == 1);
2997
2998 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2999
3000 if (raw_object->IsSimpleEnum()) return raw_object->map();
3001
3002 HandleScope scope;
3003 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003004 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
3005 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003006
3007 // Test again, since cache may have been built by preceding call.
3008 if (object->IsSimpleEnum()) return object->map();
3009
3010 return *content;
3011}
3012
3013
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003014static Object* Runtime_LocalKeys(Arguments args) {
3015 ASSERT_EQ(args.length(), 1);
3016 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3017 HandleScope scope;
3018 Handle<JSObject> object(raw_object);
3019 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
3020 LOCAL_ONLY);
3021 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
3022 // property array and since the result is mutable we have to create
3023 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003024 int length = contents->length();
3025 Handle<FixedArray> copy = Factory::NewFixedArray(length);
3026 for (int i = 0; i < length; i++) {
3027 Object* entry = contents->get(i);
3028 if (entry->IsString()) {
3029 copy->set(i, entry);
3030 } else {
3031 ASSERT(entry->IsNumber());
3032 HandleScope scope;
3033 Handle<Object> entry_handle(entry);
3034 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
3035 copy->set(i, *entry_str);
3036 }
3037 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003038 return *Factory::NewJSArrayWithElements(copy);
3039}
3040
3041
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003042static Object* Runtime_GetArgumentsProperty(Arguments args) {
3043 NoHandleAllocation ha;
3044 ASSERT(args.length() == 1);
3045
3046 // Compute the frame holding the arguments.
3047 JavaScriptFrameIterator it;
3048 it.AdvanceToArgumentsFrame();
3049 JavaScriptFrame* frame = it.frame();
3050
3051 // Get the actual number of provided arguments.
3052 const uint32_t n = frame->GetProvidedParametersCount();
3053
3054 // Try to convert the key to an index. If successful and within
3055 // index return the the argument from the frame.
3056 uint32_t index;
3057 if (Array::IndexFromObject(args[0], &index) && index < n) {
3058 return frame->GetParameter(index);
3059 }
3060
3061 // Convert the key to a string.
3062 HandleScope scope;
3063 bool exception = false;
3064 Handle<Object> converted =
3065 Execution::ToString(args.at<Object>(0), &exception);
3066 if (exception) return Failure::Exception();
3067 Handle<String> key = Handle<String>::cast(converted);
3068
3069 // Try to convert the string key into an array index.
3070 if (key->AsArrayIndex(&index)) {
3071 if (index < n) {
3072 return frame->GetParameter(index);
3073 } else {
3074 return Top::initial_object_prototype()->GetElement(index);
3075 }
3076 }
3077
3078 // Handle special arguments properties.
3079 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3080 if (key->Equals(Heap::callee_symbol())) return frame->function();
3081
3082 // Lookup in the initial Object.prototype object.
3083 return Top::initial_object_prototype()->GetProperty(*key);
3084}
3085
3086
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003087static Object* Runtime_ToFastProperties(Arguments args) {
3088 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003089 Handle<Object> object = args.at<Object>(0);
3090 if (object->IsJSObject()) {
3091 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3092 js_object->TransformToFastProperties(0);
3093 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003094 return *object;
3095}
3096
3097
3098static Object* Runtime_ToSlowProperties(Arguments args) {
3099 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003100 Handle<Object> object = args.at<Object>(0);
3101 if (object->IsJSObject()) {
3102 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003103 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003104 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003105 return *object;
3106}
3107
3108
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003109static Object* Runtime_ToBool(Arguments args) {
3110 NoHandleAllocation ha;
3111 ASSERT(args.length() == 1);
3112
3113 return args[0]->ToBoolean();
3114}
3115
3116
3117// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3118// Possible optimizations: put the type string into the oddballs.
3119static Object* Runtime_Typeof(Arguments args) {
3120 NoHandleAllocation ha;
3121
3122 Object* obj = args[0];
3123 if (obj->IsNumber()) return Heap::number_symbol();
3124 HeapObject* heap_obj = HeapObject::cast(obj);
3125
3126 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003127 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003128
3129 InstanceType instance_type = heap_obj->map()->instance_type();
3130 if (instance_type < FIRST_NONSTRING_TYPE) {
3131 return Heap::string_symbol();
3132 }
3133
3134 switch (instance_type) {
3135 case ODDBALL_TYPE:
3136 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3137 return Heap::boolean_symbol();
3138 }
3139 if (heap_obj->IsNull()) {
3140 return Heap::object_symbol();
3141 }
3142 ASSERT(heap_obj->IsUndefined());
3143 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00003144 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003145 return Heap::function_symbol();
3146 default:
3147 // For any kind of object not handled above, the spec rule for
3148 // host objects gives that it is okay to return "object"
3149 return Heap::object_symbol();
3150 }
3151}
3152
3153
3154static Object* Runtime_StringToNumber(Arguments args) {
3155 NoHandleAllocation ha;
3156 ASSERT(args.length() == 1);
3157 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003158 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003159 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3160}
3161
3162
3163static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3164 NoHandleAllocation ha;
3165 ASSERT(args.length() == 1);
3166
3167 CONVERT_CHECKED(JSArray, codes, args[0]);
3168 int length = Smi::cast(codes->length())->value();
3169
3170 // Check if the string can be ASCII.
3171 int i;
3172 for (i = 0; i < length; i++) {
3173 Object* element = codes->GetElement(i);
3174 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3175 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3176 break;
3177 }
3178
3179 Object* object = NULL;
3180 if (i == length) { // The string is ASCII.
3181 object = Heap::AllocateRawAsciiString(length);
3182 } else { // The string is not ASCII.
3183 object = Heap::AllocateRawTwoByteString(length);
3184 }
3185
3186 if (object->IsFailure()) return object;
3187 String* result = String::cast(object);
3188 for (int i = 0; i < length; i++) {
3189 Object* element = codes->GetElement(i);
3190 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003191 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003192 }
3193 return result;
3194}
3195
3196
3197// kNotEscaped is generated by the following:
3198//
3199// #!/bin/perl
3200// for (my $i = 0; $i < 256; $i++) {
3201// print "\n" if $i % 16 == 0;
3202// my $c = chr($i);
3203// my $escaped = 1;
3204// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3205// print $escaped ? "0, " : "1, ";
3206// }
3207
3208
3209static bool IsNotEscaped(uint16_t character) {
3210 // Only for 8 bit characters, the rest are always escaped (in a different way)
3211 ASSERT(character < 256);
3212 static const char kNotEscaped[256] = {
3213 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3214 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3215 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
3216 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3217 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3218 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3219 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3220 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3221 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3222 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3223 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3224 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3225 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3226 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3227 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3228 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3229 };
3230 return kNotEscaped[character] != 0;
3231}
3232
3233
3234static Object* Runtime_URIEscape(Arguments args) {
3235 const char hex_chars[] = "0123456789ABCDEF";
3236 NoHandleAllocation ha;
3237 ASSERT(args.length() == 1);
3238 CONVERT_CHECKED(String, source, args[0]);
3239
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003240 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003241
3242 int escaped_length = 0;
3243 int length = source->length();
3244 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003245 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003246 buffer->Reset(source);
3247 while (buffer->has_more()) {
3248 uint16_t character = buffer->GetNext();
3249 if (character >= 256) {
3250 escaped_length += 6;
3251 } else if (IsNotEscaped(character)) {
3252 escaped_length++;
3253 } else {
3254 escaped_length += 3;
3255 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003256 // We don't allow strings that are longer than a maximal length.
3257 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003258 Top::context()->mark_out_of_memory();
3259 return Failure::OutOfMemoryException();
3260 }
3261 }
3262 }
3263 // No length change implies no change. Return original string if no change.
3264 if (escaped_length == length) {
3265 return source;
3266 }
3267 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3268 if (o->IsFailure()) return o;
3269 String* destination = String::cast(o);
3270 int dest_position = 0;
3271
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003272 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003273 buffer->Rewind();
3274 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003275 uint16_t chr = buffer->GetNext();
3276 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003277 destination->Set(dest_position, '%');
3278 destination->Set(dest_position+1, 'u');
3279 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3280 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3281 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3282 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003283 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003284 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003285 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003286 dest_position++;
3287 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003288 destination->Set(dest_position, '%');
3289 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3290 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003291 dest_position += 3;
3292 }
3293 }
3294 return destination;
3295}
3296
3297
3298static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3299 static const signed char kHexValue['g'] = {
3300 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3301 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3302 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3303 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3304 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3305 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3306 -1, 10, 11, 12, 13, 14, 15 };
3307
3308 if (character1 > 'f') return -1;
3309 int hi = kHexValue[character1];
3310 if (hi == -1) return -1;
3311 if (character2 > 'f') return -1;
3312 int lo = kHexValue[character2];
3313 if (lo == -1) return -1;
3314 return (hi << 4) + lo;
3315}
3316
3317
ager@chromium.org870a0b62008-11-04 11:43:05 +00003318static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003319 int i,
3320 int length,
3321 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003322 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003323 int32_t hi = 0;
3324 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003325 if (character == '%' &&
3326 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003327 source->Get(i + 1) == 'u' &&
3328 (hi = TwoDigitHex(source->Get(i + 2),
3329 source->Get(i + 3))) != -1 &&
3330 (lo = TwoDigitHex(source->Get(i + 4),
3331 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003332 *step = 6;
3333 return (hi << 8) + lo;
3334 } else if (character == '%' &&
3335 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003336 (lo = TwoDigitHex(source->Get(i + 1),
3337 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003338 *step = 3;
3339 return lo;
3340 } else {
3341 *step = 1;
3342 return character;
3343 }
3344}
3345
3346
3347static Object* Runtime_URIUnescape(Arguments args) {
3348 NoHandleAllocation ha;
3349 ASSERT(args.length() == 1);
3350 CONVERT_CHECKED(String, source, args[0]);
3351
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003352 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003353
3354 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003355 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003356
3357 int unescaped_length = 0;
3358 for (int i = 0; i < length; unescaped_length++) {
3359 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003360 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003361 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003362 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003363 i += step;
3364 }
3365
3366 // No length change implies no change. Return original string if no change.
3367 if (unescaped_length == length)
3368 return source;
3369
3370 Object* o = ascii ?
3371 Heap::AllocateRawAsciiString(unescaped_length) :
3372 Heap::AllocateRawTwoByteString(unescaped_length);
3373 if (o->IsFailure()) return o;
3374 String* destination = String::cast(o);
3375
3376 int dest_position = 0;
3377 for (int i = 0; i < length; dest_position++) {
3378 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003379 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003380 i += step;
3381 }
3382 return destination;
3383}
3384
3385
3386static Object* Runtime_StringParseInt(Arguments args) {
3387 NoHandleAllocation ha;
3388
3389 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003390 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003391
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003392 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003393
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003394 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003395 int i;
3396
3397 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003398 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003399 if (i == len) return Heap::nan_value();
3400
3401 // Compute the sign (default to +).
3402 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003403 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003404 sign = -1;
3405 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003406 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003407 i++;
3408 }
3409
3410 // Compute the radix if 0.
3411 if (radix == 0) {
3412 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003413 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003414 radix = 8;
3415 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003416 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003417 if (c == 'x' || c == 'X') {
3418 radix = 16;
3419 i += 2;
3420 }
3421 }
3422 }
3423 } else if (radix == 16) {
3424 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003425 if (i + 1 < len && s->Get(i) == '0') {
3426 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003427 if (c == 'x' || c == 'X') i += 2;
3428 }
3429 }
3430
3431 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3432 double value;
3433 int end_index = StringToInt(s, i, radix, &value);
3434 if (end_index != i) {
3435 return Heap::NumberFromDouble(sign * value);
3436 }
3437 return Heap::nan_value();
3438}
3439
3440
3441static Object* Runtime_StringParseFloat(Arguments args) {
3442 NoHandleAllocation ha;
3443 CONVERT_CHECKED(String, str, args[0]);
3444
3445 // ECMA-262 section 15.1.2.3, empty string is NaN
3446 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3447
3448 // Create a number object from the value.
3449 return Heap::NumberFromDouble(value);
3450}
3451
3452
3453static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3454static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3455
3456
3457template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003458static Object* ConvertCaseHelper(String* s,
3459 int length,
3460 int input_string_length,
3461 unibrow::Mapping<Converter, 128>* mapping) {
3462 // We try this twice, once with the assumption that the result is no longer
3463 // than the input and, if that assumption breaks, again with the exact
3464 // length. This may not be pretty, but it is nicer than what was here before
3465 // and I hereby claim my vaffel-is.
3466 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003467 // Allocate the resulting string.
3468 //
3469 // NOTE: This assumes that the upper/lower case of an ascii
3470 // character is also ascii. This is currently the case, but it
3471 // might break in the future if we implement more context and locale
3472 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003473 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003474 ? Heap::AllocateRawAsciiString(length)
3475 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003476 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003477 String* result = String::cast(o);
3478 bool has_changed_character = false;
3479
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003480 // Convert all characters to upper case, assuming that they will fit
3481 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003482 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003483 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003484 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003485 // We can assume that the string is not empty
3486 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003487 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003488 bool has_next = buffer->has_more();
3489 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003490 int char_length = mapping->get(current, next, chars);
3491 if (char_length == 0) {
3492 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003493 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003494 i++;
3495 } else if (char_length == 1) {
3496 // Common case: converting the letter resulted in one character.
3497 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003498 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003499 has_changed_character = true;
3500 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003501 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003502 // We've assumed that the result would be as long as the
3503 // input but here is a character that converts to several
3504 // characters. No matter, we calculate the exact length
3505 // of the result and try the whole thing again.
3506 //
3507 // Note that this leaves room for optimization. We could just
3508 // memcpy what we already have to the result string. Also,
3509 // the result string is the last object allocated we could
3510 // "realloc" it and probably, in the vast majority of cases,
3511 // extend the existing string to be able to hold the full
3512 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003513 int next_length = 0;
3514 if (has_next) {
3515 next_length = mapping->get(next, 0, chars);
3516 if (next_length == 0) next_length = 1;
3517 }
3518 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003519 while (buffer->has_more()) {
3520 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003521 // NOTE: we use 0 as the next character here because, while
3522 // the next character may affect what a character converts to,
3523 // it does not in any case affect the length of what it convert
3524 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003525 int char_length = mapping->get(current, 0, chars);
3526 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003527 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003528 if (current_length > Smi::kMaxValue) {
3529 Top::context()->mark_out_of_memory();
3530 return Failure::OutOfMemoryException();
3531 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003532 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003533 // Try again with the real length.
3534 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003535 } else {
3536 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003537 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003538 i++;
3539 }
3540 has_changed_character = true;
3541 }
3542 current = next;
3543 }
3544 if (has_changed_character) {
3545 return result;
3546 } else {
3547 // If we didn't actually change anything in doing the conversion
3548 // we simple return the result and let the converted string
3549 // become garbage; there is no reason to keep two identical strings
3550 // alive.
3551 return s;
3552 }
3553}
3554
3555
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003556template <class Converter>
3557static Object* ConvertCase(Arguments args,
3558 unibrow::Mapping<Converter, 128>* mapping) {
3559 NoHandleAllocation ha;
3560
3561 CONVERT_CHECKED(String, s, args[0]);
3562 s->TryFlattenIfNotFlat();
3563
3564 int input_string_length = s->length();
3565 // Assume that the string is not empty; we need this assumption later
3566 if (input_string_length == 0) return s;
3567 int length = input_string_length;
3568
3569 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3570 if (answer->IsSmi()) {
3571 // Retry with correct length.
3572 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3573 }
3574 return answer; // This may be a failure.
3575}
3576
3577
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003578static Object* Runtime_StringToLowerCase(Arguments args) {
3579 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3580}
3581
3582
3583static Object* Runtime_StringToUpperCase(Arguments args) {
3584 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3585}
3586
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003587static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
3588 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
3589}
3590
3591static Object* Runtime_StringTrim(Arguments args) {
3592 NoHandleAllocation ha;
3593 ASSERT(args.length() == 3);
3594
3595 CONVERT_CHECKED(String, s, args[0]);
3596 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
3597 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
3598
3599 s->TryFlattenIfNotFlat();
3600 int length = s->length();
3601
3602 int left = 0;
3603 if (trimLeft) {
3604 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
3605 left++;
3606 }
3607 }
3608
3609 int right = length;
3610 if (trimRight) {
3611 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
3612 right--;
3613 }
3614 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003615 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003616}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003617
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003618bool Runtime::IsUpperCaseChar(uint16_t ch) {
3619 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3620 int char_length = to_upper_mapping.get(ch, 0, chars);
3621 return char_length == 0;
3622}
3623
3624
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003625static Object* Runtime_NumberToString(Arguments args) {
3626 NoHandleAllocation ha;
3627 ASSERT(args.length() == 1);
3628
3629 Object* number = args[0];
3630 RUNTIME_ASSERT(number->IsNumber());
3631
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003632 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003633}
3634
3635
3636static Object* Runtime_NumberToInteger(Arguments args) {
3637 NoHandleAllocation ha;
3638 ASSERT(args.length() == 1);
3639
3640 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003641 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003642 CONVERT_DOUBLE_CHECKED(number, obj);
3643 return Heap::NumberFromDouble(DoubleToInteger(number));
3644}
3645
3646
3647static Object* Runtime_NumberToJSUint32(Arguments args) {
3648 NoHandleAllocation ha;
3649 ASSERT(args.length() == 1);
3650
3651 Object* obj = args[0];
3652 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3653 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3654 return Heap::NumberFromUint32(number);
3655}
3656
3657
3658static Object* Runtime_NumberToJSInt32(Arguments args) {
3659 NoHandleAllocation ha;
3660 ASSERT(args.length() == 1);
3661
3662 Object* obj = args[0];
3663 if (obj->IsSmi()) return obj;
3664 CONVERT_DOUBLE_CHECKED(number, obj);
3665 return Heap::NumberFromInt32(DoubleToInt32(number));
3666}
3667
3668
ager@chromium.org870a0b62008-11-04 11:43:05 +00003669// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3670// a small integer.
3671static Object* Runtime_NumberToSmi(Arguments args) {
3672 NoHandleAllocation ha;
3673 ASSERT(args.length() == 1);
3674
3675 Object* obj = args[0];
3676 if (obj->IsSmi()) {
3677 return obj;
3678 }
3679 if (obj->IsHeapNumber()) {
3680 double value = HeapNumber::cast(obj)->value();
3681 int int_value = FastD2I(value);
3682 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3683 return Smi::FromInt(int_value);
3684 }
3685 }
3686 return Heap::nan_value();
3687}
3688
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003689
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003690static Object* Runtime_NumberAdd(Arguments args) {
3691 NoHandleAllocation ha;
3692 ASSERT(args.length() == 2);
3693
3694 CONVERT_DOUBLE_CHECKED(x, args[0]);
3695 CONVERT_DOUBLE_CHECKED(y, args[1]);
3696 return Heap::AllocateHeapNumber(x + y);
3697}
3698
3699
3700static Object* Runtime_NumberSub(Arguments args) {
3701 NoHandleAllocation ha;
3702 ASSERT(args.length() == 2);
3703
3704 CONVERT_DOUBLE_CHECKED(x, args[0]);
3705 CONVERT_DOUBLE_CHECKED(y, args[1]);
3706 return Heap::AllocateHeapNumber(x - y);
3707}
3708
3709
3710static Object* Runtime_NumberMul(Arguments args) {
3711 NoHandleAllocation ha;
3712 ASSERT(args.length() == 2);
3713
3714 CONVERT_DOUBLE_CHECKED(x, args[0]);
3715 CONVERT_DOUBLE_CHECKED(y, args[1]);
3716 return Heap::AllocateHeapNumber(x * y);
3717}
3718
3719
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003720static Object* Runtime_NumberUnaryMinus(Arguments args) {
3721 NoHandleAllocation ha;
3722 ASSERT(args.length() == 1);
3723
3724 CONVERT_DOUBLE_CHECKED(x, args[0]);
3725 return Heap::AllocateHeapNumber(-x);
3726}
3727
3728
3729static Object* Runtime_NumberDiv(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::NewNumberFromDouble(x / y);
3736}
3737
3738
3739static Object* Runtime_NumberMod(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
ager@chromium.org3811b432009-10-28 14:53:37 +00003746 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003747 // NewNumberFromDouble may return a Smi instead of a Number object
3748 return Heap::NewNumberFromDouble(x);
3749}
3750
3751
3752static Object* Runtime_StringAdd(Arguments args) {
3753 NoHandleAllocation ha;
3754 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003755 CONVERT_CHECKED(String, str1, args[0]);
3756 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003757 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003758}
3759
3760
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003761template<typename sinkchar>
3762static inline void StringBuilderConcatHelper(String* special,
3763 sinkchar* sink,
3764 FixedArray* fixed_array,
3765 int array_length) {
3766 int position = 0;
3767 for (int i = 0; i < array_length; i++) {
3768 Object* element = fixed_array->get(i);
3769 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003770 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003771 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003772 int pos;
3773 int len;
3774 if (encoded_slice > 0) {
3775 // Position and length encoded in one smi.
3776 pos = StringBuilderSubstringPosition::decode(encoded_slice);
3777 len = StringBuilderSubstringLength::decode(encoded_slice);
3778 } else {
3779 // Position and length encoded in two smis.
3780 Object* obj = fixed_array->get(++i);
3781 ASSERT(obj->IsSmi());
3782 pos = Smi::cast(obj)->value();
3783 len = -encoded_slice;
3784 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00003785 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003786 sink + position,
3787 pos,
3788 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003789 position += len;
3790 } else {
3791 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003792 int element_length = string->length();
3793 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003794 position += element_length;
3795 }
3796 }
3797}
3798
3799
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003800static Object* Runtime_StringBuilderConcat(Arguments args) {
3801 NoHandleAllocation ha;
3802 ASSERT(args.length() == 2);
3803 CONVERT_CHECKED(JSArray, array, args[0]);
3804 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003805
3806 // This assumption is used by the slice encoding in one or two smis.
3807 ASSERT(Smi::kMaxValue >= String::kMaxLength);
3808
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003809 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003810 Object* smi_array_length = array->length();
3811 if (!smi_array_length->IsSmi()) {
3812 Top::context()->mark_out_of_memory();
3813 return Failure::OutOfMemoryException();
3814 }
3815 int array_length = Smi::cast(smi_array_length)->value();
3816 if (!array->HasFastElements()) {
3817 return Top::Throw(Heap::illegal_argument_symbol());
3818 }
3819 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003820 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003821 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003822 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003823
3824 if (array_length == 0) {
3825 return Heap::empty_string();
3826 } else if (array_length == 1) {
3827 Object* first = fixed_array->get(0);
3828 if (first->IsString()) return first;
3829 }
3830
ager@chromium.org5ec48922009-05-05 07:25:34 +00003831 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003832 int position = 0;
3833 for (int i = 0; i < array_length; i++) {
3834 Object* elt = fixed_array->get(i);
3835 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003836 // Smi encoding of position and length.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003837 int len = Smi::cast(elt)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003838 if (len > 0) {
3839 // Position and length encoded in one smi.
3840 int pos = len >> 11;
3841 len &= 0x7ff;
3842 if (pos + len > special_length) {
3843 return Top::Throw(Heap::illegal_argument_symbol());
3844 }
3845 position += len;
3846 } else {
3847 // Position and length encoded in two smis.
3848 position += (-len);
3849 // Get the position and check that it is also a smi.
3850 i++;
3851 if (i >= array_length) {
3852 return Top::Throw(Heap::illegal_argument_symbol());
3853 }
3854 Object* pos = fixed_array->get(i);
3855 if (!pos->IsSmi()) {
3856 return Top::Throw(Heap::illegal_argument_symbol());
3857 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003858 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003859 } else if (elt->IsString()) {
3860 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003861 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003862 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003863 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003864 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003865 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003866 } else {
3867 return Top::Throw(Heap::illegal_argument_symbol());
3868 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003869 if (position > String::kMaxLength) {
3870 Top::context()->mark_out_of_memory();
3871 return Failure::OutOfMemoryException();
3872 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003873 }
3874
3875 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003876 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003877
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003878 if (ascii) {
3879 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003880 if (object->IsFailure()) return object;
3881 SeqAsciiString* answer = SeqAsciiString::cast(object);
3882 StringBuilderConcatHelper(special,
3883 answer->GetChars(),
3884 fixed_array,
3885 array_length);
3886 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003887 } else {
3888 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003889 if (object->IsFailure()) return object;
3890 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3891 StringBuilderConcatHelper(special,
3892 answer->GetChars(),
3893 fixed_array,
3894 array_length);
3895 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003896 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003897}
3898
3899
3900static Object* Runtime_NumberOr(Arguments args) {
3901 NoHandleAllocation ha;
3902 ASSERT(args.length() == 2);
3903
3904 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3905 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3906 return Heap::NumberFromInt32(x | y);
3907}
3908
3909
3910static Object* Runtime_NumberAnd(Arguments args) {
3911 NoHandleAllocation ha;
3912 ASSERT(args.length() == 2);
3913
3914 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3915 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3916 return Heap::NumberFromInt32(x & y);
3917}
3918
3919
3920static Object* Runtime_NumberXor(Arguments args) {
3921 NoHandleAllocation ha;
3922 ASSERT(args.length() == 2);
3923
3924 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3925 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3926 return Heap::NumberFromInt32(x ^ y);
3927}
3928
3929
3930static Object* Runtime_NumberNot(Arguments args) {
3931 NoHandleAllocation ha;
3932 ASSERT(args.length() == 1);
3933
3934 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3935 return Heap::NumberFromInt32(~x);
3936}
3937
3938
3939static Object* Runtime_NumberShl(Arguments args) {
3940 NoHandleAllocation ha;
3941 ASSERT(args.length() == 2);
3942
3943 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3944 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3945 return Heap::NumberFromInt32(x << (y & 0x1f));
3946}
3947
3948
3949static Object* Runtime_NumberShr(Arguments args) {
3950 NoHandleAllocation ha;
3951 ASSERT(args.length() == 2);
3952
3953 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3954 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3955 return Heap::NumberFromUint32(x >> (y & 0x1f));
3956}
3957
3958
3959static Object* Runtime_NumberSar(Arguments args) {
3960 NoHandleAllocation ha;
3961 ASSERT(args.length() == 2);
3962
3963 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3964 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3965 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3966}
3967
3968
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003969static Object* Runtime_NumberEquals(Arguments args) {
3970 NoHandleAllocation ha;
3971 ASSERT(args.length() == 2);
3972
3973 CONVERT_DOUBLE_CHECKED(x, args[0]);
3974 CONVERT_DOUBLE_CHECKED(y, args[1]);
3975 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3976 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3977 if (x == y) return Smi::FromInt(EQUAL);
3978 Object* result;
3979 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3980 result = Smi::FromInt(EQUAL);
3981 } else {
3982 result = Smi::FromInt(NOT_EQUAL);
3983 }
3984 return result;
3985}
3986
3987
3988static Object* Runtime_StringEquals(Arguments args) {
3989 NoHandleAllocation ha;
3990 ASSERT(args.length() == 2);
3991
3992 CONVERT_CHECKED(String, x, args[0]);
3993 CONVERT_CHECKED(String, y, args[1]);
3994
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003995 bool not_equal = !x->Equals(y);
3996 // This is slightly convoluted because the value that signifies
3997 // equality is 0 and inequality is 1 so we have to negate the result
3998 // from String::Equals.
3999 ASSERT(not_equal == 0 || not_equal == 1);
4000 STATIC_CHECK(EQUAL == 0);
4001 STATIC_CHECK(NOT_EQUAL == 1);
4002 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004003}
4004
4005
4006static Object* Runtime_NumberCompare(Arguments args) {
4007 NoHandleAllocation ha;
4008 ASSERT(args.length() == 3);
4009
4010 CONVERT_DOUBLE_CHECKED(x, args[0]);
4011 CONVERT_DOUBLE_CHECKED(y, args[1]);
4012 if (isnan(x) || isnan(y)) return args[2];
4013 if (x == y) return Smi::FromInt(EQUAL);
4014 if (isless(x, y)) return Smi::FromInt(LESS);
4015 return Smi::FromInt(GREATER);
4016}
4017
4018
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004019// Compare two Smis as if they were converted to strings and then
4020// compared lexicographically.
4021static Object* Runtime_SmiLexicographicCompare(Arguments args) {
4022 NoHandleAllocation ha;
4023 ASSERT(args.length() == 2);
4024
4025 // Arrays for the individual characters of the two Smis. Smis are
4026 // 31 bit integers and 10 decimal digits are therefore enough.
4027 static int x_elms[10];
4028 static int y_elms[10];
4029
4030 // Extract the integer values from the Smis.
4031 CONVERT_CHECKED(Smi, x, args[0]);
4032 CONVERT_CHECKED(Smi, y, args[1]);
4033 int x_value = x->value();
4034 int y_value = y->value();
4035
4036 // If the integers are equal so are the string representations.
4037 if (x_value == y_value) return Smi::FromInt(EQUAL);
4038
4039 // If one of the integers are zero the normal integer order is the
4040 // same as the lexicographic order of the string representations.
4041 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
4042
ager@chromium.org32912102009-01-16 10:38:43 +00004043 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004044 // smallest because the char code of '-' is less than the char code
4045 // of any digit. Otherwise, we make both values positive.
4046 if (x_value < 0 || y_value < 0) {
4047 if (y_value >= 0) return Smi::FromInt(LESS);
4048 if (x_value >= 0) return Smi::FromInt(GREATER);
4049 x_value = -x_value;
4050 y_value = -y_value;
4051 }
4052
4053 // Convert the integers to arrays of their decimal digits.
4054 int x_index = 0;
4055 int y_index = 0;
4056 while (x_value > 0) {
4057 x_elms[x_index++] = x_value % 10;
4058 x_value /= 10;
4059 }
4060 while (y_value > 0) {
4061 y_elms[y_index++] = y_value % 10;
4062 y_value /= 10;
4063 }
4064
4065 // Loop through the arrays of decimal digits finding the first place
4066 // where they differ.
4067 while (--x_index >= 0 && --y_index >= 0) {
4068 int diff = x_elms[x_index] - y_elms[y_index];
4069 if (diff != 0) return Smi::FromInt(diff);
4070 }
4071
4072 // If one array is a suffix of the other array, the longest array is
4073 // the representation of the largest of the Smis in the
4074 // lexicographic ordering.
4075 return Smi::FromInt(x_index - y_index);
4076}
4077
4078
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004079static Object* Runtime_StringCompare(Arguments args) {
4080 NoHandleAllocation ha;
4081 ASSERT(args.length() == 2);
4082
4083 CONVERT_CHECKED(String, x, args[0]);
4084 CONVERT_CHECKED(String, y, args[1]);
4085
4086 // A few fast case tests before we flatten.
4087 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004088 if (y->length() == 0) {
4089 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004090 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004091 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004092 return Smi::FromInt(LESS);
4093 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004094
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004095 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004096 if (d < 0) return Smi::FromInt(LESS);
4097 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004098
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004099 x->TryFlattenIfNotFlat();
4100 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004101
4102 static StringInputBuffer bufx;
4103 static StringInputBuffer bufy;
4104 bufx.Reset(x);
4105 bufy.Reset(y);
4106 while (bufx.has_more() && bufy.has_more()) {
4107 int d = bufx.GetNext() - bufy.GetNext();
4108 if (d < 0) return Smi::FromInt(LESS);
4109 else if (d > 0) return Smi::FromInt(GREATER);
4110 }
4111
4112 // x is (non-trivial) prefix of y:
4113 if (bufy.has_more()) return Smi::FromInt(LESS);
4114 // y is prefix of x:
4115 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4116}
4117
4118
4119static Object* Runtime_Math_abs(Arguments args) {
4120 NoHandleAllocation ha;
4121 ASSERT(args.length() == 1);
4122
4123 CONVERT_DOUBLE_CHECKED(x, args[0]);
4124 return Heap::AllocateHeapNumber(fabs(x));
4125}
4126
4127
4128static Object* Runtime_Math_acos(Arguments args) {
4129 NoHandleAllocation ha;
4130 ASSERT(args.length() == 1);
4131
4132 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004133 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004134}
4135
4136
4137static Object* Runtime_Math_asin(Arguments args) {
4138 NoHandleAllocation ha;
4139 ASSERT(args.length() == 1);
4140
4141 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004142 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004143}
4144
4145
4146static Object* Runtime_Math_atan(Arguments args) {
4147 NoHandleAllocation ha;
4148 ASSERT(args.length() == 1);
4149
4150 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004151 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004152}
4153
4154
4155static Object* Runtime_Math_atan2(Arguments args) {
4156 NoHandleAllocation ha;
4157 ASSERT(args.length() == 2);
4158
4159 CONVERT_DOUBLE_CHECKED(x, args[0]);
4160 CONVERT_DOUBLE_CHECKED(y, args[1]);
4161 double result;
4162 if (isinf(x) && isinf(y)) {
4163 // Make sure that the result in case of two infinite arguments
4164 // is a multiple of Pi / 4. The sign of the result is determined
4165 // by the first argument (x) and the sign of the second argument
4166 // determines the multiplier: one or three.
4167 static double kPiDividedBy4 = 0.78539816339744830962;
4168 int multiplier = (x < 0) ? -1 : 1;
4169 if (y < 0) multiplier *= 3;
4170 result = multiplier * kPiDividedBy4;
4171 } else {
4172 result = atan2(x, y);
4173 }
4174 return Heap::AllocateHeapNumber(result);
4175}
4176
4177
4178static Object* Runtime_Math_ceil(Arguments args) {
4179 NoHandleAllocation ha;
4180 ASSERT(args.length() == 1);
4181
4182 CONVERT_DOUBLE_CHECKED(x, args[0]);
4183 return Heap::NumberFromDouble(ceiling(x));
4184}
4185
4186
4187static Object* Runtime_Math_cos(Arguments args) {
4188 NoHandleAllocation ha;
4189 ASSERT(args.length() == 1);
4190
4191 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004192 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004193}
4194
4195
4196static Object* Runtime_Math_exp(Arguments args) {
4197 NoHandleAllocation ha;
4198 ASSERT(args.length() == 1);
4199
4200 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004201 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004202}
4203
4204
4205static Object* Runtime_Math_floor(Arguments args) {
4206 NoHandleAllocation ha;
4207 ASSERT(args.length() == 1);
4208
4209 CONVERT_DOUBLE_CHECKED(x, args[0]);
4210 return Heap::NumberFromDouble(floor(x));
4211}
4212
4213
4214static Object* Runtime_Math_log(Arguments args) {
4215 NoHandleAllocation ha;
4216 ASSERT(args.length() == 1);
4217
4218 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004219 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004220}
4221
4222
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004223// Helper function to compute x^y, where y is known to be an
4224// integer. Uses binary decomposition to limit the number of
4225// multiplications; see the discussion in "Hacker's Delight" by Henry
4226// S. Warren, Jr., figure 11-6, page 213.
4227static double powi(double x, int y) {
4228 ASSERT(y != kMinInt);
4229 unsigned n = (y < 0) ? -y : y;
4230 double m = x;
4231 double p = 1;
4232 while (true) {
4233 if ((n & 1) != 0) p *= m;
4234 n >>= 1;
4235 if (n == 0) {
4236 if (y < 0) {
4237 // Unfortunately, we have to be careful when p has reached
4238 // infinity in the computation, because sometimes the higher
4239 // internal precision in the pow() implementation would have
4240 // given us a finite p. This happens very rarely.
4241 double result = 1.0 / p;
4242 return (result == 0 && isinf(p))
4243 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4244 : result;
4245 } else {
4246 return p;
4247 }
4248 }
4249 m *= m;
4250 }
4251}
4252
4253
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004254static Object* Runtime_Math_pow(Arguments args) {
4255 NoHandleAllocation ha;
4256 ASSERT(args.length() == 2);
4257
4258 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004259
4260 // If the second argument is a smi, it is much faster to call the
4261 // custom powi() function than the generic pow().
4262 if (args[1]->IsSmi()) {
4263 int y = Smi::cast(args[1])->value();
4264 return Heap::AllocateHeapNumber(powi(x, y));
4265 }
4266
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004267 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004268
4269 if (!isinf(x)) {
4270 if (y == 0.5) {
4271 // It's not uncommon to use Math.pow(x, 0.5) to compute the
4272 // square root of a number. To speed up such computations, we
4273 // explictly check for this case and use the sqrt() function
4274 // which is faster than pow().
4275 return Heap::AllocateHeapNumber(sqrt(x));
4276 } else if (y == -0.5) {
4277 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4278 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
4279 }
4280 }
4281
4282 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004283 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004284 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4285 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004286 } else {
4287 return Heap::AllocateHeapNumber(pow(x, y));
4288 }
4289}
4290
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004291
4292static Object* Runtime_Math_round(Arguments args) {
4293 NoHandleAllocation ha;
4294 ASSERT(args.length() == 1);
4295
4296 CONVERT_DOUBLE_CHECKED(x, args[0]);
4297 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4298 return Heap::NumberFromDouble(floor(x + 0.5));
4299}
4300
4301
4302static Object* Runtime_Math_sin(Arguments args) {
4303 NoHandleAllocation ha;
4304 ASSERT(args.length() == 1);
4305
4306 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004307 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004308}
4309
4310
4311static Object* Runtime_Math_sqrt(Arguments args) {
4312 NoHandleAllocation ha;
4313 ASSERT(args.length() == 1);
4314
4315 CONVERT_DOUBLE_CHECKED(x, args[0]);
4316 return Heap::AllocateHeapNumber(sqrt(x));
4317}
4318
4319
4320static Object* Runtime_Math_tan(Arguments args) {
4321 NoHandleAllocation ha;
4322 ASSERT(args.length() == 1);
4323
4324 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004325 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004326}
4327
4328
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004329// The NewArguments function is only used when constructing the
4330// arguments array when calling non-functions from JavaScript in
4331// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004332static Object* Runtime_NewArguments(Arguments args) {
4333 NoHandleAllocation ha;
4334 ASSERT(args.length() == 1);
4335
4336 // ECMA-262, 3rd., 10.1.8, p.39
4337 CONVERT_CHECKED(JSFunction, callee, args[0]);
4338
4339 // Compute the frame holding the arguments.
4340 JavaScriptFrameIterator it;
4341 it.AdvanceToArgumentsFrame();
4342 JavaScriptFrame* frame = it.frame();
4343
4344 const int length = frame->GetProvidedParametersCount();
4345 Object* result = Heap::AllocateArgumentsObject(callee, length);
4346 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004347 if (length > 0) {
4348 Object* obj = Heap::AllocateFixedArray(length);
4349 if (obj->IsFailure()) return obj;
4350 FixedArray* array = FixedArray::cast(obj);
4351 ASSERT(array->length() == length);
4352 WriteBarrierMode mode = array->GetWriteBarrierMode();
4353 for (int i = 0; i < length; i++) {
4354 array->set(i, frame->GetParameter(i), mode);
4355 }
4356 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004357 }
4358 return result;
4359}
4360
4361
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004362static Object* Runtime_NewArgumentsFast(Arguments args) {
4363 NoHandleAllocation ha;
4364 ASSERT(args.length() == 3);
4365
4366 JSFunction* callee = JSFunction::cast(args[0]);
4367 Object** parameters = reinterpret_cast<Object**>(args[1]);
4368 const int length = Smi::cast(args[2])->value();
4369
4370 Object* result = Heap::AllocateArgumentsObject(callee, length);
4371 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004372 // Allocate the elements if needed.
4373 if (length > 0) {
4374 // Allocate the fixed array.
4375 Object* obj = Heap::AllocateRawFixedArray(length);
4376 if (obj->IsFailure()) return obj;
4377 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4378 FixedArray* array = FixedArray::cast(obj);
4379 array->set_length(length);
4380 WriteBarrierMode mode = array->GetWriteBarrierMode();
4381 for (int i = 0; i < length; i++) {
4382 array->set(i, *--parameters, mode);
4383 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004384 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004385 }
4386 return result;
4387}
4388
4389
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004390static Object* Runtime_NewClosure(Arguments args) {
4391 HandleScope scope;
4392 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00004393 CONVERT_ARG_CHECKED(Context, context, 0);
4394 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004395
4396 Handle<JSFunction> result =
4397 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4398 return *result;
4399}
4400
4401
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004402static Code* ComputeConstructStub(Handle<SharedFunctionInfo> shared) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004403 // TODO(385): Change this to create a construct stub specialized for
4404 // the given map to make allocation of simple objects - and maybe
4405 // arrays - much faster.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004406 if (FLAG_inline_new
4407 && shared->has_only_simple_this_property_assignments()) {
4408 ConstructStubCompiler compiler;
4409 Object* code = compiler.CompileConstructStub(*shared);
4410 if (code->IsFailure()) {
4411 return Builtins::builtin(Builtins::JSConstructStubGeneric);
4412 }
4413 return Code::cast(code);
4414 }
4415
4416 return Builtins::builtin(Builtins::JSConstructStubGeneric);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004417}
4418
4419
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004420static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004421 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004422 ASSERT(args.length() == 1);
4423
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004424 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004425
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004426 // If the constructor isn't a proper function we throw a type error.
4427 if (!constructor->IsJSFunction()) {
4428 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4429 Handle<Object> type_error =
4430 Factory::NewTypeError("not_constructor", arguments);
4431 return Top::Throw(*type_error);
4432 }
4433
4434 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004435#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004436 // Handle stepping into constructors if step into is active.
4437 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004438 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004439 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004440#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004441
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004442 if (function->has_initial_map()) {
4443 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004444 // The 'Function' function ignores the receiver object when
4445 // called using 'new' and creates a new JSFunction object that
4446 // is returned. The receiver object is only used for error
4447 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004448 // JSFunction. Factory::NewJSObject() should not be used to
4449 // allocate JSFunctions since it does not properly initialize
4450 // the shared part of the function. Since the receiver is
4451 // ignored anyway, we use the global object as the receiver
4452 // instead of a new JSFunction object. This way, errors are
4453 // reported the same way whether or not 'Function' is called
4454 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004455 return Top::context()->global();
4456 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004457 }
4458
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004459 // The function should be compiled for the optimization hints to be available.
4460 if (!function->shared()->is_compiled()) {
4461 CompileLazyShared(Handle<SharedFunctionInfo>(function->shared()),
4462 CLEAR_EXCEPTION,
4463 0);
4464 }
4465
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004466 bool first_allocation = !function->has_initial_map();
4467 Handle<JSObject> result = Factory::NewJSObject(function);
4468 if (first_allocation) {
4469 Handle<Map> map = Handle<Map>(function->initial_map());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004470 Handle<Code> stub = Handle<Code>(
4471 ComputeConstructStub(Handle<SharedFunctionInfo>(function->shared())));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004472 function->shared()->set_construct_stub(*stub);
4473 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004474
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004475 Counters::constructed_objects.Increment();
4476 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004477
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004478 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004479}
4480
4481
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004482static Object* Runtime_LazyCompile(Arguments args) {
4483 HandleScope scope;
4484 ASSERT(args.length() == 1);
4485
4486 Handle<JSFunction> function = args.at<JSFunction>(0);
4487#ifdef DEBUG
4488 if (FLAG_trace_lazy) {
4489 PrintF("[lazy: ");
4490 function->shared()->name()->Print();
4491 PrintF("]\n");
4492 }
4493#endif
4494
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004495 // Compile the target function. Here we compile using CompileLazyInLoop in
4496 // order to get the optimized version. This helps code like delta-blue
4497 // that calls performance-critical routines through constructors. A
4498 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4499 // direct call. Since the in-loop tracking takes place through CallICs
4500 // this means that things called through constructors are never known to
4501 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004502 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004503 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004504 return Failure::Exception();
4505 }
4506
4507 return function->code();
4508}
4509
4510
4511static Object* Runtime_GetCalledFunction(Arguments args) {
4512 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004513 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004514 StackFrameIterator it;
4515 // Get past the JS-to-C exit frame.
4516 ASSERT(it.frame()->is_exit());
4517 it.Advance();
4518 // Get past the CALL_NON_FUNCTION activation frame.
4519 ASSERT(it.frame()->is_java_script());
4520 it.Advance();
4521 // Argument adaptor frames do not copy the function; we have to skip
4522 // past them to get to the real calling frame.
4523 if (it.frame()->is_arguments_adaptor()) it.Advance();
4524 // Get the function from the top of the expression stack of the
4525 // calling frame.
4526 StandardFrame* frame = StandardFrame::cast(it.frame());
4527 int index = frame->ComputeExpressionsCount() - 1;
4528 Object* result = frame->GetExpression(index);
4529 return result;
4530}
4531
4532
4533static Object* Runtime_GetFunctionDelegate(Arguments args) {
4534 HandleScope scope;
4535 ASSERT(args.length() == 1);
4536 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4537 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4538}
4539
4540
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004541static Object* Runtime_GetConstructorDelegate(Arguments args) {
4542 HandleScope scope;
4543 ASSERT(args.length() == 1);
4544 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4545 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4546}
4547
4548
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004549static Object* Runtime_NewContext(Arguments args) {
4550 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004551 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004552
kasper.lund7276f142008-07-30 08:49:36 +00004553 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004554 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4555 Object* result = Heap::AllocateFunctionContext(length, function);
4556 if (result->IsFailure()) return result;
4557
4558 Top::set_context(Context::cast(result));
4559
kasper.lund7276f142008-07-30 08:49:36 +00004560 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004561}
4562
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004563static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004564 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004565 Object* js_object = object;
4566 if (!js_object->IsJSObject()) {
4567 js_object = js_object->ToObject();
4568 if (js_object->IsFailure()) {
4569 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004570 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004571 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004572 Handle<Object> result =
4573 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4574 return Top::Throw(*result);
4575 }
4576 }
4577
4578 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004579 Heap::AllocateWithContext(Top::context(),
4580 JSObject::cast(js_object),
4581 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004582 if (result->IsFailure()) return result;
4583
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004584 Context* context = Context::cast(result);
4585 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004586
kasper.lund7276f142008-07-30 08:49:36 +00004587 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004588}
4589
4590
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004591static Object* Runtime_PushContext(Arguments args) {
4592 NoHandleAllocation ha;
4593 ASSERT(args.length() == 1);
4594 return PushContextHelper(args[0], false);
4595}
4596
4597
4598static Object* Runtime_PushCatchContext(Arguments args) {
4599 NoHandleAllocation ha;
4600 ASSERT(args.length() == 1);
4601 return PushContextHelper(args[0], true);
4602}
4603
4604
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004605static Object* Runtime_LookupContext(Arguments args) {
4606 HandleScope scope;
4607 ASSERT(args.length() == 2);
4608
4609 CONVERT_ARG_CHECKED(Context, context, 0);
4610 CONVERT_ARG_CHECKED(String, name, 1);
4611
4612 int index;
4613 PropertyAttributes attributes;
4614 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004615 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004616 context->Lookup(name, flags, &index, &attributes);
4617
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004618 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004619 ASSERT(holder->IsJSObject());
4620 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004621 }
4622
4623 // No intermediate context found. Use global object by default.
4624 return Top::context()->global();
4625}
4626
4627
ager@chromium.orga1645e22009-09-09 19:27:10 +00004628// A mechanism to return a pair of Object pointers in registers (if possible).
4629// How this is achieved is calling convention-dependent.
4630// All currently supported x86 compiles uses calling conventions that are cdecl
4631// variants where a 64-bit value is returned in two 32-bit registers
4632// (edx:eax on ia32, r1:r0 on ARM).
4633// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
4634// In Win64 calling convention, a struct of two pointers is returned in memory,
4635// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004636#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004637struct ObjectPair {
4638 Object* x;
4639 Object* y;
4640};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004641
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004642static inline ObjectPair MakePair(Object* x, Object* y) {
4643 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004644 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
4645 // In Win64 they are assigned to a hidden first argument.
4646 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004647}
4648#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004649typedef uint64_t ObjectPair;
4650static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004651 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004652 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004653}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004654#endif
4655
4656
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004657static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004658 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4659 USE(attributes);
4660 return x->IsTheHole() ? Heap::undefined_value() : x;
4661}
4662
4663
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004664static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4665 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004666 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004667 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004668 JSFunction* context_extension_function =
4669 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004670 // If the holder isn't a context extension object, we just return it
4671 // as the receiver. This allows arguments objects to be used as
4672 // receivers, but only if they are put in the context scope chain
4673 // explicitly via a with-statement.
4674 Object* constructor = holder->map()->constructor();
4675 if (constructor != context_extension_function) return holder;
4676 // Fall back to using the global object as the receiver if the
4677 // property turns out to be a local variable allocated in a context
4678 // extension object - introduced via eval.
4679 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004680}
4681
4682
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004683static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004684 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00004685 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004686
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004687 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004688 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004689 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004690 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004691 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004692
4693 int index;
4694 PropertyAttributes attributes;
4695 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004696 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004697 context->Lookup(name, flags, &index, &attributes);
4698
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004699 // If the index is non-negative, the slot has been found in a local
4700 // variable or a parameter. Read it from the context object or the
4701 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004702 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004703 // If the "property" we were looking for is a local variable or an
4704 // argument in a context, the receiver is the global object; see
4705 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4706 JSObject* receiver = Top::context()->global()->global_receiver();
4707 Object* value = (holder->IsContext())
4708 ? Context::cast(*holder)->get(index)
4709 : JSObject::cast(*holder)->GetElement(index);
4710 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004711 }
4712
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004713 // If the holder is found, we read the property from it.
4714 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004715 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004716 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004717 JSObject* receiver;
4718 if (object->IsGlobalObject()) {
4719 receiver = GlobalObject::cast(object)->global_receiver();
4720 } else if (context->is_exception_holder(*holder)) {
4721 receiver = Top::context()->global()->global_receiver();
4722 } else {
4723 receiver = ComputeReceiverForNonGlobal(object);
4724 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004725 // No need to unhole the value here. This is taken care of by the
4726 // GetProperty function.
4727 Object* value = object->GetProperty(*name);
4728 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004729 }
4730
4731 if (throw_error) {
4732 // The property doesn't exist - throw exception.
4733 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004734 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004735 return MakePair(Top::Throw(*reference_error), NULL);
4736 } else {
4737 // The property doesn't exist - return undefined
4738 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4739 }
4740}
4741
4742
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004743static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004744 return LoadContextSlotHelper(args, true);
4745}
4746
4747
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004748static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004749 return LoadContextSlotHelper(args, false);
4750}
4751
4752
4753static Object* Runtime_StoreContextSlot(Arguments args) {
4754 HandleScope scope;
4755 ASSERT(args.length() == 3);
4756
4757 Handle<Object> value(args[0]);
4758 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004759 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004760
4761 int index;
4762 PropertyAttributes attributes;
4763 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004764 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004765 context->Lookup(name, flags, &index, &attributes);
4766
4767 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004768 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004769 // Ignore if read_only variable.
4770 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004771 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004772 }
4773 } else {
4774 ASSERT((attributes & READ_ONLY) == 0);
4775 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004776 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004777 USE(result);
4778 ASSERT(!result->IsFailure());
4779 }
4780 return *value;
4781 }
4782
4783 // Slow case: The property is not in a FixedArray context.
4784 // It is either in an JSObject extension context or it was not found.
4785 Handle<JSObject> context_ext;
4786
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004787 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004788 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004789 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004790 } else {
4791 // The property was not found. It needs to be stored in the global context.
4792 ASSERT(attributes == ABSENT);
4793 attributes = NONE;
4794 context_ext = Handle<JSObject>(Top::context()->global());
4795 }
4796
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004797 // Set the property, but ignore if read_only variable on the context
4798 // extension object itself.
4799 if ((attributes & READ_ONLY) == 0 ||
4800 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004801 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4802 if (set.is_null()) {
4803 // Failure::Exception is converted to a null handle in the
4804 // handle-based methods such as SetProperty. We therefore need
4805 // to convert null handles back to exceptions.
4806 ASSERT(Top::has_pending_exception());
4807 return Failure::Exception();
4808 }
4809 }
4810 return *value;
4811}
4812
4813
4814static Object* Runtime_Throw(Arguments args) {
4815 HandleScope scope;
4816 ASSERT(args.length() == 1);
4817
4818 return Top::Throw(args[0]);
4819}
4820
4821
4822static Object* Runtime_ReThrow(Arguments args) {
4823 HandleScope scope;
4824 ASSERT(args.length() == 1);
4825
4826 return Top::ReThrow(args[0]);
4827}
4828
4829
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004830static Object* Runtime_PromoteScheduledException(Arguments args) {
4831 ASSERT_EQ(0, args.length());
4832 return Top::PromoteScheduledException();
4833}
4834
4835
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004836static Object* Runtime_ThrowReferenceError(Arguments args) {
4837 HandleScope scope;
4838 ASSERT(args.length() == 1);
4839
4840 Handle<Object> name(args[0]);
4841 Handle<Object> reference_error =
4842 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4843 return Top::Throw(*reference_error);
4844}
4845
4846
4847static Object* Runtime_StackOverflow(Arguments args) {
4848 NoHandleAllocation na;
4849 return Top::StackOverflow();
4850}
4851
4852
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004853static Object* Runtime_StackGuard(Arguments args) {
4854 ASSERT(args.length() == 1);
4855
4856 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004857 if (StackGuard::IsStackOverflow()) {
4858 return Runtime_StackOverflow(args);
4859 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004860
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004861 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004862}
4863
4864
4865// NOTE: These PrintXXX functions are defined for all builds (not just
4866// DEBUG builds) because we may want to be able to trace function
4867// calls in all modes.
4868static void PrintString(String* str) {
4869 // not uncommon to have empty strings
4870 if (str->length() > 0) {
4871 SmartPointer<char> s =
4872 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4873 PrintF("%s", *s);
4874 }
4875}
4876
4877
4878static void PrintObject(Object* obj) {
4879 if (obj->IsSmi()) {
4880 PrintF("%d", Smi::cast(obj)->value());
4881 } else if (obj->IsString() || obj->IsSymbol()) {
4882 PrintString(String::cast(obj));
4883 } else if (obj->IsNumber()) {
4884 PrintF("%g", obj->Number());
4885 } else if (obj->IsFailure()) {
4886 PrintF("<failure>");
4887 } else if (obj->IsUndefined()) {
4888 PrintF("<undefined>");
4889 } else if (obj->IsNull()) {
4890 PrintF("<null>");
4891 } else if (obj->IsTrue()) {
4892 PrintF("<true>");
4893 } else if (obj->IsFalse()) {
4894 PrintF("<false>");
4895 } else {
4896 PrintF("%p", obj);
4897 }
4898}
4899
4900
4901static int StackSize() {
4902 int n = 0;
4903 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4904 return n;
4905}
4906
4907
4908static void PrintTransition(Object* result) {
4909 // indentation
4910 { const int nmax = 80;
4911 int n = StackSize();
4912 if (n <= nmax)
4913 PrintF("%4d:%*s", n, n, "");
4914 else
4915 PrintF("%4d:%*s", n, nmax, "...");
4916 }
4917
4918 if (result == NULL) {
4919 // constructor calls
4920 JavaScriptFrameIterator it;
4921 JavaScriptFrame* frame = it.frame();
4922 if (frame->IsConstructor()) PrintF("new ");
4923 // function name
4924 Object* fun = frame->function();
4925 if (fun->IsJSFunction()) {
4926 PrintObject(JSFunction::cast(fun)->shared()->name());
4927 } else {
4928 PrintObject(fun);
4929 }
4930 // function arguments
4931 // (we are intentionally only printing the actually
4932 // supplied parameters, not all parameters required)
4933 PrintF("(this=");
4934 PrintObject(frame->receiver());
4935 const int length = frame->GetProvidedParametersCount();
4936 for (int i = 0; i < length; i++) {
4937 PrintF(", ");
4938 PrintObject(frame->GetParameter(i));
4939 }
4940 PrintF(") {\n");
4941
4942 } else {
4943 // function result
4944 PrintF("} -> ");
4945 PrintObject(result);
4946 PrintF("\n");
4947 }
4948}
4949
4950
4951static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004952 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004953 NoHandleAllocation ha;
4954 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004955 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004956}
4957
4958
4959static Object* Runtime_TraceExit(Arguments args) {
4960 NoHandleAllocation ha;
4961 PrintTransition(args[0]);
4962 return args[0]; // return TOS
4963}
4964
4965
4966static Object* Runtime_DebugPrint(Arguments args) {
4967 NoHandleAllocation ha;
4968 ASSERT(args.length() == 1);
4969
4970#ifdef DEBUG
4971 if (args[0]->IsString()) {
4972 // If we have a string, assume it's a code "marker"
4973 // and print some interesting cpu debugging info.
4974 JavaScriptFrameIterator it;
4975 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004976 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
4977 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004978 } else {
4979 PrintF("DebugPrint: ");
4980 }
4981 args[0]->Print();
4982#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004983 // ShortPrint is available in release mode. Print is not.
4984 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004985#endif
4986 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004987 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004988
4989 return args[0]; // return TOS
4990}
4991
4992
4993static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004994 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004995 NoHandleAllocation ha;
4996 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004997 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004998}
4999
5000
mads.s.ager31e71382008-08-13 09:32:07 +00005001static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005002 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005003 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005004
5005 // According to ECMA-262, section 15.9.1, page 117, the precision of
5006 // the number in a Date object representing a particular instant in
5007 // time is milliseconds. Therefore, we floor the result of getting
5008 // the OS time.
5009 double millis = floor(OS::TimeCurrentMillis());
5010 return Heap::NumberFromDouble(millis);
5011}
5012
5013
5014static Object* Runtime_DateParseString(Arguments args) {
5015 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005016 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005017
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005018 CONVERT_ARG_CHECKED(String, str, 0);
5019 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005020
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005021 CONVERT_ARG_CHECKED(JSArray, output, 1);
5022 RUNTIME_ASSERT(output->HasFastElements());
5023
5024 AssertNoAllocation no_allocation;
5025
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005026 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005027 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
5028 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005029 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005030 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005031 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005032 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005033 result = DateParser::Parse(str->ToUC16Vector(), output_array);
5034 }
5035
5036 if (result) {
5037 return *output;
5038 } else {
5039 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005040 }
5041}
5042
5043
5044static Object* Runtime_DateLocalTimezone(Arguments args) {
5045 NoHandleAllocation ha;
5046 ASSERT(args.length() == 1);
5047
5048 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00005049 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005050 return Heap::AllocateStringFromUtf8(CStrVector(zone));
5051}
5052
5053
5054static Object* Runtime_DateLocalTimeOffset(Arguments args) {
5055 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005056 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005057
5058 return Heap::NumberFromDouble(OS::LocalTimeOffset());
5059}
5060
5061
5062static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
5063 NoHandleAllocation ha;
5064 ASSERT(args.length() == 1);
5065
5066 CONVERT_DOUBLE_CHECKED(x, args[0]);
5067 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
5068}
5069
5070
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005071static Object* Runtime_NumberIsFinite(Arguments args) {
5072 NoHandleAllocation ha;
5073 ASSERT(args.length() == 1);
5074
5075 CONVERT_DOUBLE_CHECKED(value, args[0]);
5076 Object* result;
5077 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
5078 result = Heap::false_value();
5079 } else {
5080 result = Heap::true_value();
5081 }
5082 return result;
5083}
5084
5085
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005086static Object* Runtime_GlobalReceiver(Arguments args) {
5087 ASSERT(args.length() == 1);
5088 Object* global = args[0];
5089 if (!global->IsJSGlobalObject()) return Heap::null_value();
5090 return JSGlobalObject::cast(global)->global_receiver();
5091}
5092
5093
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005094static Object* Runtime_CompileString(Arguments args) {
5095 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005096 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005097 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005098 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005099
ager@chromium.org381abbb2009-02-25 13:23:22 +00005100 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005101 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005102 Compiler::ValidationState validate = (is_json->IsTrue())
5103 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005104 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
5105 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005106 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005107 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005108 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005109 Handle<JSFunction> fun =
5110 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5111 return *fun;
5112}
5113
5114
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005115static Handle<JSFunction> GetBuiltinFunction(String* name) {
5116 LookupResult result;
5117 Top::global_context()->builtins()->LocalLookup(name, &result);
5118 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
5119}
5120
5121
5122static Object* CompileDirectEval(Handle<String> source) {
5123 // Compute the eval context.
5124 HandleScope scope;
5125 StackFrameLocator locator;
5126 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5127 Handle<Context> context(Context::cast(frame->context()));
5128 bool is_global = context->IsGlobalContext();
5129
ager@chromium.org381abbb2009-02-25 13:23:22 +00005130 // Compile source string in the current context.
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005131 Handle<JSFunction> boilerplate = Compiler::CompileEval(
5132 source,
5133 context,
5134 is_global,
5135 Compiler::DONT_VALIDATE_JSON);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005136 if (boilerplate.is_null()) return Failure::Exception();
5137 Handle<JSFunction> fun =
5138 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5139 return *fun;
5140}
5141
5142
5143static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
5144 ASSERT(args.length() == 2);
5145
5146 HandleScope scope;
5147
5148 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5149
5150 Handle<Object> receiver;
5151
5152 // Find where the 'eval' symbol is bound. It is unaliased only if
5153 // it is bound in the global context.
5154 StackFrameLocator locator;
5155 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5156 Handle<Context> context(Context::cast(frame->context()));
5157 int index;
5158 PropertyAttributes attributes;
5159 while (!context.is_null()) {
5160 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5161 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005162 // Stop search when eval is found or when the global context is
5163 // reached.
5164 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005165 if (context->is_function_context()) {
5166 context = Handle<Context>(Context::cast(context->closure()->context()));
5167 } else {
5168 context = Handle<Context>(context->previous());
5169 }
5170 }
5171
iposva@chromium.org245aa852009-02-10 00:49:54 +00005172 // If eval could not be resolved, it has been deleted and we need to
5173 // throw a reference error.
5174 if (attributes == ABSENT) {
5175 Handle<Object> name = Factory::eval_symbol();
5176 Handle<Object> reference_error =
5177 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5178 return Top::Throw(*reference_error);
5179 }
5180
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005181 if (context->IsGlobalContext()) {
5182 // 'eval' is bound in the global context, but it may have been overwritten.
5183 // Compare it to the builtin 'GlobalEval' function to make sure.
5184 Handle<JSFunction> global_eval =
5185 GetBuiltinFunction(Heap::global_eval_symbol());
5186 if (global_eval.is_identical_to(callee)) {
5187 // A direct eval call.
5188 if (args[1]->IsString()) {
5189 CONVERT_ARG_CHECKED(String, source, 1);
5190 // A normal eval call on a string. Compile it and return the
5191 // compiled function bound in the local context.
5192 Object* compiled_source = CompileDirectEval(source);
5193 if (compiled_source->IsFailure()) return compiled_source;
5194 receiver = Handle<Object>(frame->receiver());
5195 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5196 } else {
5197 // An eval call that is not called on a string. Global eval
5198 // deals better with this.
5199 receiver = Handle<Object>(Top::global_context()->global());
5200 }
5201 } else {
5202 // 'eval' is overwritten. Just call the function with the given arguments.
5203 receiver = Handle<Object>(Top::global_context()->global());
5204 }
5205 } else {
5206 // 'eval' is not bound in the global context. Just call the function
5207 // with the given arguments. This is not necessarily the global eval.
5208 if (receiver->IsContext()) {
5209 context = Handle<Context>::cast(receiver);
5210 receiver = Handle<Object>(context->get(index));
5211 }
5212 }
5213
5214 Handle<FixedArray> call = Factory::NewFixedArray(2);
5215 call->set(0, *callee);
5216 call->set(1, *receiver);
5217 return *call;
5218}
5219
5220
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005221static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5222 // This utility adjusts the property attributes for newly created Function
5223 // object ("new Function(...)") by changing the map.
5224 // All it does is changing the prototype property to enumerable
5225 // as specified in ECMA262, 15.3.5.2.
5226 HandleScope scope;
5227 ASSERT(args.length() == 1);
5228 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5229 ASSERT(func->map()->instance_type() ==
5230 Top::function_instance_map()->instance_type());
5231 ASSERT(func->map()->instance_size() ==
5232 Top::function_instance_map()->instance_size());
5233 func->set_map(*Top::function_instance_map());
5234 return *func;
5235}
5236
5237
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005238// Push an array unto an array of arrays if it is not already in the
5239// array. Returns true if the element was pushed on the stack and
5240// false otherwise.
5241static Object* Runtime_PushIfAbsent(Arguments args) {
5242 ASSERT(args.length() == 2);
5243 CONVERT_CHECKED(JSArray, array, args[0]);
5244 CONVERT_CHECKED(JSArray, element, args[1]);
5245 RUNTIME_ASSERT(array->HasFastElements());
5246 int length = Smi::cast(array->length())->value();
5247 FixedArray* elements = FixedArray::cast(array->elements());
5248 for (int i = 0; i < length; i++) {
5249 if (elements->get(i) == element) return Heap::false_value();
5250 }
5251 Object* obj = array->SetFastElement(length, element);
5252 if (obj->IsFailure()) return obj;
5253 return Heap::true_value();
5254}
5255
5256
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005257/**
5258 * A simple visitor visits every element of Array's.
5259 * The backend storage can be a fixed array for fast elements case,
5260 * or a dictionary for sparse array. Since Dictionary is a subtype
5261 * of FixedArray, the class can be used by both fast and slow cases.
5262 * The second parameter of the constructor, fast_elements, specifies
5263 * whether the storage is a FixedArray or Dictionary.
5264 *
5265 * An index limit is used to deal with the situation that a result array
5266 * length overflows 32-bit non-negative integer.
5267 */
5268class ArrayConcatVisitor {
5269 public:
5270 ArrayConcatVisitor(Handle<FixedArray> storage,
5271 uint32_t index_limit,
5272 bool fast_elements) :
5273 storage_(storage), index_limit_(index_limit),
5274 fast_elements_(fast_elements), index_offset_(0) { }
5275
5276 void visit(uint32_t i, Handle<Object> elm) {
5277 uint32_t index = i + index_offset_;
5278 if (index >= index_limit_) return;
5279
5280 if (fast_elements_) {
5281 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5282 storage_->set(index, *elm);
5283
5284 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005285 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5286 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005287 Factory::DictionaryAtNumberPut(dict, index, elm);
5288 if (!result.is_identical_to(dict))
5289 storage_ = result;
5290 }
5291 }
5292
5293 void increase_index_offset(uint32_t delta) {
5294 index_offset_ += delta;
5295 }
5296
5297 private:
5298 Handle<FixedArray> storage_;
5299 uint32_t index_limit_;
5300 bool fast_elements_;
5301 uint32_t index_offset_;
5302};
5303
5304
ager@chromium.org3811b432009-10-28 14:53:37 +00005305template<class ExternalArrayClass, class ElementType>
5306static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
5307 bool elements_are_ints,
5308 bool elements_are_guaranteed_smis,
5309 uint32_t range,
5310 ArrayConcatVisitor* visitor) {
5311 Handle<ExternalArrayClass> array(
5312 ExternalArrayClass::cast(receiver->elements()));
5313 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
5314
5315 if (visitor != NULL) {
5316 if (elements_are_ints) {
5317 if (elements_are_guaranteed_smis) {
5318 for (uint32_t j = 0; j < len; j++) {
5319 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
5320 visitor->visit(j, e);
5321 }
5322 } else {
5323 for (uint32_t j = 0; j < len; j++) {
5324 int64_t val = static_cast<int64_t>(array->get(j));
5325 if (Smi::IsValid(static_cast<intptr_t>(val))) {
5326 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
5327 visitor->visit(j, e);
5328 } else {
5329 Handle<Object> e(
5330 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
5331 visitor->visit(j, e);
5332 }
5333 }
5334 }
5335 } else {
5336 for (uint32_t j = 0; j < len; j++) {
5337 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
5338 visitor->visit(j, e);
5339 }
5340 }
5341 }
5342
5343 return len;
5344}
5345
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005346/**
5347 * A helper function that visits elements of a JSObject. Only elements
5348 * whose index between 0 and range (exclusive) are visited.
5349 *
5350 * If the third parameter, visitor, is not NULL, the visitor is called
5351 * with parameters, 'visitor_index_offset + element index' and the element.
5352 *
5353 * It returns the number of visisted elements.
5354 */
5355static uint32_t IterateElements(Handle<JSObject> receiver,
5356 uint32_t range,
5357 ArrayConcatVisitor* visitor) {
5358 uint32_t num_of_elements = 0;
5359
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005360 switch (receiver->GetElementsKind()) {
5361 case JSObject::FAST_ELEMENTS: {
5362 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5363 uint32_t len = elements->length();
5364 if (range < len) {
5365 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005366 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005367
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005368 for (uint32_t j = 0; j < len; j++) {
5369 Handle<Object> e(elements->get(j));
5370 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005371 num_of_elements++;
5372 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005373 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005374 }
5375 }
5376 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005377 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005378 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005379 case JSObject::PIXEL_ELEMENTS: {
5380 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
5381 uint32_t len = pixels->length();
5382 if (range < len) {
5383 len = range;
5384 }
5385
5386 for (uint32_t j = 0; j < len; j++) {
5387 num_of_elements++;
5388 if (visitor != NULL) {
5389 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
5390 visitor->visit(j, e);
5391 }
5392 }
5393 break;
5394 }
ager@chromium.org3811b432009-10-28 14:53:37 +00005395 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
5396 num_of_elements =
5397 IterateExternalArrayElements<ExternalByteArray, int8_t>(
5398 receiver, true, true, range, visitor);
5399 break;
5400 }
5401 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
5402 num_of_elements =
5403 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
5404 receiver, true, true, range, visitor);
5405 break;
5406 }
5407 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
5408 num_of_elements =
5409 IterateExternalArrayElements<ExternalShortArray, int16_t>(
5410 receiver, true, true, range, visitor);
5411 break;
5412 }
5413 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
5414 num_of_elements =
5415 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
5416 receiver, true, true, range, visitor);
5417 break;
5418 }
5419 case JSObject::EXTERNAL_INT_ELEMENTS: {
5420 num_of_elements =
5421 IterateExternalArrayElements<ExternalIntArray, int32_t>(
5422 receiver, true, false, range, visitor);
5423 break;
5424 }
5425 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
5426 num_of_elements =
5427 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
5428 receiver, true, false, range, visitor);
5429 break;
5430 }
5431 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
5432 num_of_elements =
5433 IterateExternalArrayElements<ExternalFloatArray, float>(
5434 receiver, false, false, range, visitor);
5435 break;
5436 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005437 case JSObject::DICTIONARY_ELEMENTS: {
5438 Handle<NumberDictionary> dict(receiver->element_dictionary());
5439 uint32_t capacity = dict->Capacity();
5440 for (uint32_t j = 0; j < capacity; j++) {
5441 Handle<Object> k(dict->KeyAt(j));
5442 if (dict->IsKey(*k)) {
5443 ASSERT(k->IsNumber());
5444 uint32_t index = static_cast<uint32_t>(k->Number());
5445 if (index < range) {
5446 num_of_elements++;
5447 if (visitor) {
5448 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
5449 }
5450 }
5451 }
5452 }
5453 break;
5454 }
5455 default:
5456 UNREACHABLE();
5457 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005458 }
5459
5460 return num_of_elements;
5461}
5462
5463
5464/**
5465 * A helper function that visits elements of an Array object, and elements
5466 * on its prototypes.
5467 *
5468 * Elements on prototypes are visited first, and only elements whose indices
5469 * less than Array length are visited.
5470 *
5471 * If a ArrayConcatVisitor object is given, the visitor is called with
5472 * parameters, element's index + visitor_index_offset and the element.
5473 */
5474static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5475 ArrayConcatVisitor* visitor) {
5476 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5477 Handle<Object> obj = array;
5478
5479 static const int kEstimatedPrototypes = 3;
5480 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5481
5482 // Visit prototype first. If an element on the prototype is shadowed by
5483 // the inheritor using the same index, the ArrayConcatVisitor visits
5484 // the prototype element before the shadowing element.
5485 // The visitor can simply overwrite the old value by new value using
5486 // the same index. This follows Array::concat semantics.
5487 while (!obj->IsNull()) {
5488 objects.Add(Handle<JSObject>::cast(obj));
5489 obj = Handle<Object>(obj->GetPrototype());
5490 }
5491
5492 uint32_t nof_elements = 0;
5493 for (int i = objects.length() - 1; i >= 0; i--) {
5494 Handle<JSObject> obj = objects[i];
5495 nof_elements +=
5496 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5497 }
5498
5499 return nof_elements;
5500}
5501
5502
5503/**
5504 * A helper function of Runtime_ArrayConcat.
5505 *
5506 * The first argument is an Array of arrays and objects. It is the
5507 * same as the arguments array of Array::concat JS function.
5508 *
5509 * If an argument is an Array object, the function visits array
5510 * elements. If an argument is not an Array object, the function
5511 * visits the object as if it is an one-element array.
5512 *
5513 * If the result array index overflows 32-bit integer, the rounded
5514 * non-negative number is used as new length. For example, if one
5515 * array length is 2^32 - 1, second array length is 1, the
5516 * concatenated array length is 0.
5517 */
5518static uint32_t IterateArguments(Handle<JSArray> arguments,
5519 ArrayConcatVisitor* visitor) {
5520 uint32_t visited_elements = 0;
5521 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5522
5523 for (uint32_t i = 0; i < num_of_args; i++) {
5524 Handle<Object> obj(arguments->GetElement(i));
5525 if (obj->IsJSArray()) {
5526 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5527 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5528 uint32_t nof_elements =
5529 IterateArrayAndPrototypeElements(array, visitor);
5530 // Total elements of array and its prototype chain can be more than
5531 // the array length, but ArrayConcat can only concatenate at most
5532 // the array length number of elements.
5533 visited_elements += (nof_elements > len) ? len : nof_elements;
5534 if (visitor) visitor->increase_index_offset(len);
5535
5536 } else {
5537 if (visitor) {
5538 visitor->visit(0, obj);
5539 visitor->increase_index_offset(1);
5540 }
5541 visited_elements++;
5542 }
5543 }
5544 return visited_elements;
5545}
5546
5547
5548/**
5549 * Array::concat implementation.
5550 * See ECMAScript 262, 15.4.4.4.
5551 */
5552static Object* Runtime_ArrayConcat(Arguments args) {
5553 ASSERT(args.length() == 1);
5554 HandleScope handle_scope;
5555
5556 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5557 Handle<JSArray> arguments(arg_arrays);
5558
5559 // Pass 1: estimate the number of elements of the result
5560 // (it could be more than real numbers if prototype has elements).
5561 uint32_t result_length = 0;
5562 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5563
5564 { AssertNoAllocation nogc;
5565 for (uint32_t i = 0; i < num_of_args; i++) {
5566 Object* obj = arguments->GetElement(i);
5567 if (obj->IsJSArray()) {
5568 result_length +=
5569 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5570 } else {
5571 result_length++;
5572 }
5573 }
5574 }
5575
5576 // Allocate an empty array, will set length and content later.
5577 Handle<JSArray> result = Factory::NewJSArray(0);
5578
5579 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5580 // If estimated number of elements is more than half of length, a
5581 // fixed array (fast case) is more time and space-efficient than a
5582 // dictionary.
5583 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5584
5585 Handle<FixedArray> storage;
5586 if (fast_case) {
5587 // The backing storage array must have non-existing elements to
5588 // preserve holes across concat operations.
5589 storage = Factory::NewFixedArrayWithHoles(result_length);
5590
5591 } else {
5592 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5593 uint32_t at_least_space_for = estimate_nof_elements +
5594 (estimate_nof_elements >> 2);
5595 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005596 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005597 }
5598
5599 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5600
5601 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5602
5603 IterateArguments(arguments, &visitor);
5604
5605 result->set_length(*len);
5606 result->set_elements(*storage);
5607
5608 return *result;
5609}
5610
5611
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005612// This will not allocate (flatten the string), but it may run
5613// very slowly for very deeply nested ConsStrings. For debugging use only.
5614static Object* Runtime_GlobalPrint(Arguments args) {
5615 NoHandleAllocation ha;
5616 ASSERT(args.length() == 1);
5617
5618 CONVERT_CHECKED(String, string, args[0]);
5619 StringInputBuffer buffer(string);
5620 while (buffer.has_more()) {
5621 uint16_t character = buffer.GetNext();
5622 PrintF("%c", character);
5623 }
5624 return string;
5625}
5626
ager@chromium.org5ec48922009-05-05 07:25:34 +00005627// Moves all own elements of an object, that are below a limit, to positions
5628// starting at zero. All undefined values are placed after non-undefined values,
5629// and are followed by non-existing element. Does not change the length
5630// property.
5631// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005632static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005633 ASSERT(args.length() == 2);
5634 CONVERT_CHECKED(JSObject, object, args[0]);
5635 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5636 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005637}
5638
5639
5640// Move contents of argument 0 (an array) to argument 1 (an array)
5641static Object* Runtime_MoveArrayContents(Arguments args) {
5642 ASSERT(args.length() == 2);
5643 CONVERT_CHECKED(JSArray, from, args[0]);
5644 CONVERT_CHECKED(JSArray, to, args[1]);
5645 to->SetContent(FixedArray::cast(from->elements()));
5646 to->set_length(from->length());
5647 from->SetContent(Heap::empty_fixed_array());
5648 from->set_length(0);
5649 return to;
5650}
5651
5652
5653// How many elements does this array have?
5654static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5655 ASSERT(args.length() == 1);
5656 CONVERT_CHECKED(JSArray, array, args[0]);
5657 HeapObject* elements = array->elements();
5658 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005659 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005660 } else {
5661 return array->length();
5662 }
5663}
5664
5665
5666// Returns an array that tells you where in the [0, length) interval an array
5667// might have elements. Can either return keys or intervals. Keys can have
5668// gaps in (undefined). Intervals can also span over some undefined keys.
5669static Object* Runtime_GetArrayKeys(Arguments args) {
5670 ASSERT(args.length() == 2);
5671 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005672 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005673 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005674 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005675 // Create an array and get all the keys into it, then remove all the
5676 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005677 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005678 int keys_length = keys->length();
5679 for (int i = 0; i < keys_length; i++) {
5680 Object* key = keys->get(i);
5681 uint32_t index;
5682 if (!Array::IndexFromObject(key, &index) || index >= length) {
5683 // Zap invalid keys.
5684 keys->set_undefined(i);
5685 }
5686 }
5687 return *Factory::NewJSArrayWithElements(keys);
5688 } else {
5689 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5690 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005691 single_interval->set(0,
5692 Smi::FromInt(-1),
5693 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005694 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5695 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005696 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005697 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005698 single_interval->set(1, *length_object);
5699 return *Factory::NewJSArrayWithElements(single_interval);
5700 }
5701}
5702
5703
5704// DefineAccessor takes an optional final argument which is the
5705// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5706// to the way accessors are implemented, it is set for both the getter
5707// and setter on the first call to DefineAccessor and ignored on
5708// subsequent calls.
5709static Object* Runtime_DefineAccessor(Arguments args) {
5710 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5711 // Compute attributes.
5712 PropertyAttributes attributes = NONE;
5713 if (args.length() == 5) {
5714 CONVERT_CHECKED(Smi, attrs, args[4]);
5715 int value = attrs->value();
5716 // Only attribute bits should be set.
5717 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5718 attributes = static_cast<PropertyAttributes>(value);
5719 }
5720
5721 CONVERT_CHECKED(JSObject, obj, args[0]);
5722 CONVERT_CHECKED(String, name, args[1]);
5723 CONVERT_CHECKED(Smi, flag, args[2]);
5724 CONVERT_CHECKED(JSFunction, fun, args[3]);
5725 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5726}
5727
5728
5729static Object* Runtime_LookupAccessor(Arguments args) {
5730 ASSERT(args.length() == 3);
5731 CONVERT_CHECKED(JSObject, obj, args[0]);
5732 CONVERT_CHECKED(String, name, args[1]);
5733 CONVERT_CHECKED(Smi, flag, args[2]);
5734 return obj->LookupAccessor(name, flag->value() == 0);
5735}
5736
5737
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005738#ifdef ENABLE_DEBUGGER_SUPPORT
5739static Object* Runtime_DebugBreak(Arguments args) {
5740 ASSERT(args.length() == 0);
5741 return Execution::DebugBreakHelper();
5742}
5743
5744
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005745// Helper functions for wrapping and unwrapping stack frame ids.
5746static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005747 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005748 return Smi::FromInt(id >> 2);
5749}
5750
5751
5752static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5753 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5754}
5755
5756
5757// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005758// args[0]: debug event listener function to set or null or undefined for
5759// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005760// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005761static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005762 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005763 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5764 args[0]->IsUndefined() ||
5765 args[0]->IsNull());
5766 Handle<Object> callback = args.at<Object>(0);
5767 Handle<Object> data = args.at<Object>(1);
5768 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005769
5770 return Heap::undefined_value();
5771}
5772
5773
5774static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005775 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005776 StackGuard::DebugBreak();
5777 return Heap::undefined_value();
5778}
5779
5780
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005781// Find the length of the prototype chain that is to to handled as one. If a
5782// prototype object is hidden it is to be viewed as part of the the object it
5783// is prototype for.
5784static int LocalPrototypeChainLength(JSObject* obj) {
5785 int count = 1;
5786 Object* proto = obj->GetPrototype();
5787 while (proto->IsJSObject() &&
5788 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5789 count++;
5790 proto = JSObject::cast(proto)->GetPrototype();
5791 }
5792 return count;
5793}
5794
5795
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005796static Object* DebugLookupResultValue(Object* receiver, String* name,
5797 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005798 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005799 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005800 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005801 case NORMAL:
5802 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005803 if (value->IsTheHole()) {
5804 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005805 }
5806 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005807 case FIELD:
5808 value =
5809 JSObject::cast(
5810 result->holder())->FastPropertyAt(result->GetFieldIndex());
5811 if (value->IsTheHole()) {
5812 return Heap::undefined_value();
5813 }
5814 return value;
5815 case CONSTANT_FUNCTION:
5816 return result->GetConstantFunction();
5817 case CALLBACKS: {
5818 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005819 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005820 value = receiver->GetPropertyWithCallback(
5821 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005822 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005823 value = Top::pending_exception();
5824 Top::clear_pending_exception();
5825 if (caught_exception != NULL) {
5826 *caught_exception = true;
5827 }
5828 }
5829 return value;
5830 } else {
5831 return Heap::undefined_value();
5832 }
5833 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005834 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005835 case MAP_TRANSITION:
5836 case CONSTANT_TRANSITION:
5837 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005838 return Heap::undefined_value();
5839 default:
5840 UNREACHABLE();
5841 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005842 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005843 return Heap::undefined_value();
5844}
5845
5846
ager@chromium.org32912102009-01-16 10:38:43 +00005847// Get debugger related details for an object property.
5848// args[0]: object holding property
5849// args[1]: name of the property
5850//
5851// The array returned contains the following information:
5852// 0: Property value
5853// 1: Property details
5854// 2: Property value is exception
5855// 3: Getter function if defined
5856// 4: Setter function if defined
5857// Items 2-4 are only filled if the property has either a getter or a setter
5858// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005859static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005860 HandleScope scope;
5861
5862 ASSERT(args.length() == 2);
5863
5864 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5865 CONVERT_ARG_CHECKED(String, name, 1);
5866
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005867 // Make sure to set the current context to the context before the debugger was
5868 // entered (if the debugger is entered). The reason for switching context here
5869 // is that for some property lookups (accessors and interceptors) callbacks
5870 // into the embedding application can occour, and the embedding application
5871 // could have the assumption that its own global context is the current
5872 // context and not some internal debugger context.
5873 SaveContext save;
5874 if (Debug::InDebugger()) {
5875 Top::set_context(*Debug::debugger_entry()->GetContext());
5876 }
5877
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005878 // Skip the global proxy as it has no properties and always delegates to the
5879 // real global object.
5880 if (obj->IsJSGlobalProxy()) {
5881 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5882 }
5883
5884
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005885 // Check if the name is trivially convertible to an index and get the element
5886 // if so.
5887 uint32_t index;
5888 if (name->AsArrayIndex(&index)) {
5889 Handle<FixedArray> details = Factory::NewFixedArray(2);
5890 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5891 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5892 return *Factory::NewJSArrayWithElements(details);
5893 }
5894
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005895 // Find the number of objects making up this.
5896 int length = LocalPrototypeChainLength(*obj);
5897
5898 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005899 Handle<JSObject> jsproto = obj;
5900 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005901 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005902 jsproto->LocalLookup(*name, &result);
5903 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005904 // LookupResult is not GC safe as it holds raw object pointers.
5905 // GC can happen later in this code so put the required fields into
5906 // local variables using handles when required for later use.
5907 PropertyType result_type = result.type();
5908 Handle<Object> result_callback_obj;
5909 if (result_type == CALLBACKS) {
5910 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5911 }
5912 Smi* property_details = result.GetPropertyDetails().AsSmi();
5913 // DebugLookupResultValue can cause GC so details from LookupResult needs
5914 // to be copied to handles before this.
5915 bool caught_exception = false;
5916 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
5917 &caught_exception);
5918 if (raw_value->IsFailure()) return raw_value;
5919 Handle<Object> value(raw_value);
5920
5921 // If the callback object is a fixed array then it contains JavaScript
5922 // getter and/or setter.
5923 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5924 result_callback_obj->IsFixedArray();
5925 Handle<FixedArray> details =
5926 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
5927 details->set(0, *value);
5928 details->set(1, property_details);
5929 if (hasJavaScriptAccessors) {
5930 details->set(2,
5931 caught_exception ? Heap::true_value()
5932 : Heap::false_value());
5933 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
5934 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
5935 }
5936
5937 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005938 }
5939 if (i < length - 1) {
5940 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5941 }
5942 }
5943
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005944 return Heap::undefined_value();
5945}
5946
5947
5948static Object* Runtime_DebugGetProperty(Arguments args) {
5949 HandleScope scope;
5950
5951 ASSERT(args.length() == 2);
5952
5953 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5954 CONVERT_ARG_CHECKED(String, name, 1);
5955
5956 LookupResult result;
5957 obj->Lookup(*name, &result);
5958 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005959 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005960 }
5961 return Heap::undefined_value();
5962}
5963
5964
5965// Return the names of the local named properties.
5966// args[0]: object
5967static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5968 HandleScope scope;
5969 ASSERT(args.length() == 1);
5970 if (!args[0]->IsJSObject()) {
5971 return Heap::undefined_value();
5972 }
5973 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5974
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005975 // Skip the global proxy as it has no properties and always delegates to the
5976 // real global object.
5977 if (obj->IsJSGlobalProxy()) {
5978 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5979 }
5980
5981 // Find the number of objects making up this.
5982 int length = LocalPrototypeChainLength(*obj);
5983
5984 // Find the number of local properties for each of the objects.
5985 int* local_property_count = NewArray<int>(length);
5986 int total_property_count = 0;
5987 Handle<JSObject> jsproto = obj;
5988 for (int i = 0; i < length; i++) {
5989 int n;
5990 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5991 local_property_count[i] = n;
5992 total_property_count += n;
5993 if (i < length - 1) {
5994 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5995 }
5996 }
5997
5998 // Allocate an array with storage for all the property names.
5999 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
6000
6001 // Get the property names.
6002 jsproto = obj;
ager@chromium.orgc730f772009-11-11 10:11:16 +00006003 int proto_with_hidden_properties = 0;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006004 for (int i = 0; i < length; i++) {
6005 jsproto->GetLocalPropertyNames(*names,
6006 i == 0 ? 0 : local_property_count[i - 1]);
ager@chromium.orgc730f772009-11-11 10:11:16 +00006007 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
6008 proto_with_hidden_properties++;
6009 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006010 if (i < length - 1) {
6011 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6012 }
6013 }
6014
ager@chromium.orgc730f772009-11-11 10:11:16 +00006015 // Filter out name of hidden propeties object.
6016 if (proto_with_hidden_properties > 0) {
6017 Handle<FixedArray> old_names = names;
6018 names = Factory::NewFixedArray(
6019 names->length() - proto_with_hidden_properties);
6020 int dest_pos = 0;
6021 for (int i = 0; i < total_property_count; i++) {
6022 Object* name = old_names->get(i);
6023 if (name == Heap::hidden_symbol()) {
6024 continue;
6025 }
6026 names->set(dest_pos++, name);
6027 }
6028 }
6029
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006030 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006031 return *Factory::NewJSArrayWithElements(names);
6032}
6033
6034
6035// Return the names of the local indexed properties.
6036// args[0]: object
6037static Object* Runtime_DebugLocalElementNames(Arguments args) {
6038 HandleScope scope;
6039 ASSERT(args.length() == 1);
6040 if (!args[0]->IsJSObject()) {
6041 return Heap::undefined_value();
6042 }
6043 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6044
6045 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
6046 Handle<FixedArray> names = Factory::NewFixedArray(n);
6047 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
6048 return *Factory::NewJSArrayWithElements(names);
6049}
6050
6051
6052// Return the property type calculated from the property details.
6053// args[0]: smi with property details.
6054static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
6055 ASSERT(args.length() == 1);
6056 CONVERT_CHECKED(Smi, details, args[0]);
6057 PropertyType type = PropertyDetails(details).type();
6058 return Smi::FromInt(static_cast<int>(type));
6059}
6060
6061
6062// Return the property attribute calculated from the property details.
6063// args[0]: smi with property details.
6064static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
6065 ASSERT(args.length() == 1);
6066 CONVERT_CHECKED(Smi, details, args[0]);
6067 PropertyAttributes attributes = PropertyDetails(details).attributes();
6068 return Smi::FromInt(static_cast<int>(attributes));
6069}
6070
6071
6072// Return the property insertion index calculated from the property details.
6073// args[0]: smi with property details.
6074static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
6075 ASSERT(args.length() == 1);
6076 CONVERT_CHECKED(Smi, details, args[0]);
6077 int index = PropertyDetails(details).index();
6078 return Smi::FromInt(index);
6079}
6080
6081
6082// Return information on whether an object has a named or indexed interceptor.
6083// args[0]: object
6084static Object* Runtime_DebugInterceptorInfo(Arguments args) {
6085 HandleScope scope;
6086 ASSERT(args.length() == 1);
6087 if (!args[0]->IsJSObject()) {
6088 return Smi::FromInt(0);
6089 }
6090 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6091
6092 int result = 0;
6093 if (obj->HasNamedInterceptor()) result |= 2;
6094 if (obj->HasIndexedInterceptor()) result |= 1;
6095
6096 return Smi::FromInt(result);
6097}
6098
6099
6100// Return property names from named interceptor.
6101// args[0]: object
6102static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
6103 HandleScope scope;
6104 ASSERT(args.length() == 1);
6105 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006106
ager@chromium.org32912102009-01-16 10:38:43 +00006107 if (obj->HasNamedInterceptor()) {
6108 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
6109 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6110 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006111 return Heap::undefined_value();
6112}
6113
6114
6115// Return element names from indexed interceptor.
6116// args[0]: object
6117static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
6118 HandleScope scope;
6119 ASSERT(args.length() == 1);
6120 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006121
ager@chromium.org32912102009-01-16 10:38:43 +00006122 if (obj->HasIndexedInterceptor()) {
6123 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
6124 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6125 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006126 return Heap::undefined_value();
6127}
6128
6129
6130// Return property value from named interceptor.
6131// args[0]: object
6132// args[1]: property name
6133static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
6134 HandleScope scope;
6135 ASSERT(args.length() == 2);
6136 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6137 RUNTIME_ASSERT(obj->HasNamedInterceptor());
6138 CONVERT_ARG_CHECKED(String, name, 1);
6139
6140 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006141 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006142}
6143
6144
6145// Return element value from indexed interceptor.
6146// args[0]: object
6147// args[1]: index
6148static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
6149 HandleScope scope;
6150 ASSERT(args.length() == 2);
6151 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6152 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
6153 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
6154
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006155 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006156}
6157
6158
6159static Object* Runtime_CheckExecutionState(Arguments args) {
6160 ASSERT(args.length() >= 1);
6161 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00006162 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006163 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006164 return Top::Throw(Heap::illegal_execution_state_symbol());
6165 }
6166
6167 return Heap::true_value();
6168}
6169
6170
6171static Object* Runtime_GetFrameCount(Arguments args) {
6172 HandleScope scope;
6173 ASSERT(args.length() == 1);
6174
6175 // Check arguments.
6176 Object* result = Runtime_CheckExecutionState(args);
6177 if (result->IsFailure()) return result;
6178
6179 // Count all frames which are relevant to debugging stack trace.
6180 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006181 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006182 if (id == StackFrame::NO_ID) {
6183 // If there is no JavaScript stack frame count is 0.
6184 return Smi::FromInt(0);
6185 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006186 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
6187 return Smi::FromInt(n);
6188}
6189
6190
6191static const int kFrameDetailsFrameIdIndex = 0;
6192static const int kFrameDetailsReceiverIndex = 1;
6193static const int kFrameDetailsFunctionIndex = 2;
6194static const int kFrameDetailsArgumentCountIndex = 3;
6195static const int kFrameDetailsLocalCountIndex = 4;
6196static const int kFrameDetailsSourcePositionIndex = 5;
6197static const int kFrameDetailsConstructCallIndex = 6;
6198static const int kFrameDetailsDebuggerFrameIndex = 7;
6199static const int kFrameDetailsFirstDynamicIndex = 8;
6200
6201// Return an array with frame details
6202// args[0]: number: break id
6203// args[1]: number: frame index
6204//
6205// The array returned contains the following information:
6206// 0: Frame id
6207// 1: Receiver
6208// 2: Function
6209// 3: Argument count
6210// 4: Local count
6211// 5: Source position
6212// 6: Constructor call
6213// 7: Debugger frame
6214// Arguments name, value
6215// Locals name, value
6216static Object* Runtime_GetFrameDetails(Arguments args) {
6217 HandleScope scope;
6218 ASSERT(args.length() == 2);
6219
6220 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006221 Object* check = Runtime_CheckExecutionState(args);
6222 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006223 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6224
6225 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006226 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006227 if (id == StackFrame::NO_ID) {
6228 // If there are no JavaScript stack frames return undefined.
6229 return Heap::undefined_value();
6230 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006231 int count = 0;
6232 JavaScriptFrameIterator it(id);
6233 for (; !it.done(); it.Advance()) {
6234 if (count == index) break;
6235 count++;
6236 }
6237 if (it.done()) return Heap::undefined_value();
6238
6239 // Traverse the saved contexts chain to find the active context for the
6240 // selected frame.
6241 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006242 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006243 save = save->prev();
6244 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006245 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006246
6247 // Get the frame id.
6248 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6249
6250 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006251 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006252
6253 // Check for constructor frame.
6254 bool constructor = it.frame()->IsConstructor();
6255
6256 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006257 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006258 ScopeInfo<> info(*code);
6259
6260 // Get the context.
6261 Handle<Context> context(Context::cast(it.frame()->context()));
6262
6263 // Get the locals names and values into a temporary array.
6264 //
6265 // TODO(1240907): Hide compiler-introduced stack variables
6266 // (e.g. .result)? For users of the debugger, they will probably be
6267 // confusing.
6268 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6269 for (int i = 0; i < info.NumberOfLocals(); i++) {
6270 // Name of the local.
6271 locals->set(i * 2, *info.LocalName(i));
6272
6273 // Fetch the value of the local - either from the stack or from a
6274 // heap-allocated context.
6275 if (i < info.number_of_stack_slots()) {
6276 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6277 } else {
6278 Handle<String> name = info.LocalName(i);
6279 // Traverse the context chain to the function context as all local
6280 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006281 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006282 context = Handle<Context>(context->previous());
6283 }
6284 ASSERT(context->is_function_context());
6285 locals->set(i * 2 + 1,
6286 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6287 NULL)));
6288 }
6289 }
6290
6291 // Now advance to the arguments adapter frame (if any). If contains all
6292 // the provided parameters and
6293
6294 // Now advance to the arguments adapter frame (if any). It contains all
6295 // the provided parameters whereas the function frame always have the number
6296 // of arguments matching the functions parameters. The rest of the
6297 // information (except for what is collected above) is the same.
6298 it.AdvanceToArgumentsFrame();
6299
6300 // Find the number of arguments to fill. At least fill the number of
6301 // parameters for the function and fill more if more parameters are provided.
6302 int argument_count = info.number_of_parameters();
6303 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6304 argument_count = it.frame()->GetProvidedParametersCount();
6305 }
6306
6307 // Calculate the size of the result.
6308 int details_size = kFrameDetailsFirstDynamicIndex +
6309 2 * (argument_count + info.NumberOfLocals());
6310 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6311
6312 // Add the frame id.
6313 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6314
6315 // Add the function (same as in function frame).
6316 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6317
6318 // Add the arguments count.
6319 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6320
6321 // Add the locals count
6322 details->set(kFrameDetailsLocalCountIndex,
6323 Smi::FromInt(info.NumberOfLocals()));
6324
6325 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006326 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006327 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6328 } else {
6329 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6330 }
6331
6332 // Add the constructor information.
6333 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6334
6335 // Add information on whether this frame is invoked in the debugger context.
6336 details->set(kFrameDetailsDebuggerFrameIndex,
6337 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6338
6339 // Fill the dynamic part.
6340 int details_index = kFrameDetailsFirstDynamicIndex;
6341
6342 // Add arguments name and value.
6343 for (int i = 0; i < argument_count; i++) {
6344 // Name of the argument.
6345 if (i < info.number_of_parameters()) {
6346 details->set(details_index++, *info.parameter_name(i));
6347 } else {
6348 details->set(details_index++, Heap::undefined_value());
6349 }
6350
6351 // Parameter value.
6352 if (i < it.frame()->GetProvidedParametersCount()) {
6353 details->set(details_index++, it.frame()->GetParameter(i));
6354 } else {
6355 details->set(details_index++, Heap::undefined_value());
6356 }
6357 }
6358
6359 // Add locals name and value from the temporary copy from the function frame.
6360 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6361 details->set(details_index++, locals->get(i));
6362 }
6363
6364 // Add the receiver (same as in function frame).
6365 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6366 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6367 Handle<Object> receiver(it.frame()->receiver());
6368 if (!receiver->IsJSObject()) {
6369 // If the receiver is NOT a JSObject we have hit an optimization
6370 // where a value object is not converted into a wrapped JS objects.
6371 // To hide this optimization from the debugger, we wrap the receiver
6372 // by creating correct wrapper object based on the calling frame's
6373 // global context.
6374 it.Advance();
6375 Handle<Context> calling_frames_global_context(
6376 Context::cast(Context::cast(it.frame()->context())->global_context()));
6377 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6378 }
6379 details->set(kFrameDetailsReceiverIndex, *receiver);
6380
6381 ASSERT_EQ(details_size, details_index);
6382 return *Factory::NewJSArrayWithElements(details);
6383}
6384
6385
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006386// Copy all the context locals into an object used to materialize a scope.
6387static void CopyContextLocalsToScopeObject(Handle<Code> code,
6388 ScopeInfo<>& scope_info,
6389 Handle<Context> context,
6390 Handle<JSObject> scope_object) {
6391 // Fill all context locals to the context extension.
6392 for (int i = Context::MIN_CONTEXT_SLOTS;
6393 i < scope_info.number_of_context_slots();
6394 i++) {
6395 int context_index =
6396 ScopeInfo<>::ContextSlotIndex(*code,
6397 *scope_info.context_slot_name(i),
6398 NULL);
6399
6400 // Don't include the arguments shadow (.arguments) context variable.
6401 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6402 SetProperty(scope_object,
6403 scope_info.context_slot_name(i),
6404 Handle<Object>(context->get(context_index)), NONE);
6405 }
6406 }
6407}
6408
6409
6410// Create a plain JSObject which materializes the local scope for the specified
6411// frame.
6412static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6413 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6414 Handle<Code> code(function->code());
6415 ScopeInfo<> scope_info(*code);
6416
6417 // Allocate and initialize a JSObject with all the arguments, stack locals
6418 // heap locals and extension properties of the debugged function.
6419 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6420
6421 // First fill all parameters.
6422 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6423 SetProperty(local_scope,
6424 scope_info.parameter_name(i),
6425 Handle<Object>(frame->GetParameter(i)), NONE);
6426 }
6427
6428 // Second fill all stack locals.
6429 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6430 SetProperty(local_scope,
6431 scope_info.stack_slot_name(i),
6432 Handle<Object>(frame->GetExpression(i)), NONE);
6433 }
6434
6435 // Third fill all context locals.
6436 Handle<Context> frame_context(Context::cast(frame->context()));
6437 Handle<Context> function_context(frame_context->fcontext());
6438 CopyContextLocalsToScopeObject(code, scope_info,
6439 function_context, local_scope);
6440
6441 // Finally copy any properties from the function context extension. This will
6442 // be variables introduced by eval.
6443 if (function_context->closure() == *function) {
6444 if (function_context->has_extension() &&
6445 !function_context->IsGlobalContext()) {
6446 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006447 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006448 for (int i = 0; i < keys->length(); i++) {
6449 // Names of variables introduced by eval are strings.
6450 ASSERT(keys->get(i)->IsString());
6451 Handle<String> key(String::cast(keys->get(i)));
6452 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6453 }
6454 }
6455 }
6456 return local_scope;
6457}
6458
6459
6460// Create a plain JSObject which materializes the closure content for the
6461// context.
6462static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6463 ASSERT(context->is_function_context());
6464
6465 Handle<Code> code(context->closure()->code());
6466 ScopeInfo<> scope_info(*code);
6467
6468 // Allocate and initialize a JSObject with all the content of theis function
6469 // closure.
6470 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6471
6472 // Check whether the arguments shadow object exists.
6473 int arguments_shadow_index =
6474 ScopeInfo<>::ContextSlotIndex(*code,
6475 Heap::arguments_shadow_symbol(),
6476 NULL);
6477 if (arguments_shadow_index >= 0) {
6478 // In this case all the arguments are available in the arguments shadow
6479 // object.
6480 Handle<JSObject> arguments_shadow(
6481 JSObject::cast(context->get(arguments_shadow_index)));
6482 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6483 SetProperty(closure_scope,
6484 scope_info.parameter_name(i),
6485 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6486 }
6487 }
6488
6489 // Fill all context locals to the context extension.
6490 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6491
6492 // Finally copy any properties from the function context extension. This will
6493 // be variables introduced by eval.
6494 if (context->has_extension()) {
6495 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006496 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006497 for (int i = 0; i < keys->length(); i++) {
6498 // Names of variables introduced by eval are strings.
6499 ASSERT(keys->get(i)->IsString());
6500 Handle<String> key(String::cast(keys->get(i)));
6501 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6502 }
6503 }
6504
6505 return closure_scope;
6506}
6507
6508
6509// Iterate over the actual scopes visible from a stack frame. All scopes are
6510// backed by an actual context except the local scope, which is inserted
6511// "artifically" in the context chain.
6512class ScopeIterator {
6513 public:
6514 enum ScopeType {
6515 ScopeTypeGlobal = 0,
6516 ScopeTypeLocal,
6517 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00006518 ScopeTypeClosure,
6519 // Every catch block contains an implicit with block (its parameter is
6520 // a JSContextExtensionObject) that extends current scope with a variable
6521 // holding exception object. Such with blocks are treated as scopes of their
6522 // own type.
6523 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006524 };
6525
6526 explicit ScopeIterator(JavaScriptFrame* frame)
6527 : frame_(frame),
6528 function_(JSFunction::cast(frame->function())),
6529 context_(Context::cast(frame->context())),
6530 local_done_(false),
6531 at_local_(false) {
6532
6533 // Check whether the first scope is actually a local scope.
6534 if (context_->IsGlobalContext()) {
6535 // If there is a stack slot for .result then this local scope has been
6536 // created for evaluating top level code and it is not a real local scope.
6537 // Checking for the existence of .result seems fragile, but the scope info
6538 // saved with the code object does not otherwise have that information.
6539 Handle<Code> code(function_->code());
6540 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6541 at_local_ = index < 0;
6542 } else if (context_->is_function_context()) {
6543 at_local_ = true;
6544 }
6545 }
6546
6547 // More scopes?
6548 bool Done() { return context_.is_null(); }
6549
6550 // Move to the next scope.
6551 void Next() {
6552 // If at a local scope mark the local scope as passed.
6553 if (at_local_) {
6554 at_local_ = false;
6555 local_done_ = true;
6556
6557 // If the current context is not associated with the local scope the
6558 // current context is the next real scope, so don't move to the next
6559 // context in this case.
6560 if (context_->closure() != *function_) {
6561 return;
6562 }
6563 }
6564
6565 // The global scope is always the last in the chain.
6566 if (context_->IsGlobalContext()) {
6567 context_ = Handle<Context>();
6568 return;
6569 }
6570
6571 // Move to the next context.
6572 if (context_->is_function_context()) {
6573 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6574 } else {
6575 context_ = Handle<Context>(context_->previous());
6576 }
6577
6578 // If passing the local scope indicate that the current scope is now the
6579 // local scope.
6580 if (!local_done_ &&
6581 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6582 at_local_ = true;
6583 }
6584 }
6585
6586 // Return the type of the current scope.
6587 int Type() {
6588 if (at_local_) {
6589 return ScopeTypeLocal;
6590 }
6591 if (context_->IsGlobalContext()) {
6592 ASSERT(context_->global()->IsGlobalObject());
6593 return ScopeTypeGlobal;
6594 }
6595 if (context_->is_function_context()) {
6596 return ScopeTypeClosure;
6597 }
6598 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00006599 // Current scope is either an explicit with statement or a with statement
6600 // implicitely generated for a catch block.
6601 // If the extension object here is a JSContextExtensionObject then
6602 // current with statement is one frome a catch block otherwise it's a
6603 // regular with statement.
6604 if (context_->extension()->IsJSContextExtensionObject()) {
6605 return ScopeTypeCatch;
6606 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006607 return ScopeTypeWith;
6608 }
6609
6610 // Return the JavaScript object with the content of the current scope.
6611 Handle<JSObject> ScopeObject() {
6612 switch (Type()) {
6613 case ScopeIterator::ScopeTypeGlobal:
6614 return Handle<JSObject>(CurrentContext()->global());
6615 break;
6616 case ScopeIterator::ScopeTypeLocal:
6617 // Materialize the content of the local scope into a JSObject.
6618 return MaterializeLocalScope(frame_);
6619 break;
6620 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00006621 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006622 // Return the with object.
6623 return Handle<JSObject>(CurrentContext()->extension());
6624 break;
6625 case ScopeIterator::ScopeTypeClosure:
6626 // Materialize the content of the closure scope into a JSObject.
6627 return MaterializeClosure(CurrentContext());
6628 break;
6629 }
6630 UNREACHABLE();
6631 return Handle<JSObject>();
6632 }
6633
6634 // Return the context for this scope. For the local context there might not
6635 // be an actual context.
6636 Handle<Context> CurrentContext() {
6637 if (at_local_ && context_->closure() != *function_) {
6638 return Handle<Context>();
6639 }
6640 return context_;
6641 }
6642
6643#ifdef DEBUG
6644 // Debug print of the content of the current scope.
6645 void DebugPrint() {
6646 switch (Type()) {
6647 case ScopeIterator::ScopeTypeGlobal:
6648 PrintF("Global:\n");
6649 CurrentContext()->Print();
6650 break;
6651
6652 case ScopeIterator::ScopeTypeLocal: {
6653 PrintF("Local:\n");
6654 Handle<Code> code(function_->code());
6655 ScopeInfo<> scope_info(*code);
6656 scope_info.Print();
6657 if (!CurrentContext().is_null()) {
6658 CurrentContext()->Print();
6659 if (CurrentContext()->has_extension()) {
6660 Handle<JSObject> extension =
6661 Handle<JSObject>(CurrentContext()->extension());
6662 if (extension->IsJSContextExtensionObject()) {
6663 extension->Print();
6664 }
6665 }
6666 }
6667 break;
6668 }
6669
6670 case ScopeIterator::ScopeTypeWith: {
6671 PrintF("With:\n");
6672 Handle<JSObject> extension =
6673 Handle<JSObject>(CurrentContext()->extension());
6674 extension->Print();
6675 break;
6676 }
6677
ager@chromium.orga1645e22009-09-09 19:27:10 +00006678 case ScopeIterator::ScopeTypeCatch: {
6679 PrintF("Catch:\n");
6680 Handle<JSObject> extension =
6681 Handle<JSObject>(CurrentContext()->extension());
6682 extension->Print();
6683 break;
6684 }
6685
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006686 case ScopeIterator::ScopeTypeClosure: {
6687 PrintF("Closure:\n");
6688 CurrentContext()->Print();
6689 if (CurrentContext()->has_extension()) {
6690 Handle<JSObject> extension =
6691 Handle<JSObject>(CurrentContext()->extension());
6692 if (extension->IsJSContextExtensionObject()) {
6693 extension->Print();
6694 }
6695 }
6696 break;
6697 }
6698
6699 default:
6700 UNREACHABLE();
6701 }
6702 PrintF("\n");
6703 }
6704#endif
6705
6706 private:
6707 JavaScriptFrame* frame_;
6708 Handle<JSFunction> function_;
6709 Handle<Context> context_;
6710 bool local_done_;
6711 bool at_local_;
6712
6713 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6714};
6715
6716
6717static Object* Runtime_GetScopeCount(Arguments args) {
6718 HandleScope scope;
6719 ASSERT(args.length() == 2);
6720
6721 // Check arguments.
6722 Object* check = Runtime_CheckExecutionState(args);
6723 if (check->IsFailure()) return check;
6724 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6725
6726 // Get the frame where the debugging is performed.
6727 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6728 JavaScriptFrameIterator it(id);
6729 JavaScriptFrame* frame = it.frame();
6730
6731 // Count the visible scopes.
6732 int n = 0;
6733 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6734 n++;
6735 }
6736
6737 return Smi::FromInt(n);
6738}
6739
6740
6741static const int kScopeDetailsTypeIndex = 0;
6742static const int kScopeDetailsObjectIndex = 1;
6743static const int kScopeDetailsSize = 2;
6744
6745// Return an array with scope details
6746// args[0]: number: break id
6747// args[1]: number: frame index
6748// args[2]: number: scope index
6749//
6750// The array returned contains the following information:
6751// 0: Scope type
6752// 1: Scope object
6753static Object* Runtime_GetScopeDetails(Arguments args) {
6754 HandleScope scope;
6755 ASSERT(args.length() == 3);
6756
6757 // Check arguments.
6758 Object* check = Runtime_CheckExecutionState(args);
6759 if (check->IsFailure()) return check;
6760 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6761 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6762
6763 // Get the frame where the debugging is performed.
6764 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6765 JavaScriptFrameIterator frame_it(id);
6766 JavaScriptFrame* frame = frame_it.frame();
6767
6768 // Find the requested scope.
6769 int n = 0;
6770 ScopeIterator it(frame);
6771 for (; !it.Done() && n < index; it.Next()) {
6772 n++;
6773 }
6774 if (it.Done()) {
6775 return Heap::undefined_value();
6776 }
6777
6778 // Calculate the size of the result.
6779 int details_size = kScopeDetailsSize;
6780 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6781
6782 // Fill in scope details.
6783 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6784 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6785
6786 return *Factory::NewJSArrayWithElements(details);
6787}
6788
6789
6790static Object* Runtime_DebugPrintScopes(Arguments args) {
6791 HandleScope scope;
6792 ASSERT(args.length() == 0);
6793
6794#ifdef DEBUG
6795 // Print the scopes for the top frame.
6796 StackFrameLocator locator;
6797 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6798 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6799 it.DebugPrint();
6800 }
6801#endif
6802 return Heap::undefined_value();
6803}
6804
6805
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006806static Object* Runtime_GetCFrames(Arguments args) {
6807 HandleScope scope;
6808 ASSERT(args.length() == 1);
6809 Object* result = Runtime_CheckExecutionState(args);
6810 if (result->IsFailure()) return result;
6811
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006812#if V8_HOST_ARCH_64_BIT
6813 UNIMPLEMENTED();
6814 return Heap::undefined_value();
6815#else
6816
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006817 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006818 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6819 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006820 if (frames_count == OS::kStackWalkError) {
6821 return Heap::undefined_value();
6822 }
6823
6824 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6825 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6826 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6827 for (int i = 0; i < frames_count; i++) {
6828 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6829 frame_value->SetProperty(
6830 *address_str,
6831 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6832 NONE);
6833
6834 // Get the stack walk text for this frame.
6835 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006836 int frame_text_length = StrLength(frames[i].text);
6837 if (frame_text_length > 0) {
6838 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006839 frame_text = Factory::NewStringFromAscii(str);
6840 }
6841
6842 if (!frame_text.is_null()) {
6843 frame_value->SetProperty(*text_str, *frame_text, NONE);
6844 }
6845
6846 frames_array->set(i, *frame_value);
6847 }
6848 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006849#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006850}
6851
6852
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006853static Object* Runtime_GetThreadCount(Arguments args) {
6854 HandleScope scope;
6855 ASSERT(args.length() == 1);
6856
6857 // Check arguments.
6858 Object* result = Runtime_CheckExecutionState(args);
6859 if (result->IsFailure()) return result;
6860
6861 // Count all archived V8 threads.
6862 int n = 0;
6863 for (ThreadState* thread = ThreadState::FirstInUse();
6864 thread != NULL;
6865 thread = thread->Next()) {
6866 n++;
6867 }
6868
6869 // Total number of threads is current thread and archived threads.
6870 return Smi::FromInt(n + 1);
6871}
6872
6873
6874static const int kThreadDetailsCurrentThreadIndex = 0;
6875static const int kThreadDetailsThreadIdIndex = 1;
6876static const int kThreadDetailsSize = 2;
6877
6878// Return an array with thread details
6879// args[0]: number: break id
6880// args[1]: number: thread index
6881//
6882// The array returned contains the following information:
6883// 0: Is current thread?
6884// 1: Thread id
6885static Object* Runtime_GetThreadDetails(Arguments args) {
6886 HandleScope scope;
6887 ASSERT(args.length() == 2);
6888
6889 // Check arguments.
6890 Object* check = Runtime_CheckExecutionState(args);
6891 if (check->IsFailure()) return check;
6892 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6893
6894 // Allocate array for result.
6895 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6896
6897 // Thread index 0 is current thread.
6898 if (index == 0) {
6899 // Fill the details.
6900 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6901 details->set(kThreadDetailsThreadIdIndex,
6902 Smi::FromInt(ThreadManager::CurrentId()));
6903 } else {
6904 // Find the thread with the requested index.
6905 int n = 1;
6906 ThreadState* thread = ThreadState::FirstInUse();
6907 while (index != n && thread != NULL) {
6908 thread = thread->Next();
6909 n++;
6910 }
6911 if (thread == NULL) {
6912 return Heap::undefined_value();
6913 }
6914
6915 // Fill the details.
6916 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6917 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6918 }
6919
6920 // Convert to JS array and return.
6921 return *Factory::NewJSArrayWithElements(details);
6922}
6923
6924
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006925static Object* Runtime_GetBreakLocations(Arguments args) {
6926 HandleScope scope;
6927 ASSERT(args.length() == 1);
6928
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006929 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6930 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006931 // Find the number of break points
6932 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6933 if (break_locations->IsUndefined()) return Heap::undefined_value();
6934 // Return array as JS array
6935 return *Factory::NewJSArrayWithElements(
6936 Handle<FixedArray>::cast(break_locations));
6937}
6938
6939
6940// Set a break point in a function
6941// args[0]: function
6942// args[1]: number: break source position (within the function source)
6943// args[2]: number: break point object
6944static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6945 HandleScope scope;
6946 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006947 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6948 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006949 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6950 RUNTIME_ASSERT(source_position >= 0);
6951 Handle<Object> break_point_object_arg = args.at<Object>(2);
6952
6953 // Set break point.
6954 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6955
6956 return Heap::undefined_value();
6957}
6958
6959
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006960Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6961 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006962 // Iterate the heap looking for SharedFunctionInfo generated from the
6963 // script. The inner most SharedFunctionInfo containing the source position
6964 // for the requested break point is found.
6965 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6966 // which is found is not compiled it is compiled and the heap is iterated
6967 // again as the compilation might create inner functions from the newly
6968 // compiled function and the actual requested break point might be in one of
6969 // these functions.
6970 bool done = false;
6971 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006972 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006973 Handle<SharedFunctionInfo> target;
6974 // The current candidate for the last function in script:
6975 Handle<SharedFunctionInfo> last;
6976 while (!done) {
6977 HeapIterator iterator;
6978 while (iterator.has_next()) {
6979 HeapObject* obj = iterator.next();
6980 ASSERT(obj != NULL);
6981 if (obj->IsSharedFunctionInfo()) {
6982 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6983 if (shared->script() == *script) {
6984 // If the SharedFunctionInfo found has the requested script data and
6985 // contains the source position it is a candidate.
6986 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006987 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006988 start_position = shared->start_position();
6989 }
6990 if (start_position <= position &&
6991 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00006992 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006993 // candidate this is the new candidate.
6994 if (target.is_null()) {
6995 target_start_position = start_position;
6996 target = shared;
6997 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00006998 if (target_start_position == start_position &&
6999 shared->end_position() == target->end_position()) {
7000 // If a top-level function contain only one function
7001 // declartion the source for the top-level and the function is
7002 // the same. In that case prefer the non top-level function.
7003 if (!shared->is_toplevel()) {
7004 target_start_position = start_position;
7005 target = shared;
7006 }
7007 } else if (target_start_position <= start_position &&
7008 shared->end_position() <= target->end_position()) {
7009 // This containment check includes equality as a function inside
7010 // a top-level function can share either start or end position
7011 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007012 target_start_position = start_position;
7013 target = shared;
7014 }
7015 }
7016 }
7017
7018 // Keep track of the last function in the script.
7019 if (last.is_null() ||
7020 shared->end_position() > last->start_position()) {
7021 last = shared;
7022 }
7023 }
7024 }
7025 }
7026
7027 // Make sure some candidate is selected.
7028 if (target.is_null()) {
7029 if (!last.is_null()) {
7030 // Position after the last function - use last.
7031 target = last;
7032 } else {
7033 // Unable to find function - possibly script without any function.
7034 return Heap::undefined_value();
7035 }
7036 }
7037
7038 // If the candidate found is compiled we are done. NOTE: when lazy
7039 // compilation of inner functions is introduced some additional checking
7040 // needs to be done here to compile inner functions.
7041 done = target->is_compiled();
7042 if (!done) {
7043 // If the candidate is not compiled compile it to reveal any inner
7044 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007045 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007046 }
7047 }
7048
7049 return *target;
7050}
7051
7052
7053// Change the state of a break point in a script. NOTE: Regarding performance
7054// see the NOTE for GetScriptFromScriptData.
7055// args[0]: script to set break point in
7056// args[1]: number: break source position (within the script source)
7057// args[2]: number: break point object
7058static Object* Runtime_SetScriptBreakPoint(Arguments args) {
7059 HandleScope scope;
7060 ASSERT(args.length() == 3);
7061 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
7062 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7063 RUNTIME_ASSERT(source_position >= 0);
7064 Handle<Object> break_point_object_arg = args.at<Object>(2);
7065
7066 // Get the script from the script wrapper.
7067 RUNTIME_ASSERT(wrapper->value()->IsScript());
7068 Handle<Script> script(Script::cast(wrapper->value()));
7069
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007070 Object* result = Runtime::FindSharedFunctionInfoInScript(
7071 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007072 if (!result->IsUndefined()) {
7073 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
7074 // Find position within function. The script position might be before the
7075 // source position of the first function.
7076 int position;
7077 if (shared->start_position() > source_position) {
7078 position = 0;
7079 } else {
7080 position = source_position - shared->start_position();
7081 }
7082 Debug::SetBreakPoint(shared, position, break_point_object_arg);
7083 }
7084 return Heap::undefined_value();
7085}
7086
7087
7088// Clear a break point
7089// args[0]: number: break point object
7090static Object* Runtime_ClearBreakPoint(Arguments args) {
7091 HandleScope scope;
7092 ASSERT(args.length() == 1);
7093 Handle<Object> break_point_object_arg = args.at<Object>(0);
7094
7095 // Clear break point.
7096 Debug::ClearBreakPoint(break_point_object_arg);
7097
7098 return Heap::undefined_value();
7099}
7100
7101
7102// Change the state of break on exceptions
7103// args[0]: boolean indicating uncaught exceptions
7104// args[1]: boolean indicating on/off
7105static Object* Runtime_ChangeBreakOnException(Arguments args) {
7106 HandleScope scope;
7107 ASSERT(args.length() == 2);
7108 ASSERT(args[0]->IsNumber());
7109 ASSERT(args[1]->IsBoolean());
7110
7111 // Update break point state
7112 ExceptionBreakType type =
7113 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
7114 bool enable = args[1]->ToBoolean()->IsTrue();
7115 Debug::ChangeBreakOnException(type, enable);
7116 return Heap::undefined_value();
7117}
7118
7119
7120// Prepare for stepping
7121// args[0]: break id for checking execution state
7122// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00007123// args[2]: number of times to perform the step, for step out it is the number
7124// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007125static Object* Runtime_PrepareStep(Arguments args) {
7126 HandleScope scope;
7127 ASSERT(args.length() == 3);
7128 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007129 Object* check = Runtime_CheckExecutionState(args);
7130 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007131 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
7132 return Top::Throw(Heap::illegal_argument_symbol());
7133 }
7134
7135 // Get the step action and check validity.
7136 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
7137 if (step_action != StepIn &&
7138 step_action != StepNext &&
7139 step_action != StepOut &&
7140 step_action != StepInMin &&
7141 step_action != StepMin) {
7142 return Top::Throw(Heap::illegal_argument_symbol());
7143 }
7144
7145 // Get the number of steps.
7146 int step_count = NumberToInt32(args[2]);
7147 if (step_count < 1) {
7148 return Top::Throw(Heap::illegal_argument_symbol());
7149 }
7150
ager@chromium.orga1645e22009-09-09 19:27:10 +00007151 // Clear all current stepping setup.
7152 Debug::ClearStepping();
7153
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007154 // Prepare step.
7155 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
7156 return Heap::undefined_value();
7157}
7158
7159
7160// Clear all stepping set by PrepareStep.
7161static Object* Runtime_ClearStepping(Arguments args) {
7162 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007163 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007164 Debug::ClearStepping();
7165 return Heap::undefined_value();
7166}
7167
7168
7169// Creates a copy of the with context chain. The copy of the context chain is
7170// is linked to the function context supplied.
7171static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
7172 Handle<Context> function_context) {
7173 // At the bottom of the chain. Return the function context to link to.
7174 if (context_chain->is_function_context()) {
7175 return function_context;
7176 }
7177
7178 // Recursively copy the with contexts.
7179 Handle<Context> previous(context_chain->previous());
7180 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
7181 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007182 CopyWithContextChain(function_context, previous),
7183 extension,
7184 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007185}
7186
7187
7188// Helper function to find or create the arguments object for
7189// Runtime_DebugEvaluate.
7190static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
7191 Handle<JSFunction> function,
7192 Handle<Code> code,
7193 const ScopeInfo<>* sinfo,
7194 Handle<Context> function_context) {
7195 // Try to find the value of 'arguments' to pass as parameter. If it is not
7196 // found (that is the debugged function does not reference 'arguments' and
7197 // does not support eval) then create an 'arguments' object.
7198 int index;
7199 if (sinfo->number_of_stack_slots() > 0) {
7200 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
7201 if (index != -1) {
7202 return Handle<Object>(frame->GetExpression(index));
7203 }
7204 }
7205
7206 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
7207 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
7208 NULL);
7209 if (index != -1) {
7210 return Handle<Object>(function_context->get(index));
7211 }
7212 }
7213
7214 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007215 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
7216 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007217 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007218 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007219 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007220 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007221 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007222 return arguments;
7223}
7224
7225
7226// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00007227// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007228// extension part has all the parameters and locals of the function on the
7229// stack frame. A function which calls eval with the code to evaluate is then
7230// compiled in this context and called in this context. As this context
7231// replaces the context of the function on the stack frame a new (empty)
7232// function is created as well to be used as the closure for the context.
7233// This function and the context acts as replacements for the function on the
7234// stack frame presenting the same view of the values of parameters and
7235// local variables as if the piece of JavaScript was evaluated at the point
7236// where the function on the stack frame is currently stopped.
7237static Object* Runtime_DebugEvaluate(Arguments args) {
7238 HandleScope scope;
7239
7240 // Check the execution state and decode arguments frame and source to be
7241 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007242 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007243 Object* check_result = Runtime_CheckExecutionState(args);
7244 if (check_result->IsFailure()) return check_result;
7245 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7246 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007247 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
7248
7249 // Handle the processing of break.
7250 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007251
7252 // Get the frame where the debugging is performed.
7253 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7254 JavaScriptFrameIterator it(id);
7255 JavaScriptFrame* frame = it.frame();
7256 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7257 Handle<Code> code(function->code());
7258 ScopeInfo<> sinfo(*code);
7259
7260 // Traverse the saved contexts chain to find the active context for the
7261 // selected frame.
7262 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007263 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007264 save = save->prev();
7265 }
7266 ASSERT(save != NULL);
7267 SaveContext savex;
7268 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007269
7270 // Create the (empty) function replacing the function on the stack frame for
7271 // the purpose of evaluating in the context created below. It is important
7272 // that this function does not describe any parameters and local variables
7273 // in the context. If it does then this will cause problems with the lookup
7274 // in Context::Lookup, where context slots for parameters and local variables
7275 // are looked at before the extension object.
7276 Handle<JSFunction> go_between =
7277 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7278 go_between->set_context(function->context());
7279#ifdef DEBUG
7280 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7281 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7282 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7283#endif
7284
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007285 // Materialize the content of the local scope into a JSObject.
7286 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007287
7288 // Allocate a new context for the debug evaluation and set the extension
7289 // object build.
7290 Handle<Context> context =
7291 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007292 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007293 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007294 Handle<Context> frame_context(Context::cast(frame->context()));
7295 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007296 context = CopyWithContextChain(frame_context, context);
7297
7298 // Wrap the evaluation statement in a new function compiled in the newly
7299 // created context. The function has one parameter which has to be called
7300 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007301 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007302 // function(arguments,__source__) {return eval(__source__);}
7303 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00007304 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007305 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007306 Handle<String> function_source =
7307 Factory::NewStringFromAscii(Vector<const char>(source_str,
7308 source_str_length));
7309 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007310 Compiler::CompileEval(function_source,
7311 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007312 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007313 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007314 if (boilerplate.is_null()) return Failure::Exception();
7315 Handle<JSFunction> compiled_function =
7316 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7317
7318 // Invoke the result of the compilation to get the evaluation function.
7319 bool has_pending_exception;
7320 Handle<Object> receiver(frame->receiver());
7321 Handle<Object> evaluation_function =
7322 Execution::Call(compiled_function, receiver, 0, NULL,
7323 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007324 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007325
7326 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7327 function_context);
7328
7329 // Invoke the evaluation function and return the result.
7330 const int argc = 2;
7331 Object** argv[argc] = { arguments.location(),
7332 Handle<Object>::cast(source).location() };
7333 Handle<Object> result =
7334 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7335 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007336 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007337
7338 // Skip the global proxy as it has no properties and always delegates to the
7339 // real global object.
7340 if (result->IsJSGlobalProxy()) {
7341 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7342 }
7343
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007344 return *result;
7345}
7346
7347
7348static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7349 HandleScope scope;
7350
7351 // Check the execution state and decode arguments frame and source to be
7352 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007353 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007354 Object* check_result = Runtime_CheckExecutionState(args);
7355 if (check_result->IsFailure()) return check_result;
7356 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007357 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7358
7359 // Handle the processing of break.
7360 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007361
7362 // Enter the top context from before the debugger was invoked.
7363 SaveContext save;
7364 SaveContext* top = &save;
7365 while (top != NULL && *top->context() == *Debug::debug_context()) {
7366 top = top->prev();
7367 }
7368 if (top != NULL) {
7369 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007370 }
7371
7372 // Get the global context now set to the top context from before the
7373 // debugger was invoked.
7374 Handle<Context> context = Top::global_context();
7375
7376 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007377 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007378 Handle<JSFunction>(Compiler::CompileEval(source,
7379 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007380 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007381 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007382 if (boilerplate.is_null()) return Failure::Exception();
7383 Handle<JSFunction> compiled_function =
7384 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7385 context));
7386
7387 // Invoke the result of the compilation to get the evaluation function.
7388 bool has_pending_exception;
7389 Handle<Object> receiver = Top::global();
7390 Handle<Object> result =
7391 Execution::Call(compiled_function, receiver, 0, NULL,
7392 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007393 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007394 return *result;
7395}
7396
7397
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007398static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7399 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007400 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007401
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007402 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007403 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007404
7405 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007406 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007407 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7408 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7409 // because using
7410 // instances->set(i, *GetScriptWrapper(script))
7411 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7412 // already have deferenced the instances handle.
7413 Handle<JSValue> wrapper = GetScriptWrapper(script);
7414 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007415 }
7416
7417 // Return result as a JS array.
7418 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7419 Handle<JSArray>::cast(result)->SetContent(*instances);
7420 return *result;
7421}
7422
7423
7424// Helper function used by Runtime_DebugReferencedBy below.
7425static int DebugReferencedBy(JSObject* target,
7426 Object* instance_filter, int max_references,
7427 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007428 JSFunction* arguments_function) {
7429 NoHandleAllocation ha;
7430 AssertNoAllocation no_alloc;
7431
7432 // Iterate the heap.
7433 int count = 0;
7434 JSObject* last = NULL;
7435 HeapIterator iterator;
7436 while (iterator.has_next() &&
7437 (max_references == 0 || count < max_references)) {
7438 // Only look at all JSObjects.
7439 HeapObject* heap_obj = iterator.next();
7440 if (heap_obj->IsJSObject()) {
7441 // Skip context extension objects and argument arrays as these are
7442 // checked in the context of functions using them.
7443 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007444 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007445 obj->map()->constructor() == arguments_function) {
7446 continue;
7447 }
7448
7449 // Check if the JS object has a reference to the object looked for.
7450 if (obj->ReferencesObject(target)) {
7451 // Check instance filter if supplied. This is normally used to avoid
7452 // references from mirror objects (see Runtime_IsInPrototypeChain).
7453 if (!instance_filter->IsUndefined()) {
7454 Object* V = obj;
7455 while (true) {
7456 Object* prototype = V->GetPrototype();
7457 if (prototype->IsNull()) {
7458 break;
7459 }
7460 if (instance_filter == prototype) {
7461 obj = NULL; // Don't add this object.
7462 break;
7463 }
7464 V = prototype;
7465 }
7466 }
7467
7468 if (obj != NULL) {
7469 // Valid reference found add to instance array if supplied an update
7470 // count.
7471 if (instances != NULL && count < instances_size) {
7472 instances->set(count, obj);
7473 }
7474 last = obj;
7475 count++;
7476 }
7477 }
7478 }
7479 }
7480
7481 // Check for circular reference only. This can happen when the object is only
7482 // referenced from mirrors and has a circular reference in which case the
7483 // object is not really alive and would have been garbage collected if not
7484 // referenced from the mirror.
7485 if (count == 1 && last == target) {
7486 count = 0;
7487 }
7488
7489 // Return the number of referencing objects found.
7490 return count;
7491}
7492
7493
7494// Scan the heap for objects with direct references to an object
7495// args[0]: the object to find references to
7496// args[1]: constructor function for instances to exclude (Mirror)
7497// args[2]: the the maximum number of objects to return
7498static Object* Runtime_DebugReferencedBy(Arguments args) {
7499 ASSERT(args.length() == 3);
7500
7501 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007502 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007503
7504 // Check parameters.
7505 CONVERT_CHECKED(JSObject, target, args[0]);
7506 Object* instance_filter = args[1];
7507 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7508 instance_filter->IsJSObject());
7509 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7510 RUNTIME_ASSERT(max_references >= 0);
7511
7512 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007513 JSObject* arguments_boilerplate =
7514 Top::context()->global_context()->arguments_boilerplate();
7515 JSFunction* arguments_function =
7516 JSFunction::cast(arguments_boilerplate->map()->constructor());
7517
7518 // Get the number of referencing objects.
7519 int count;
7520 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007521 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007522
7523 // Allocate an array to hold the result.
7524 Object* object = Heap::AllocateFixedArray(count);
7525 if (object->IsFailure()) return object;
7526 FixedArray* instances = FixedArray::cast(object);
7527
7528 // Fill the referencing objects.
7529 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007530 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007531
7532 // Return result as JS array.
7533 Object* result =
7534 Heap::AllocateJSObject(
7535 Top::context()->global_context()->array_function());
7536 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7537 return result;
7538}
7539
7540
7541// Helper function used by Runtime_DebugConstructedBy below.
7542static int DebugConstructedBy(JSFunction* constructor, int max_references,
7543 FixedArray* instances, int instances_size) {
7544 AssertNoAllocation no_alloc;
7545
7546 // Iterate the heap.
7547 int count = 0;
7548 HeapIterator iterator;
7549 while (iterator.has_next() &&
7550 (max_references == 0 || count < max_references)) {
7551 // Only look at all JSObjects.
7552 HeapObject* heap_obj = iterator.next();
7553 if (heap_obj->IsJSObject()) {
7554 JSObject* obj = JSObject::cast(heap_obj);
7555 if (obj->map()->constructor() == constructor) {
7556 // Valid reference found add to instance array if supplied an update
7557 // count.
7558 if (instances != NULL && count < instances_size) {
7559 instances->set(count, obj);
7560 }
7561 count++;
7562 }
7563 }
7564 }
7565
7566 // Return the number of referencing objects found.
7567 return count;
7568}
7569
7570
7571// Scan the heap for objects constructed by a specific function.
7572// args[0]: the constructor to find instances of
7573// args[1]: the the maximum number of objects to return
7574static Object* Runtime_DebugConstructedBy(Arguments args) {
7575 ASSERT(args.length() == 2);
7576
7577 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007578 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007579
7580 // Check parameters.
7581 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7582 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7583 RUNTIME_ASSERT(max_references >= 0);
7584
7585 // Get the number of referencing objects.
7586 int count;
7587 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7588
7589 // Allocate an array to hold the result.
7590 Object* object = Heap::AllocateFixedArray(count);
7591 if (object->IsFailure()) return object;
7592 FixedArray* instances = FixedArray::cast(object);
7593
7594 // Fill the referencing objects.
7595 count = DebugConstructedBy(constructor, max_references, instances, count);
7596
7597 // Return result as JS array.
7598 Object* result =
7599 Heap::AllocateJSObject(
7600 Top::context()->global_context()->array_function());
7601 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7602 return result;
7603}
7604
7605
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007606// Find the effective prototype object as returned by __proto__.
7607// args[0]: the object to find the prototype for.
7608static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007609 ASSERT(args.length() == 1);
7610
7611 CONVERT_CHECKED(JSObject, obj, args[0]);
7612
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007613 // Use the __proto__ accessor.
7614 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007615}
7616
7617
7618static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007619 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007620 CPU::DebugBreak();
7621 return Heap::undefined_value();
7622}
7623
7624
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007625static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007626#ifdef DEBUG
7627 HandleScope scope;
7628 ASSERT(args.length() == 1);
7629 // Get the function and make sure it is compiled.
7630 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7631 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7632 return Failure::Exception();
7633 }
7634 func->code()->PrintLn();
7635#endif // DEBUG
7636 return Heap::undefined_value();
7637}
ager@chromium.org9085a012009-05-11 19:22:57 +00007638
7639
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007640static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
7641#ifdef DEBUG
7642 HandleScope scope;
7643 ASSERT(args.length() == 1);
7644 // Get the function and make sure it is compiled.
7645 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7646 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7647 return Failure::Exception();
7648 }
7649 func->shared()->construct_stub()->PrintLn();
7650#endif // DEBUG
7651 return Heap::undefined_value();
7652}
7653
7654
ager@chromium.org9085a012009-05-11 19:22:57 +00007655static Object* Runtime_FunctionGetInferredName(Arguments args) {
7656 NoHandleAllocation ha;
7657 ASSERT(args.length() == 1);
7658
7659 CONVERT_CHECKED(JSFunction, f, args[0]);
7660 return f->shared()->inferred_name();
7661}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007662#endif // ENABLE_DEBUGGER_SUPPORT
7663
7664
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007665// Finds the script object from the script data. NOTE: This operation uses
7666// heap traversal to find the function generated for the source position
7667// for the requested break point. For lazily compiled functions several heap
7668// traversals might be required rendering this operation as a rather slow
7669// operation. However for setting break points which is normally done through
7670// some kind of user interaction the performance is not crucial.
7671static Handle<Object> Runtime_GetScriptFromScriptName(
7672 Handle<String> script_name) {
7673 // Scan the heap for Script objects to find the script with the requested
7674 // script data.
7675 Handle<Script> script;
7676 HeapIterator iterator;
7677 while (script.is_null() && iterator.has_next()) {
7678 HeapObject* obj = iterator.next();
7679 // If a script is found check if it has the script data requested.
7680 if (obj->IsScript()) {
7681 if (Script::cast(obj)->name()->IsString()) {
7682 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7683 script = Handle<Script>(Script::cast(obj));
7684 }
7685 }
7686 }
7687 }
7688
7689 // If no script with the requested script data is found return undefined.
7690 if (script.is_null()) return Factory::undefined_value();
7691
7692 // Return the script found.
7693 return GetScriptWrapper(script);
7694}
7695
7696
7697// Get the script object from script data. NOTE: Regarding performance
7698// see the NOTE for GetScriptFromScriptData.
7699// args[0]: script data for the script to find the source for
7700static Object* Runtime_GetScript(Arguments args) {
7701 HandleScope scope;
7702
7703 ASSERT(args.length() == 1);
7704
7705 CONVERT_CHECKED(String, script_name, args[0]);
7706
7707 // Find the requested script.
7708 Handle<Object> result =
7709 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7710 return *result;
7711}
7712
7713
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007714// Determines whether the given stack frame should be displayed in
7715// a stack trace. The caller is the error constructor that asked
7716// for the stack trace to be collected. The first time a construct
7717// call to this function is encountered it is skipped. The seen_caller
7718// in/out parameter is used to remember if the caller has been seen
7719// yet.
7720static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
7721 bool* seen_caller) {
7722 // Only display JS frames.
7723 if (!raw_frame->is_java_script())
7724 return false;
7725 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7726 Object* raw_fun = frame->function();
7727 // Not sure when this can happen but skip it just in case.
7728 if (!raw_fun->IsJSFunction())
7729 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007730 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007731 *seen_caller = true;
7732 return false;
7733 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007734 // Skip all frames until we've seen the caller. Also, skip the most
7735 // obvious builtin calls. Some builtin calls (such as Number.ADD
7736 // which is invoked using 'call') are very difficult to recognize
7737 // so we're leaving them in for now.
7738 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007739}
7740
7741
7742// Collect the raw data for a stack trace. Returns an array of three
7743// element segments each containing a receiver, function and native
7744// code offset.
7745static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007746 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007747 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007748 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
7749
7750 HandleScope scope;
7751
7752 int initial_size = limit < 10 ? limit : 10;
7753 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007754
7755 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007756 // If the caller parameter is a function we skip frames until we're
7757 // under it before starting to collect.
7758 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007759 int cursor = 0;
7760 int frames_seen = 0;
7761 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007762 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007763 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007764 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007765 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007766 Object* recv = frame->receiver();
7767 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007768 Address pc = frame->pc();
7769 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007770 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007771 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007772 if (cursor + 2 < elements->length()) {
7773 elements->set(cursor++, recv);
7774 elements->set(cursor++, fun);
7775 elements->set(cursor++, offset, SKIP_WRITE_BARRIER);
7776 } else {
7777 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007778 Handle<Object> recv_handle(recv);
7779 Handle<Object> fun_handle(fun);
7780 SetElement(result, cursor++, recv_handle);
7781 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007782 SetElement(result, cursor++, Handle<Smi>(offset));
7783 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007784 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007785 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007786 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007787
7788 result->set_length(Smi::FromInt(cursor), SKIP_WRITE_BARRIER);
7789
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007790 return *result;
7791}
7792
7793
ager@chromium.org3811b432009-10-28 14:53:37 +00007794// Returns V8 version as a string.
7795static Object* Runtime_GetV8Version(Arguments args) {
7796 ASSERT_EQ(args.length(), 0);
7797
7798 NoHandleAllocation ha;
7799
7800 const char* version_string = v8::V8::GetVersion();
7801
7802 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
7803}
7804
7805
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007806static Object* Runtime_Abort(Arguments args) {
7807 ASSERT(args.length() == 2);
7808 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7809 Smi::cast(args[1])->value());
7810 Top::PrintStack();
7811 OS::Abort();
7812 UNREACHABLE();
7813 return NULL;
7814}
7815
7816
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007817static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
7818 ASSERT(args.length() == 0);
7819 HandleScope::DeleteExtensions();
7820 return Heap::undefined_value();
7821}
7822
7823
kasper.lund44510672008-07-25 07:37:58 +00007824#ifdef DEBUG
7825// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7826// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007827static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007828 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007829 HandleScope scope;
7830 Handle<JSArray> result = Factory::NewJSArray(0);
7831 int index = 0;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007832#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007833 { \
7834 HandleScope inner; \
7835 Handle<String> name = \
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007836 Factory::NewStringFromAscii( \
7837 Vector<const char>(#Name, StrLength(#Name))); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007838 Handle<JSArray> pair = Factory::NewJSArray(0); \
7839 SetElement(pair, 0, name); \
7840 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7841 SetElement(result, index++, pair); \
7842 }
7843 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7844#undef ADD_ENTRY
7845 return *result;
7846}
kasper.lund44510672008-07-25 07:37:58 +00007847#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007848
7849
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007850static Object* Runtime_Log(Arguments args) {
7851 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007852 CONVERT_CHECKED(String, format, args[0]);
7853 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007854 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007855 Logger::LogRuntime(chars, elms);
7856 return Heap::undefined_value();
7857}
7858
7859
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007860static Object* Runtime_IS_VAR(Arguments args) {
7861 UNREACHABLE(); // implemented as macro in the parser
7862 return NULL;
7863}
7864
7865
7866// ----------------------------------------------------------------------------
7867// Implementation of Runtime
7868
ager@chromium.orga1645e22009-09-09 19:27:10 +00007869#define F(name, nargs, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007870 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +00007871 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007872
7873static Runtime::Function Runtime_functions[] = {
7874 RUNTIME_FUNCTION_LIST(F)
ager@chromium.orga1645e22009-09-09 19:27:10 +00007875 { NULL, NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007876};
7877
7878#undef F
7879
7880
7881Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7882 ASSERT(0 <= fid && fid < kNofFunctions);
7883 return &Runtime_functions[fid];
7884}
7885
7886
7887Runtime::Function* Runtime::FunctionForName(const char* name) {
7888 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7889 if (strcmp(f->name, name) == 0) {
7890 return f;
7891 }
7892 }
7893 return NULL;
7894}
7895
7896
7897void Runtime::PerformGC(Object* result) {
7898 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007899 if (failure->IsRetryAfterGC()) {
7900 // Try to do a garbage collection; ignore it if it fails. The C
7901 // entry stub will throw an out-of-memory exception in that case.
7902 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7903 } else {
7904 // Handle last resort GC and make sure to allow future allocations
7905 // to grow the heap without causing GCs (if possible).
7906 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007907 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007908 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007909}
7910
7911
7912} } // namespace v8::internal