blob: 6ae22330a0a0734f1ead2ff5354ebfac523a672a [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
ager@chromium.org6141cbe2009-11-20 12:14:52 +00001753 // Number of parts added by compiled replacement plus preceeding
1754 // string and possibly suffix after last match. It is possible for
1755 // all components to use two elements when encoded as two smis.
1756 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 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]);
ager@chromium.org6141cbe2009-11-20 12:14:52 +00002359 Object* from = args[1];
2360 Object* to = args[2];
2361 int start, end;
2362 // We have a fast integer-only case here to avoid a conversion to double in
2363 // the common case where from and to are Smis.
2364 if (from->IsSmi() && to->IsSmi()) {
2365 start = Smi::cast(from)->value();
2366 end = Smi::cast(to)->value();
2367 } else {
2368 CONVERT_DOUBLE_CHECKED(from_number, from);
2369 CONVERT_DOUBLE_CHECKED(to_number, to);
2370 start = FastD2I(from_number);
2371 end = FastD2I(to_number);
2372 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002373 RUNTIME_ASSERT(end >= start);
2374 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002375 RUNTIME_ASSERT(end <= value->length());
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002376 return value->SubString(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002377}
2378
2379
ager@chromium.org41826e72009-03-30 13:30:57 +00002380static Object* Runtime_StringMatch(Arguments args) {
2381 ASSERT_EQ(3, args.length());
2382
2383 CONVERT_ARG_CHECKED(String, subject, 0);
2384 CONVERT_ARG_CHECKED(JSRegExp, regexp, 1);
2385 CONVERT_ARG_CHECKED(JSArray, regexp_info, 2);
2386 HandleScope handles;
2387
2388 Handle<Object> match = RegExpImpl::Exec(regexp, subject, 0, regexp_info);
2389
2390 if (match.is_null()) {
2391 return Failure::Exception();
2392 }
2393 if (match->IsNull()) {
2394 return Heap::null_value();
2395 }
2396 int length = subject->length();
2397
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00002398 CompilationZoneScope zone_space(DELETE_ON_EXIT);
ager@chromium.org41826e72009-03-30 13:30:57 +00002399 ZoneList<int> offsets(8);
2400 do {
2401 int start;
2402 int end;
2403 {
2404 AssertNoAllocation no_alloc;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00002405 FixedArray* elements = FixedArray::cast(regexp_info->elements());
ager@chromium.org41826e72009-03-30 13:30:57 +00002406 start = Smi::cast(elements->get(RegExpImpl::kFirstCapture))->value();
2407 end = Smi::cast(elements->get(RegExpImpl::kFirstCapture + 1))->value();
2408 }
2409 offsets.Add(start);
2410 offsets.Add(end);
2411 int index = start < end ? end : end + 1;
2412 if (index > length) break;
2413 match = RegExpImpl::Exec(regexp, subject, index, regexp_info);
2414 if (match.is_null()) {
2415 return Failure::Exception();
2416 }
2417 } while (!match->IsNull());
2418 int matches = offsets.length() / 2;
2419 Handle<FixedArray> elements = Factory::NewFixedArray(matches);
2420 for (int i = 0; i < matches ; i++) {
2421 int from = offsets.at(i * 2);
2422 int to = offsets.at(i * 2 + 1);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002423 elements->set(i, *Factory::NewSubString(subject, from, to));
ager@chromium.org41826e72009-03-30 13:30:57 +00002424 }
2425 Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
2426 result->set_length(Smi::FromInt(matches));
2427 return *result;
2428}
2429
2430
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002431static Object* Runtime_NumberToRadixString(Arguments args) {
2432 NoHandleAllocation ha;
2433 ASSERT(args.length() == 2);
2434
ager@chromium.orgeadaf222009-06-16 09:43:10 +00002435 // Fast case where the result is a one character string.
2436 if (args[0]->IsSmi() && args[1]->IsSmi()) {
2437 int value = Smi::cast(args[0])->value();
2438 int radix = Smi::cast(args[1])->value();
2439 if (value >= 0 && value < radix) {
2440 RUNTIME_ASSERT(radix <= 36);
2441 // Character array used for conversion.
2442 static const char kCharTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
2443 return Heap::LookupSingleCharacterStringFromCode(kCharTable[value]);
2444 }
2445 }
2446
2447 // Slow case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002448 CONVERT_DOUBLE_CHECKED(value, args[0]);
2449 if (isnan(value)) {
2450 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2451 }
2452 if (isinf(value)) {
2453 if (value < 0) {
2454 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2455 }
2456 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2457 }
2458 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
2459 int radix = FastD2I(radix_number);
2460 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2461 char* str = DoubleToRadixCString(value, radix);
2462 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2463 DeleteArray(str);
2464 return result;
2465}
2466
2467
2468static Object* Runtime_NumberToFixed(Arguments args) {
2469 NoHandleAllocation ha;
2470 ASSERT(args.length() == 2);
2471
2472 CONVERT_DOUBLE_CHECKED(value, args[0]);
2473 if (isnan(value)) {
2474 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2475 }
2476 if (isinf(value)) {
2477 if (value < 0) {
2478 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2479 }
2480 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2481 }
2482 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2483 int f = FastD2I(f_number);
2484 RUNTIME_ASSERT(f >= 0);
2485 char* str = DoubleToFixedCString(value, f);
2486 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2487 DeleteArray(str);
2488 return res;
2489}
2490
2491
2492static Object* Runtime_NumberToExponential(Arguments args) {
2493 NoHandleAllocation ha;
2494 ASSERT(args.length() == 2);
2495
2496 CONVERT_DOUBLE_CHECKED(value, args[0]);
2497 if (isnan(value)) {
2498 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2499 }
2500 if (isinf(value)) {
2501 if (value < 0) {
2502 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2503 }
2504 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2505 }
2506 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2507 int f = FastD2I(f_number);
2508 RUNTIME_ASSERT(f >= -1 && f <= 20);
2509 char* str = DoubleToExponentialCString(value, f);
2510 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2511 DeleteArray(str);
2512 return res;
2513}
2514
2515
2516static Object* Runtime_NumberToPrecision(Arguments args) {
2517 NoHandleAllocation ha;
2518 ASSERT(args.length() == 2);
2519
2520 CONVERT_DOUBLE_CHECKED(value, args[0]);
2521 if (isnan(value)) {
2522 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
2523 }
2524 if (isinf(value)) {
2525 if (value < 0) {
2526 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
2527 }
2528 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
2529 }
2530 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
2531 int f = FastD2I(f_number);
2532 RUNTIME_ASSERT(f >= 1 && f <= 21);
2533 char* str = DoubleToPrecisionCString(value, f);
2534 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
2535 DeleteArray(str);
2536 return res;
2537}
2538
2539
2540// Returns a single character string where first character equals
2541// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002542static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002543 if (index < static_cast<uint32_t>(string->length())) {
2544 string->TryFlattenIfNotFlat();
ager@chromium.org870a0b62008-11-04 11:43:05 +00002545 return LookupSingleCharacterStringFromCode(
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002546 string->Get(index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002547 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002548 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002549}
2550
2551
2552Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
2553 // Handle [] indexing on Strings
2554 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002555 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
2556 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002557 }
2558
2559 // Handle [] indexing on String objects
2560 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002561 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
2562 Handle<Object> result =
2563 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
2564 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002565 }
2566
2567 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002568 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002569 return prototype->GetElement(index);
2570 }
2571
2572 return object->GetElement(index);
2573}
2574
2575
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002576Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
2577 HandleScope scope;
2578
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002579 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002580 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002581 Handle<Object> error =
2582 Factory::NewTypeError("non_object_property_load",
2583 HandleVector(args, 2));
2584 return Top::Throw(*error);
2585 }
2586
2587 // Check if the given key is an array index.
2588 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002589 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002590 return GetElementOrCharAt(object, index);
2591 }
2592
2593 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002594 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002595 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002596 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002597 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002598 bool has_pending_exception = false;
2599 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002600 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002601 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002602 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002603 }
2604
ager@chromium.org32912102009-01-16 10:38:43 +00002605 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002606 // the element if so.
2607 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002608 return GetElementOrCharAt(object, index);
2609 } else {
2610 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002611 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002612 }
2613}
2614
2615
2616static Object* Runtime_GetProperty(Arguments args) {
2617 NoHandleAllocation ha;
2618 ASSERT(args.length() == 2);
2619
2620 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002621 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002622
2623 return Runtime::GetObjectProperty(object, key);
2624}
2625
2626
ager@chromium.org7c537e22008-10-16 08:43:32 +00002627
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002628// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002629static Object* Runtime_KeyedGetProperty(Arguments args) {
2630 NoHandleAllocation ha;
2631 ASSERT(args.length() == 2);
2632
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002633 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00002634 // itself.
2635 //
2636 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00002637 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00002638 // global proxy object never has properties. This is the case
2639 // because the global proxy object forwards everything to its hidden
2640 // prototype including local lookups.
2641 //
2642 // Additionally, we need to make sure that we do not cache results
2643 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002644 if (args[0]->IsJSObject() &&
2645 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00002646 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002647 args[1]->IsString()) {
2648 JSObject* receiver = JSObject::cast(args[0]);
2649 String* key = String::cast(args[1]);
2650 if (receiver->HasFastProperties()) {
2651 // Attempt to use lookup cache.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002652 Map* receiver_map = receiver->map();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002653 int offset = KeyedLookupCache::Lookup(receiver_map, key);
2654 if (offset != -1) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002655 Object* value = receiver->FastPropertyAt(offset);
2656 return value->IsTheHole() ? Heap::undefined_value() : value;
2657 }
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002658 // Lookup cache miss. Perform lookup and update the cache if appropriate.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002659 LookupResult result;
2660 receiver->LocalLookup(key, &result);
2661 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
2662 int offset = result.GetFieldIndex();
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002663 KeyedLookupCache::Update(receiver_map, key, offset);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002664 return receiver->FastPropertyAt(offset);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002665 }
2666 } else {
2667 // Attempt dictionary lookup.
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00002668 StringDictionary* dictionary = receiver->property_dictionary();
2669 int entry = dictionary->FindEntry(key);
2670 if ((entry != StringDictionary::kNotFound) &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002671 (dictionary->DetailsAt(entry).type() == NORMAL)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00002672 Object* value = dictionary->ValueAt(entry);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002673 if (!receiver->IsGlobalObject()) return value;
2674 value = JSGlobalPropertyCell::cast(value)->value();
2675 if (!value->IsTheHole()) return value;
2676 // If value is the hole do the general lookup.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002677 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00002678 }
2679 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002680
2681 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002682 return Runtime::GetObjectProperty(args.at<Object>(0),
2683 args.at<Object>(1));
2684}
2685
2686
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002687Object* Runtime::SetObjectProperty(Handle<Object> object,
2688 Handle<Object> key,
2689 Handle<Object> value,
2690 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002691 HandleScope scope;
2692
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002693 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002694 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002695 Handle<Object> error =
2696 Factory::NewTypeError("non_object_property_store",
2697 HandleVector(args, 2));
2698 return Top::Throw(*error);
2699 }
2700
2701 // If the object isn't a JavaScript object, we ignore the store.
2702 if (!object->IsJSObject()) return *value;
2703
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002704 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
2705
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002706 // Check if the given key is an array index.
2707 uint32_t index;
2708 if (Array::IndexFromObject(*key, &index)) {
2709 ASSERT(attr == NONE);
2710
2711 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2712 // of a string using [] notation. We need to support this too in
2713 // JavaScript.
2714 // In the case of a String object we just need to redirect the assignment to
2715 // the underlying string if the index is in range. Since the underlying
2716 // string does nothing with the assignment then we can ignore such
2717 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002718 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002719 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002720 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002721
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002722 Handle<Object> result = SetElement(js_object, index, value);
2723 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002724 return *value;
2725 }
2726
2727 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002728 Handle<Object> result;
2729 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002730 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002731 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002732 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002733 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00002734 key_string->TryFlattenIfNotFlat();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002735 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002736 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002737 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002738 return *value;
2739 }
2740
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002741 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002742 bool has_pending_exception = false;
2743 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2744 if (has_pending_exception) return Failure::Exception();
2745 Handle<String> name = Handle<String>::cast(converted);
2746
2747 if (name->AsArrayIndex(&index)) {
2748 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002749 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002750 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002751 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002752 }
2753}
2754
2755
ager@chromium.org65dad4b2009-04-23 08:48:43 +00002756Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
2757 Handle<Object> key,
2758 Handle<Object> value,
2759 PropertyAttributes attr) {
2760 HandleScope scope;
2761
2762 // Check if the given key is an array index.
2763 uint32_t index;
2764 if (Array::IndexFromObject(*key, &index)) {
2765 ASSERT(attr == NONE);
2766
2767 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
2768 // of a string using [] notation. We need to support this too in
2769 // JavaScript.
2770 // In the case of a String object we just need to redirect the assignment to
2771 // the underlying string if the index is in range. Since the underlying
2772 // string does nothing with the assignment then we can ignore such
2773 // assignments.
2774 if (js_object->IsStringObjectWithCharacterAt(index)) {
2775 return *value;
2776 }
2777
2778 return js_object->SetElement(index, *value);
2779 }
2780
2781 if (key->IsString()) {
2782 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
2783 ASSERT(attr == NONE);
2784 return js_object->SetElement(index, *value);
2785 } else {
2786 Handle<String> key_string = Handle<String>::cast(key);
2787 key_string->TryFlattenIfNotFlat();
2788 return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
2789 *value,
2790 attr);
2791 }
2792 }
2793
2794 // Call-back into JavaScript to convert the key to a string.
2795 bool has_pending_exception = false;
2796 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2797 if (has_pending_exception) return Failure::Exception();
2798 Handle<String> name = Handle<String>::cast(converted);
2799
2800 if (name->AsArrayIndex(&index)) {
2801 ASSERT(attr == NONE);
2802 return js_object->SetElement(index, *value);
2803 } else {
2804 return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
2805 }
2806}
2807
2808
ager@chromium.orge2902be2009-06-08 12:21:35 +00002809Object* Runtime::ForceDeleteObjectProperty(Handle<JSObject> js_object,
2810 Handle<Object> key) {
2811 HandleScope scope;
2812
2813 // Check if the given key is an array index.
2814 uint32_t index;
2815 if (Array::IndexFromObject(*key, &index)) {
2816 // In Firefox/SpiderMonkey, Safari and Opera you can access the
2817 // characters of a string using [] notation. In the case of a
2818 // String object we just need to redirect the deletion to the
2819 // underlying string if the index is in range. Since the
2820 // underlying string does nothing with the deletion, we can ignore
2821 // such deletions.
2822 if (js_object->IsStringObjectWithCharacterAt(index)) {
2823 return Heap::true_value();
2824 }
2825
2826 return js_object->DeleteElement(index, JSObject::FORCE_DELETION);
2827 }
2828
2829 Handle<String> key_string;
2830 if (key->IsString()) {
2831 key_string = Handle<String>::cast(key);
2832 } else {
2833 // Call-back into JavaScript to convert the key to a string.
2834 bool has_pending_exception = false;
2835 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
2836 if (has_pending_exception) return Failure::Exception();
2837 key_string = Handle<String>::cast(converted);
2838 }
2839
2840 key_string->TryFlattenIfNotFlat();
2841 return js_object->DeleteProperty(*key_string, JSObject::FORCE_DELETION);
2842}
2843
2844
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002845static Object* Runtime_SetProperty(Arguments args) {
2846 NoHandleAllocation ha;
2847 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
2848
2849 Handle<Object> object = args.at<Object>(0);
2850 Handle<Object> key = args.at<Object>(1);
2851 Handle<Object> value = args.at<Object>(2);
2852
2853 // Compute attributes.
2854 PropertyAttributes attributes = NONE;
2855 if (args.length() == 4) {
2856 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002857 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002858 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002859 RUNTIME_ASSERT(
2860 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2861 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002862 }
2863 return Runtime::SetObjectProperty(object, key, value, attributes);
2864}
2865
2866
2867// Set a local property, even if it is READ_ONLY. If the property does not
2868// exist, it will be added with attributes NONE.
2869static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
2870 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002871 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002872 CONVERT_CHECKED(JSObject, object, args[0]);
2873 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002874 // Compute attributes.
2875 PropertyAttributes attributes = NONE;
2876 if (args.length() == 4) {
2877 CONVERT_CHECKED(Smi, value_obj, args[3]);
2878 int unchecked_value = value_obj->value();
2879 // Only attribute bits should be set.
2880 RUNTIME_ASSERT(
2881 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
2882 attributes = static_cast<PropertyAttributes>(unchecked_value);
2883 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002884
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00002885 return object->
2886 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002887}
2888
2889
2890static Object* Runtime_DeleteProperty(Arguments args) {
2891 NoHandleAllocation ha;
2892 ASSERT(args.length() == 2);
2893
2894 CONVERT_CHECKED(JSObject, object, args[0]);
2895 CONVERT_CHECKED(String, key, args[1]);
ager@chromium.orge2902be2009-06-08 12:21:35 +00002896 return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002897}
2898
2899
ager@chromium.org9085a012009-05-11 19:22:57 +00002900static Object* HasLocalPropertyImplementation(Handle<JSObject> object,
2901 Handle<String> key) {
2902 if (object->HasLocalProperty(*key)) return Heap::true_value();
2903 // Handle hidden prototypes. If there's a hidden prototype above this thing
2904 // then we have to check it for properties, because they are supposed to
2905 // look like they are on this object.
2906 Handle<Object> proto(object->GetPrototype());
2907 if (proto->IsJSObject() &&
2908 Handle<JSObject>::cast(proto)->map()->is_hidden_prototype()) {
2909 return HasLocalPropertyImplementation(Handle<JSObject>::cast(proto), key);
2910 }
2911 return Heap::false_value();
2912}
2913
2914
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002915static Object* Runtime_HasLocalProperty(Arguments args) {
2916 NoHandleAllocation ha;
2917 ASSERT(args.length() == 2);
2918 CONVERT_CHECKED(String, key, args[1]);
2919
ager@chromium.org9085a012009-05-11 19:22:57 +00002920 Object* obj = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002921 // Only JS objects can have properties.
ager@chromium.org9085a012009-05-11 19:22:57 +00002922 if (obj->IsJSObject()) {
2923 JSObject* object = JSObject::cast(obj);
2924 // Fast case - no interceptors.
2925 if (object->HasRealNamedProperty(key)) return Heap::true_value();
2926 // Slow case. Either it's not there or we have an interceptor. We should
2927 // have handles for this kind of deal.
2928 HandleScope scope;
2929 return HasLocalPropertyImplementation(Handle<JSObject>(object),
2930 Handle<String>(key));
2931 } else if (obj->IsString()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002932 // Well, there is one exception: Handle [] on strings.
2933 uint32_t index;
2934 if (key->AsArrayIndex(&index)) {
ager@chromium.org9085a012009-05-11 19:22:57 +00002935 String* string = String::cast(obj);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002936 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002937 return Heap::true_value();
2938 }
2939 }
2940 return Heap::false_value();
2941}
2942
2943
2944static Object* Runtime_HasProperty(Arguments args) {
2945 NoHandleAllocation na;
2946 ASSERT(args.length() == 2);
2947
2948 // Only JS objects can have properties.
2949 if (args[0]->IsJSObject()) {
2950 JSObject* object = JSObject::cast(args[0]);
2951 CONVERT_CHECKED(String, key, args[1]);
2952 if (object->HasProperty(key)) return Heap::true_value();
2953 }
2954 return Heap::false_value();
2955}
2956
2957
2958static Object* Runtime_HasElement(Arguments args) {
2959 NoHandleAllocation na;
2960 ASSERT(args.length() == 2);
2961
2962 // Only JS objects can have elements.
2963 if (args[0]->IsJSObject()) {
2964 JSObject* object = JSObject::cast(args[0]);
2965 CONVERT_CHECKED(Smi, index_obj, args[1]);
2966 uint32_t index = index_obj->value();
2967 if (object->HasElement(index)) return Heap::true_value();
2968 }
2969 return Heap::false_value();
2970}
2971
2972
2973static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2974 NoHandleAllocation ha;
2975 ASSERT(args.length() == 2);
2976
2977 CONVERT_CHECKED(JSObject, object, args[0]);
2978 CONVERT_CHECKED(String, key, args[1]);
2979
2980 uint32_t index;
2981 if (key->AsArrayIndex(&index)) {
2982 return Heap::ToBoolean(object->HasElement(index));
2983 }
2984
ager@chromium.org870a0b62008-11-04 11:43:05 +00002985 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2986 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002987}
2988
2989
2990static Object* Runtime_GetPropertyNames(Arguments args) {
2991 HandleScope scope;
2992 ASSERT(args.length() == 1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00002993 CONVERT_ARG_CHECKED(JSObject, object, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002994 return *GetKeysFor(object);
2995}
2996
2997
2998// Returns either a FixedArray as Runtime_GetPropertyNames,
2999// or, if the given object has an enum cache that contains
3000// all enumerable properties of the object and its prototypes
3001// have none, the map of the object. This is used to speed up
3002// the check for deletions during a for-in.
3003static Object* Runtime_GetPropertyNamesFast(Arguments args) {
3004 ASSERT(args.length() == 1);
3005
3006 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3007
3008 if (raw_object->IsSimpleEnum()) return raw_object->map();
3009
3010 HandleScope scope;
3011 Handle<JSObject> object(raw_object);
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003012 Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
3013 INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003014
3015 // Test again, since cache may have been built by preceding call.
3016 if (object->IsSimpleEnum()) return object->map();
3017
3018 return *content;
3019}
3020
3021
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003022static Object* Runtime_LocalKeys(Arguments args) {
3023 ASSERT_EQ(args.length(), 1);
3024 CONVERT_CHECKED(JSObject, raw_object, args[0]);
3025 HandleScope scope;
3026 Handle<JSObject> object(raw_object);
3027 Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
3028 LOCAL_ONLY);
3029 // Some fast paths through GetKeysInFixedArrayFor reuse a cached
3030 // property array and since the result is mutable we have to create
3031 // a fresh clone on each invocation.
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003032 int length = contents->length();
3033 Handle<FixedArray> copy = Factory::NewFixedArray(length);
3034 for (int i = 0; i < length; i++) {
3035 Object* entry = contents->get(i);
3036 if (entry->IsString()) {
3037 copy->set(i, entry);
3038 } else {
3039 ASSERT(entry->IsNumber());
3040 HandleScope scope;
3041 Handle<Object> entry_handle(entry);
3042 Handle<Object> entry_str = Factory::NumberToString(entry_handle);
3043 copy->set(i, *entry_str);
3044 }
3045 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00003046 return *Factory::NewJSArrayWithElements(copy);
3047}
3048
3049
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003050static Object* Runtime_GetArgumentsProperty(Arguments args) {
3051 NoHandleAllocation ha;
3052 ASSERT(args.length() == 1);
3053
3054 // Compute the frame holding the arguments.
3055 JavaScriptFrameIterator it;
3056 it.AdvanceToArgumentsFrame();
3057 JavaScriptFrame* frame = it.frame();
3058
3059 // Get the actual number of provided arguments.
3060 const uint32_t n = frame->GetProvidedParametersCount();
3061
3062 // Try to convert the key to an index. If successful and within
3063 // index return the the argument from the frame.
3064 uint32_t index;
3065 if (Array::IndexFromObject(args[0], &index) && index < n) {
3066 return frame->GetParameter(index);
3067 }
3068
3069 // Convert the key to a string.
3070 HandleScope scope;
3071 bool exception = false;
3072 Handle<Object> converted =
3073 Execution::ToString(args.at<Object>(0), &exception);
3074 if (exception) return Failure::Exception();
3075 Handle<String> key = Handle<String>::cast(converted);
3076
3077 // Try to convert the string key into an array index.
3078 if (key->AsArrayIndex(&index)) {
3079 if (index < n) {
3080 return frame->GetParameter(index);
3081 } else {
3082 return Top::initial_object_prototype()->GetElement(index);
3083 }
3084 }
3085
3086 // Handle special arguments properties.
3087 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
3088 if (key->Equals(Heap::callee_symbol())) return frame->function();
3089
3090 // Lookup in the initial Object.prototype object.
3091 return Top::initial_object_prototype()->GetProperty(*key);
3092}
3093
3094
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003095static Object* Runtime_ToFastProperties(Arguments args) {
3096 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003097 Handle<Object> object = args.at<Object>(0);
3098 if (object->IsJSObject()) {
3099 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
3100 js_object->TransformToFastProperties(0);
3101 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003102 return *object;
3103}
3104
3105
3106static Object* Runtime_ToSlowProperties(Arguments args) {
3107 ASSERT(args.length() == 1);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003108 Handle<Object> object = args.at<Object>(0);
3109 if (object->IsJSObject()) {
3110 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00003111 js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
kasperl@chromium.org7ccf0242009-03-04 12:22:05 +00003112 }
kasperl@chromium.org061ef742009-02-27 12:16:20 +00003113 return *object;
3114}
3115
3116
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003117static Object* Runtime_ToBool(Arguments args) {
3118 NoHandleAllocation ha;
3119 ASSERT(args.length() == 1);
3120
3121 return args[0]->ToBoolean();
3122}
3123
3124
3125// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
3126// Possible optimizations: put the type string into the oddballs.
3127static Object* Runtime_Typeof(Arguments args) {
3128 NoHandleAllocation ha;
3129
3130 Object* obj = args[0];
3131 if (obj->IsNumber()) return Heap::number_symbol();
3132 HeapObject* heap_obj = HeapObject::cast(obj);
3133
3134 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003135 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003136
3137 InstanceType instance_type = heap_obj->map()->instance_type();
3138 if (instance_type < FIRST_NONSTRING_TYPE) {
3139 return Heap::string_symbol();
3140 }
3141
3142 switch (instance_type) {
3143 case ODDBALL_TYPE:
3144 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
3145 return Heap::boolean_symbol();
3146 }
3147 if (heap_obj->IsNull()) {
3148 return Heap::object_symbol();
3149 }
3150 ASSERT(heap_obj->IsUndefined());
3151 return Heap::undefined_symbol();
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00003152 case JS_FUNCTION_TYPE: case JS_REGEXP_TYPE:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003153 return Heap::function_symbol();
3154 default:
3155 // For any kind of object not handled above, the spec rule for
3156 // host objects gives that it is okay to return "object"
3157 return Heap::object_symbol();
3158 }
3159}
3160
3161
3162static Object* Runtime_StringToNumber(Arguments args) {
3163 NoHandleAllocation ha;
3164 ASSERT(args.length() == 1);
3165 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003166 subject->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003167 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
3168}
3169
3170
3171static Object* Runtime_StringFromCharCodeArray(Arguments args) {
3172 NoHandleAllocation ha;
3173 ASSERT(args.length() == 1);
3174
3175 CONVERT_CHECKED(JSArray, codes, args[0]);
3176 int length = Smi::cast(codes->length())->value();
3177
3178 // Check if the string can be ASCII.
3179 int i;
3180 for (i = 0; i < length; i++) {
3181 Object* element = codes->GetElement(i);
3182 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
3183 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
3184 break;
3185 }
3186
3187 Object* object = NULL;
3188 if (i == length) { // The string is ASCII.
3189 object = Heap::AllocateRawAsciiString(length);
3190 } else { // The string is not ASCII.
3191 object = Heap::AllocateRawTwoByteString(length);
3192 }
3193
3194 if (object->IsFailure()) return object;
3195 String* result = String::cast(object);
3196 for (int i = 0; i < length; i++) {
3197 Object* element = codes->GetElement(i);
3198 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003199 result->Set(i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003200 }
3201 return result;
3202}
3203
3204
3205// kNotEscaped is generated by the following:
3206//
3207// #!/bin/perl
3208// for (my $i = 0; $i < 256; $i++) {
3209// print "\n" if $i % 16 == 0;
3210// my $c = chr($i);
3211// my $escaped = 1;
3212// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
3213// print $escaped ? "0, " : "1, ";
3214// }
3215
3216
3217static bool IsNotEscaped(uint16_t character) {
3218 // Only for 8 bit characters, the rest are always escaped (in a different way)
3219 ASSERT(character < 256);
3220 static const char kNotEscaped[256] = {
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, 1, 1, 0, 1, 1, 1,
3224 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3225 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3226 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
3227 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
3228 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3229 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3230 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3231 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3232 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3233 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3234 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3235 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3236 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3237 };
3238 return kNotEscaped[character] != 0;
3239}
3240
3241
3242static Object* Runtime_URIEscape(Arguments args) {
3243 const char hex_chars[] = "0123456789ABCDEF";
3244 NoHandleAllocation ha;
3245 ASSERT(args.length() == 1);
3246 CONVERT_CHECKED(String, source, args[0]);
3247
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003248 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003249
3250 int escaped_length = 0;
3251 int length = source->length();
3252 {
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003253 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003254 buffer->Reset(source);
3255 while (buffer->has_more()) {
3256 uint16_t character = buffer->GetNext();
3257 if (character >= 256) {
3258 escaped_length += 6;
3259 } else if (IsNotEscaped(character)) {
3260 escaped_length++;
3261 } else {
3262 escaped_length += 3;
3263 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003264 // We don't allow strings that are longer than a maximal length.
3265 if (escaped_length > String::kMaxLength) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003266 Top::context()->mark_out_of_memory();
3267 return Failure::OutOfMemoryException();
3268 }
3269 }
3270 }
3271 // No length change implies no change. Return original string if no change.
3272 if (escaped_length == length) {
3273 return source;
3274 }
3275 Object* o = Heap::AllocateRawAsciiString(escaped_length);
3276 if (o->IsFailure()) return o;
3277 String* destination = String::cast(o);
3278 int dest_position = 0;
3279
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003280 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003281 buffer->Rewind();
3282 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00003283 uint16_t chr = buffer->GetNext();
3284 if (chr >= 256) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003285 destination->Set(dest_position, '%');
3286 destination->Set(dest_position+1, 'u');
3287 destination->Set(dest_position+2, hex_chars[chr >> 12]);
3288 destination->Set(dest_position+3, hex_chars[(chr >> 8) & 0xf]);
3289 destination->Set(dest_position+4, hex_chars[(chr >> 4) & 0xf]);
3290 destination->Set(dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003291 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00003292 } else if (IsNotEscaped(chr)) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003293 destination->Set(dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003294 dest_position++;
3295 } else {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003296 destination->Set(dest_position, '%');
3297 destination->Set(dest_position+1, hex_chars[chr >> 4]);
3298 destination->Set(dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003299 dest_position += 3;
3300 }
3301 }
3302 return destination;
3303}
3304
3305
3306static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
3307 static const signed char kHexValue['g'] = {
3308 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3309 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3310 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3311 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3312 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3313 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3314 -1, 10, 11, 12, 13, 14, 15 };
3315
3316 if (character1 > 'f') return -1;
3317 int hi = kHexValue[character1];
3318 if (hi == -1) return -1;
3319 if (character2 > 'f') return -1;
3320 int lo = kHexValue[character2];
3321 if (lo == -1) return -1;
3322 return (hi << 4) + lo;
3323}
3324
3325
ager@chromium.org870a0b62008-11-04 11:43:05 +00003326static inline int Unescape(String* source,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003327 int i,
3328 int length,
3329 int* step) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003330 uint16_t character = source->Get(i);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003331 int32_t hi = 0;
3332 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003333 if (character == '%' &&
3334 i <= length - 6 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003335 source->Get(i + 1) == 'u' &&
3336 (hi = TwoDigitHex(source->Get(i + 2),
3337 source->Get(i + 3))) != -1 &&
3338 (lo = TwoDigitHex(source->Get(i + 4),
3339 source->Get(i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003340 *step = 6;
3341 return (hi << 8) + lo;
3342 } else if (character == '%' &&
3343 i <= length - 3 &&
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003344 (lo = TwoDigitHex(source->Get(i + 1),
3345 source->Get(i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003346 *step = 3;
3347 return lo;
3348 } else {
3349 *step = 1;
3350 return character;
3351 }
3352}
3353
3354
3355static Object* Runtime_URIUnescape(Arguments args) {
3356 NoHandleAllocation ha;
3357 ASSERT(args.length() == 1);
3358 CONVERT_CHECKED(String, source, args[0]);
3359
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003360 source->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003361
3362 bool ascii = true;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003363 int length = source->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003364
3365 int unescaped_length = 0;
3366 for (int i = 0; i < length; unescaped_length++) {
3367 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003368 if (Unescape(source, i, length, &step) > String::kMaxAsciiCharCode) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003369 ascii = false;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003370 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003371 i += step;
3372 }
3373
3374 // No length change implies no change. Return original string if no change.
3375 if (unescaped_length == length)
3376 return source;
3377
3378 Object* o = ascii ?
3379 Heap::AllocateRawAsciiString(unescaped_length) :
3380 Heap::AllocateRawTwoByteString(unescaped_length);
3381 if (o->IsFailure()) return o;
3382 String* destination = String::cast(o);
3383
3384 int dest_position = 0;
3385 for (int i = 0; i < length; dest_position++) {
3386 int step;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003387 destination->Set(dest_position, Unescape(source, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003388 i += step;
3389 }
3390 return destination;
3391}
3392
3393
3394static Object* Runtime_StringParseInt(Arguments args) {
3395 NoHandleAllocation ha;
3396
3397 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003398 CONVERT_SMI_CHECKED(radix, args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003399
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003400 s->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003401
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003402 int len = s->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003403 int i;
3404
3405 // Skip leading white space.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003406 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003407 if (i == len) return Heap::nan_value();
3408
3409 // Compute the sign (default to +).
3410 int sign = 1;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003411 if (s->Get(i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003412 sign = -1;
3413 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003414 } else if (s->Get(i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003415 i++;
3416 }
3417
3418 // Compute the radix if 0.
3419 if (radix == 0) {
3420 radix = 10;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003421 if (i < len && s->Get(i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003422 radix = 8;
3423 if (i + 1 < len) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003424 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003425 if (c == 'x' || c == 'X') {
3426 radix = 16;
3427 i += 2;
3428 }
3429 }
3430 }
3431 } else if (radix == 16) {
3432 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003433 if (i + 1 < len && s->Get(i) == '0') {
3434 int c = s->Get(i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003435 if (c == 'x' || c == 'X') i += 2;
3436 }
3437 }
3438
3439 RUNTIME_ASSERT(2 <= radix && radix <= 36);
3440 double value;
3441 int end_index = StringToInt(s, i, radix, &value);
3442 if (end_index != i) {
3443 return Heap::NumberFromDouble(sign * value);
3444 }
3445 return Heap::nan_value();
3446}
3447
3448
3449static Object* Runtime_StringParseFloat(Arguments args) {
3450 NoHandleAllocation ha;
3451 CONVERT_CHECKED(String, str, args[0]);
3452
3453 // ECMA-262 section 15.1.2.3, empty string is NaN
3454 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
3455
3456 // Create a number object from the value.
3457 return Heap::NumberFromDouble(value);
3458}
3459
3460
3461static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
3462static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
3463
3464
3465template <class Converter>
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003466static Object* ConvertCaseHelper(String* s,
3467 int length,
3468 int input_string_length,
3469 unibrow::Mapping<Converter, 128>* mapping) {
3470 // We try this twice, once with the assumption that the result is no longer
3471 // than the input and, if that assumption breaks, again with the exact
3472 // length. This may not be pretty, but it is nicer than what was here before
3473 // and I hereby claim my vaffel-is.
3474 //
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003475 // Allocate the resulting string.
3476 //
3477 // NOTE: This assumes that the upper/lower case of an ascii
3478 // character is also ascii. This is currently the case, but it
3479 // might break in the future if we implement more context and locale
3480 // dependent upper/lower conversions.
ager@chromium.org5ec48922009-05-05 07:25:34 +00003481 Object* o = s->IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003482 ? Heap::AllocateRawAsciiString(length)
3483 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003484 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003485 String* result = String::cast(o);
3486 bool has_changed_character = false;
3487
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003488 // Convert all characters to upper case, assuming that they will fit
3489 // in the buffer
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00003490 Access<StringInputBuffer> buffer(&runtime_string_input_buffer);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003491 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003492 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003493 // We can assume that the string is not empty
3494 uc32 current = buffer->GetNext();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003495 for (int i = 0; i < length;) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00003496 bool has_next = buffer->has_more();
3497 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003498 int char_length = mapping->get(current, next, chars);
3499 if (char_length == 0) {
3500 // The case conversion of this character is the character itself.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003501 result->Set(i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003502 i++;
3503 } else if (char_length == 1) {
3504 // Common case: converting the letter resulted in one character.
3505 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003506 result->Set(i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003507 has_changed_character = true;
3508 i++;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003509 } else if (length == input_string_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003510 // We've assumed that the result would be as long as the
3511 // input but here is a character that converts to several
3512 // characters. No matter, we calculate the exact length
3513 // of the result and try the whole thing again.
3514 //
3515 // Note that this leaves room for optimization. We could just
3516 // memcpy what we already have to the result string. Also,
3517 // the result string is the last object allocated we could
3518 // "realloc" it and probably, in the vast majority of cases,
3519 // extend the existing string to be able to hold the full
3520 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00003521 int next_length = 0;
3522 if (has_next) {
3523 next_length = mapping->get(next, 0, chars);
3524 if (next_length == 0) next_length = 1;
3525 }
3526 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003527 while (buffer->has_more()) {
3528 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003529 // NOTE: we use 0 as the next character here because, while
3530 // the next character may affect what a character converts to,
3531 // it does not in any case affect the length of what it convert
3532 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003533 int char_length = mapping->get(current, 0, chars);
3534 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00003535 current_length += char_length;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003536 if (current_length > Smi::kMaxValue) {
3537 Top::context()->mark_out_of_memory();
3538 return Failure::OutOfMemoryException();
3539 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003540 }
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003541 // Try again with the real length.
3542 return Smi::FromInt(current_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003543 } else {
3544 for (int j = 0; j < char_length; j++) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003545 result->Set(i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003546 i++;
3547 }
3548 has_changed_character = true;
3549 }
3550 current = next;
3551 }
3552 if (has_changed_character) {
3553 return result;
3554 } else {
3555 // If we didn't actually change anything in doing the conversion
3556 // we simple return the result and let the converted string
3557 // become garbage; there is no reason to keep two identical strings
3558 // alive.
3559 return s;
3560 }
3561}
3562
3563
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003564template <class Converter>
3565static Object* ConvertCase(Arguments args,
3566 unibrow::Mapping<Converter, 128>* mapping) {
3567 NoHandleAllocation ha;
3568
3569 CONVERT_CHECKED(String, s, args[0]);
3570 s->TryFlattenIfNotFlat();
3571
3572 int input_string_length = s->length();
3573 // Assume that the string is not empty; we need this assumption later
3574 if (input_string_length == 0) return s;
3575 int length = input_string_length;
3576
3577 Object* answer = ConvertCaseHelper(s, length, length, mapping);
3578 if (answer->IsSmi()) {
3579 // Retry with correct length.
3580 answer = ConvertCaseHelper(s, Smi::cast(answer)->value(), length, mapping);
3581 }
3582 return answer; // This may be a failure.
3583}
3584
3585
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003586static Object* Runtime_StringToLowerCase(Arguments args) {
3587 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
3588}
3589
3590
3591static Object* Runtime_StringToUpperCase(Arguments args) {
3592 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
3593}
3594
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003595static inline bool IsTrimWhiteSpace(unibrow::uchar c) {
3596 return unibrow::WhiteSpace::Is(c) || c == 0x200b;
3597}
3598
3599static Object* Runtime_StringTrim(Arguments args) {
3600 NoHandleAllocation ha;
3601 ASSERT(args.length() == 3);
3602
3603 CONVERT_CHECKED(String, s, args[0]);
3604 CONVERT_BOOLEAN_CHECKED(trimLeft, args[1]);
3605 CONVERT_BOOLEAN_CHECKED(trimRight, args[2]);
3606
3607 s->TryFlattenIfNotFlat();
3608 int length = s->length();
3609
3610 int left = 0;
3611 if (trimLeft) {
3612 while (left < length && IsTrimWhiteSpace(s->Get(left))) {
3613 left++;
3614 }
3615 }
3616
3617 int right = length;
3618 if (trimRight) {
3619 while (right > left && IsTrimWhiteSpace(s->Get(right - 1))) {
3620 right--;
3621 }
3622 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003623 return s->SubString(left, right);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003624}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003625
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00003626bool Runtime::IsUpperCaseChar(uint16_t ch) {
3627 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth];
3628 int char_length = to_upper_mapping.get(ch, 0, chars);
3629 return char_length == 0;
3630}
3631
3632
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003633static Object* Runtime_NumberToString(Arguments args) {
3634 NoHandleAllocation ha;
3635 ASSERT(args.length() == 1);
3636
3637 Object* number = args[0];
3638 RUNTIME_ASSERT(number->IsNumber());
3639
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00003640 return Heap::NumberToString(number);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003641}
3642
3643
3644static Object* Runtime_NumberToInteger(Arguments args) {
3645 NoHandleAllocation ha;
3646 ASSERT(args.length() == 1);
3647
3648 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003649 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003650 CONVERT_DOUBLE_CHECKED(number, obj);
3651 return Heap::NumberFromDouble(DoubleToInteger(number));
3652}
3653
3654
3655static Object* Runtime_NumberToJSUint32(Arguments args) {
3656 NoHandleAllocation ha;
3657 ASSERT(args.length() == 1);
3658
3659 Object* obj = args[0];
3660 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
3661 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
3662 return Heap::NumberFromUint32(number);
3663}
3664
3665
3666static Object* Runtime_NumberToJSInt32(Arguments args) {
3667 NoHandleAllocation ha;
3668 ASSERT(args.length() == 1);
3669
3670 Object* obj = args[0];
3671 if (obj->IsSmi()) return obj;
3672 CONVERT_DOUBLE_CHECKED(number, obj);
3673 return Heap::NumberFromInt32(DoubleToInt32(number));
3674}
3675
3676
ager@chromium.org870a0b62008-11-04 11:43:05 +00003677// Converts a Number to a Smi, if possible. Returns NaN if the number is not
3678// a small integer.
3679static Object* Runtime_NumberToSmi(Arguments args) {
3680 NoHandleAllocation ha;
3681 ASSERT(args.length() == 1);
3682
3683 Object* obj = args[0];
3684 if (obj->IsSmi()) {
3685 return obj;
3686 }
3687 if (obj->IsHeapNumber()) {
3688 double value = HeapNumber::cast(obj)->value();
3689 int int_value = FastD2I(value);
3690 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
3691 return Smi::FromInt(int_value);
3692 }
3693 }
3694 return Heap::nan_value();
3695}
3696
ager@chromium.org65dad4b2009-04-23 08:48:43 +00003697
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003698static Object* Runtime_NumberAdd(Arguments args) {
3699 NoHandleAllocation ha;
3700 ASSERT(args.length() == 2);
3701
3702 CONVERT_DOUBLE_CHECKED(x, args[0]);
3703 CONVERT_DOUBLE_CHECKED(y, args[1]);
3704 return Heap::AllocateHeapNumber(x + y);
3705}
3706
3707
3708static Object* Runtime_NumberSub(Arguments args) {
3709 NoHandleAllocation ha;
3710 ASSERT(args.length() == 2);
3711
3712 CONVERT_DOUBLE_CHECKED(x, args[0]);
3713 CONVERT_DOUBLE_CHECKED(y, args[1]);
3714 return Heap::AllocateHeapNumber(x - y);
3715}
3716
3717
3718static Object* Runtime_NumberMul(Arguments args) {
3719 NoHandleAllocation ha;
3720 ASSERT(args.length() == 2);
3721
3722 CONVERT_DOUBLE_CHECKED(x, args[0]);
3723 CONVERT_DOUBLE_CHECKED(y, args[1]);
3724 return Heap::AllocateHeapNumber(x * y);
3725}
3726
3727
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003728static Object* Runtime_NumberUnaryMinus(Arguments args) {
3729 NoHandleAllocation ha;
3730 ASSERT(args.length() == 1);
3731
3732 CONVERT_DOUBLE_CHECKED(x, args[0]);
3733 return Heap::AllocateHeapNumber(-x);
3734}
3735
3736
3737static Object* Runtime_NumberDiv(Arguments args) {
3738 NoHandleAllocation ha;
3739 ASSERT(args.length() == 2);
3740
3741 CONVERT_DOUBLE_CHECKED(x, args[0]);
3742 CONVERT_DOUBLE_CHECKED(y, args[1]);
3743 return Heap::NewNumberFromDouble(x / y);
3744}
3745
3746
3747static Object* Runtime_NumberMod(Arguments args) {
3748 NoHandleAllocation ha;
3749 ASSERT(args.length() == 2);
3750
3751 CONVERT_DOUBLE_CHECKED(x, args[0]);
3752 CONVERT_DOUBLE_CHECKED(y, args[1]);
3753
ager@chromium.org3811b432009-10-28 14:53:37 +00003754 x = modulo(x, y);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003755 // NewNumberFromDouble may return a Smi instead of a Number object
3756 return Heap::NewNumberFromDouble(x);
3757}
3758
3759
3760static Object* Runtime_StringAdd(Arguments args) {
3761 NoHandleAllocation ha;
3762 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003763 CONVERT_CHECKED(String, str1, args[0]);
3764 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00003765 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003766}
3767
3768
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003769template<typename sinkchar>
3770static inline void StringBuilderConcatHelper(String* special,
3771 sinkchar* sink,
3772 FixedArray* fixed_array,
3773 int array_length) {
3774 int position = 0;
3775 for (int i = 0; i < array_length; i++) {
3776 Object* element = fixed_array->get(i);
3777 if (element->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003778 // Smi encoding of position and length.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003779 int encoded_slice = Smi::cast(element)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003780 int pos;
3781 int len;
3782 if (encoded_slice > 0) {
3783 // Position and length encoded in one smi.
3784 pos = StringBuilderSubstringPosition::decode(encoded_slice);
3785 len = StringBuilderSubstringLength::decode(encoded_slice);
3786 } else {
3787 // Position and length encoded in two smis.
3788 Object* obj = fixed_array->get(++i);
3789 ASSERT(obj->IsSmi());
3790 pos = Smi::cast(obj)->value();
3791 len = -encoded_slice;
3792 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00003793 String::WriteToFlat(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00003794 sink + position,
3795 pos,
3796 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003797 position += len;
3798 } else {
3799 String* string = String::cast(element);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003800 int element_length = string->length();
3801 String::WriteToFlat(string, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003802 position += element_length;
3803 }
3804 }
3805}
3806
3807
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003808static Object* Runtime_StringBuilderConcat(Arguments args) {
3809 NoHandleAllocation ha;
3810 ASSERT(args.length() == 2);
3811 CONVERT_CHECKED(JSArray, array, args[0]);
3812 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003813
3814 // This assumption is used by the slice encoding in one or two smis.
3815 ASSERT(Smi::kMaxValue >= String::kMaxLength);
3816
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003817 int special_length = special->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003818 Object* smi_array_length = array->length();
3819 if (!smi_array_length->IsSmi()) {
3820 Top::context()->mark_out_of_memory();
3821 return Failure::OutOfMemoryException();
3822 }
3823 int array_length = Smi::cast(smi_array_length)->value();
3824 if (!array->HasFastElements()) {
3825 return Top::Throw(Heap::illegal_argument_symbol());
3826 }
3827 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003828 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003829 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003830 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003831
3832 if (array_length == 0) {
3833 return Heap::empty_string();
3834 } else if (array_length == 1) {
3835 Object* first = fixed_array->get(0);
3836 if (first->IsString()) return first;
3837 }
3838
ager@chromium.org5ec48922009-05-05 07:25:34 +00003839 bool ascii = special->IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003840 int position = 0;
3841 for (int i = 0; i < array_length; i++) {
3842 Object* elt = fixed_array->get(i);
3843 if (elt->IsSmi()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003844 // Smi encoding of position and length.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003845 int len = Smi::cast(elt)->value();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00003846 if (len > 0) {
3847 // Position and length encoded in one smi.
3848 int pos = len >> 11;
3849 len &= 0x7ff;
3850 if (pos + len > special_length) {
3851 return Top::Throw(Heap::illegal_argument_symbol());
3852 }
3853 position += len;
3854 } else {
3855 // Position and length encoded in two smis.
3856 position += (-len);
3857 // Get the position and check that it is also a smi.
3858 i++;
3859 if (i >= array_length) {
3860 return Top::Throw(Heap::illegal_argument_symbol());
3861 }
3862 Object* pos = fixed_array->get(i);
3863 if (!pos->IsSmi()) {
3864 return Top::Throw(Heap::illegal_argument_symbol());
3865 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003866 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003867 } else if (elt->IsString()) {
3868 String* element = String::cast(elt);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00003869 int element_length = element->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003870 position += element_length;
ager@chromium.org5ec48922009-05-05 07:25:34 +00003871 if (ascii && !element->IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003872 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003873 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003874 } else {
3875 return Top::Throw(Heap::illegal_argument_symbol());
3876 }
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00003877 if (position > String::kMaxLength) {
3878 Top::context()->mark_out_of_memory();
3879 return Failure::OutOfMemoryException();
3880 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003881 }
3882
3883 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003884 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003885
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003886 if (ascii) {
3887 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003888 if (object->IsFailure()) return object;
3889 SeqAsciiString* answer = SeqAsciiString::cast(object);
3890 StringBuilderConcatHelper(special,
3891 answer->GetChars(),
3892 fixed_array,
3893 array_length);
3894 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003895 } else {
3896 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003897 if (object->IsFailure()) return object;
3898 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
3899 StringBuilderConcatHelper(special,
3900 answer->GetChars(),
3901 fixed_array,
3902 array_length);
3903 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003904 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003905}
3906
3907
3908static Object* Runtime_NumberOr(Arguments args) {
3909 NoHandleAllocation ha;
3910 ASSERT(args.length() == 2);
3911
3912 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3913 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3914 return Heap::NumberFromInt32(x | y);
3915}
3916
3917
3918static Object* Runtime_NumberAnd(Arguments args) {
3919 NoHandleAllocation ha;
3920 ASSERT(args.length() == 2);
3921
3922 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3923 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3924 return Heap::NumberFromInt32(x & y);
3925}
3926
3927
3928static Object* Runtime_NumberXor(Arguments args) {
3929 NoHandleAllocation ha;
3930 ASSERT(args.length() == 2);
3931
3932 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3933 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3934 return Heap::NumberFromInt32(x ^ y);
3935}
3936
3937
3938static Object* Runtime_NumberNot(Arguments args) {
3939 NoHandleAllocation ha;
3940 ASSERT(args.length() == 1);
3941
3942 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3943 return Heap::NumberFromInt32(~x);
3944}
3945
3946
3947static Object* Runtime_NumberShl(Arguments args) {
3948 NoHandleAllocation ha;
3949 ASSERT(args.length() == 2);
3950
3951 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3952 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3953 return Heap::NumberFromInt32(x << (y & 0x1f));
3954}
3955
3956
3957static Object* Runtime_NumberShr(Arguments args) {
3958 NoHandleAllocation ha;
3959 ASSERT(args.length() == 2);
3960
3961 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
3962 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3963 return Heap::NumberFromUint32(x >> (y & 0x1f));
3964}
3965
3966
3967static Object* Runtime_NumberSar(Arguments args) {
3968 NoHandleAllocation ha;
3969 ASSERT(args.length() == 2);
3970
3971 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
3972 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
3973 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
3974}
3975
3976
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003977static Object* Runtime_NumberEquals(Arguments args) {
3978 NoHandleAllocation ha;
3979 ASSERT(args.length() == 2);
3980
3981 CONVERT_DOUBLE_CHECKED(x, args[0]);
3982 CONVERT_DOUBLE_CHECKED(y, args[1]);
3983 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
3984 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
3985 if (x == y) return Smi::FromInt(EQUAL);
3986 Object* result;
3987 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
3988 result = Smi::FromInt(EQUAL);
3989 } else {
3990 result = Smi::FromInt(NOT_EQUAL);
3991 }
3992 return result;
3993}
3994
3995
3996static Object* Runtime_StringEquals(Arguments args) {
3997 NoHandleAllocation ha;
3998 ASSERT(args.length() == 2);
3999
4000 CONVERT_CHECKED(String, x, args[0]);
4001 CONVERT_CHECKED(String, y, args[1]);
4002
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004003 bool not_equal = !x->Equals(y);
4004 // This is slightly convoluted because the value that signifies
4005 // equality is 0 and inequality is 1 so we have to negate the result
4006 // from String::Equals.
4007 ASSERT(not_equal == 0 || not_equal == 1);
4008 STATIC_CHECK(EQUAL == 0);
4009 STATIC_CHECK(NOT_EQUAL == 1);
4010 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004011}
4012
4013
4014static Object* Runtime_NumberCompare(Arguments args) {
4015 NoHandleAllocation ha;
4016 ASSERT(args.length() == 3);
4017
4018 CONVERT_DOUBLE_CHECKED(x, args[0]);
4019 CONVERT_DOUBLE_CHECKED(y, args[1]);
4020 if (isnan(x) || isnan(y)) return args[2];
4021 if (x == y) return Smi::FromInt(EQUAL);
4022 if (isless(x, y)) return Smi::FromInt(LESS);
4023 return Smi::FromInt(GREATER);
4024}
4025
4026
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004027// Compare two Smis as if they were converted to strings and then
4028// compared lexicographically.
4029static Object* Runtime_SmiLexicographicCompare(Arguments args) {
4030 NoHandleAllocation ha;
4031 ASSERT(args.length() == 2);
4032
4033 // Arrays for the individual characters of the two Smis. Smis are
4034 // 31 bit integers and 10 decimal digits are therefore enough.
4035 static int x_elms[10];
4036 static int y_elms[10];
4037
4038 // Extract the integer values from the Smis.
4039 CONVERT_CHECKED(Smi, x, args[0]);
4040 CONVERT_CHECKED(Smi, y, args[1]);
4041 int x_value = x->value();
4042 int y_value = y->value();
4043
4044 // If the integers are equal so are the string representations.
4045 if (x_value == y_value) return Smi::FromInt(EQUAL);
4046
4047 // If one of the integers are zero the normal integer order is the
4048 // same as the lexicographic order of the string representations.
4049 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
4050
ager@chromium.org32912102009-01-16 10:38:43 +00004051 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004052 // smallest because the char code of '-' is less than the char code
4053 // of any digit. Otherwise, we make both values positive.
4054 if (x_value < 0 || y_value < 0) {
4055 if (y_value >= 0) return Smi::FromInt(LESS);
4056 if (x_value >= 0) return Smi::FromInt(GREATER);
4057 x_value = -x_value;
4058 y_value = -y_value;
4059 }
4060
4061 // Convert the integers to arrays of their decimal digits.
4062 int x_index = 0;
4063 int y_index = 0;
4064 while (x_value > 0) {
4065 x_elms[x_index++] = x_value % 10;
4066 x_value /= 10;
4067 }
4068 while (y_value > 0) {
4069 y_elms[y_index++] = y_value % 10;
4070 y_value /= 10;
4071 }
4072
4073 // Loop through the arrays of decimal digits finding the first place
4074 // where they differ.
4075 while (--x_index >= 0 && --y_index >= 0) {
4076 int diff = x_elms[x_index] - y_elms[y_index];
4077 if (diff != 0) return Smi::FromInt(diff);
4078 }
4079
4080 // If one array is a suffix of the other array, the longest array is
4081 // the representation of the largest of the Smis in the
4082 // lexicographic ordering.
4083 return Smi::FromInt(x_index - y_index);
4084}
4085
4086
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004087static Object* Runtime_StringCompare(Arguments args) {
4088 NoHandleAllocation ha;
4089 ASSERT(args.length() == 2);
4090
4091 CONVERT_CHECKED(String, x, args[0]);
4092 CONVERT_CHECKED(String, y, args[1]);
4093
4094 // A few fast case tests before we flatten.
4095 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004096 if (y->length() == 0) {
4097 if (x->length() == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004098 return Smi::FromInt(GREATER);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004099 } else if (x->length() == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004100 return Smi::FromInt(LESS);
4101 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004102
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004103 int d = x->Get(0) - y->Get(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004104 if (d < 0) return Smi::FromInt(LESS);
4105 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004106
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004107 x->TryFlattenIfNotFlat();
4108 y->TryFlattenIfNotFlat();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004109
4110 static StringInputBuffer bufx;
4111 static StringInputBuffer bufy;
4112 bufx.Reset(x);
4113 bufy.Reset(y);
4114 while (bufx.has_more() && bufy.has_more()) {
4115 int d = bufx.GetNext() - bufy.GetNext();
4116 if (d < 0) return Smi::FromInt(LESS);
4117 else if (d > 0) return Smi::FromInt(GREATER);
4118 }
4119
4120 // x is (non-trivial) prefix of y:
4121 if (bufy.has_more()) return Smi::FromInt(LESS);
4122 // y is prefix of x:
4123 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
4124}
4125
4126
4127static Object* Runtime_Math_abs(Arguments args) {
4128 NoHandleAllocation ha;
4129 ASSERT(args.length() == 1);
4130
4131 CONVERT_DOUBLE_CHECKED(x, args[0]);
4132 return Heap::AllocateHeapNumber(fabs(x));
4133}
4134
4135
4136static Object* Runtime_Math_acos(Arguments args) {
4137 NoHandleAllocation ha;
4138 ASSERT(args.length() == 1);
4139
4140 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004141 return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004142}
4143
4144
4145static Object* Runtime_Math_asin(Arguments args) {
4146 NoHandleAllocation ha;
4147 ASSERT(args.length() == 1);
4148
4149 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004150 return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004151}
4152
4153
4154static Object* Runtime_Math_atan(Arguments args) {
4155 NoHandleAllocation ha;
4156 ASSERT(args.length() == 1);
4157
4158 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004159 return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004160}
4161
4162
4163static Object* Runtime_Math_atan2(Arguments args) {
4164 NoHandleAllocation ha;
4165 ASSERT(args.length() == 2);
4166
4167 CONVERT_DOUBLE_CHECKED(x, args[0]);
4168 CONVERT_DOUBLE_CHECKED(y, args[1]);
4169 double result;
4170 if (isinf(x) && isinf(y)) {
4171 // Make sure that the result in case of two infinite arguments
4172 // is a multiple of Pi / 4. The sign of the result is determined
4173 // by the first argument (x) and the sign of the second argument
4174 // determines the multiplier: one or three.
4175 static double kPiDividedBy4 = 0.78539816339744830962;
4176 int multiplier = (x < 0) ? -1 : 1;
4177 if (y < 0) multiplier *= 3;
4178 result = multiplier * kPiDividedBy4;
4179 } else {
4180 result = atan2(x, y);
4181 }
4182 return Heap::AllocateHeapNumber(result);
4183}
4184
4185
4186static Object* Runtime_Math_ceil(Arguments args) {
4187 NoHandleAllocation ha;
4188 ASSERT(args.length() == 1);
4189
4190 CONVERT_DOUBLE_CHECKED(x, args[0]);
4191 return Heap::NumberFromDouble(ceiling(x));
4192}
4193
4194
4195static Object* Runtime_Math_cos(Arguments args) {
4196 NoHandleAllocation ha;
4197 ASSERT(args.length() == 1);
4198
4199 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004200 return TranscendentalCache::Get(TranscendentalCache::COS, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004201}
4202
4203
4204static Object* Runtime_Math_exp(Arguments args) {
4205 NoHandleAllocation ha;
4206 ASSERT(args.length() == 1);
4207
4208 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004209 return TranscendentalCache::Get(TranscendentalCache::EXP, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004210}
4211
4212
4213static Object* Runtime_Math_floor(Arguments args) {
4214 NoHandleAllocation ha;
4215 ASSERT(args.length() == 1);
4216
4217 CONVERT_DOUBLE_CHECKED(x, args[0]);
4218 return Heap::NumberFromDouble(floor(x));
4219}
4220
4221
4222static Object* Runtime_Math_log(Arguments args) {
4223 NoHandleAllocation ha;
4224 ASSERT(args.length() == 1);
4225
4226 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004227 return TranscendentalCache::Get(TranscendentalCache::LOG, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004228}
4229
4230
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004231// Helper function to compute x^y, where y is known to be an
4232// integer. Uses binary decomposition to limit the number of
4233// multiplications; see the discussion in "Hacker's Delight" by Henry
4234// S. Warren, Jr., figure 11-6, page 213.
4235static double powi(double x, int y) {
4236 ASSERT(y != kMinInt);
4237 unsigned n = (y < 0) ? -y : y;
4238 double m = x;
4239 double p = 1;
4240 while (true) {
4241 if ((n & 1) != 0) p *= m;
4242 n >>= 1;
4243 if (n == 0) {
4244 if (y < 0) {
4245 // Unfortunately, we have to be careful when p has reached
4246 // infinity in the computation, because sometimes the higher
4247 // internal precision in the pow() implementation would have
4248 // given us a finite p. This happens very rarely.
4249 double result = 1.0 / p;
4250 return (result == 0 && isinf(p))
4251 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
4252 : result;
4253 } else {
4254 return p;
4255 }
4256 }
4257 m *= m;
4258 }
4259}
4260
4261
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004262static Object* Runtime_Math_pow(Arguments args) {
4263 NoHandleAllocation ha;
4264 ASSERT(args.length() == 2);
4265
4266 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004267
4268 // If the second argument is a smi, it is much faster to call the
4269 // custom powi() function than the generic pow().
4270 if (args[1]->IsSmi()) {
4271 int y = Smi::cast(args[1])->value();
4272 return Heap::AllocateHeapNumber(powi(x, y));
4273 }
4274
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004275 CONVERT_DOUBLE_CHECKED(y, args[1]);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00004276
4277 if (!isinf(x)) {
4278 if (y == 0.5) {
4279 // It's not uncommon to use Math.pow(x, 0.5) to compute the
4280 // square root of a number. To speed up such computations, we
4281 // explictly check for this case and use the sqrt() function
4282 // which is faster than pow().
4283 return Heap::AllocateHeapNumber(sqrt(x));
4284 } else if (y == -0.5) {
4285 // Optimized using Math.pow(x, -0.5) == 1 / Math.pow(x, 0.5).
4286 return Heap::AllocateHeapNumber(1.0 / sqrt(x));
4287 }
4288 }
4289
4290 if (y == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004291 return Smi::FromInt(1);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004292 } else if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
4293 return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004294 } else {
4295 return Heap::AllocateHeapNumber(pow(x, y));
4296 }
4297}
4298
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004299
4300static Object* Runtime_Math_round(Arguments args) {
4301 NoHandleAllocation ha;
4302 ASSERT(args.length() == 1);
4303
4304 CONVERT_DOUBLE_CHECKED(x, args[0]);
4305 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
4306 return Heap::NumberFromDouble(floor(x + 0.5));
4307}
4308
4309
4310static Object* Runtime_Math_sin(Arguments args) {
4311 NoHandleAllocation ha;
4312 ASSERT(args.length() == 1);
4313
4314 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004315 return TranscendentalCache::Get(TranscendentalCache::SIN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004316}
4317
4318
4319static Object* Runtime_Math_sqrt(Arguments args) {
4320 NoHandleAllocation ha;
4321 ASSERT(args.length() == 1);
4322
4323 CONVERT_DOUBLE_CHECKED(x, args[0]);
4324 return Heap::AllocateHeapNumber(sqrt(x));
4325}
4326
4327
4328static Object* Runtime_Math_tan(Arguments args) {
4329 NoHandleAllocation ha;
4330 ASSERT(args.length() == 1);
4331
4332 CONVERT_DOUBLE_CHECKED(x, args[0]);
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004333 return TranscendentalCache::Get(TranscendentalCache::TAN, x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004334}
4335
4336
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004337// The NewArguments function is only used when constructing the
4338// arguments array when calling non-functions from JavaScript in
4339// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004340static Object* Runtime_NewArguments(Arguments args) {
4341 NoHandleAllocation ha;
4342 ASSERT(args.length() == 1);
4343
4344 // ECMA-262, 3rd., 10.1.8, p.39
4345 CONVERT_CHECKED(JSFunction, callee, args[0]);
4346
4347 // Compute the frame holding the arguments.
4348 JavaScriptFrameIterator it;
4349 it.AdvanceToArgumentsFrame();
4350 JavaScriptFrame* frame = it.frame();
4351
4352 const int length = frame->GetProvidedParametersCount();
4353 Object* result = Heap::AllocateArgumentsObject(callee, length);
4354 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004355 if (length > 0) {
4356 Object* obj = Heap::AllocateFixedArray(length);
4357 if (obj->IsFailure()) return obj;
4358 FixedArray* array = FixedArray::cast(obj);
4359 ASSERT(array->length() == length);
4360 WriteBarrierMode mode = array->GetWriteBarrierMode();
4361 for (int i = 0; i < length; i++) {
4362 array->set(i, frame->GetParameter(i), mode);
4363 }
4364 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004365 }
4366 return result;
4367}
4368
4369
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004370static Object* Runtime_NewArgumentsFast(Arguments args) {
4371 NoHandleAllocation ha;
4372 ASSERT(args.length() == 3);
4373
4374 JSFunction* callee = JSFunction::cast(args[0]);
4375 Object** parameters = reinterpret_cast<Object**>(args[1]);
4376 const int length = Smi::cast(args[2])->value();
4377
4378 Object* result = Heap::AllocateArgumentsObject(callee, length);
4379 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004380 // Allocate the elements if needed.
4381 if (length > 0) {
4382 // Allocate the fixed array.
4383 Object* obj = Heap::AllocateRawFixedArray(length);
4384 if (obj->IsFailure()) return obj;
4385 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
4386 FixedArray* array = FixedArray::cast(obj);
4387 array->set_length(length);
4388 WriteBarrierMode mode = array->GetWriteBarrierMode();
4389 for (int i = 0; i < length; i++) {
4390 array->set(i, *--parameters, mode);
4391 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004392 JSObject::cast(result)->set_elements(FixedArray::cast(obj));
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00004393 }
4394 return result;
4395}
4396
4397
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004398static Object* Runtime_NewClosure(Arguments args) {
4399 HandleScope scope;
4400 ASSERT(args.length() == 2);
ager@chromium.org3811b432009-10-28 14:53:37 +00004401 CONVERT_ARG_CHECKED(Context, context, 0);
4402 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004403
4404 Handle<JSFunction> result =
4405 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4406 return *result;
4407}
4408
4409
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004410static Code* ComputeConstructStub(Handle<SharedFunctionInfo> shared) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004411 // TODO(385): Change this to create a construct stub specialized for
4412 // the given map to make allocation of simple objects - and maybe
4413 // arrays - much faster.
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004414 if (FLAG_inline_new
4415 && shared->has_only_simple_this_property_assignments()) {
4416 ConstructStubCompiler compiler;
4417 Object* code = compiler.CompileConstructStub(*shared);
4418 if (code->IsFailure()) {
4419 return Builtins::builtin(Builtins::JSConstructStubGeneric);
4420 }
4421 return Code::cast(code);
4422 }
4423
4424 return Builtins::builtin(Builtins::JSConstructStubGeneric);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004425}
4426
4427
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004428static Object* Runtime_NewObject(Arguments args) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004429 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004430 ASSERT(args.length() == 1);
4431
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004432 Handle<Object> constructor = args.at<Object>(0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004433
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004434 // If the constructor isn't a proper function we throw a type error.
4435 if (!constructor->IsJSFunction()) {
4436 Vector< Handle<Object> > arguments = HandleVector(&constructor, 1);
4437 Handle<Object> type_error =
4438 Factory::NewTypeError("not_constructor", arguments);
4439 return Top::Throw(*type_error);
4440 }
4441
4442 Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004443#ifdef ENABLE_DEBUGGER_SUPPORT
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004444 // Handle stepping into constructors if step into is active.
4445 if (Debug::StepInActive()) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00004446 Debug::HandleStepIn(function, Handle<Object>::null(), 0, true);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004447 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +00004448#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004449
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004450 if (function->has_initial_map()) {
4451 if (function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004452 // The 'Function' function ignores the receiver object when
4453 // called using 'new' and creates a new JSFunction object that
4454 // is returned. The receiver object is only used for error
4455 // reporting if an error occurs when constructing the new
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004456 // JSFunction. Factory::NewJSObject() should not be used to
4457 // allocate JSFunctions since it does not properly initialize
4458 // the shared part of the function. Since the receiver is
4459 // ignored anyway, we use the global object as the receiver
4460 // instead of a new JSFunction object. This way, errors are
4461 // reported the same way whether or not 'Function' is called
4462 // using 'new'.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004463 return Top::context()->global();
4464 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004465 }
4466
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004467 // The function should be compiled for the optimization hints to be available.
4468 if (!function->shared()->is_compiled()) {
4469 CompileLazyShared(Handle<SharedFunctionInfo>(function->shared()),
4470 CLEAR_EXCEPTION,
4471 0);
4472 }
4473
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004474 bool first_allocation = !function->has_initial_map();
4475 Handle<JSObject> result = Factory::NewJSObject(function);
4476 if (first_allocation) {
4477 Handle<Map> map = Handle<Map>(function->initial_map());
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004478 Handle<Code> stub = Handle<Code>(
4479 ComputeConstructStub(Handle<SharedFunctionInfo>(function->shared())));
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004480 function->shared()->set_construct_stub(*stub);
4481 }
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004482
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00004483 Counters::constructed_objects.Increment();
4484 Counters::constructed_objects_runtime.Increment();
ager@chromium.org18ad94b2009-09-02 08:22:29 +00004485
ager@chromium.org5aa501c2009-06-23 07:57:28 +00004486 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004487}
4488
4489
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004490static Object* Runtime_LazyCompile(Arguments args) {
4491 HandleScope scope;
4492 ASSERT(args.length() == 1);
4493
4494 Handle<JSFunction> function = args.at<JSFunction>(0);
4495#ifdef DEBUG
4496 if (FLAG_trace_lazy) {
4497 PrintF("[lazy: ");
4498 function->shared()->name()->Print();
4499 PrintF("]\n");
4500 }
4501#endif
4502
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004503 // Compile the target function. Here we compile using CompileLazyInLoop in
4504 // order to get the optimized version. This helps code like delta-blue
4505 // that calls performance-critical routines through constructors. A
4506 // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
4507 // direct call. Since the in-loop tracking takes place through CallICs
4508 // this means that things called through constructors are never known to
4509 // be in loops. We compile them as if they are in loops here just in case.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004510 ASSERT(!function->is_compiled());
kasperl@chromium.org71affb52009-05-26 05:44:31 +00004511 if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004512 return Failure::Exception();
4513 }
4514
4515 return function->code();
4516}
4517
4518
4519static Object* Runtime_GetCalledFunction(Arguments args) {
4520 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00004521 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004522 StackFrameIterator it;
4523 // Get past the JS-to-C exit frame.
4524 ASSERT(it.frame()->is_exit());
4525 it.Advance();
4526 // Get past the CALL_NON_FUNCTION activation frame.
4527 ASSERT(it.frame()->is_java_script());
4528 it.Advance();
4529 // Argument adaptor frames do not copy the function; we have to skip
4530 // past them to get to the real calling frame.
4531 if (it.frame()->is_arguments_adaptor()) it.Advance();
4532 // Get the function from the top of the expression stack of the
4533 // calling frame.
4534 StandardFrame* frame = StandardFrame::cast(it.frame());
4535 int index = frame->ComputeExpressionsCount() - 1;
4536 Object* result = frame->GetExpression(index);
4537 return result;
4538}
4539
4540
4541static Object* Runtime_GetFunctionDelegate(Arguments args) {
4542 HandleScope scope;
4543 ASSERT(args.length() == 1);
4544 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4545 return *Execution::GetFunctionDelegate(args.at<Object>(0));
4546}
4547
4548
sgjesse@chromium.org05521fc2009-05-21 07:37:44 +00004549static Object* Runtime_GetConstructorDelegate(Arguments args) {
4550 HandleScope scope;
4551 ASSERT(args.length() == 1);
4552 RUNTIME_ASSERT(!args[0]->IsJSFunction());
4553 return *Execution::GetConstructorDelegate(args.at<Object>(0));
4554}
4555
4556
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004557static Object* Runtime_NewContext(Arguments args) {
4558 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00004559 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004560
kasper.lund7276f142008-07-30 08:49:36 +00004561 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004562 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
4563 Object* result = Heap::AllocateFunctionContext(length, function);
4564 if (result->IsFailure()) return result;
4565
4566 Top::set_context(Context::cast(result));
4567
kasper.lund7276f142008-07-30 08:49:36 +00004568 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004569}
4570
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004571static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004572 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004573 Object* js_object = object;
4574 if (!js_object->IsJSObject()) {
4575 js_object = js_object->ToObject();
4576 if (js_object->IsFailure()) {
4577 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004578 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004579 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004580 Handle<Object> result =
4581 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
4582 return Top::Throw(*result);
4583 }
4584 }
4585
4586 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004587 Heap::AllocateWithContext(Top::context(),
4588 JSObject::cast(js_object),
4589 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004590 if (result->IsFailure()) return result;
4591
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004592 Context* context = Context::cast(result);
4593 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004594
kasper.lund7276f142008-07-30 08:49:36 +00004595 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004596}
4597
4598
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004599static Object* Runtime_PushContext(Arguments args) {
4600 NoHandleAllocation ha;
4601 ASSERT(args.length() == 1);
4602 return PushContextHelper(args[0], false);
4603}
4604
4605
4606static Object* Runtime_PushCatchContext(Arguments args) {
4607 NoHandleAllocation ha;
4608 ASSERT(args.length() == 1);
4609 return PushContextHelper(args[0], true);
4610}
4611
4612
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004613static Object* Runtime_LookupContext(Arguments args) {
4614 HandleScope scope;
4615 ASSERT(args.length() == 2);
4616
4617 CONVERT_ARG_CHECKED(Context, context, 0);
4618 CONVERT_ARG_CHECKED(String, name, 1);
4619
4620 int index;
4621 PropertyAttributes attributes;
4622 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004623 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004624 context->Lookup(name, flags, &index, &attributes);
4625
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004626 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004627 ASSERT(holder->IsJSObject());
4628 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004629 }
4630
4631 // No intermediate context found. Use global object by default.
4632 return Top::context()->global();
4633}
4634
4635
ager@chromium.orga1645e22009-09-09 19:27:10 +00004636// A mechanism to return a pair of Object pointers in registers (if possible).
4637// How this is achieved is calling convention-dependent.
4638// All currently supported x86 compiles uses calling conventions that are cdecl
4639// variants where a 64-bit value is returned in two 32-bit registers
4640// (edx:eax on ia32, r1:r0 on ARM).
4641// In AMD-64 calling convention a struct of two pointers is returned in rdx:rax.
4642// In Win64 calling convention, a struct of two pointers is returned in memory,
4643// allocated by the caller, and passed as a pointer in a hidden first parameter.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004644#ifdef V8_HOST_ARCH_64_BIT
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004645struct ObjectPair {
4646 Object* x;
4647 Object* y;
4648};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004649
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004650static inline ObjectPair MakePair(Object* x, Object* y) {
4651 ObjectPair result = {x, y};
ager@chromium.orga1645e22009-09-09 19:27:10 +00004652 // Pointers x and y returned in rax and rdx, in AMD-x64-abi.
4653 // In Win64 they are assigned to a hidden first argument.
4654 return result;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004655}
4656#else
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004657typedef uint64_t ObjectPair;
4658static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004659 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004660 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004661}
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00004662#endif
4663
4664
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004665static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004666 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
4667 USE(attributes);
4668 return x->IsTheHole() ? Heap::undefined_value() : x;
4669}
4670
4671
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004672static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
4673 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004674 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004675 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004676 JSFunction* context_extension_function =
4677 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004678 // If the holder isn't a context extension object, we just return it
4679 // as the receiver. This allows arguments objects to be used as
4680 // receivers, but only if they are put in the context scope chain
4681 // explicitly via a with-statement.
4682 Object* constructor = holder->map()->constructor();
4683 if (constructor != context_extension_function) return holder;
4684 // Fall back to using the global object as the receiver if the
4685 // property turns out to be a local variable allocated in a context
4686 // extension object - introduced via eval.
4687 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004688}
4689
4690
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004691static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004692 HandleScope scope;
ager@chromium.orga1645e22009-09-09 19:27:10 +00004693 ASSERT_EQ(2, args.length());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004694
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004695 if (!args[0]->IsContext() || !args[1]->IsString()) {
ager@chromium.org3e875802009-06-29 08:26:34 +00004696 return MakePair(Top::ThrowIllegalOperation(), NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004697 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004698 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004699 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004700
4701 int index;
4702 PropertyAttributes attributes;
4703 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004704 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004705 context->Lookup(name, flags, &index, &attributes);
4706
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004707 // If the index is non-negative, the slot has been found in a local
4708 // variable or a parameter. Read it from the context object or the
4709 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004710 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004711 // If the "property" we were looking for is a local variable or an
4712 // argument in a context, the receiver is the global object; see
4713 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
4714 JSObject* receiver = Top::context()->global()->global_receiver();
4715 Object* value = (holder->IsContext())
4716 ? Context::cast(*holder)->get(index)
4717 : JSObject::cast(*holder)->GetElement(index);
4718 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004719 }
4720
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004721 // If the holder is found, we read the property from it.
4722 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004723 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004724 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004725 JSObject* receiver;
4726 if (object->IsGlobalObject()) {
4727 receiver = GlobalObject::cast(object)->global_receiver();
4728 } else if (context->is_exception_holder(*holder)) {
4729 receiver = Top::context()->global()->global_receiver();
4730 } else {
4731 receiver = ComputeReceiverForNonGlobal(object);
4732 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004733 // No need to unhole the value here. This is taken care of by the
4734 // GetProperty function.
4735 Object* value = object->GetProperty(*name);
4736 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004737 }
4738
4739 if (throw_error) {
4740 // The property doesn't exist - throw exception.
4741 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004742 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004743 return MakePair(Top::Throw(*reference_error), NULL);
4744 } else {
4745 // The property doesn't exist - return undefined
4746 return MakePair(Heap::undefined_value(), Heap::undefined_value());
4747 }
4748}
4749
4750
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004751static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004752 return LoadContextSlotHelper(args, true);
4753}
4754
4755
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004756static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004757 return LoadContextSlotHelper(args, false);
4758}
4759
4760
4761static Object* Runtime_StoreContextSlot(Arguments args) {
4762 HandleScope scope;
4763 ASSERT(args.length() == 3);
4764
4765 Handle<Object> value(args[0]);
4766 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004767 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004768
4769 int index;
4770 PropertyAttributes attributes;
4771 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004772 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004773 context->Lookup(name, flags, &index, &attributes);
4774
4775 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004776 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004777 // Ignore if read_only variable.
4778 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004779 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004780 }
4781 } else {
4782 ASSERT((attributes & READ_ONLY) == 0);
4783 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004784 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004785 USE(result);
4786 ASSERT(!result->IsFailure());
4787 }
4788 return *value;
4789 }
4790
4791 // Slow case: The property is not in a FixedArray context.
4792 // It is either in an JSObject extension context or it was not found.
4793 Handle<JSObject> context_ext;
4794
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004795 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004796 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004797 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004798 } else {
4799 // The property was not found. It needs to be stored in the global context.
4800 ASSERT(attributes == ABSENT);
4801 attributes = NONE;
4802 context_ext = Handle<JSObject>(Top::context()->global());
4803 }
4804
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00004805 // Set the property, but ignore if read_only variable on the context
4806 // extension object itself.
4807 if ((attributes & READ_ONLY) == 0 ||
4808 (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004809 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
4810 if (set.is_null()) {
4811 // Failure::Exception is converted to a null handle in the
4812 // handle-based methods such as SetProperty. We therefore need
4813 // to convert null handles back to exceptions.
4814 ASSERT(Top::has_pending_exception());
4815 return Failure::Exception();
4816 }
4817 }
4818 return *value;
4819}
4820
4821
4822static Object* Runtime_Throw(Arguments args) {
4823 HandleScope scope;
4824 ASSERT(args.length() == 1);
4825
4826 return Top::Throw(args[0]);
4827}
4828
4829
4830static Object* Runtime_ReThrow(Arguments args) {
4831 HandleScope scope;
4832 ASSERT(args.length() == 1);
4833
4834 return Top::ReThrow(args[0]);
4835}
4836
4837
ager@chromium.orgc4c92722009-11-18 14:12:51 +00004838static Object* Runtime_PromoteScheduledException(Arguments args) {
4839 ASSERT_EQ(0, args.length());
4840 return Top::PromoteScheduledException();
4841}
4842
4843
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004844static Object* Runtime_ThrowReferenceError(Arguments args) {
4845 HandleScope scope;
4846 ASSERT(args.length() == 1);
4847
4848 Handle<Object> name(args[0]);
4849 Handle<Object> reference_error =
4850 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
4851 return Top::Throw(*reference_error);
4852}
4853
4854
4855static Object* Runtime_StackOverflow(Arguments args) {
4856 NoHandleAllocation na;
4857 return Top::StackOverflow();
4858}
4859
4860
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004861static Object* Runtime_StackGuard(Arguments args) {
4862 ASSERT(args.length() == 1);
4863
4864 // First check if this is a real stack overflow.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00004865 if (StackGuard::IsStackOverflow()) {
4866 return Runtime_StackOverflow(args);
4867 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004868
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00004869 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004870}
4871
4872
4873// NOTE: These PrintXXX functions are defined for all builds (not just
4874// DEBUG builds) because we may want to be able to trace function
4875// calls in all modes.
4876static void PrintString(String* str) {
4877 // not uncommon to have empty strings
4878 if (str->length() > 0) {
4879 SmartPointer<char> s =
4880 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
4881 PrintF("%s", *s);
4882 }
4883}
4884
4885
4886static void PrintObject(Object* obj) {
4887 if (obj->IsSmi()) {
4888 PrintF("%d", Smi::cast(obj)->value());
4889 } else if (obj->IsString() || obj->IsSymbol()) {
4890 PrintString(String::cast(obj));
4891 } else if (obj->IsNumber()) {
4892 PrintF("%g", obj->Number());
4893 } else if (obj->IsFailure()) {
4894 PrintF("<failure>");
4895 } else if (obj->IsUndefined()) {
4896 PrintF("<undefined>");
4897 } else if (obj->IsNull()) {
4898 PrintF("<null>");
4899 } else if (obj->IsTrue()) {
4900 PrintF("<true>");
4901 } else if (obj->IsFalse()) {
4902 PrintF("<false>");
4903 } else {
4904 PrintF("%p", obj);
4905 }
4906}
4907
4908
4909static int StackSize() {
4910 int n = 0;
4911 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
4912 return n;
4913}
4914
4915
4916static void PrintTransition(Object* result) {
4917 // indentation
4918 { const int nmax = 80;
4919 int n = StackSize();
4920 if (n <= nmax)
4921 PrintF("%4d:%*s", n, n, "");
4922 else
4923 PrintF("%4d:%*s", n, nmax, "...");
4924 }
4925
4926 if (result == NULL) {
4927 // constructor calls
4928 JavaScriptFrameIterator it;
4929 JavaScriptFrame* frame = it.frame();
4930 if (frame->IsConstructor()) PrintF("new ");
4931 // function name
4932 Object* fun = frame->function();
4933 if (fun->IsJSFunction()) {
4934 PrintObject(JSFunction::cast(fun)->shared()->name());
4935 } else {
4936 PrintObject(fun);
4937 }
4938 // function arguments
4939 // (we are intentionally only printing the actually
4940 // supplied parameters, not all parameters required)
4941 PrintF("(this=");
4942 PrintObject(frame->receiver());
4943 const int length = frame->GetProvidedParametersCount();
4944 for (int i = 0; i < length; i++) {
4945 PrintF(", ");
4946 PrintObject(frame->GetParameter(i));
4947 }
4948 PrintF(") {\n");
4949
4950 } else {
4951 // function result
4952 PrintF("} -> ");
4953 PrintObject(result);
4954 PrintF("\n");
4955 }
4956}
4957
4958
4959static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004960 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004961 NoHandleAllocation ha;
4962 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004963 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004964}
4965
4966
4967static Object* Runtime_TraceExit(Arguments args) {
4968 NoHandleAllocation ha;
4969 PrintTransition(args[0]);
4970 return args[0]; // return TOS
4971}
4972
4973
4974static Object* Runtime_DebugPrint(Arguments args) {
4975 NoHandleAllocation ha;
4976 ASSERT(args.length() == 1);
4977
4978#ifdef DEBUG
4979 if (args[0]->IsString()) {
4980 // If we have a string, assume it's a code "marker"
4981 // and print some interesting cpu debugging info.
4982 JavaScriptFrameIterator it;
4983 JavaScriptFrame* frame = it.frame();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00004984 PrintF("fp = %p, sp = %p, caller_sp = %p: ",
4985 frame->fp(), frame->sp(), frame->caller_sp());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004986 } else {
4987 PrintF("DebugPrint: ");
4988 }
4989 args[0]->Print();
4990#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004991 // ShortPrint is available in release mode. Print is not.
4992 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004993#endif
4994 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00004995 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004996
4997 return args[0]; // return TOS
4998}
4999
5000
5001static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005002 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005003 NoHandleAllocation ha;
5004 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005005 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005006}
5007
5008
mads.s.ager31e71382008-08-13 09:32:07 +00005009static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005010 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005011 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005012
5013 // According to ECMA-262, section 15.9.1, page 117, the precision of
5014 // the number in a Date object representing a particular instant in
5015 // time is milliseconds. Therefore, we floor the result of getting
5016 // the OS time.
5017 double millis = floor(OS::TimeCurrentMillis());
5018 return Heap::NumberFromDouble(millis);
5019}
5020
5021
5022static Object* Runtime_DateParseString(Arguments args) {
5023 HandleScope scope;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005024 ASSERT(args.length() == 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005025
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005026 CONVERT_ARG_CHECKED(String, str, 0);
5027 FlattenString(str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005028
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005029 CONVERT_ARG_CHECKED(JSArray, output, 1);
5030 RUNTIME_ASSERT(output->HasFastElements());
5031
5032 AssertNoAllocation no_allocation;
5033
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005034 FixedArray* output_array = FixedArray::cast(output->elements());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005035 RUNTIME_ASSERT(output_array->length() >= DateParser::OUTPUT_SIZE);
5036 bool result;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005037 if (str->IsAsciiRepresentation()) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005038 result = DateParser::Parse(str->ToAsciiVector(), output_array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005039 } else {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005040 ASSERT(str->IsTwoByteRepresentation());
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00005041 result = DateParser::Parse(str->ToUC16Vector(), output_array);
5042 }
5043
5044 if (result) {
5045 return *output;
5046 } else {
5047 return Heap::null_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005048 }
5049}
5050
5051
5052static Object* Runtime_DateLocalTimezone(Arguments args) {
5053 NoHandleAllocation ha;
5054 ASSERT(args.length() == 1);
5055
5056 CONVERT_DOUBLE_CHECKED(x, args[0]);
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +00005057 const char* zone = OS::LocalTimezone(x);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005058 return Heap::AllocateStringFromUtf8(CStrVector(zone));
5059}
5060
5061
5062static Object* Runtime_DateLocalTimeOffset(Arguments args) {
5063 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00005064 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005065
5066 return Heap::NumberFromDouble(OS::LocalTimeOffset());
5067}
5068
5069
5070static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
5071 NoHandleAllocation ha;
5072 ASSERT(args.length() == 1);
5073
5074 CONVERT_DOUBLE_CHECKED(x, args[0]);
5075 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
5076}
5077
5078
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005079static Object* Runtime_NumberIsFinite(Arguments args) {
5080 NoHandleAllocation ha;
5081 ASSERT(args.length() == 1);
5082
5083 CONVERT_DOUBLE_CHECKED(value, args[0]);
5084 Object* result;
5085 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
5086 result = Heap::false_value();
5087 } else {
5088 result = Heap::true_value();
5089 }
5090 return result;
5091}
5092
5093
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005094static Object* Runtime_GlobalReceiver(Arguments args) {
5095 ASSERT(args.length() == 1);
5096 Object* global = args[0];
5097 if (!global->IsJSGlobalObject()) return Heap::null_value();
5098 return JSGlobalObject::cast(global)->global_receiver();
5099}
5100
5101
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005102static Object* Runtime_CompileString(Arguments args) {
5103 HandleScope scope;
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005104 ASSERT_EQ(2, args.length());
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00005105 CONVERT_ARG_CHECKED(String, source, 0);
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005106 CONVERT_ARG_CHECKED(Oddball, is_json, 1)
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005107
ager@chromium.org381abbb2009-02-25 13:23:22 +00005108 // Compile source string in the global context.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005109 Handle<Context> context(Top::context()->global_context());
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005110 Compiler::ValidationState validate = (is_json->IsTrue())
5111 ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005112 Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
5113 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00005114 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005115 validate);
ager@chromium.org381abbb2009-02-25 13:23:22 +00005116 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005117 Handle<JSFunction> fun =
5118 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5119 return *fun;
5120}
5121
5122
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005123static Handle<JSFunction> GetBuiltinFunction(String* name) {
5124 LookupResult result;
5125 Top::global_context()->builtins()->LocalLookup(name, &result);
5126 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
5127}
5128
5129
5130static Object* CompileDirectEval(Handle<String> source) {
5131 // Compute the eval context.
5132 HandleScope scope;
5133 StackFrameLocator locator;
5134 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5135 Handle<Context> context(Context::cast(frame->context()));
5136 bool is_global = context->IsGlobalContext();
5137
ager@chromium.org381abbb2009-02-25 13:23:22 +00005138 // Compile source string in the current context.
ager@chromium.orgadd848f2009-08-13 12:44:13 +00005139 Handle<JSFunction> boilerplate = Compiler::CompileEval(
5140 source,
5141 context,
5142 is_global,
5143 Compiler::DONT_VALIDATE_JSON);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005144 if (boilerplate.is_null()) return Failure::Exception();
5145 Handle<JSFunction> fun =
5146 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5147 return *fun;
5148}
5149
5150
5151static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
5152 ASSERT(args.length() == 2);
5153
5154 HandleScope scope;
5155
5156 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
5157
5158 Handle<Object> receiver;
5159
5160 // Find where the 'eval' symbol is bound. It is unaliased only if
5161 // it is bound in the global context.
5162 StackFrameLocator locator;
5163 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
5164 Handle<Context> context(Context::cast(frame->context()));
5165 int index;
5166 PropertyAttributes attributes;
5167 while (!context.is_null()) {
5168 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
5169 &index, &attributes);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005170 // Stop search when eval is found or when the global context is
5171 // reached.
5172 if (attributes != ABSENT || context->IsGlobalContext()) break;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005173 if (context->is_function_context()) {
5174 context = Handle<Context>(Context::cast(context->closure()->context()));
5175 } else {
5176 context = Handle<Context>(context->previous());
5177 }
5178 }
5179
iposva@chromium.org245aa852009-02-10 00:49:54 +00005180 // If eval could not be resolved, it has been deleted and we need to
5181 // throw a reference error.
5182 if (attributes == ABSENT) {
5183 Handle<Object> name = Factory::eval_symbol();
5184 Handle<Object> reference_error =
5185 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
5186 return Top::Throw(*reference_error);
5187 }
5188
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005189 if (context->IsGlobalContext()) {
5190 // 'eval' is bound in the global context, but it may have been overwritten.
5191 // Compare it to the builtin 'GlobalEval' function to make sure.
5192 Handle<JSFunction> global_eval =
5193 GetBuiltinFunction(Heap::global_eval_symbol());
5194 if (global_eval.is_identical_to(callee)) {
5195 // A direct eval call.
5196 if (args[1]->IsString()) {
5197 CONVERT_ARG_CHECKED(String, source, 1);
5198 // A normal eval call on a string. Compile it and return the
5199 // compiled function bound in the local context.
5200 Object* compiled_source = CompileDirectEval(source);
5201 if (compiled_source->IsFailure()) return compiled_source;
5202 receiver = Handle<Object>(frame->receiver());
5203 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
5204 } else {
5205 // An eval call that is not called on a string. Global eval
5206 // deals better with this.
5207 receiver = Handle<Object>(Top::global_context()->global());
5208 }
5209 } else {
5210 // 'eval' is overwritten. Just call the function with the given arguments.
5211 receiver = Handle<Object>(Top::global_context()->global());
5212 }
5213 } else {
5214 // 'eval' is not bound in the global context. Just call the function
5215 // with the given arguments. This is not necessarily the global eval.
5216 if (receiver->IsContext()) {
5217 context = Handle<Context>::cast(receiver);
5218 receiver = Handle<Object>(context->get(index));
5219 }
5220 }
5221
5222 Handle<FixedArray> call = Factory::NewFixedArray(2);
5223 call->set(0, *callee);
5224 call->set(1, *receiver);
5225 return *call;
5226}
5227
5228
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005229static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
5230 // This utility adjusts the property attributes for newly created Function
5231 // object ("new Function(...)") by changing the map.
5232 // All it does is changing the prototype property to enumerable
5233 // as specified in ECMA262, 15.3.5.2.
5234 HandleScope scope;
5235 ASSERT(args.length() == 1);
5236 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5237 ASSERT(func->map()->instance_type() ==
5238 Top::function_instance_map()->instance_type());
5239 ASSERT(func->map()->instance_size() ==
5240 Top::function_instance_map()->instance_size());
5241 func->set_map(*Top::function_instance_map());
5242 return *func;
5243}
5244
5245
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005246// Push an array unto an array of arrays if it is not already in the
5247// array. Returns true if the element was pushed on the stack and
5248// false otherwise.
5249static Object* Runtime_PushIfAbsent(Arguments args) {
5250 ASSERT(args.length() == 2);
5251 CONVERT_CHECKED(JSArray, array, args[0]);
5252 CONVERT_CHECKED(JSArray, element, args[1]);
5253 RUNTIME_ASSERT(array->HasFastElements());
5254 int length = Smi::cast(array->length())->value();
5255 FixedArray* elements = FixedArray::cast(array->elements());
5256 for (int i = 0; i < length; i++) {
5257 if (elements->get(i) == element) return Heap::false_value();
5258 }
5259 Object* obj = array->SetFastElement(length, element);
5260 if (obj->IsFailure()) return obj;
5261 return Heap::true_value();
5262}
5263
5264
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005265/**
5266 * A simple visitor visits every element of Array's.
5267 * The backend storage can be a fixed array for fast elements case,
5268 * or a dictionary for sparse array. Since Dictionary is a subtype
5269 * of FixedArray, the class can be used by both fast and slow cases.
5270 * The second parameter of the constructor, fast_elements, specifies
5271 * whether the storage is a FixedArray or Dictionary.
5272 *
5273 * An index limit is used to deal with the situation that a result array
5274 * length overflows 32-bit non-negative integer.
5275 */
5276class ArrayConcatVisitor {
5277 public:
5278 ArrayConcatVisitor(Handle<FixedArray> storage,
5279 uint32_t index_limit,
5280 bool fast_elements) :
5281 storage_(storage), index_limit_(index_limit),
5282 fast_elements_(fast_elements), index_offset_(0) { }
5283
5284 void visit(uint32_t i, Handle<Object> elm) {
5285 uint32_t index = i + index_offset_;
5286 if (index >= index_limit_) return;
5287
5288 if (fast_elements_) {
5289 ASSERT(index < static_cast<uint32_t>(storage_->length()));
5290 storage_->set(index, *elm);
5291
5292 } else {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005293 Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
5294 Handle<NumberDictionary> result =
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005295 Factory::DictionaryAtNumberPut(dict, index, elm);
5296 if (!result.is_identical_to(dict))
5297 storage_ = result;
5298 }
5299 }
5300
5301 void increase_index_offset(uint32_t delta) {
5302 index_offset_ += delta;
5303 }
5304
5305 private:
5306 Handle<FixedArray> storage_;
5307 uint32_t index_limit_;
5308 bool fast_elements_;
5309 uint32_t index_offset_;
5310};
5311
5312
ager@chromium.org3811b432009-10-28 14:53:37 +00005313template<class ExternalArrayClass, class ElementType>
5314static uint32_t IterateExternalArrayElements(Handle<JSObject> receiver,
5315 bool elements_are_ints,
5316 bool elements_are_guaranteed_smis,
5317 uint32_t range,
5318 ArrayConcatVisitor* visitor) {
5319 Handle<ExternalArrayClass> array(
5320 ExternalArrayClass::cast(receiver->elements()));
5321 uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
5322
5323 if (visitor != NULL) {
5324 if (elements_are_ints) {
5325 if (elements_are_guaranteed_smis) {
5326 for (uint32_t j = 0; j < len; j++) {
5327 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
5328 visitor->visit(j, e);
5329 }
5330 } else {
5331 for (uint32_t j = 0; j < len; j++) {
5332 int64_t val = static_cast<int64_t>(array->get(j));
5333 if (Smi::IsValid(static_cast<intptr_t>(val))) {
5334 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
5335 visitor->visit(j, e);
5336 } else {
5337 Handle<Object> e(
5338 Heap::AllocateHeapNumber(static_cast<ElementType>(val)));
5339 visitor->visit(j, e);
5340 }
5341 }
5342 }
5343 } else {
5344 for (uint32_t j = 0; j < len; j++) {
5345 Handle<Object> e(Heap::AllocateHeapNumber(array->get(j)));
5346 visitor->visit(j, e);
5347 }
5348 }
5349 }
5350
5351 return len;
5352}
5353
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005354/**
5355 * A helper function that visits elements of a JSObject. Only elements
5356 * whose index between 0 and range (exclusive) are visited.
5357 *
5358 * If the third parameter, visitor, is not NULL, the visitor is called
5359 * with parameters, 'visitor_index_offset + element index' and the element.
5360 *
5361 * It returns the number of visisted elements.
5362 */
5363static uint32_t IterateElements(Handle<JSObject> receiver,
5364 uint32_t range,
5365 ArrayConcatVisitor* visitor) {
5366 uint32_t num_of_elements = 0;
5367
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005368 switch (receiver->GetElementsKind()) {
5369 case JSObject::FAST_ELEMENTS: {
5370 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
5371 uint32_t len = elements->length();
5372 if (range < len) {
5373 len = range;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005374 }
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005375
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005376 for (uint32_t j = 0; j < len; j++) {
5377 Handle<Object> e(elements->get(j));
5378 if (!e->IsTheHole()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005379 num_of_elements++;
5380 if (visitor) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005381 visitor->visit(j, e);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005382 }
5383 }
5384 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005385 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005386 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005387 case JSObject::PIXEL_ELEMENTS: {
5388 Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
5389 uint32_t len = pixels->length();
5390 if (range < len) {
5391 len = range;
5392 }
5393
5394 for (uint32_t j = 0; j < len; j++) {
5395 num_of_elements++;
5396 if (visitor != NULL) {
5397 Handle<Smi> e(Smi::FromInt(pixels->get(j)));
5398 visitor->visit(j, e);
5399 }
5400 }
5401 break;
5402 }
ager@chromium.org3811b432009-10-28 14:53:37 +00005403 case JSObject::EXTERNAL_BYTE_ELEMENTS: {
5404 num_of_elements =
5405 IterateExternalArrayElements<ExternalByteArray, int8_t>(
5406 receiver, true, true, range, visitor);
5407 break;
5408 }
5409 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
5410 num_of_elements =
5411 IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
5412 receiver, true, true, range, visitor);
5413 break;
5414 }
5415 case JSObject::EXTERNAL_SHORT_ELEMENTS: {
5416 num_of_elements =
5417 IterateExternalArrayElements<ExternalShortArray, int16_t>(
5418 receiver, true, true, range, visitor);
5419 break;
5420 }
5421 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
5422 num_of_elements =
5423 IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
5424 receiver, true, true, range, visitor);
5425 break;
5426 }
5427 case JSObject::EXTERNAL_INT_ELEMENTS: {
5428 num_of_elements =
5429 IterateExternalArrayElements<ExternalIntArray, int32_t>(
5430 receiver, true, false, range, visitor);
5431 break;
5432 }
5433 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
5434 num_of_elements =
5435 IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
5436 receiver, true, false, range, visitor);
5437 break;
5438 }
5439 case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
5440 num_of_elements =
5441 IterateExternalArrayElements<ExternalFloatArray, float>(
5442 receiver, false, false, range, visitor);
5443 break;
5444 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00005445 case JSObject::DICTIONARY_ELEMENTS: {
5446 Handle<NumberDictionary> dict(receiver->element_dictionary());
5447 uint32_t capacity = dict->Capacity();
5448 for (uint32_t j = 0; j < capacity; j++) {
5449 Handle<Object> k(dict->KeyAt(j));
5450 if (dict->IsKey(*k)) {
5451 ASSERT(k->IsNumber());
5452 uint32_t index = static_cast<uint32_t>(k->Number());
5453 if (index < range) {
5454 num_of_elements++;
5455 if (visitor) {
5456 visitor->visit(index, Handle<Object>(dict->ValueAt(j)));
5457 }
5458 }
5459 }
5460 }
5461 break;
5462 }
5463 default:
5464 UNREACHABLE();
5465 break;
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005466 }
5467
5468 return num_of_elements;
5469}
5470
5471
5472/**
5473 * A helper function that visits elements of an Array object, and elements
5474 * on its prototypes.
5475 *
5476 * Elements on prototypes are visited first, and only elements whose indices
5477 * less than Array length are visited.
5478 *
5479 * If a ArrayConcatVisitor object is given, the visitor is called with
5480 * parameters, element's index + visitor_index_offset and the element.
5481 */
5482static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
5483 ArrayConcatVisitor* visitor) {
5484 uint32_t range = static_cast<uint32_t>(array->length()->Number());
5485 Handle<Object> obj = array;
5486
5487 static const int kEstimatedPrototypes = 3;
5488 List< Handle<JSObject> > objects(kEstimatedPrototypes);
5489
5490 // Visit prototype first. If an element on the prototype is shadowed by
5491 // the inheritor using the same index, the ArrayConcatVisitor visits
5492 // the prototype element before the shadowing element.
5493 // The visitor can simply overwrite the old value by new value using
5494 // the same index. This follows Array::concat semantics.
5495 while (!obj->IsNull()) {
5496 objects.Add(Handle<JSObject>::cast(obj));
5497 obj = Handle<Object>(obj->GetPrototype());
5498 }
5499
5500 uint32_t nof_elements = 0;
5501 for (int i = objects.length() - 1; i >= 0; i--) {
5502 Handle<JSObject> obj = objects[i];
5503 nof_elements +=
5504 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
5505 }
5506
5507 return nof_elements;
5508}
5509
5510
5511/**
5512 * A helper function of Runtime_ArrayConcat.
5513 *
5514 * The first argument is an Array of arrays and objects. It is the
5515 * same as the arguments array of Array::concat JS function.
5516 *
5517 * If an argument is an Array object, the function visits array
5518 * elements. If an argument is not an Array object, the function
5519 * visits the object as if it is an one-element array.
5520 *
5521 * If the result array index overflows 32-bit integer, the rounded
5522 * non-negative number is used as new length. For example, if one
5523 * array length is 2^32 - 1, second array length is 1, the
5524 * concatenated array length is 0.
5525 */
5526static uint32_t IterateArguments(Handle<JSArray> arguments,
5527 ArrayConcatVisitor* visitor) {
5528 uint32_t visited_elements = 0;
5529 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5530
5531 for (uint32_t i = 0; i < num_of_args; i++) {
5532 Handle<Object> obj(arguments->GetElement(i));
5533 if (obj->IsJSArray()) {
5534 Handle<JSArray> array = Handle<JSArray>::cast(obj);
5535 uint32_t len = static_cast<uint32_t>(array->length()->Number());
5536 uint32_t nof_elements =
5537 IterateArrayAndPrototypeElements(array, visitor);
5538 // Total elements of array and its prototype chain can be more than
5539 // the array length, but ArrayConcat can only concatenate at most
5540 // the array length number of elements.
5541 visited_elements += (nof_elements > len) ? len : nof_elements;
5542 if (visitor) visitor->increase_index_offset(len);
5543
5544 } else {
5545 if (visitor) {
5546 visitor->visit(0, obj);
5547 visitor->increase_index_offset(1);
5548 }
5549 visited_elements++;
5550 }
5551 }
5552 return visited_elements;
5553}
5554
5555
5556/**
5557 * Array::concat implementation.
5558 * See ECMAScript 262, 15.4.4.4.
5559 */
5560static Object* Runtime_ArrayConcat(Arguments args) {
5561 ASSERT(args.length() == 1);
5562 HandleScope handle_scope;
5563
5564 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
5565 Handle<JSArray> arguments(arg_arrays);
5566
5567 // Pass 1: estimate the number of elements of the result
5568 // (it could be more than real numbers if prototype has elements).
5569 uint32_t result_length = 0;
5570 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
5571
5572 { AssertNoAllocation nogc;
5573 for (uint32_t i = 0; i < num_of_args; i++) {
5574 Object* obj = arguments->GetElement(i);
5575 if (obj->IsJSArray()) {
5576 result_length +=
5577 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
5578 } else {
5579 result_length++;
5580 }
5581 }
5582 }
5583
5584 // Allocate an empty array, will set length and content later.
5585 Handle<JSArray> result = Factory::NewJSArray(0);
5586
5587 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
5588 // If estimated number of elements is more than half of length, a
5589 // fixed array (fast case) is more time and space-efficient than a
5590 // dictionary.
5591 bool fast_case = (estimate_nof_elements * 2) >= result_length;
5592
5593 Handle<FixedArray> storage;
5594 if (fast_case) {
5595 // The backing storage array must have non-existing elements to
5596 // preserve holes across concat operations.
5597 storage = Factory::NewFixedArrayWithHoles(result_length);
5598
5599 } else {
5600 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
5601 uint32_t at_least_space_for = estimate_nof_elements +
5602 (estimate_nof_elements >> 2);
5603 storage = Handle<FixedArray>::cast(
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005604 Factory::NewNumberDictionary(at_least_space_for));
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005605 }
5606
5607 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
5608
5609 ArrayConcatVisitor visitor(storage, result_length, fast_case);
5610
5611 IterateArguments(arguments, &visitor);
5612
5613 result->set_length(*len);
5614 result->set_elements(*storage);
5615
5616 return *result;
5617}
5618
5619
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005620// This will not allocate (flatten the string), but it may run
5621// very slowly for very deeply nested ConsStrings. For debugging use only.
5622static Object* Runtime_GlobalPrint(Arguments args) {
5623 NoHandleAllocation ha;
5624 ASSERT(args.length() == 1);
5625
5626 CONVERT_CHECKED(String, string, args[0]);
5627 StringInputBuffer buffer(string);
5628 while (buffer.has_more()) {
5629 uint16_t character = buffer.GetNext();
5630 PrintF("%c", character);
5631 }
5632 return string;
5633}
5634
ager@chromium.org5ec48922009-05-05 07:25:34 +00005635// Moves all own elements of an object, that are below a limit, to positions
5636// starting at zero. All undefined values are placed after non-undefined values,
5637// and are followed by non-existing element. Does not change the length
5638// property.
5639// Returns the number of non-undefined elements collected.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005640static Object* Runtime_RemoveArrayHoles(Arguments args) {
ager@chromium.org5ec48922009-05-05 07:25:34 +00005641 ASSERT(args.length() == 2);
5642 CONVERT_CHECKED(JSObject, object, args[0]);
5643 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
5644 return object->PrepareElementsForSort(limit);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005645}
5646
5647
5648// Move contents of argument 0 (an array) to argument 1 (an array)
5649static Object* Runtime_MoveArrayContents(Arguments args) {
5650 ASSERT(args.length() == 2);
5651 CONVERT_CHECKED(JSArray, from, args[0]);
5652 CONVERT_CHECKED(JSArray, to, args[1]);
5653 to->SetContent(FixedArray::cast(from->elements()));
5654 to->set_length(from->length());
5655 from->SetContent(Heap::empty_fixed_array());
5656 from->set_length(0);
5657 return to;
5658}
5659
5660
5661// How many elements does this array have?
5662static Object* Runtime_EstimateNumberOfElements(Arguments args) {
5663 ASSERT(args.length() == 1);
5664 CONVERT_CHECKED(JSArray, array, args[0]);
5665 HeapObject* elements = array->elements();
5666 if (elements->IsDictionary()) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00005667 return Smi::FromInt(NumberDictionary::cast(elements)->NumberOfElements());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005668 } else {
5669 return array->length();
5670 }
5671}
5672
5673
5674// Returns an array that tells you where in the [0, length) interval an array
5675// might have elements. Can either return keys or intervals. Keys can have
5676// gaps in (undefined). Intervals can also span over some undefined keys.
5677static Object* Runtime_GetArrayKeys(Arguments args) {
5678 ASSERT(args.length() == 2);
5679 HandleScope scope;
ager@chromium.org5ec48922009-05-05 07:25:34 +00005680 CONVERT_ARG_CHECKED(JSObject, array, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005681 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005682 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005683 // Create an array and get all the keys into it, then remove all the
5684 // keys that are not integers in the range 0 to length-1.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005685 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005686 int keys_length = keys->length();
5687 for (int i = 0; i < keys_length; i++) {
5688 Object* key = keys->get(i);
5689 uint32_t index;
5690 if (!Array::IndexFromObject(key, &index) || index >= length) {
5691 // Zap invalid keys.
5692 keys->set_undefined(i);
5693 }
5694 }
5695 return *Factory::NewJSArrayWithElements(keys);
5696 } else {
5697 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
5698 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005699 single_interval->set(0,
5700 Smi::FromInt(-1),
5701 SKIP_WRITE_BARRIER);
ager@chromium.org5ec48922009-05-05 07:25:34 +00005702 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
5703 uint32_t min_length = actual_length < length ? actual_length : length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005704 Handle<Object> length_object =
ager@chromium.org5ec48922009-05-05 07:25:34 +00005705 Factory::NewNumber(static_cast<double>(min_length));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005706 single_interval->set(1, *length_object);
5707 return *Factory::NewJSArrayWithElements(single_interval);
5708 }
5709}
5710
5711
5712// DefineAccessor takes an optional final argument which is the
5713// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
5714// to the way accessors are implemented, it is set for both the getter
5715// and setter on the first call to DefineAccessor and ignored on
5716// subsequent calls.
5717static Object* Runtime_DefineAccessor(Arguments args) {
5718 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
5719 // Compute attributes.
5720 PropertyAttributes attributes = NONE;
5721 if (args.length() == 5) {
5722 CONVERT_CHECKED(Smi, attrs, args[4]);
5723 int value = attrs->value();
5724 // Only attribute bits should be set.
5725 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
5726 attributes = static_cast<PropertyAttributes>(value);
5727 }
5728
5729 CONVERT_CHECKED(JSObject, obj, args[0]);
5730 CONVERT_CHECKED(String, name, args[1]);
5731 CONVERT_CHECKED(Smi, flag, args[2]);
5732 CONVERT_CHECKED(JSFunction, fun, args[3]);
5733 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
5734}
5735
5736
5737static Object* Runtime_LookupAccessor(Arguments args) {
5738 ASSERT(args.length() == 3);
5739 CONVERT_CHECKED(JSObject, obj, args[0]);
5740 CONVERT_CHECKED(String, name, args[1]);
5741 CONVERT_CHECKED(Smi, flag, args[2]);
5742 return obj->LookupAccessor(name, flag->value() == 0);
5743}
5744
5745
ager@chromium.org65dad4b2009-04-23 08:48:43 +00005746#ifdef ENABLE_DEBUGGER_SUPPORT
5747static Object* Runtime_DebugBreak(Arguments args) {
5748 ASSERT(args.length() == 0);
5749 return Execution::DebugBreakHelper();
5750}
5751
5752
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005753// Helper functions for wrapping and unwrapping stack frame ids.
5754static Smi* WrapFrameId(StackFrame::Id id) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005755 ASSERT(IsAligned(OffsetFrom(id), static_cast<intptr_t>(4)));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005756 return Smi::FromInt(id >> 2);
5757}
5758
5759
5760static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
5761 return static_cast<StackFrame::Id>(wrapped->value() << 2);
5762}
5763
5764
5765// Adds a JavaScript function as a debug event listener.
iposva@chromium.org245aa852009-02-10 00:49:54 +00005766// args[0]: debug event listener function to set or null or undefined for
5767// clearing the event listener function
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005768// args[1]: object supplied during callback
iposva@chromium.org245aa852009-02-10 00:49:54 +00005769static Object* Runtime_SetDebugEventListener(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005770 ASSERT(args.length() == 2);
iposva@chromium.org245aa852009-02-10 00:49:54 +00005771 RUNTIME_ASSERT(args[0]->IsJSFunction() ||
5772 args[0]->IsUndefined() ||
5773 args[0]->IsNull());
5774 Handle<Object> callback = args.at<Object>(0);
5775 Handle<Object> data = args.at<Object>(1);
5776 Debugger::SetEventListener(callback, data);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005777
5778 return Heap::undefined_value();
5779}
5780
5781
5782static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005783 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005784 StackGuard::DebugBreak();
5785 return Heap::undefined_value();
5786}
5787
5788
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005789// Find the length of the prototype chain that is to to handled as one. If a
5790// prototype object is hidden it is to be viewed as part of the the object it
5791// is prototype for.
5792static int LocalPrototypeChainLength(JSObject* obj) {
5793 int count = 1;
5794 Object* proto = obj->GetPrototype();
5795 while (proto->IsJSObject() &&
5796 JSObject::cast(proto)->map()->is_hidden_prototype()) {
5797 count++;
5798 proto = JSObject::cast(proto)->GetPrototype();
5799 }
5800 return count;
5801}
5802
5803
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005804static Object* DebugLookupResultValue(Object* receiver, String* name,
5805 LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00005806 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005807 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005808 switch (result->type()) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00005809 case NORMAL:
5810 value = result->holder()->GetNormalizedProperty(result);
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005811 if (value->IsTheHole()) {
5812 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005813 }
5814 return value;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005815 case FIELD:
5816 value =
5817 JSObject::cast(
5818 result->holder())->FastPropertyAt(result->GetFieldIndex());
5819 if (value->IsTheHole()) {
5820 return Heap::undefined_value();
5821 }
5822 return value;
5823 case CONSTANT_FUNCTION:
5824 return result->GetConstantFunction();
5825 case CALLBACKS: {
5826 Object* structure = result->GetCallbackObject();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005827 if (structure->IsProxy() || structure->IsAccessorInfo()) {
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005828 value = receiver->GetPropertyWithCallback(
5829 receiver, structure, name, result->holder());
ager@chromium.org381abbb2009-02-25 13:23:22 +00005830 if (value->IsException()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00005831 value = Top::pending_exception();
5832 Top::clear_pending_exception();
5833 if (caught_exception != NULL) {
5834 *caught_exception = true;
5835 }
5836 }
5837 return value;
5838 } else {
5839 return Heap::undefined_value();
5840 }
5841 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005842 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005843 case MAP_TRANSITION:
5844 case CONSTANT_TRANSITION:
5845 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005846 return Heap::undefined_value();
5847 default:
5848 UNREACHABLE();
5849 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005850 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005851 return Heap::undefined_value();
5852}
5853
5854
ager@chromium.org32912102009-01-16 10:38:43 +00005855// Get debugger related details for an object property.
5856// args[0]: object holding property
5857// args[1]: name of the property
5858//
5859// The array returned contains the following information:
5860// 0: Property value
5861// 1: Property details
5862// 2: Property value is exception
5863// 3: Getter function if defined
5864// 4: Setter function if defined
5865// Items 2-4 are only filled if the property has either a getter or a setter
5866// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005867static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005868 HandleScope scope;
5869
5870 ASSERT(args.length() == 2);
5871
5872 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5873 CONVERT_ARG_CHECKED(String, name, 1);
5874
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00005875 // Make sure to set the current context to the context before the debugger was
5876 // entered (if the debugger is entered). The reason for switching context here
5877 // is that for some property lookups (accessors and interceptors) callbacks
5878 // into the embedding application can occour, and the embedding application
5879 // could have the assumption that its own global context is the current
5880 // context and not some internal debugger context.
5881 SaveContext save;
5882 if (Debug::InDebugger()) {
5883 Top::set_context(*Debug::debugger_entry()->GetContext());
5884 }
5885
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005886 // Skip the global proxy as it has no properties and always delegates to the
5887 // real global object.
5888 if (obj->IsJSGlobalProxy()) {
5889 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5890 }
5891
5892
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005893 // Check if the name is trivially convertible to an index and get the element
5894 // if so.
5895 uint32_t index;
5896 if (name->AsArrayIndex(&index)) {
5897 Handle<FixedArray> details = Factory::NewFixedArray(2);
5898 details->set(0, Runtime::GetElementOrCharAt(obj, index));
5899 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
5900 return *Factory::NewJSArrayWithElements(details);
5901 }
5902
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005903 // Find the number of objects making up this.
5904 int length = LocalPrototypeChainLength(*obj);
5905
5906 // Try local lookup on each of the objects.
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005907 Handle<JSObject> jsproto = obj;
5908 for (int i = 0; i < length; i++) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005909 LookupResult result;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005910 jsproto->LocalLookup(*name, &result);
5911 if (result.IsProperty()) {
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00005912 // LookupResult is not GC safe as it holds raw object pointers.
5913 // GC can happen later in this code so put the required fields into
5914 // local variables using handles when required for later use.
5915 PropertyType result_type = result.type();
5916 Handle<Object> result_callback_obj;
5917 if (result_type == CALLBACKS) {
5918 result_callback_obj = Handle<Object>(result.GetCallbackObject());
5919 }
5920 Smi* property_details = result.GetPropertyDetails().AsSmi();
5921 // DebugLookupResultValue can cause GC so details from LookupResult needs
5922 // to be copied to handles before this.
5923 bool caught_exception = false;
5924 Object* raw_value = DebugLookupResultValue(*obj, *name, &result,
5925 &caught_exception);
5926 if (raw_value->IsFailure()) return raw_value;
5927 Handle<Object> value(raw_value);
5928
5929 // If the callback object is a fixed array then it contains JavaScript
5930 // getter and/or setter.
5931 bool hasJavaScriptAccessors = result_type == CALLBACKS &&
5932 result_callback_obj->IsFixedArray();
5933 Handle<FixedArray> details =
5934 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
5935 details->set(0, *value);
5936 details->set(1, property_details);
5937 if (hasJavaScriptAccessors) {
5938 details->set(2,
5939 caught_exception ? Heap::true_value()
5940 : Heap::false_value());
5941 details->set(3, FixedArray::cast(*result_callback_obj)->get(0));
5942 details->set(4, FixedArray::cast(*result_callback_obj)->get(1));
5943 }
5944
5945 return *Factory::NewJSArrayWithElements(details);
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005946 }
5947 if (i < length - 1) {
5948 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
5949 }
5950 }
5951
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005952 return Heap::undefined_value();
5953}
5954
5955
5956static Object* Runtime_DebugGetProperty(Arguments args) {
5957 HandleScope scope;
5958
5959 ASSERT(args.length() == 2);
5960
5961 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5962 CONVERT_ARG_CHECKED(String, name, 1);
5963
5964 LookupResult result;
5965 obj->Lookup(*name, &result);
5966 if (result.IsProperty()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00005967 return DebugLookupResultValue(*obj, *name, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005968 }
5969 return Heap::undefined_value();
5970}
5971
5972
5973// Return the names of the local named properties.
5974// args[0]: object
5975static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
5976 HandleScope scope;
5977 ASSERT(args.length() == 1);
5978 if (!args[0]->IsJSObject()) {
5979 return Heap::undefined_value();
5980 }
5981 CONVERT_ARG_CHECKED(JSObject, obj, 0);
5982
ager@chromium.orgddb913d2009-01-27 10:01:48 +00005983 // Skip the global proxy as it has no properties and always delegates to the
5984 // real global object.
5985 if (obj->IsJSGlobalProxy()) {
5986 obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
5987 }
5988
5989 // Find the number of objects making up this.
5990 int length = LocalPrototypeChainLength(*obj);
5991
5992 // Find the number of local properties for each of the objects.
5993 int* local_property_count = NewArray<int>(length);
5994 int total_property_count = 0;
5995 Handle<JSObject> jsproto = obj;
5996 for (int i = 0; i < length; i++) {
5997 int n;
5998 n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
5999 local_property_count[i] = n;
6000 total_property_count += n;
6001 if (i < length - 1) {
6002 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6003 }
6004 }
6005
6006 // Allocate an array with storage for all the property names.
6007 Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
6008
6009 // Get the property names.
6010 jsproto = obj;
ager@chromium.orgc730f772009-11-11 10:11:16 +00006011 int proto_with_hidden_properties = 0;
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006012 for (int i = 0; i < length; i++) {
6013 jsproto->GetLocalPropertyNames(*names,
6014 i == 0 ? 0 : local_property_count[i - 1]);
ager@chromium.orgc730f772009-11-11 10:11:16 +00006015 if (!GetHiddenProperties(jsproto, false)->IsUndefined()) {
6016 proto_with_hidden_properties++;
6017 }
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006018 if (i < length - 1) {
6019 jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
6020 }
6021 }
6022
ager@chromium.orgc730f772009-11-11 10:11:16 +00006023 // Filter out name of hidden propeties object.
6024 if (proto_with_hidden_properties > 0) {
6025 Handle<FixedArray> old_names = names;
6026 names = Factory::NewFixedArray(
6027 names->length() - proto_with_hidden_properties);
6028 int dest_pos = 0;
6029 for (int i = 0; i < total_property_count; i++) {
6030 Object* name = old_names->get(i);
6031 if (name == Heap::hidden_symbol()) {
6032 continue;
6033 }
6034 names->set(dest_pos++, name);
6035 }
6036 }
6037
ager@chromium.orgddb913d2009-01-27 10:01:48 +00006038 DeleteArray(local_property_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006039 return *Factory::NewJSArrayWithElements(names);
6040}
6041
6042
6043// Return the names of the local indexed properties.
6044// args[0]: object
6045static Object* Runtime_DebugLocalElementNames(Arguments args) {
6046 HandleScope scope;
6047 ASSERT(args.length() == 1);
6048 if (!args[0]->IsJSObject()) {
6049 return Heap::undefined_value();
6050 }
6051 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6052
6053 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
6054 Handle<FixedArray> names = Factory::NewFixedArray(n);
6055 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
6056 return *Factory::NewJSArrayWithElements(names);
6057}
6058
6059
6060// Return the property type calculated from the property details.
6061// args[0]: smi with property details.
6062static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
6063 ASSERT(args.length() == 1);
6064 CONVERT_CHECKED(Smi, details, args[0]);
6065 PropertyType type = PropertyDetails(details).type();
6066 return Smi::FromInt(static_cast<int>(type));
6067}
6068
6069
6070// Return the property attribute calculated from the property details.
6071// args[0]: smi with property details.
6072static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
6073 ASSERT(args.length() == 1);
6074 CONVERT_CHECKED(Smi, details, args[0]);
6075 PropertyAttributes attributes = PropertyDetails(details).attributes();
6076 return Smi::FromInt(static_cast<int>(attributes));
6077}
6078
6079
6080// Return the property insertion index calculated from the property details.
6081// args[0]: smi with property details.
6082static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
6083 ASSERT(args.length() == 1);
6084 CONVERT_CHECKED(Smi, details, args[0]);
6085 int index = PropertyDetails(details).index();
6086 return Smi::FromInt(index);
6087}
6088
6089
6090// Return information on whether an object has a named or indexed interceptor.
6091// args[0]: object
6092static Object* Runtime_DebugInterceptorInfo(Arguments args) {
6093 HandleScope scope;
6094 ASSERT(args.length() == 1);
6095 if (!args[0]->IsJSObject()) {
6096 return Smi::FromInt(0);
6097 }
6098 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6099
6100 int result = 0;
6101 if (obj->HasNamedInterceptor()) result |= 2;
6102 if (obj->HasIndexedInterceptor()) result |= 1;
6103
6104 return Smi::FromInt(result);
6105}
6106
6107
6108// Return property names from named interceptor.
6109// args[0]: object
6110static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
6111 HandleScope scope;
6112 ASSERT(args.length() == 1);
6113 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006114
ager@chromium.org32912102009-01-16 10:38:43 +00006115 if (obj->HasNamedInterceptor()) {
6116 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
6117 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6118 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006119 return Heap::undefined_value();
6120}
6121
6122
6123// Return element names from indexed interceptor.
6124// args[0]: object
6125static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
6126 HandleScope scope;
6127 ASSERT(args.length() == 1);
6128 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006129
ager@chromium.org32912102009-01-16 10:38:43 +00006130 if (obj->HasIndexedInterceptor()) {
6131 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
6132 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
6133 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006134 return Heap::undefined_value();
6135}
6136
6137
6138// Return property value from named interceptor.
6139// args[0]: object
6140// args[1]: property name
6141static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
6142 HandleScope scope;
6143 ASSERT(args.length() == 2);
6144 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6145 RUNTIME_ASSERT(obj->HasNamedInterceptor());
6146 CONVERT_ARG_CHECKED(String, name, 1);
6147
6148 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006149 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006150}
6151
6152
6153// Return element value from indexed interceptor.
6154// args[0]: object
6155// args[1]: index
6156static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
6157 HandleScope scope;
6158 ASSERT(args.length() == 2);
6159 CONVERT_ARG_CHECKED(JSObject, obj, 0);
6160 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
6161 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
6162
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006163 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006164}
6165
6166
6167static Object* Runtime_CheckExecutionState(Arguments args) {
6168 ASSERT(args.length() >= 1);
6169 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00006170 // Check that the break id is valid.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006171 if (Debug::break_id() == 0 || break_id != Debug::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006172 return Top::Throw(Heap::illegal_execution_state_symbol());
6173 }
6174
6175 return Heap::true_value();
6176}
6177
6178
6179static Object* Runtime_GetFrameCount(Arguments args) {
6180 HandleScope scope;
6181 ASSERT(args.length() == 1);
6182
6183 // Check arguments.
6184 Object* result = Runtime_CheckExecutionState(args);
6185 if (result->IsFailure()) return result;
6186
6187 // Count all frames which are relevant to debugging stack trace.
6188 int n = 0;
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006189 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006190 if (id == StackFrame::NO_ID) {
6191 // If there is no JavaScript stack frame count is 0.
6192 return Smi::FromInt(0);
6193 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006194 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
6195 return Smi::FromInt(n);
6196}
6197
6198
6199static const int kFrameDetailsFrameIdIndex = 0;
6200static const int kFrameDetailsReceiverIndex = 1;
6201static const int kFrameDetailsFunctionIndex = 2;
6202static const int kFrameDetailsArgumentCountIndex = 3;
6203static const int kFrameDetailsLocalCountIndex = 4;
6204static const int kFrameDetailsSourcePositionIndex = 5;
6205static const int kFrameDetailsConstructCallIndex = 6;
6206static const int kFrameDetailsDebuggerFrameIndex = 7;
6207static const int kFrameDetailsFirstDynamicIndex = 8;
6208
6209// Return an array with frame details
6210// args[0]: number: break id
6211// args[1]: number: frame index
6212//
6213// The array returned contains the following information:
6214// 0: Frame id
6215// 1: Receiver
6216// 2: Function
6217// 3: Argument count
6218// 4: Local count
6219// 5: Source position
6220// 6: Constructor call
6221// 7: Debugger frame
6222// Arguments name, value
6223// Locals name, value
6224static Object* Runtime_GetFrameDetails(Arguments args) {
6225 HandleScope scope;
6226 ASSERT(args.length() == 2);
6227
6228 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00006229 Object* check = Runtime_CheckExecutionState(args);
6230 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006231 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6232
6233 // Find the relevant frame with the requested index.
kasperl@chromium.org7be3c992009-03-12 07:19:55 +00006234 StackFrame::Id id = Debug::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00006235 if (id == StackFrame::NO_ID) {
6236 // If there are no JavaScript stack frames return undefined.
6237 return Heap::undefined_value();
6238 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006239 int count = 0;
6240 JavaScriptFrameIterator it(id);
6241 for (; !it.done(); it.Advance()) {
6242 if (count == index) break;
6243 count++;
6244 }
6245 if (it.done()) return Heap::undefined_value();
6246
6247 // Traverse the saved contexts chain to find the active context for the
6248 // selected frame.
6249 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006250 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006251 save = save->prev();
6252 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00006253 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006254
6255 // Get the frame id.
6256 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
6257
6258 // Find source position.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006259 int position = it.frame()->code()->SourcePosition(it.frame()->pc());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006260
6261 // Check for constructor frame.
6262 bool constructor = it.frame()->IsConstructor();
6263
6264 // Get code and read scope info from it for local variable information.
kasperl@chromium.org061ef742009-02-27 12:16:20 +00006265 Handle<Code> code(it.frame()->code());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006266 ScopeInfo<> info(*code);
6267
6268 // Get the context.
6269 Handle<Context> context(Context::cast(it.frame()->context()));
6270
6271 // Get the locals names and values into a temporary array.
6272 //
6273 // TODO(1240907): Hide compiler-introduced stack variables
6274 // (e.g. .result)? For users of the debugger, they will probably be
6275 // confusing.
6276 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
6277 for (int i = 0; i < info.NumberOfLocals(); i++) {
6278 // Name of the local.
6279 locals->set(i * 2, *info.LocalName(i));
6280
6281 // Fetch the value of the local - either from the stack or from a
6282 // heap-allocated context.
6283 if (i < info.number_of_stack_slots()) {
6284 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
6285 } else {
6286 Handle<String> name = info.LocalName(i);
6287 // Traverse the context chain to the function context as all local
6288 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00006289 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006290 context = Handle<Context>(context->previous());
6291 }
6292 ASSERT(context->is_function_context());
6293 locals->set(i * 2 + 1,
6294 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
6295 NULL)));
6296 }
6297 }
6298
6299 // Now advance to the arguments adapter frame (if any). If contains all
6300 // the provided parameters and
6301
6302 // Now advance to the arguments adapter frame (if any). It contains all
6303 // the provided parameters whereas the function frame always have the number
6304 // of arguments matching the functions parameters. The rest of the
6305 // information (except for what is collected above) is the same.
6306 it.AdvanceToArgumentsFrame();
6307
6308 // Find the number of arguments to fill. At least fill the number of
6309 // parameters for the function and fill more if more parameters are provided.
6310 int argument_count = info.number_of_parameters();
6311 if (argument_count < it.frame()->GetProvidedParametersCount()) {
6312 argument_count = it.frame()->GetProvidedParametersCount();
6313 }
6314
6315 // Calculate the size of the result.
6316 int details_size = kFrameDetailsFirstDynamicIndex +
6317 2 * (argument_count + info.NumberOfLocals());
6318 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6319
6320 // Add the frame id.
6321 details->set(kFrameDetailsFrameIdIndex, *frame_id);
6322
6323 // Add the function (same as in function frame).
6324 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
6325
6326 // Add the arguments count.
6327 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
6328
6329 // Add the locals count
6330 details->set(kFrameDetailsLocalCountIndex,
6331 Smi::FromInt(info.NumberOfLocals()));
6332
6333 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00006334 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006335 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
6336 } else {
6337 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
6338 }
6339
6340 // Add the constructor information.
6341 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
6342
6343 // Add information on whether this frame is invoked in the debugger context.
6344 details->set(kFrameDetailsDebuggerFrameIndex,
6345 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
6346
6347 // Fill the dynamic part.
6348 int details_index = kFrameDetailsFirstDynamicIndex;
6349
6350 // Add arguments name and value.
6351 for (int i = 0; i < argument_count; i++) {
6352 // Name of the argument.
6353 if (i < info.number_of_parameters()) {
6354 details->set(details_index++, *info.parameter_name(i));
6355 } else {
6356 details->set(details_index++, Heap::undefined_value());
6357 }
6358
6359 // Parameter value.
6360 if (i < it.frame()->GetProvidedParametersCount()) {
6361 details->set(details_index++, it.frame()->GetParameter(i));
6362 } else {
6363 details->set(details_index++, Heap::undefined_value());
6364 }
6365 }
6366
6367 // Add locals name and value from the temporary copy from the function frame.
6368 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
6369 details->set(details_index++, locals->get(i));
6370 }
6371
6372 // Add the receiver (same as in function frame).
6373 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
6374 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
6375 Handle<Object> receiver(it.frame()->receiver());
6376 if (!receiver->IsJSObject()) {
6377 // If the receiver is NOT a JSObject we have hit an optimization
6378 // where a value object is not converted into a wrapped JS objects.
6379 // To hide this optimization from the debugger, we wrap the receiver
6380 // by creating correct wrapper object based on the calling frame's
6381 // global context.
6382 it.Advance();
6383 Handle<Context> calling_frames_global_context(
6384 Context::cast(Context::cast(it.frame()->context())->global_context()));
6385 receiver = Factory::ToObject(receiver, calling_frames_global_context);
6386 }
6387 details->set(kFrameDetailsReceiverIndex, *receiver);
6388
6389 ASSERT_EQ(details_size, details_index);
6390 return *Factory::NewJSArrayWithElements(details);
6391}
6392
6393
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006394// Copy all the context locals into an object used to materialize a scope.
6395static void CopyContextLocalsToScopeObject(Handle<Code> code,
6396 ScopeInfo<>& scope_info,
6397 Handle<Context> context,
6398 Handle<JSObject> scope_object) {
6399 // Fill all context locals to the context extension.
6400 for (int i = Context::MIN_CONTEXT_SLOTS;
6401 i < scope_info.number_of_context_slots();
6402 i++) {
6403 int context_index =
6404 ScopeInfo<>::ContextSlotIndex(*code,
6405 *scope_info.context_slot_name(i),
6406 NULL);
6407
6408 // Don't include the arguments shadow (.arguments) context variable.
6409 if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
6410 SetProperty(scope_object,
6411 scope_info.context_slot_name(i),
6412 Handle<Object>(context->get(context_index)), NONE);
6413 }
6414 }
6415}
6416
6417
6418// Create a plain JSObject which materializes the local scope for the specified
6419// frame.
6420static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
6421 Handle<JSFunction> function(JSFunction::cast(frame->function()));
6422 Handle<Code> code(function->code());
6423 ScopeInfo<> scope_info(*code);
6424
6425 // Allocate and initialize a JSObject with all the arguments, stack locals
6426 // heap locals and extension properties of the debugged function.
6427 Handle<JSObject> local_scope = Factory::NewJSObject(Top::object_function());
6428
6429 // First fill all parameters.
6430 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6431 SetProperty(local_scope,
6432 scope_info.parameter_name(i),
6433 Handle<Object>(frame->GetParameter(i)), NONE);
6434 }
6435
6436 // Second fill all stack locals.
6437 for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
6438 SetProperty(local_scope,
6439 scope_info.stack_slot_name(i),
6440 Handle<Object>(frame->GetExpression(i)), NONE);
6441 }
6442
6443 // Third fill all context locals.
6444 Handle<Context> frame_context(Context::cast(frame->context()));
6445 Handle<Context> function_context(frame_context->fcontext());
6446 CopyContextLocalsToScopeObject(code, scope_info,
6447 function_context, local_scope);
6448
6449 // Finally copy any properties from the function context extension. This will
6450 // be variables introduced by eval.
6451 if (function_context->closure() == *function) {
6452 if (function_context->has_extension() &&
6453 !function_context->IsGlobalContext()) {
6454 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006455 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006456 for (int i = 0; i < keys->length(); i++) {
6457 // Names of variables introduced by eval are strings.
6458 ASSERT(keys->get(i)->IsString());
6459 Handle<String> key(String::cast(keys->get(i)));
6460 SetProperty(local_scope, key, GetProperty(ext, key), NONE);
6461 }
6462 }
6463 }
6464 return local_scope;
6465}
6466
6467
6468// Create a plain JSObject which materializes the closure content for the
6469// context.
6470static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
6471 ASSERT(context->is_function_context());
6472
6473 Handle<Code> code(context->closure()->code());
6474 ScopeInfo<> scope_info(*code);
6475
6476 // Allocate and initialize a JSObject with all the content of theis function
6477 // closure.
6478 Handle<JSObject> closure_scope = Factory::NewJSObject(Top::object_function());
6479
6480 // Check whether the arguments shadow object exists.
6481 int arguments_shadow_index =
6482 ScopeInfo<>::ContextSlotIndex(*code,
6483 Heap::arguments_shadow_symbol(),
6484 NULL);
6485 if (arguments_shadow_index >= 0) {
6486 // In this case all the arguments are available in the arguments shadow
6487 // object.
6488 Handle<JSObject> arguments_shadow(
6489 JSObject::cast(context->get(arguments_shadow_index)));
6490 for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
6491 SetProperty(closure_scope,
6492 scope_info.parameter_name(i),
6493 Handle<Object>(arguments_shadow->GetElement(i)), NONE);
6494 }
6495 }
6496
6497 // Fill all context locals to the context extension.
6498 CopyContextLocalsToScopeObject(code, scope_info, context, closure_scope);
6499
6500 // Finally copy any properties from the function context extension. This will
6501 // be variables introduced by eval.
6502 if (context->has_extension()) {
6503 Handle<JSObject> ext(JSObject::cast(context->extension()));
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00006504 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006505 for (int i = 0; i < keys->length(); i++) {
6506 // Names of variables introduced by eval are strings.
6507 ASSERT(keys->get(i)->IsString());
6508 Handle<String> key(String::cast(keys->get(i)));
6509 SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
6510 }
6511 }
6512
6513 return closure_scope;
6514}
6515
6516
6517// Iterate over the actual scopes visible from a stack frame. All scopes are
6518// backed by an actual context except the local scope, which is inserted
6519// "artifically" in the context chain.
6520class ScopeIterator {
6521 public:
6522 enum ScopeType {
6523 ScopeTypeGlobal = 0,
6524 ScopeTypeLocal,
6525 ScopeTypeWith,
ager@chromium.orga1645e22009-09-09 19:27:10 +00006526 ScopeTypeClosure,
6527 // Every catch block contains an implicit with block (its parameter is
6528 // a JSContextExtensionObject) that extends current scope with a variable
6529 // holding exception object. Such with blocks are treated as scopes of their
6530 // own type.
6531 ScopeTypeCatch
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006532 };
6533
6534 explicit ScopeIterator(JavaScriptFrame* frame)
6535 : frame_(frame),
6536 function_(JSFunction::cast(frame->function())),
6537 context_(Context::cast(frame->context())),
6538 local_done_(false),
6539 at_local_(false) {
6540
6541 // Check whether the first scope is actually a local scope.
6542 if (context_->IsGlobalContext()) {
6543 // If there is a stack slot for .result then this local scope has been
6544 // created for evaluating top level code and it is not a real local scope.
6545 // Checking for the existence of .result seems fragile, but the scope info
6546 // saved with the code object does not otherwise have that information.
6547 Handle<Code> code(function_->code());
6548 int index = ScopeInfo<>::StackSlotIndex(*code, Heap::result_symbol());
6549 at_local_ = index < 0;
6550 } else if (context_->is_function_context()) {
6551 at_local_ = true;
6552 }
6553 }
6554
6555 // More scopes?
6556 bool Done() { return context_.is_null(); }
6557
6558 // Move to the next scope.
6559 void Next() {
6560 // If at a local scope mark the local scope as passed.
6561 if (at_local_) {
6562 at_local_ = false;
6563 local_done_ = true;
6564
6565 // If the current context is not associated with the local scope the
6566 // current context is the next real scope, so don't move to the next
6567 // context in this case.
6568 if (context_->closure() != *function_) {
6569 return;
6570 }
6571 }
6572
6573 // The global scope is always the last in the chain.
6574 if (context_->IsGlobalContext()) {
6575 context_ = Handle<Context>();
6576 return;
6577 }
6578
6579 // Move to the next context.
6580 if (context_->is_function_context()) {
6581 context_ = Handle<Context>(Context::cast(context_->closure()->context()));
6582 } else {
6583 context_ = Handle<Context>(context_->previous());
6584 }
6585
6586 // If passing the local scope indicate that the current scope is now the
6587 // local scope.
6588 if (!local_done_ &&
6589 (context_->IsGlobalContext() || (context_->is_function_context()))) {
6590 at_local_ = true;
6591 }
6592 }
6593
6594 // Return the type of the current scope.
6595 int Type() {
6596 if (at_local_) {
6597 return ScopeTypeLocal;
6598 }
6599 if (context_->IsGlobalContext()) {
6600 ASSERT(context_->global()->IsGlobalObject());
6601 return ScopeTypeGlobal;
6602 }
6603 if (context_->is_function_context()) {
6604 return ScopeTypeClosure;
6605 }
6606 ASSERT(context_->has_extension());
ager@chromium.orga1645e22009-09-09 19:27:10 +00006607 // Current scope is either an explicit with statement or a with statement
6608 // implicitely generated for a catch block.
6609 // If the extension object here is a JSContextExtensionObject then
6610 // current with statement is one frome a catch block otherwise it's a
6611 // regular with statement.
6612 if (context_->extension()->IsJSContextExtensionObject()) {
6613 return ScopeTypeCatch;
6614 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006615 return ScopeTypeWith;
6616 }
6617
6618 // Return the JavaScript object with the content of the current scope.
6619 Handle<JSObject> ScopeObject() {
6620 switch (Type()) {
6621 case ScopeIterator::ScopeTypeGlobal:
6622 return Handle<JSObject>(CurrentContext()->global());
6623 break;
6624 case ScopeIterator::ScopeTypeLocal:
6625 // Materialize the content of the local scope into a JSObject.
6626 return MaterializeLocalScope(frame_);
6627 break;
6628 case ScopeIterator::ScopeTypeWith:
ager@chromium.orga1645e22009-09-09 19:27:10 +00006629 case ScopeIterator::ScopeTypeCatch:
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006630 // Return the with object.
6631 return Handle<JSObject>(CurrentContext()->extension());
6632 break;
6633 case ScopeIterator::ScopeTypeClosure:
6634 // Materialize the content of the closure scope into a JSObject.
6635 return MaterializeClosure(CurrentContext());
6636 break;
6637 }
6638 UNREACHABLE();
6639 return Handle<JSObject>();
6640 }
6641
6642 // Return the context for this scope. For the local context there might not
6643 // be an actual context.
6644 Handle<Context> CurrentContext() {
6645 if (at_local_ && context_->closure() != *function_) {
6646 return Handle<Context>();
6647 }
6648 return context_;
6649 }
6650
6651#ifdef DEBUG
6652 // Debug print of the content of the current scope.
6653 void DebugPrint() {
6654 switch (Type()) {
6655 case ScopeIterator::ScopeTypeGlobal:
6656 PrintF("Global:\n");
6657 CurrentContext()->Print();
6658 break;
6659
6660 case ScopeIterator::ScopeTypeLocal: {
6661 PrintF("Local:\n");
6662 Handle<Code> code(function_->code());
6663 ScopeInfo<> scope_info(*code);
6664 scope_info.Print();
6665 if (!CurrentContext().is_null()) {
6666 CurrentContext()->Print();
6667 if (CurrentContext()->has_extension()) {
6668 Handle<JSObject> extension =
6669 Handle<JSObject>(CurrentContext()->extension());
6670 if (extension->IsJSContextExtensionObject()) {
6671 extension->Print();
6672 }
6673 }
6674 }
6675 break;
6676 }
6677
6678 case ScopeIterator::ScopeTypeWith: {
6679 PrintF("With:\n");
6680 Handle<JSObject> extension =
6681 Handle<JSObject>(CurrentContext()->extension());
6682 extension->Print();
6683 break;
6684 }
6685
ager@chromium.orga1645e22009-09-09 19:27:10 +00006686 case ScopeIterator::ScopeTypeCatch: {
6687 PrintF("Catch:\n");
6688 Handle<JSObject> extension =
6689 Handle<JSObject>(CurrentContext()->extension());
6690 extension->Print();
6691 break;
6692 }
6693
ager@chromium.orgeadaf222009-06-16 09:43:10 +00006694 case ScopeIterator::ScopeTypeClosure: {
6695 PrintF("Closure:\n");
6696 CurrentContext()->Print();
6697 if (CurrentContext()->has_extension()) {
6698 Handle<JSObject> extension =
6699 Handle<JSObject>(CurrentContext()->extension());
6700 if (extension->IsJSContextExtensionObject()) {
6701 extension->Print();
6702 }
6703 }
6704 break;
6705 }
6706
6707 default:
6708 UNREACHABLE();
6709 }
6710 PrintF("\n");
6711 }
6712#endif
6713
6714 private:
6715 JavaScriptFrame* frame_;
6716 Handle<JSFunction> function_;
6717 Handle<Context> context_;
6718 bool local_done_;
6719 bool at_local_;
6720
6721 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
6722};
6723
6724
6725static Object* Runtime_GetScopeCount(Arguments args) {
6726 HandleScope scope;
6727 ASSERT(args.length() == 2);
6728
6729 // Check arguments.
6730 Object* check = Runtime_CheckExecutionState(args);
6731 if (check->IsFailure()) return check;
6732 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6733
6734 // Get the frame where the debugging is performed.
6735 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6736 JavaScriptFrameIterator it(id);
6737 JavaScriptFrame* frame = it.frame();
6738
6739 // Count the visible scopes.
6740 int n = 0;
6741 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6742 n++;
6743 }
6744
6745 return Smi::FromInt(n);
6746}
6747
6748
6749static const int kScopeDetailsTypeIndex = 0;
6750static const int kScopeDetailsObjectIndex = 1;
6751static const int kScopeDetailsSize = 2;
6752
6753// Return an array with scope details
6754// args[0]: number: break id
6755// args[1]: number: frame index
6756// args[2]: number: scope index
6757//
6758// The array returned contains the following information:
6759// 0: Scope type
6760// 1: Scope object
6761static Object* Runtime_GetScopeDetails(Arguments args) {
6762 HandleScope scope;
6763 ASSERT(args.length() == 3);
6764
6765 // Check arguments.
6766 Object* check = Runtime_CheckExecutionState(args);
6767 if (check->IsFailure()) return check;
6768 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
6769 CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
6770
6771 // Get the frame where the debugging is performed.
6772 StackFrame::Id id = UnwrapFrameId(wrapped_id);
6773 JavaScriptFrameIterator frame_it(id);
6774 JavaScriptFrame* frame = frame_it.frame();
6775
6776 // Find the requested scope.
6777 int n = 0;
6778 ScopeIterator it(frame);
6779 for (; !it.Done() && n < index; it.Next()) {
6780 n++;
6781 }
6782 if (it.Done()) {
6783 return Heap::undefined_value();
6784 }
6785
6786 // Calculate the size of the result.
6787 int details_size = kScopeDetailsSize;
6788 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
6789
6790 // Fill in scope details.
6791 details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
6792 details->set(kScopeDetailsObjectIndex, *it.ScopeObject());
6793
6794 return *Factory::NewJSArrayWithElements(details);
6795}
6796
6797
6798static Object* Runtime_DebugPrintScopes(Arguments args) {
6799 HandleScope scope;
6800 ASSERT(args.length() == 0);
6801
6802#ifdef DEBUG
6803 // Print the scopes for the top frame.
6804 StackFrameLocator locator;
6805 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
6806 for (ScopeIterator it(frame); !it.Done(); it.Next()) {
6807 it.DebugPrint();
6808 }
6809#endif
6810 return Heap::undefined_value();
6811}
6812
6813
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006814static Object* Runtime_GetCFrames(Arguments args) {
6815 HandleScope scope;
6816 ASSERT(args.length() == 1);
6817 Object* result = Runtime_CheckExecutionState(args);
6818 if (result->IsFailure()) return result;
6819
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006820#if V8_HOST_ARCH_64_BIT
6821 UNIMPLEMENTED();
6822 return Heap::undefined_value();
6823#else
6824
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006825 static const int kMaxCFramesSize = 200;
ager@chromium.org65dad4b2009-04-23 08:48:43 +00006826 ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
6827 int frames_count = OS::StackWalk(frames);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006828 if (frames_count == OS::kStackWalkError) {
6829 return Heap::undefined_value();
6830 }
6831
6832 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
6833 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
6834 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
6835 for (int i = 0; i < frames_count; i++) {
6836 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
6837 frame_value->SetProperty(
6838 *address_str,
6839 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
6840 NONE);
6841
6842 // Get the stack walk text for this frame.
6843 Handle<String> frame_text;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00006844 int frame_text_length = StrLength(frames[i].text);
6845 if (frame_text_length > 0) {
6846 Vector<const char> str(frames[i].text, frame_text_length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006847 frame_text = Factory::NewStringFromAscii(str);
6848 }
6849
6850 if (!frame_text.is_null()) {
6851 frame_value->SetProperty(*text_str, *frame_text, NONE);
6852 }
6853
6854 frames_array->set(i, *frame_value);
6855 }
6856 return *Factory::NewJSArrayWithElements(frames_array);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00006857#endif // V8_HOST_ARCH_64_BIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006858}
6859
6860
ager@chromium.orgbb29dc92009-03-24 13:25:23 +00006861static Object* Runtime_GetThreadCount(Arguments args) {
6862 HandleScope scope;
6863 ASSERT(args.length() == 1);
6864
6865 // Check arguments.
6866 Object* result = Runtime_CheckExecutionState(args);
6867 if (result->IsFailure()) return result;
6868
6869 // Count all archived V8 threads.
6870 int n = 0;
6871 for (ThreadState* thread = ThreadState::FirstInUse();
6872 thread != NULL;
6873 thread = thread->Next()) {
6874 n++;
6875 }
6876
6877 // Total number of threads is current thread and archived threads.
6878 return Smi::FromInt(n + 1);
6879}
6880
6881
6882static const int kThreadDetailsCurrentThreadIndex = 0;
6883static const int kThreadDetailsThreadIdIndex = 1;
6884static const int kThreadDetailsSize = 2;
6885
6886// Return an array with thread details
6887// args[0]: number: break id
6888// args[1]: number: thread index
6889//
6890// The array returned contains the following information:
6891// 0: Is current thread?
6892// 1: Thread id
6893static Object* Runtime_GetThreadDetails(Arguments args) {
6894 HandleScope scope;
6895 ASSERT(args.length() == 2);
6896
6897 // Check arguments.
6898 Object* check = Runtime_CheckExecutionState(args);
6899 if (check->IsFailure()) return check;
6900 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
6901
6902 // Allocate array for result.
6903 Handle<FixedArray> details = Factory::NewFixedArray(kThreadDetailsSize);
6904
6905 // Thread index 0 is current thread.
6906 if (index == 0) {
6907 // Fill the details.
6908 details->set(kThreadDetailsCurrentThreadIndex, Heap::true_value());
6909 details->set(kThreadDetailsThreadIdIndex,
6910 Smi::FromInt(ThreadManager::CurrentId()));
6911 } else {
6912 // Find the thread with the requested index.
6913 int n = 1;
6914 ThreadState* thread = ThreadState::FirstInUse();
6915 while (index != n && thread != NULL) {
6916 thread = thread->Next();
6917 n++;
6918 }
6919 if (thread == NULL) {
6920 return Heap::undefined_value();
6921 }
6922
6923 // Fill the details.
6924 details->set(kThreadDetailsCurrentThreadIndex, Heap::false_value());
6925 details->set(kThreadDetailsThreadIdIndex, Smi::FromInt(thread->id()));
6926 }
6927
6928 // Convert to JS array and return.
6929 return *Factory::NewJSArrayWithElements(details);
6930}
6931
6932
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006933static Object* Runtime_GetBreakLocations(Arguments args) {
6934 HandleScope scope;
6935 ASSERT(args.length() == 1);
6936
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006937 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6938 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006939 // Find the number of break points
6940 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
6941 if (break_locations->IsUndefined()) return Heap::undefined_value();
6942 // Return array as JS array
6943 return *Factory::NewJSArrayWithElements(
6944 Handle<FixedArray>::cast(break_locations));
6945}
6946
6947
6948// Set a break point in a function
6949// args[0]: function
6950// args[1]: number: break source position (within the function source)
6951// args[2]: number: break point object
6952static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
6953 HandleScope scope;
6954 ASSERT(args.length() == 3);
ager@chromium.org5aa501c2009-06-23 07:57:28 +00006955 CONVERT_ARG_CHECKED(JSFunction, fun, 0);
6956 Handle<SharedFunctionInfo> shared(fun->shared());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006957 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
6958 RUNTIME_ASSERT(source_position >= 0);
6959 Handle<Object> break_point_object_arg = args.at<Object>(2);
6960
6961 // Set break point.
6962 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
6963
6964 return Heap::undefined_value();
6965}
6966
6967
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00006968Object* Runtime::FindSharedFunctionInfoInScript(Handle<Script> script,
6969 int position) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006970 // Iterate the heap looking for SharedFunctionInfo generated from the
6971 // script. The inner most SharedFunctionInfo containing the source position
6972 // for the requested break point is found.
6973 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
6974 // which is found is not compiled it is compiled and the heap is iterated
6975 // again as the compilation might create inner functions from the newly
6976 // compiled function and the actual requested break point might be in one of
6977 // these functions.
6978 bool done = false;
6979 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00006980 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006981 Handle<SharedFunctionInfo> target;
6982 // The current candidate for the last function in script:
6983 Handle<SharedFunctionInfo> last;
6984 while (!done) {
6985 HeapIterator iterator;
6986 while (iterator.has_next()) {
6987 HeapObject* obj = iterator.next();
6988 ASSERT(obj != NULL);
6989 if (obj->IsSharedFunctionInfo()) {
6990 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
6991 if (shared->script() == *script) {
6992 // If the SharedFunctionInfo found has the requested script data and
6993 // contains the source position it is a candidate.
6994 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00006995 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00006996 start_position = shared->start_position();
6997 }
6998 if (start_position <= position &&
6999 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00007000 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007001 // candidate this is the new candidate.
7002 if (target.is_null()) {
7003 target_start_position = start_position;
7004 target = shared;
7005 } else {
ager@chromium.orga1645e22009-09-09 19:27:10 +00007006 if (target_start_position == start_position &&
7007 shared->end_position() == target->end_position()) {
7008 // If a top-level function contain only one function
7009 // declartion the source for the top-level and the function is
7010 // the same. In that case prefer the non top-level function.
7011 if (!shared->is_toplevel()) {
7012 target_start_position = start_position;
7013 target = shared;
7014 }
7015 } else if (target_start_position <= start_position &&
7016 shared->end_position() <= target->end_position()) {
7017 // This containment check includes equality as a function inside
7018 // a top-level function can share either start or end position
7019 // with the top-level function.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007020 target_start_position = start_position;
7021 target = shared;
7022 }
7023 }
7024 }
7025
7026 // Keep track of the last function in the script.
7027 if (last.is_null() ||
7028 shared->end_position() > last->start_position()) {
7029 last = shared;
7030 }
7031 }
7032 }
7033 }
7034
7035 // Make sure some candidate is selected.
7036 if (target.is_null()) {
7037 if (!last.is_null()) {
7038 // Position after the last function - use last.
7039 target = last;
7040 } else {
7041 // Unable to find function - possibly script without any function.
7042 return Heap::undefined_value();
7043 }
7044 }
7045
7046 // If the candidate found is compiled we are done. NOTE: when lazy
7047 // compilation of inner functions is introduced some additional checking
7048 // needs to be done here to compile inner functions.
7049 done = target->is_compiled();
7050 if (!done) {
7051 // If the candidate is not compiled compile it to reveal any inner
7052 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007053 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007054 }
7055 }
7056
7057 return *target;
7058}
7059
7060
7061// Change the state of a break point in a script. NOTE: Regarding performance
7062// see the NOTE for GetScriptFromScriptData.
7063// args[0]: script to set break point in
7064// args[1]: number: break source position (within the script source)
7065// args[2]: number: break point object
7066static Object* Runtime_SetScriptBreakPoint(Arguments args) {
7067 HandleScope scope;
7068 ASSERT(args.length() == 3);
7069 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
7070 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
7071 RUNTIME_ASSERT(source_position >= 0);
7072 Handle<Object> break_point_object_arg = args.at<Object>(2);
7073
7074 // Get the script from the script wrapper.
7075 RUNTIME_ASSERT(wrapper->value()->IsScript());
7076 Handle<Script> script(Script::cast(wrapper->value()));
7077
kasperl@chromium.orgd1e3e722009-04-14 13:38:25 +00007078 Object* result = Runtime::FindSharedFunctionInfoInScript(
7079 script, source_position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007080 if (!result->IsUndefined()) {
7081 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
7082 // Find position within function. The script position might be before the
7083 // source position of the first function.
7084 int position;
7085 if (shared->start_position() > source_position) {
7086 position = 0;
7087 } else {
7088 position = source_position - shared->start_position();
7089 }
7090 Debug::SetBreakPoint(shared, position, break_point_object_arg);
7091 }
7092 return Heap::undefined_value();
7093}
7094
7095
7096// Clear a break point
7097// args[0]: number: break point object
7098static Object* Runtime_ClearBreakPoint(Arguments args) {
7099 HandleScope scope;
7100 ASSERT(args.length() == 1);
7101 Handle<Object> break_point_object_arg = args.at<Object>(0);
7102
7103 // Clear break point.
7104 Debug::ClearBreakPoint(break_point_object_arg);
7105
7106 return Heap::undefined_value();
7107}
7108
7109
7110// Change the state of break on exceptions
7111// args[0]: boolean indicating uncaught exceptions
7112// args[1]: boolean indicating on/off
7113static Object* Runtime_ChangeBreakOnException(Arguments args) {
7114 HandleScope scope;
7115 ASSERT(args.length() == 2);
7116 ASSERT(args[0]->IsNumber());
7117 ASSERT(args[1]->IsBoolean());
7118
7119 // Update break point state
7120 ExceptionBreakType type =
7121 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
7122 bool enable = args[1]->ToBoolean()->IsTrue();
7123 Debug::ChangeBreakOnException(type, enable);
7124 return Heap::undefined_value();
7125}
7126
7127
7128// Prepare for stepping
7129// args[0]: break id for checking execution state
7130// args[1]: step action from the enumeration StepAction
ager@chromium.orga1645e22009-09-09 19:27:10 +00007131// args[2]: number of times to perform the step, for step out it is the number
7132// of frames to step down.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007133static Object* Runtime_PrepareStep(Arguments args) {
7134 HandleScope scope;
7135 ASSERT(args.length() == 3);
7136 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00007137 Object* check = Runtime_CheckExecutionState(args);
7138 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007139 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
7140 return Top::Throw(Heap::illegal_argument_symbol());
7141 }
7142
7143 // Get the step action and check validity.
7144 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
7145 if (step_action != StepIn &&
7146 step_action != StepNext &&
7147 step_action != StepOut &&
7148 step_action != StepInMin &&
7149 step_action != StepMin) {
7150 return Top::Throw(Heap::illegal_argument_symbol());
7151 }
7152
7153 // Get the number of steps.
7154 int step_count = NumberToInt32(args[2]);
7155 if (step_count < 1) {
7156 return Top::Throw(Heap::illegal_argument_symbol());
7157 }
7158
ager@chromium.orga1645e22009-09-09 19:27:10 +00007159 // Clear all current stepping setup.
7160 Debug::ClearStepping();
7161
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007162 // Prepare step.
7163 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
7164 return Heap::undefined_value();
7165}
7166
7167
7168// Clear all stepping set by PrepareStep.
7169static Object* Runtime_ClearStepping(Arguments args) {
7170 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007171 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007172 Debug::ClearStepping();
7173 return Heap::undefined_value();
7174}
7175
7176
7177// Creates a copy of the with context chain. The copy of the context chain is
7178// is linked to the function context supplied.
7179static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
7180 Handle<Context> function_context) {
7181 // At the bottom of the chain. Return the function context to link to.
7182 if (context_chain->is_function_context()) {
7183 return function_context;
7184 }
7185
7186 // Recursively copy the with contexts.
7187 Handle<Context> previous(context_chain->previous());
7188 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
7189 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007190 CopyWithContextChain(function_context, previous),
7191 extension,
7192 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007193}
7194
7195
7196// Helper function to find or create the arguments object for
7197// Runtime_DebugEvaluate.
7198static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
7199 Handle<JSFunction> function,
7200 Handle<Code> code,
7201 const ScopeInfo<>* sinfo,
7202 Handle<Context> function_context) {
7203 // Try to find the value of 'arguments' to pass as parameter. If it is not
7204 // found (that is the debugged function does not reference 'arguments' and
7205 // does not support eval) then create an 'arguments' object.
7206 int index;
7207 if (sinfo->number_of_stack_slots() > 0) {
7208 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
7209 if (index != -1) {
7210 return Handle<Object>(frame->GetExpression(index));
7211 }
7212 }
7213
7214 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
7215 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
7216 NULL);
7217 if (index != -1) {
7218 return Handle<Object>(function_context->get(index));
7219 }
7220 }
7221
7222 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007223 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
7224 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007225 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007226 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00007227 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007228 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00007229 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007230 return arguments;
7231}
7232
7233
7234// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00007235// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007236// extension part has all the parameters and locals of the function on the
7237// stack frame. A function which calls eval with the code to evaluate is then
7238// compiled in this context and called in this context. As this context
7239// replaces the context of the function on the stack frame a new (empty)
7240// function is created as well to be used as the closure for the context.
7241// This function and the context acts as replacements for the function on the
7242// stack frame presenting the same view of the values of parameters and
7243// local variables as if the piece of JavaScript was evaluated at the point
7244// where the function on the stack frame is currently stopped.
7245static Object* Runtime_DebugEvaluate(Arguments args) {
7246 HandleScope scope;
7247
7248 // Check the execution state and decode arguments frame and source to be
7249 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007250 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007251 Object* check_result = Runtime_CheckExecutionState(args);
7252 if (check_result->IsFailure()) return check_result;
7253 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
7254 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007255 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
7256
7257 // Handle the processing of break.
7258 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007259
7260 // Get the frame where the debugging is performed.
7261 StackFrame::Id id = UnwrapFrameId(wrapped_id);
7262 JavaScriptFrameIterator it(id);
7263 JavaScriptFrame* frame = it.frame();
7264 Handle<JSFunction> function(JSFunction::cast(frame->function()));
7265 Handle<Code> code(function->code());
7266 ScopeInfo<> sinfo(*code);
7267
7268 // Traverse the saved contexts chain to find the active context for the
7269 // selected frame.
7270 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00007271 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007272 save = save->prev();
7273 }
7274 ASSERT(save != NULL);
7275 SaveContext savex;
7276 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007277
7278 // Create the (empty) function replacing the function on the stack frame for
7279 // the purpose of evaluating in the context created below. It is important
7280 // that this function does not describe any parameters and local variables
7281 // in the context. If it does then this will cause problems with the lookup
7282 // in Context::Lookup, where context slots for parameters and local variables
7283 // are looked at before the extension object.
7284 Handle<JSFunction> go_between =
7285 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
7286 go_between->set_context(function->context());
7287#ifdef DEBUG
7288 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
7289 ASSERT(go_between_sinfo.number_of_parameters() == 0);
7290 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
7291#endif
7292
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007293 // Materialize the content of the local scope into a JSObject.
7294 Handle<JSObject> local_scope = MaterializeLocalScope(frame);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007295
7296 // Allocate a new context for the debug evaluation and set the extension
7297 // object build.
7298 Handle<Context> context =
7299 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007300 context->set_extension(*local_scope);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007301 // Copy any with contexts present and chain them in front of this context.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007302 Handle<Context> frame_context(Context::cast(frame->context()));
7303 Handle<Context> function_context(frame_context->fcontext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007304 context = CopyWithContextChain(frame_context, context);
7305
7306 // Wrap the evaluation statement in a new function compiled in the newly
7307 // created context. The function has one parameter which has to be called
7308 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00007309 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007310 // function(arguments,__source__) {return eval(__source__);}
7311 static const char* source_str =
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +00007312 "(function(arguments,__source__){return eval(__source__);})";
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007313 static const int source_str_length = StrLength(source_str);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007314 Handle<String> function_source =
7315 Factory::NewStringFromAscii(Vector<const char>(source_str,
7316 source_str_length));
7317 Handle<JSFunction> boilerplate =
ager@chromium.org381abbb2009-02-25 13:23:22 +00007318 Compiler::CompileEval(function_source,
7319 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007320 context->IsGlobalContext(),
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007321 Compiler::DONT_VALIDATE_JSON);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007322 if (boilerplate.is_null()) return Failure::Exception();
7323 Handle<JSFunction> compiled_function =
7324 Factory::NewFunctionFromBoilerplate(boilerplate, context);
7325
7326 // Invoke the result of the compilation to get the evaluation function.
7327 bool has_pending_exception;
7328 Handle<Object> receiver(frame->receiver());
7329 Handle<Object> evaluation_function =
7330 Execution::Call(compiled_function, receiver, 0, NULL,
7331 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007332 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007333
7334 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
7335 function_context);
7336
7337 // Invoke the evaluation function and return the result.
7338 const int argc = 2;
7339 Object** argv[argc] = { arguments.location(),
7340 Handle<Object>::cast(source).location() };
7341 Handle<Object> result =
7342 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
7343 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007344 if (has_pending_exception) return Failure::Exception();
ager@chromium.orgeadaf222009-06-16 09:43:10 +00007345
7346 // Skip the global proxy as it has no properties and always delegates to the
7347 // real global object.
7348 if (result->IsJSGlobalProxy()) {
7349 result = Handle<JSObject>(JSObject::cast(result->GetPrototype()));
7350 }
7351
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007352 return *result;
7353}
7354
7355
7356static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
7357 HandleScope scope;
7358
7359 // Check the execution state and decode arguments frame and source to be
7360 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007361 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007362 Object* check_result = Runtime_CheckExecutionState(args);
7363 if (check_result->IsFailure()) return check_result;
7364 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00007365 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
7366
7367 // Handle the processing of break.
7368 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007369
7370 // Enter the top context from before the debugger was invoked.
7371 SaveContext save;
7372 SaveContext* top = &save;
7373 while (top != NULL && *top->context() == *Debug::debug_context()) {
7374 top = top->prev();
7375 }
7376 if (top != NULL) {
7377 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007378 }
7379
7380 // Get the global context now set to the top context from before the
7381 // debugger was invoked.
7382 Handle<Context> context = Top::global_context();
7383
7384 // Compile the source to be evaluated.
ager@chromium.org381abbb2009-02-25 13:23:22 +00007385 Handle<JSFunction> boilerplate =
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007386 Handle<JSFunction>(Compiler::CompileEval(source,
7387 context,
ager@chromium.org3a37e9b2009-04-27 09:26:21 +00007388 true,
ager@chromium.orgadd848f2009-08-13 12:44:13 +00007389 Compiler::DONT_VALIDATE_JSON));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007390 if (boilerplate.is_null()) return Failure::Exception();
7391 Handle<JSFunction> compiled_function =
7392 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
7393 context));
7394
7395 // Invoke the result of the compilation to get the evaluation function.
7396 bool has_pending_exception;
7397 Handle<Object> receiver = Top::global();
7398 Handle<Object> result =
7399 Execution::Call(compiled_function, receiver, 0, NULL,
7400 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00007401 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007402 return *result;
7403}
7404
7405
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007406static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
7407 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00007408 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007409
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007410 // Fill the script objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007411 Handle<FixedArray> instances = Debug::GetLoadedScripts();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007412
7413 // Convert the script objects to proper JS objects.
kasperl@chromium.org71affb52009-05-26 05:44:31 +00007414 for (int i = 0; i < instances->length(); i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00007415 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
7416 // Get the script wrapper in a local handle before calling GetScriptWrapper,
7417 // because using
7418 // instances->set(i, *GetScriptWrapper(script))
7419 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
7420 // already have deferenced the instances handle.
7421 Handle<JSValue> wrapper = GetScriptWrapper(script);
7422 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007423 }
7424
7425 // Return result as a JS array.
7426 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
7427 Handle<JSArray>::cast(result)->SetContent(*instances);
7428 return *result;
7429}
7430
7431
7432// Helper function used by Runtime_DebugReferencedBy below.
7433static int DebugReferencedBy(JSObject* target,
7434 Object* instance_filter, int max_references,
7435 FixedArray* instances, int instances_size,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007436 JSFunction* arguments_function) {
7437 NoHandleAllocation ha;
7438 AssertNoAllocation no_alloc;
7439
7440 // Iterate the heap.
7441 int count = 0;
7442 JSObject* last = NULL;
7443 HeapIterator iterator;
7444 while (iterator.has_next() &&
7445 (max_references == 0 || count < max_references)) {
7446 // Only look at all JSObjects.
7447 HeapObject* heap_obj = iterator.next();
7448 if (heap_obj->IsJSObject()) {
7449 // Skip context extension objects and argument arrays as these are
7450 // checked in the context of functions using them.
7451 JSObject* obj = JSObject::cast(heap_obj);
iposva@chromium.org245aa852009-02-10 00:49:54 +00007452 if (obj->IsJSContextExtensionObject() ||
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007453 obj->map()->constructor() == arguments_function) {
7454 continue;
7455 }
7456
7457 // Check if the JS object has a reference to the object looked for.
7458 if (obj->ReferencesObject(target)) {
7459 // Check instance filter if supplied. This is normally used to avoid
7460 // references from mirror objects (see Runtime_IsInPrototypeChain).
7461 if (!instance_filter->IsUndefined()) {
7462 Object* V = obj;
7463 while (true) {
7464 Object* prototype = V->GetPrototype();
7465 if (prototype->IsNull()) {
7466 break;
7467 }
7468 if (instance_filter == prototype) {
7469 obj = NULL; // Don't add this object.
7470 break;
7471 }
7472 V = prototype;
7473 }
7474 }
7475
7476 if (obj != NULL) {
7477 // Valid reference found add to instance array if supplied an update
7478 // count.
7479 if (instances != NULL && count < instances_size) {
7480 instances->set(count, obj);
7481 }
7482 last = obj;
7483 count++;
7484 }
7485 }
7486 }
7487 }
7488
7489 // Check for circular reference only. This can happen when the object is only
7490 // referenced from mirrors and has a circular reference in which case the
7491 // object is not really alive and would have been garbage collected if not
7492 // referenced from the mirror.
7493 if (count == 1 && last == target) {
7494 count = 0;
7495 }
7496
7497 // Return the number of referencing objects found.
7498 return count;
7499}
7500
7501
7502// Scan the heap for objects with direct references to an object
7503// args[0]: the object to find references to
7504// args[1]: constructor function for instances to exclude (Mirror)
7505// args[2]: the the maximum number of objects to return
7506static Object* Runtime_DebugReferencedBy(Arguments args) {
7507 ASSERT(args.length() == 3);
7508
7509 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007510 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007511
7512 // Check parameters.
7513 CONVERT_CHECKED(JSObject, target, args[0]);
7514 Object* instance_filter = args[1];
7515 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
7516 instance_filter->IsJSObject());
7517 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
7518 RUNTIME_ASSERT(max_references >= 0);
7519
7520 // Get the constructor function for context extension and arguments array.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007521 JSObject* arguments_boilerplate =
7522 Top::context()->global_context()->arguments_boilerplate();
7523 JSFunction* arguments_function =
7524 JSFunction::cast(arguments_boilerplate->map()->constructor());
7525
7526 // Get the number of referencing objects.
7527 int count;
7528 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007529 NULL, 0, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007530
7531 // Allocate an array to hold the result.
7532 Object* object = Heap::AllocateFixedArray(count);
7533 if (object->IsFailure()) return object;
7534 FixedArray* instances = FixedArray::cast(object);
7535
7536 // Fill the referencing objects.
7537 count = DebugReferencedBy(target, instance_filter, max_references,
iposva@chromium.org245aa852009-02-10 00:49:54 +00007538 instances, count, arguments_function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007539
7540 // Return result as JS array.
7541 Object* result =
7542 Heap::AllocateJSObject(
7543 Top::context()->global_context()->array_function());
7544 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7545 return result;
7546}
7547
7548
7549// Helper function used by Runtime_DebugConstructedBy below.
7550static int DebugConstructedBy(JSFunction* constructor, int max_references,
7551 FixedArray* instances, int instances_size) {
7552 AssertNoAllocation no_alloc;
7553
7554 // Iterate the heap.
7555 int count = 0;
7556 HeapIterator iterator;
7557 while (iterator.has_next() &&
7558 (max_references == 0 || count < max_references)) {
7559 // Only look at all JSObjects.
7560 HeapObject* heap_obj = iterator.next();
7561 if (heap_obj->IsJSObject()) {
7562 JSObject* obj = JSObject::cast(heap_obj);
7563 if (obj->map()->constructor() == constructor) {
7564 // Valid reference found add to instance array if supplied an update
7565 // count.
7566 if (instances != NULL && count < instances_size) {
7567 instances->set(count, obj);
7568 }
7569 count++;
7570 }
7571 }
7572 }
7573
7574 // Return the number of referencing objects found.
7575 return count;
7576}
7577
7578
7579// Scan the heap for objects constructed by a specific function.
7580// args[0]: the constructor to find instances of
7581// args[1]: the the maximum number of objects to return
7582static Object* Runtime_DebugConstructedBy(Arguments args) {
7583 ASSERT(args.length() == 2);
7584
7585 // First perform a full GC in order to avoid dead objects.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007586 Heap::CollectAllGarbage(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007587
7588 // Check parameters.
7589 CONVERT_CHECKED(JSFunction, constructor, args[0]);
7590 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
7591 RUNTIME_ASSERT(max_references >= 0);
7592
7593 // Get the number of referencing objects.
7594 int count;
7595 count = DebugConstructedBy(constructor, max_references, NULL, 0);
7596
7597 // Allocate an array to hold the result.
7598 Object* object = Heap::AllocateFixedArray(count);
7599 if (object->IsFailure()) return object;
7600 FixedArray* instances = FixedArray::cast(object);
7601
7602 // Fill the referencing objects.
7603 count = DebugConstructedBy(constructor, max_references, instances, count);
7604
7605 // Return result as JS array.
7606 Object* result =
7607 Heap::AllocateJSObject(
7608 Top::context()->global_context()->array_function());
7609 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
7610 return result;
7611}
7612
7613
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007614// Find the effective prototype object as returned by __proto__.
7615// args[0]: the object to find the prototype for.
7616static Object* Runtime_DebugGetPrototype(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007617 ASSERT(args.length() == 1);
7618
7619 CONVERT_CHECKED(JSObject, obj, args[0]);
7620
ager@chromium.orgddb913d2009-01-27 10:01:48 +00007621 // Use the __proto__ accessor.
7622 return Accessors::ObjectPrototype.getter(obj, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007623}
7624
7625
7626static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007627 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007628 CPU::DebugBreak();
7629 return Heap::undefined_value();
7630}
7631
7632
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007633static Object* Runtime_DebugDisassembleFunction(Arguments args) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007634#ifdef DEBUG
7635 HandleScope scope;
7636 ASSERT(args.length() == 1);
7637 // Get the function and make sure it is compiled.
7638 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7639 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7640 return Failure::Exception();
7641 }
7642 func->code()->PrintLn();
7643#endif // DEBUG
7644 return Heap::undefined_value();
7645}
ager@chromium.org9085a012009-05-11 19:22:57 +00007646
7647
ager@chromium.org18ad94b2009-09-02 08:22:29 +00007648static Object* Runtime_DebugDisassembleConstructor(Arguments args) {
7649#ifdef DEBUG
7650 HandleScope scope;
7651 ASSERT(args.length() == 1);
7652 // Get the function and make sure it is compiled.
7653 CONVERT_ARG_CHECKED(JSFunction, func, 0);
7654 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
7655 return Failure::Exception();
7656 }
7657 func->shared()->construct_stub()->PrintLn();
7658#endif // DEBUG
7659 return Heap::undefined_value();
7660}
7661
7662
ager@chromium.org9085a012009-05-11 19:22:57 +00007663static Object* Runtime_FunctionGetInferredName(Arguments args) {
7664 NoHandleAllocation ha;
7665 ASSERT(args.length() == 1);
7666
7667 CONVERT_CHECKED(JSFunction, f, args[0]);
7668 return f->shared()->inferred_name();
7669}
ager@chromium.org65dad4b2009-04-23 08:48:43 +00007670#endif // ENABLE_DEBUGGER_SUPPORT
7671
7672
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007673// Finds the script object from the script data. NOTE: This operation uses
7674// heap traversal to find the function generated for the source position
7675// for the requested break point. For lazily compiled functions several heap
7676// traversals might be required rendering this operation as a rather slow
7677// operation. However for setting break points which is normally done through
7678// some kind of user interaction the performance is not crucial.
7679static Handle<Object> Runtime_GetScriptFromScriptName(
7680 Handle<String> script_name) {
7681 // Scan the heap for Script objects to find the script with the requested
7682 // script data.
7683 Handle<Script> script;
7684 HeapIterator iterator;
7685 while (script.is_null() && iterator.has_next()) {
7686 HeapObject* obj = iterator.next();
7687 // If a script is found check if it has the script data requested.
7688 if (obj->IsScript()) {
7689 if (Script::cast(obj)->name()->IsString()) {
7690 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
7691 script = Handle<Script>(Script::cast(obj));
7692 }
7693 }
7694 }
7695 }
7696
7697 // If no script with the requested script data is found return undefined.
7698 if (script.is_null()) return Factory::undefined_value();
7699
7700 // Return the script found.
7701 return GetScriptWrapper(script);
7702}
7703
7704
7705// Get the script object from script data. NOTE: Regarding performance
7706// see the NOTE for GetScriptFromScriptData.
7707// args[0]: script data for the script to find the source for
7708static Object* Runtime_GetScript(Arguments args) {
7709 HandleScope scope;
7710
7711 ASSERT(args.length() == 1);
7712
7713 CONVERT_CHECKED(String, script_name, args[0]);
7714
7715 // Find the requested script.
7716 Handle<Object> result =
7717 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
7718 return *result;
7719}
7720
7721
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007722// Determines whether the given stack frame should be displayed in
7723// a stack trace. The caller is the error constructor that asked
7724// for the stack trace to be collected. The first time a construct
7725// call to this function is encountered it is skipped. The seen_caller
7726// in/out parameter is used to remember if the caller has been seen
7727// yet.
7728static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller,
7729 bool* seen_caller) {
7730 // Only display JS frames.
7731 if (!raw_frame->is_java_script())
7732 return false;
7733 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
7734 Object* raw_fun = frame->function();
7735 // Not sure when this can happen but skip it just in case.
7736 if (!raw_fun->IsJSFunction())
7737 return false;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007738 if ((raw_fun == caller) && !(*seen_caller)) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007739 *seen_caller = true;
7740 return false;
7741 }
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007742 // Skip all frames until we've seen the caller. Also, skip the most
7743 // obvious builtin calls. Some builtin calls (such as Number.ADD
7744 // which is invoked using 'call') are very difficult to recognize
7745 // so we're leaving them in for now.
7746 return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007747}
7748
7749
7750// Collect the raw data for a stack trace. Returns an array of three
7751// element segments each containing a receiver, function and native
7752// code offset.
7753static Object* Runtime_CollectStackTrace(Arguments args) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007754 ASSERT_EQ(args.length(), 2);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007755 Handle<Object> caller = args.at<Object>(0);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007756 CONVERT_NUMBER_CHECKED(int32_t, limit, Int32, args[1]);
7757
7758 HandleScope scope;
7759
7760 int initial_size = limit < 10 ? limit : 10;
7761 Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007762
7763 StackFrameIterator iter;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007764 // If the caller parameter is a function we skip frames until we're
7765 // under it before starting to collect.
7766 bool seen_caller = !caller->IsJSFunction();
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007767 int cursor = 0;
7768 int frames_seen = 0;
7769 while (!iter.done() && frames_seen < limit) {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007770 StackFrame* raw_frame = iter.frame();
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007771 if (ShowFrameInStackTrace(raw_frame, *caller, &seen_caller)) {
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007772 frames_seen++;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007773 JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007774 Object* recv = frame->receiver();
7775 Object* fun = frame->function();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007776 Address pc = frame->pc();
7777 Address start = frame->code()->address();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007778 Smi* offset = Smi::FromInt(static_cast<int>(pc - start));
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007779 FixedArray* elements = FixedArray::cast(result->elements());
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007780 if (cursor + 2 < elements->length()) {
7781 elements->set(cursor++, recv);
7782 elements->set(cursor++, fun);
7783 elements->set(cursor++, offset, SKIP_WRITE_BARRIER);
7784 } else {
7785 HandleScope scope;
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +00007786 Handle<Object> recv_handle(recv);
7787 Handle<Object> fun_handle(fun);
7788 SetElement(result, cursor++, recv_handle);
7789 SetElement(result, cursor++, fun_handle);
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007790 SetElement(result, cursor++, Handle<Smi>(offset));
7791 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007792 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007793 iter.Advance();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007794 }
kasperl@chromium.org86f77b72009-07-06 08:21:57 +00007795
7796 result->set_length(Smi::FromInt(cursor), SKIP_WRITE_BARRIER);
7797
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00007798 return *result;
7799}
7800
7801
ager@chromium.org3811b432009-10-28 14:53:37 +00007802// Returns V8 version as a string.
7803static Object* Runtime_GetV8Version(Arguments args) {
7804 ASSERT_EQ(args.length(), 0);
7805
7806 NoHandleAllocation ha;
7807
7808 const char* version_string = v8::V8::GetVersion();
7809
7810 return Heap::AllocateStringFromAscii(CStrVector(version_string), NOT_TENURED);
7811}
7812
7813
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007814static Object* Runtime_Abort(Arguments args) {
7815 ASSERT(args.length() == 2);
7816 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
7817 Smi::cast(args[1])->value());
7818 Top::PrintStack();
7819 OS::Abort();
7820 UNREACHABLE();
7821 return NULL;
7822}
7823
7824
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007825static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
7826 ASSERT(args.length() == 0);
7827 HandleScope::DeleteExtensions();
7828 return Heap::undefined_value();
7829}
7830
7831
kasper.lund44510672008-07-25 07:37:58 +00007832#ifdef DEBUG
7833// ListNatives is ONLY used by the fuzz-natives.js in debug mode
7834// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007835static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00007836 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007837 HandleScope scope;
7838 Handle<JSArray> result = Factory::NewJSArray(0);
7839 int index = 0;
ager@chromium.orga1645e22009-09-09 19:27:10 +00007840#define ADD_ENTRY(Name, argc, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007841 { \
7842 HandleScope inner; \
7843 Handle<String> name = \
ager@chromium.orgc4c92722009-11-18 14:12:51 +00007844 Factory::NewStringFromAscii( \
7845 Vector<const char>(#Name, StrLength(#Name))); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007846 Handle<JSArray> pair = Factory::NewJSArray(0); \
7847 SetElement(pair, 0, name); \
7848 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
7849 SetElement(result, index++, pair); \
7850 }
7851 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
7852#undef ADD_ENTRY
7853 return *result;
7854}
kasper.lund44510672008-07-25 07:37:58 +00007855#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007856
7857
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007858static Object* Runtime_Log(Arguments args) {
7859 ASSERT(args.length() == 2);
ager@chromium.org381abbb2009-02-25 13:23:22 +00007860 CONVERT_CHECKED(String, format, args[0]);
7861 CONVERT_CHECKED(JSArray, elms, args[1]);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007862 Vector<const char> chars = format->ToAsciiVector();
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00007863 Logger::LogRuntime(chars, elms);
7864 return Heap::undefined_value();
7865}
7866
7867
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007868static Object* Runtime_IS_VAR(Arguments args) {
7869 UNREACHABLE(); // implemented as macro in the parser
7870 return NULL;
7871}
7872
7873
7874// ----------------------------------------------------------------------------
7875// Implementation of Runtime
7876
ager@chromium.orga1645e22009-09-09 19:27:10 +00007877#define F(name, nargs, ressize) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007878 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
ager@chromium.orga1645e22009-09-09 19:27:10 +00007879 static_cast<int>(Runtime::k##name), ressize },
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007880
7881static Runtime::Function Runtime_functions[] = {
7882 RUNTIME_FUNCTION_LIST(F)
ager@chromium.orga1645e22009-09-09 19:27:10 +00007883 { NULL, NULL, NULL, 0, -1, 0 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007884};
7885
7886#undef F
7887
7888
7889Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
7890 ASSERT(0 <= fid && fid < kNofFunctions);
7891 return &Runtime_functions[fid];
7892}
7893
7894
7895Runtime::Function* Runtime::FunctionForName(const char* name) {
7896 for (Function* f = Runtime_functions; f->name != NULL; f++) {
7897 if (strcmp(f->name, name) == 0) {
7898 return f;
7899 }
7900 }
7901 return NULL;
7902}
7903
7904
7905void Runtime::PerformGC(Object* result) {
7906 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007907 if (failure->IsRetryAfterGC()) {
7908 // Try to do a garbage collection; ignore it if it fails. The C
7909 // entry stub will throw an out-of-memory exception in that case.
7910 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
7911 } else {
7912 // Handle last resort GC and make sure to allow future allocations
7913 // to grow the heap without causing GCs (if possible).
7914 Counters::gc_last_resort_from_js.Increment();
ager@chromium.orgab99eea2009-08-25 07:05:41 +00007915 Heap::CollectAllGarbage(false);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00007916 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00007917}
7918
7919
7920} } // namespace v8::internal