blob: fd3672461be78114225020f7aca5e38397f529e1 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "v8.h"
31
32#include "accessors.h"
33#include "api.h"
34#include "arguments.h"
35#include "compiler.h"
36#include "cpu.h"
37#include "dateparser.h"
38#include "debug.h"
39#include "execution.h"
40#include "jsregexp.h"
41#include "platform.h"
42#include "runtime.h"
43#include "scopeinfo.h"
44#include "v8threads.h"
ager@chromium.org7c537e22008-10-16 08:43:32 +000045#include "smart-pointer.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000046
47namespace v8 { namespace internal {
48
49
50#define RUNTIME_ASSERT(value) do { \
51 if (!(value)) return IllegalOperation(); \
52} while (false)
53
54// Cast the given object to a value of the specified type and store
55// it in a variable with the given name. If the object is not of the
56// expected type call IllegalOperation and return.
57#define CONVERT_CHECKED(Type, name, obj) \
58 RUNTIME_ASSERT(obj->Is##Type()); \
59 Type* name = Type::cast(obj);
60
61#define CONVERT_ARG_CHECKED(Type, name, index) \
62 RUNTIME_ASSERT(args[index]->Is##Type()); \
63 Handle<Type> name = args.at<Type>(index);
64
kasper.lundbd3ec4e2008-07-09 11:06:54 +000065// Cast the given object to a boolean and store it in a variable with
66// the given name. If the object is not a boolean call IllegalOperation
67// and return.
68#define CONVERT_BOOLEAN_CHECKED(name, obj) \
69 RUNTIME_ASSERT(obj->IsBoolean()); \
70 bool name = (obj)->IsTrue();
71
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000072// Cast the given object to a double and store it in a variable with
73// the given name. If the object is not a number (as opposed to
74// the number not-a-number) call IllegalOperation and return.
75#define CONVERT_DOUBLE_CHECKED(name, obj) \
76 RUNTIME_ASSERT(obj->IsNumber()); \
77 double name = (obj)->Number();
78
79// Call the specified converter on the object *comand store the result in
80// a variable of the specified type with the given name. If the
81// object is not a Number call IllegalOperation and return.
82#define CONVERT_NUMBER_CHECKED(type, name, Type, obj) \
83 RUNTIME_ASSERT(obj->IsNumber()); \
84 type name = NumberTo##Type(obj);
85
86// Non-reentrant string buffer for efficient general use in this file.
87static StaticResource<StringInputBuffer> string_input_buffer;
88
89
90static Object* IllegalOperation() {
91 return Top::Throw(Heap::illegal_access_symbol());
92}
93
94
95static Object* Runtime_CloneObjectLiteralBoilerplate(Arguments args) {
96 CONVERT_CHECKED(JSObject, boilerplate, args[0]);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000097 return Heap::CopyJSObject(boilerplate);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000098}
99
100
ager@chromium.org236ad962008-09-25 09:45:57 +0000101static Handle<Map> ComputeObjectLiteralMap(
102 Handle<Context> context,
103 Handle<FixedArray> constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000104 bool* is_result_from_cache) {
ager@chromium.org32912102009-01-16 10:38:43 +0000105 int number_of_properties = constant_properties->length() / 2;
ager@chromium.org236ad962008-09-25 09:45:57 +0000106 if (FLAG_canonicalize_object_literal_maps) {
107 // First find prefix of consecutive symbol keys.
ager@chromium.org236ad962008-09-25 09:45:57 +0000108 int number_of_symbol_keys = 0;
109 while ((number_of_symbol_keys < number_of_properties) &&
110 (constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
111 number_of_symbol_keys++;
112 }
113 // Based on the number of prefix symbols key we decide whether
114 // to use the map cache in the global context.
115 const int kMaxKeys = 10;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000116 if ((number_of_symbol_keys == number_of_properties) &&
117 (number_of_symbol_keys < kMaxKeys)) {
ager@chromium.org236ad962008-09-25 09:45:57 +0000118 // Create the fixed array with the key.
119 Handle<FixedArray> keys = Factory::NewFixedArray(number_of_symbol_keys);
120 for (int i = 0; i < number_of_symbol_keys; i++) {
121 keys->set(i, constant_properties->get(i*2));
122 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000123 *is_result_from_cache = true;
ager@chromium.org236ad962008-09-25 09:45:57 +0000124 return Factory::ObjectLiteralMapFromCache(context, keys);
125 }
126 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000127 *is_result_from_cache = false;
ager@chromium.org32912102009-01-16 10:38:43 +0000128 return Factory::CopyMap(
129 Handle<Map>(context->object_function()->initial_map()),
130 number_of_properties);
ager@chromium.org236ad962008-09-25 09:45:57 +0000131}
132
133
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000134static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
135 HandleScope scope;
136 ASSERT(args.length() == 3);
137 // Copy the arguments.
138 Handle<FixedArray> literals = args.at<FixedArray>(0);
139 int literals_index = Smi::cast(args[1])->value();
140 Handle<FixedArray> constant_properties = args.at<FixedArray>(2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000141
142 // Get the global context from the literals array. This is the
143 // context in which the function was created and we use the object
144 // function from this context to create the object literal. We do
145 // not use the object function from the current global context
146 // because this might be the object function from another context
147 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000148 Handle<Context> context =
149 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
150
151 bool is_result_from_cache;
152 Handle<Map> map = ComputeObjectLiteralMap(context,
153 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000154 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000155
ager@chromium.org236ad962008-09-25 09:45:57 +0000156 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
ager@chromium.org32912102009-01-16 10:38:43 +0000157 { // Add the constant properties to the boilerplate.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000158 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000159 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
160 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000161 for (int index = 0; index < length; index +=2) {
162 Handle<Object> key(constant_properties->get(index+0));
163 Handle<Object> value(constant_properties->get(index+1));
164 uint32_t element_index = 0;
165 if (key->IsSymbol()) {
166 // If key is a symbol it is not an array element.
167 Handle<String> name(String::cast(*key));
168 ASSERT(!name->AsArrayIndex(&element_index));
169 SetProperty(boilerplate, name, value, NONE);
170 } else if (Array::IndexFromObject(*key, &element_index)) {
171 // Array index (uint32).
172 SetElement(boilerplate, element_index, value);
173 } else {
174 // Non-uint32 number.
175 ASSERT(key->IsNumber());
176 double num = key->Number();
177 char arr[100];
178 Vector<char> buffer(arr, ARRAY_SIZE(arr));
179 const char* str = DoubleToCString(num, buffer);
180 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
181 SetProperty(boilerplate, name, value, NONE);
182 }
183 }
184 }
185
186 // Update the functions literal and return the boilerplate.
187 literals->set(literals_index, *boilerplate);
188
189 return *boilerplate;
190}
191
192
193static Object* Runtime_CreateArrayLiteral(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000194 // Takes a FixedArray of elements containing the literal elements of
195 // the array literal and produces JSArray with those elements.
196 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000197 // which contains the context from which to get the Array function
198 // to use for creating the array literal.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000199 ASSERT(args.length() == 2);
200 CONVERT_CHECKED(FixedArray, elements, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000201 CONVERT_CHECKED(FixedArray, literals, args[1]);
ager@chromium.org236ad962008-09-25 09:45:57 +0000202 JSFunction* constructor =
203 JSFunction::GlobalContextFromLiterals(literals)->array_function();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000204 // Create the JSArray.
205 Object* object = Heap::AllocateJSObject(constructor);
206 if (object->IsFailure()) return object;
207
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000208 // Copy the elements.
209 Object* content = elements->Copy();
210 if (content->IsFailure()) return content;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000211
212 // Set the elements.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000213 JSArray::cast(object)->SetContent(FixedArray::cast(content));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000214 return object;
215}
216
217
ager@chromium.org32912102009-01-16 10:38:43 +0000218static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
219 ASSERT(args.length() == 2);
220 CONVERT_CHECKED(String, key, args[0]);
221 Object* value = args[1];
222 // Create a catch context extension object.
223 JSFunction* constructor =
224 Top::context()->global_context()->context_extension_function();
225 Object* object = Heap::AllocateJSObject(constructor);
226 if (object->IsFailure()) return object;
227 // Assign the exception value to the catch variable and make sure
228 // that the catch variable is DontDelete.
229 value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
230 if (value->IsFailure()) return value;
231 return object;
232}
233
234
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000235static Object* Runtime_ClassOf(Arguments args) {
236 NoHandleAllocation ha;
237 ASSERT(args.length() == 1);
238 Object* obj = args[0];
239 if (!obj->IsJSObject()) return Heap::null_value();
240 return JSObject::cast(obj)->class_name();
241}
242
ager@chromium.org7c537e22008-10-16 08:43:32 +0000243
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000244static Object* Runtime_HasStringClass(Arguments args) {
245 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::String_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000246}
247
248
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000249static Object* Runtime_HasDateClass(Arguments args) {
250 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Date_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000251}
252
253
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000254static Object* Runtime_HasArrayClass(Arguments args) {
255 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Array_symbol()));
256}
257
258
259static Object* Runtime_HasFunctionClass(Arguments args) {
260 return Heap::ToBoolean(
261 args[0]->HasSpecificClassOf(Heap::function_class_symbol()));
262}
263
264
265static Object* Runtime_HasNumberClass(Arguments args) {
266 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Number_symbol()));
267}
268
269
270static Object* Runtime_HasBooleanClass(Arguments args) {
271 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Boolean_symbol()));
272}
273
274
275static Object* Runtime_HasArgumentsClass(Arguments args) {
276 return Heap::ToBoolean(
277 args[0]->HasSpecificClassOf(Heap::Arguments_symbol()));
278}
279
280
281static Object* Runtime_HasRegExpClass(Arguments args) {
282 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::RegExp_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000283}
284
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000285
286static Object* Runtime_IsInPrototypeChain(Arguments args) {
287 NoHandleAllocation ha;
288 ASSERT(args.length() == 2);
289 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
290 Object* O = args[0];
291 Object* V = args[1];
292 while (true) {
293 Object* prototype = V->GetPrototype();
294 if (prototype->IsNull()) return Heap::false_value();
295 if (O == prototype) return Heap::true_value();
296 V = prototype;
297 }
298}
299
300
301static Object* Runtime_IsConstructCall(Arguments args) {
302 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000303 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000304 JavaScriptFrameIterator it;
305 return Heap::ToBoolean(it.frame()->IsConstructor());
306}
307
308
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000309static Object* Runtime_RegExpCompile(Arguments args) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000310 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000311 ASSERT(args.length() == 3);
ager@chromium.org236ad962008-09-25 09:45:57 +0000312 CONVERT_CHECKED(JSRegExp, raw_re, args[0]);
313 Handle<JSRegExp> re(raw_re);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000314 CONVERT_CHECKED(String, raw_pattern, args[1]);
315 Handle<String> pattern(raw_pattern);
316 CONVERT_CHECKED(String, raw_flags, args[2]);
317 Handle<String> flags(raw_flags);
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000318 Handle<Object> result = RegExpImpl::Compile(re, pattern, flags);
319 if (result.is_null()) return Failure::Exception();
320 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000321}
322
323
324static Object* Runtime_CreateApiFunction(Arguments args) {
325 HandleScope scope;
326 ASSERT(args.length() == 1);
327 CONVERT_CHECKED(FunctionTemplateInfo, raw_data, args[0]);
328 Handle<FunctionTemplateInfo> data(raw_data);
329 return *Factory::CreateApiFunction(data);
330}
331
332
333static Object* Runtime_IsTemplate(Arguments args) {
334 ASSERT(args.length() == 1);
335 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000336 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000337 return Heap::ToBoolean(result);
338}
339
340
341static Object* Runtime_GetTemplateField(Arguments args) {
342 ASSERT(args.length() == 2);
343 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000344 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000345 int index = field->value();
346 int offset = index * kPointerSize + HeapObject::kHeaderSize;
347 InstanceType type = templ->map()->instance_type();
348 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
349 type == OBJECT_TEMPLATE_INFO_TYPE);
350 RUNTIME_ASSERT(offset > 0);
351 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
352 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
353 } else {
354 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
355 }
356 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000357}
358
359
ager@chromium.org870a0b62008-11-04 11:43:05 +0000360static Object* Runtime_DisableAccessChecks(Arguments args) {
361 ASSERT(args.length() == 1);
362 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000363 Map* old_map = object->map();
364 bool needs_access_checks = old_map->is_access_check_needed();
365 if (needs_access_checks) {
366 // Copy map so it won't interfere constructor's initial map.
367 Object* new_map = old_map->CopyDropTransitions();
368 if (new_map->IsFailure()) return new_map;
369
370 Map::cast(new_map)->set_is_access_check_needed(false);
371 object->set_map(Map::cast(new_map));
372 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000373 return needs_access_checks ? Heap::true_value() : Heap::false_value();
374}
375
376
377static Object* Runtime_EnableAccessChecks(Arguments args) {
378 ASSERT(args.length() == 1);
379 CONVERT_CHECKED(HeapObject, object, args[0]);
ager@chromium.org32912102009-01-16 10:38:43 +0000380 Map* old_map = object->map();
381 if (!old_map->is_access_check_needed()) {
382 // Copy map so it won't interfere constructor's initial map.
383 Object* new_map = old_map->CopyDropTransitions();
384 if (new_map->IsFailure()) return new_map;
385
386 Map::cast(new_map)->set_is_access_check_needed(true);
387 object->set_map(Map::cast(new_map));
388 }
ager@chromium.org870a0b62008-11-04 11:43:05 +0000389 return Heap::undefined_value();
390}
391
392
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000393static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
394 HandleScope scope;
395 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
396 Handle<Object> args[2] = { type_handle, name };
397 Handle<Object> error =
398 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
399 return Top::Throw(*error);
400}
401
402
403static Object* Runtime_DeclareGlobals(Arguments args) {
404 HandleScope scope;
405 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
406
407 CONVERT_ARG_CHECKED(FixedArray, pairs, 0);
408 Handle<Context> context = args.at<Context>(1);
409 bool is_eval = Smi::cast(args[2])->value() == 1;
410
411 // Compute the property attributes. According to ECMA-262, section
412 // 13, page 71, the property must be read-only and
413 // non-deletable. However, neither SpiderMonkey nor KJS creates the
414 // property as read-only, so we don't either.
415 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
416
417 // Only optimize the object if we intend to add more than 5 properties.
418 OptimizedObjectForAddingMultipleProperties ba(global, pairs->length()/2 > 5);
419
420 // Traverse the name/value pairs and set the properties.
421 int length = pairs->length();
422 for (int i = 0; i < length; i += 2) {
423 HandleScope scope;
424 Handle<String> name(String::cast(pairs->get(i)));
425 Handle<Object> value(pairs->get(i + 1));
426
427 // We have to declare a global const property. To capture we only
428 // assign to it when evaluating the assignment for "const x =
429 // <expr>" the initial value is the hole.
430 bool is_const_property = value->IsTheHole();
431
432 if (value->IsUndefined() || is_const_property) {
433 // Lookup the property in the global object, and don't set the
434 // value of the variable if the property is already there.
435 LookupResult lookup;
436 global->Lookup(*name, &lookup);
437 if (lookup.IsProperty()) {
438 // Determine if the property is local by comparing the holder
439 // against the global object. The information will be used to
440 // avoid throwing re-declaration errors when declaring
441 // variables or constants that exist in the prototype chain.
442 bool is_local = (*global == lookup.holder());
443 // Get the property attributes and determine if the property is
444 // read-only.
445 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
446 bool is_read_only = (attributes & READ_ONLY) != 0;
447 if (lookup.type() == INTERCEPTOR) {
448 // If the interceptor says the property is there, we
449 // just return undefined without overwriting the property.
450 // Otherwise, we continue to setting the property.
451 if (attributes != ABSENT) {
452 // Check if the existing property conflicts with regards to const.
453 if (is_local && (is_read_only || is_const_property)) {
454 const char* type = (is_read_only) ? "const" : "var";
455 return ThrowRedeclarationError(type, name);
456 };
457 // The property already exists without conflicting: Go to
458 // the next declaration.
459 continue;
460 }
461 // Fall-through and introduce the absent property by using
462 // SetProperty.
463 } else {
464 if (is_local && (is_read_only || is_const_property)) {
465 const char* type = (is_read_only) ? "const" : "var";
466 return ThrowRedeclarationError(type, name);
467 }
468 // The property already exists without conflicting: Go to
469 // the next declaration.
470 continue;
471 }
472 }
473 } else {
474 // Copy the function and update its context. Use it as value.
475 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
476 Handle<JSFunction> function =
477 Factory::NewFunctionFromBoilerplate(boilerplate, context);
478 value = function;
479 }
480
481 LookupResult lookup;
482 global->LocalLookup(*name, &lookup);
483
484 PropertyAttributes attributes = is_const_property
485 ? static_cast<PropertyAttributes>(base | READ_ONLY)
486 : base;
487
488 if (lookup.IsProperty()) {
489 // There's a local property that we need to overwrite because
490 // we're either declaring a function or there's an interceptor
491 // that claims the property is absent.
492
493 // Check for conflicting re-declarations. We cannot have
494 // conflicting types in case of intercepted properties because
495 // they are absent.
496 if (lookup.type() != INTERCEPTOR &&
497 (lookup.IsReadOnly() || is_const_property)) {
498 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
499 return ThrowRedeclarationError(type, name);
500 }
501 SetProperty(global, name, value, attributes);
502 } else {
503 // If a property with this name does not already exist on the
504 // global object add the property locally. We take special
505 // precautions to always add it as a local property even in case
506 // of callbacks in the prototype chain (this rules out using
507 // SetProperty). Also, we must use the handle-based version to
508 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000509 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000510 }
511 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000512
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000513 return Heap::undefined_value();
514}
515
516
517static Object* Runtime_DeclareContextSlot(Arguments args) {
518 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000519 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000520
ager@chromium.org7c537e22008-10-16 08:43:32 +0000521 CONVERT_ARG_CHECKED(Context, context, 0);
522 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000523 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000524 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000525 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000526 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000527
528 // Declarations are always done in the function context.
529 context = Handle<Context>(context->fcontext());
530
531 int index;
532 PropertyAttributes attributes;
533 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000534 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000535 context->Lookup(name, flags, &index, &attributes);
536
537 if (attributes != ABSENT) {
538 // The name was declared before; check for conflicting
539 // re-declarations: This is similar to the code in parser.cc in
540 // the AstBuildingParser::Declare function.
541 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
542 // Functions are not read-only.
543 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
544 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
545 return ThrowRedeclarationError(type, name);
546 }
547
548 // Initialize it if necessary.
549 if (*initial_value != NULL) {
550 if (index >= 0) {
551 // The variable or constant context slot should always be in
552 // the function context; not in any outer context nor in the
553 // arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000554 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000555 if (((attributes & READ_ONLY) == 0) ||
556 context->get(index)->IsTheHole()) {
557 context->set(index, *initial_value);
558 }
559 } else {
560 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000561 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000562 SetProperty(context_ext, name, initial_value, mode);
563 }
564 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000565
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000566 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000567 // The property is not in the function context. It needs to be
568 // "declared" in the function context's extension context, or in the
569 // global context.
570 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000571 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000572 // The function context's extension context exists - use it.
573 context_ext = Handle<JSObject>(context->extension());
574 } else {
575 // The function context's extension context does not exists - allocate
576 // it.
577 context_ext = Factory::NewJSObject(Top::context_extension_function());
578 // And store it in the extension slot.
579 context->set_extension(*context_ext);
580 }
581 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000582
ager@chromium.org7c537e22008-10-16 08:43:32 +0000583 // Declare the property by setting it to the initial value if provided,
584 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
585 // constant declarations).
586 ASSERT(!context_ext->HasLocalProperty(*name));
587 Handle<Object> value(Heap::undefined_value());
588 if (*initial_value != NULL) value = initial_value;
589 SetProperty(context_ext, name, value, mode);
590 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
591 }
592
593 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000594}
595
596
597static Object* Runtime_InitializeVarGlobal(Arguments args) {
598 NoHandleAllocation nha;
599
600 // Determine if we need to assign to the variable if it already
601 // exists (based on the number of arguments).
602 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
603 bool assign = args.length() == 2;
604
605 CONVERT_ARG_CHECKED(String, name, 0);
606 GlobalObject* global = Top::context()->global();
607
608 // According to ECMA-262, section 12.2, page 62, the property must
609 // not be deletable.
610 PropertyAttributes attributes = DONT_DELETE;
611
612 // Lookup the property locally in the global object. If it isn't
613 // there, we add the property and take special precautions to always
614 // add it as a local property even in case of callbacks in the
615 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000616 // We have IgnoreAttributesAndSetLocalProperty for this.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000617 LookupResult lookup;
618 global->LocalLookup(*name, &lookup);
619 if (!lookup.IsProperty()) {
620 Object* value = (assign) ? args[1] : Heap::undefined_value();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000621 return global->IgnoreAttributesAndSetLocalProperty(*name,
622 value,
623 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000624 }
625
626 // Determine if this is a redeclaration of something read-only.
627 if (lookup.IsReadOnly()) {
628 return ThrowRedeclarationError("const", name);
629 }
630
631 // Determine if this is a redeclaration of an intercepted read-only
632 // property and figure out if the property exists at all.
633 bool found = true;
634 PropertyType type = lookup.type();
635 if (type == INTERCEPTOR) {
636 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
637 if (intercepted == ABSENT) {
638 // The interceptor claims the property isn't there. We need to
639 // make sure to introduce it.
640 found = false;
641 } else if ((intercepted & READ_ONLY) != 0) {
642 // The property is present, but read-only. Since we're trying to
643 // overwrite it with a variable declaration we must throw a
644 // re-declaration error.
645 return ThrowRedeclarationError("const", name);
646 }
647 // Restore global object from context (in case of GC).
648 global = Top::context()->global();
649 }
650
651 if (found && !assign) {
652 // The global property is there and we're not assigning any value
653 // to it. Just return.
654 return Heap::undefined_value();
655 }
656
657 // Assign the value (or undefined) to the property.
658 Object* value = (assign) ? args[1] : Heap::undefined_value();
659 return global->SetProperty(&lookup, *name, value, attributes);
660}
661
662
663static Object* Runtime_InitializeConstGlobal(Arguments args) {
664 // All constants are declared with an initial value. The name
665 // of the constant is the first argument and the initial value
666 // is the second.
667 RUNTIME_ASSERT(args.length() == 2);
668 CONVERT_ARG_CHECKED(String, name, 0);
669 Handle<Object> value = args.at<Object>(1);
670
671 // Get the current global object from top.
672 GlobalObject* global = Top::context()->global();
673
674 // According to ECMA-262, section 12.2, page 62, the property must
675 // not be deletable. Since it's a const, it must be READ_ONLY too.
676 PropertyAttributes attributes =
677 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
678
679 // Lookup the property locally in the global object. If it isn't
680 // there, we add the property and take special precautions to always
681 // add it as a local property even in case of callbacks in the
682 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000683 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000684 LookupResult lookup;
685 global->LocalLookup(*name, &lookup);
686 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000687 return global->IgnoreAttributesAndSetLocalProperty(*name,
688 *value,
689 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000690 }
691
692 // Determine if this is a redeclaration of something not
693 // read-only. In case the result is hidden behind an interceptor we
694 // need to ask it for the property attributes.
695 if (!lookup.IsReadOnly()) {
696 if (lookup.type() != INTERCEPTOR) {
697 return ThrowRedeclarationError("var", name);
698 }
699
700 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
701
702 // Throw re-declaration error if the intercepted property is present
703 // but not read-only.
704 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
705 return ThrowRedeclarationError("var", name);
706 }
707
708 // Restore global object from context (in case of GC) and continue
709 // with setting the value because the property is either absent or
710 // read-only. We also have to do redo the lookup.
711 global = Top::context()->global();
712
713 // BUG 1213579: Handle the case where we have to set a read-only
714 // property through an interceptor and only do it if it's
715 // uninitialized, e.g. the hole. Nirk...
716 global->SetProperty(*name, *value, attributes);
717 return *value;
718 }
719
720 // Set the value, but only we're assigning the initial value to a
721 // constant. For now, we determine this by checking if the
722 // current value is the hole.
723 PropertyType type = lookup.type();
724 if (type == FIELD) {
725 FixedArray* properties = global->properties();
726 int index = lookup.GetFieldIndex();
727 if (properties->get(index)->IsTheHole()) {
728 properties->set(index, *value);
729 }
730 } else if (type == NORMAL) {
731 Dictionary* dictionary = global->property_dictionary();
732 int entry = lookup.GetDictionaryEntry();
733 if (dictionary->ValueAt(entry)->IsTheHole()) {
734 dictionary->ValueAtPut(entry, *value);
735 }
736 } else {
737 // Ignore re-initialization of constants that have already been
738 // assigned a function value.
739 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
740 }
741
742 // Use the set value as the result of the operation.
743 return *value;
744}
745
746
747static Object* Runtime_InitializeConstContextSlot(Arguments args) {
748 HandleScope scope;
749 ASSERT(args.length() == 3);
750
751 Handle<Object> value(args[0]);
752 ASSERT(!value->IsTheHole());
753 CONVERT_ARG_CHECKED(Context, context, 1);
754 Handle<String> name(String::cast(args[2]));
755
756 // Initializations are always done in the function context.
757 context = Handle<Context>(context->fcontext());
758
759 int index;
760 PropertyAttributes attributes;
761 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000762 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000763 context->Lookup(name, flags, &index, &attributes);
764
765 // The property should always be present. It is always declared
766 // before being initialized through DeclareContextSlot.
767 ASSERT(attributes != ABSENT && (attributes & READ_ONLY) != 0);
768
769 // If the slot is in the context, we set it but only if it hasn't
770 // been set before.
771 if (index >= 0) {
772 // The constant context slot should always be in the function
773 // context; not in any outer context nor in the arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000774 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000775 if (context->get(index)->IsTheHole()) {
776 context->set(index, *value);
777 }
778 return *value;
779 }
780
781 // Otherwise, the slot must be in a JS object extension.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000782 Handle<JSObject> context_ext(JSObject::cast(*holder));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000783
784 // We must initialize the value only if it wasn't initialized
785 // before, e.g. for const declarations in a loop. The property has
786 // the hole value if it wasn't initialized yet. NOTE: We cannot use
787 // GetProperty() to get the current value as it 'unholes' the value.
788 LookupResult lookup;
789 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
790 ASSERT(lookup.IsProperty()); // the property was declared
791 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
792
793 PropertyType type = lookup.type();
794 if (type == FIELD) {
795 FixedArray* properties = context_ext->properties();
796 int index = lookup.GetFieldIndex();
797 if (properties->get(index)->IsTheHole()) {
798 properties->set(index, *value);
799 }
800 } else if (type == NORMAL) {
801 Dictionary* dictionary = context_ext->property_dictionary();
802 int entry = lookup.GetDictionaryEntry();
803 if (dictionary->ValueAt(entry)->IsTheHole()) {
804 dictionary->ValueAtPut(entry, *value);
805 }
806 } else {
807 // We should not reach here. Any real, named property should be
808 // either a field or a dictionary slot.
809 UNREACHABLE();
810 }
811 return *value;
812}
813
814
815static Object* Runtime_RegExpExec(Arguments args) {
816 HandleScope scope;
817 ASSERT(args.length() == 3);
ager@chromium.org236ad962008-09-25 09:45:57 +0000818 CONVERT_CHECKED(JSRegExp, raw_regexp, args[0]);
819 Handle<JSRegExp> regexp(raw_regexp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000820 CONVERT_CHECKED(String, raw_subject, args[1]);
821 Handle<String> subject(raw_subject);
822 Handle<Object> index(args[2]);
823 ASSERT(index->IsNumber());
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000824 Handle<Object> result = RegExpImpl::Exec(regexp, subject, index);
825 if (result.is_null()) return Failure::Exception();
826 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000827}
828
829
830static Object* Runtime_RegExpExecGlobal(Arguments args) {
831 HandleScope scope;
832 ASSERT(args.length() == 2);
ager@chromium.org236ad962008-09-25 09:45:57 +0000833 CONVERT_CHECKED(JSRegExp, raw_regexp, args[0]);
834 Handle<JSRegExp> regexp(raw_regexp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000835 CONVERT_CHECKED(String, raw_subject, args[1]);
836 Handle<String> subject(raw_subject);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000837 Handle<Object> result = RegExpImpl::ExecGlobal(regexp, subject);
838 if (result.is_null()) return Failure::Exception();
839 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000840}
841
842
843static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
844 HandleScope scope;
845 ASSERT(args.length() == 4);
846 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
847 int index = Smi::cast(args[1])->value();
848 Handle<String> pattern = args.at<String>(2);
849 Handle<String> flags = args.at<String>(3);
850
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000851 // Get the RegExp function from the context in the literals array.
852 // This is the RegExp function from the context in which the
853 // function was created. We do not use the RegExp function from the
854 // current global context because this might be the RegExp function
855 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000856 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +0000857 Handle<JSFunction>(
858 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000859 // Compute the regular expression literal.
860 bool has_pending_exception;
861 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000862 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
863 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000864 if (has_pending_exception) {
865 ASSERT(Top::has_pending_exception());
866 return Failure::Exception();
867 }
868 literals->set(index, *regexp);
869 return *regexp;
870}
871
872
873static Object* Runtime_FunctionGetName(Arguments args) {
874 NoHandleAllocation ha;
875 ASSERT(args.length() == 1);
876
877 CONVERT_CHECKED(JSFunction, f, args[0]);
878 return f->shared()->name();
879}
880
881
ager@chromium.org236ad962008-09-25 09:45:57 +0000882static Object* Runtime_FunctionSetName(Arguments args) {
883 NoHandleAllocation ha;
884 ASSERT(args.length() == 2);
885
886 CONVERT_CHECKED(JSFunction, f, args[0]);
887 CONVERT_CHECKED(String, name, args[1]);
888 f->shared()->set_name(name);
889 return Heap::undefined_value();
890}
891
892
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000893static Object* Runtime_FunctionGetScript(Arguments args) {
894 HandleScope scope;
895 ASSERT(args.length() == 1);
896
897 CONVERT_CHECKED(JSFunction, fun, args[0]);
898 Handle<Object> script = Handle<Object>(fun->shared()->script());
899 if (!script->IsScript()) return Heap::undefined_value();
900
901 return *GetScriptWrapper(Handle<Script>::cast(script));
902}
903
904
905static Object* Runtime_FunctionGetSourceCode(Arguments args) {
906 NoHandleAllocation ha;
907 ASSERT(args.length() == 1);
908
909 CONVERT_CHECKED(JSFunction, f, args[0]);
910 return f->shared()->GetSourceCode();
911}
912
913
914static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
915 NoHandleAllocation ha;
916 ASSERT(args.length() == 1);
917
918 CONVERT_CHECKED(JSFunction, fun, args[0]);
919 int pos = fun->shared()->start_position();
920 return Smi::FromInt(pos);
921}
922
923
924static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
925 NoHandleAllocation ha;
926 ASSERT(args.length() == 2);
927
928 CONVERT_CHECKED(JSFunction, fun, args[0]);
929 CONVERT_CHECKED(String, name, args[1]);
930 fun->SetInstanceClassName(name);
931 return Heap::undefined_value();
932}
933
934
935static Object* Runtime_FunctionSetLength(Arguments args) {
936 NoHandleAllocation ha;
937 ASSERT(args.length() == 2);
938
939 CONVERT_CHECKED(JSFunction, fun, args[0]);
940 CONVERT_CHECKED(Smi, length, args[1]);
941 fun->shared()->set_length(length->value());
942 return length;
943}
944
945
946static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000947 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000948 ASSERT(args.length() == 2);
949
950 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000951 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
952 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000953 return args[0]; // return TOS
954}
955
956
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000957static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
958 NoHandleAllocation ha;
959 ASSERT(args.length() == 1);
960
961 CONVERT_CHECKED(JSFunction, f, args[0]);
962 // The function_data field of the shared function info is used exclusively by
963 // the API.
964 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
965 : Heap::false_value();
966}
967
968
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000969static Object* Runtime_SetCode(Arguments args) {
970 HandleScope scope;
971 ASSERT(args.length() == 2);
972
973 CONVERT_CHECKED(JSFunction, raw_target, args[0]);
974 Handle<JSFunction> target(raw_target);
975 Handle<Object> code = args.at<Object>(1);
976
977 Handle<Context> context(target->context());
978
979 if (!code->IsNull()) {
980 RUNTIME_ASSERT(code->IsJSFunction());
981 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
982 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
983 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
984 return Failure::Exception();
985 }
986 // Set the code, formal parameter count, and the length of the target
987 // function.
988 target->set_code(fun->code());
989 target->shared()->set_length(fun->shared()->length());
990 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000991 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +0000992 // Set the source code of the target function to undefined.
993 // SetCode is only used for built-in constructors like String,
994 // Array, and Object, and some web code
995 // doesn't like seeing source code for constructors.
996 target->shared()->set_script(Heap::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000997 context = Handle<Context>(fun->context());
998
999 // Make sure we get a fresh copy of the literal vector to avoid
1000 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001001 int number_of_literals = fun->NumberOfLiterals();
1002 Handle<FixedArray> literals =
1003 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001004 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001005 // Insert the object, regexp and array functions in the literals
1006 // array prefix. These are the functions that will be used when
1007 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +00001008 literals->set(JSFunction::kLiteralGlobalContextIndex,
1009 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001010 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00001011 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001012 }
1013
1014 target->set_context(*context);
1015 return *target;
1016}
1017
1018
1019static Object* CharCodeAt(String* subject, Object* index) {
1020 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001021 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001022 // Flatten the string. If someone wants to get a char at an index
1023 // in a cons string, it is likely that more indices will be
1024 // accessed.
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001025 subject->TryFlatten(StringShape(subject));
ager@chromium.org870a0b62008-11-04 11:43:05 +00001026 StringShape shape(subject);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001027 if (i >= static_cast<uint32_t>(subject->length(shape))) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001028 return Heap::nan_value();
1029 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001030 return Smi::FromInt(subject->Get(shape, i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001031}
1032
1033
1034static Object* Runtime_StringCharCodeAt(Arguments args) {
1035 NoHandleAllocation ha;
1036 ASSERT(args.length() == 2);
1037
1038 CONVERT_CHECKED(String, subject, args[0]);
1039 Object* index = args[1];
1040 return CharCodeAt(subject, index);
1041}
1042
1043
1044static Object* Runtime_CharFromCode(Arguments args) {
1045 NoHandleAllocation ha;
1046 ASSERT(args.length() == 1);
1047 uint32_t code;
1048 if (Array::IndexFromObject(args[0], &code)) {
1049 if (code <= 0xffff) {
1050 return Heap::LookupSingleCharacterStringFromCode(code);
1051 }
1052 }
1053 return Heap::empty_string();
1054}
1055
1056
ager@chromium.org7c537e22008-10-16 08:43:32 +00001057// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1058// limit, we can fix the size of tables.
1059static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001060// Reduce alphabet to this size.
1061static const int kBMAlphabetSize = 0x100;
1062// For patterns below this length, the skip length of Boyer-Moore is too short
1063// to compensate for the algorithmic overhead compared to simple brute force.
1064static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001065
ager@chromium.org7c537e22008-10-16 08:43:32 +00001066// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1067// shift. Only allows the last kBMMaxShift characters of the needle
1068// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001069class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001070 public:
1071 BMGoodSuffixBuffers() {}
1072 inline void init(int needle_length) {
1073 ASSERT(needle_length > 1);
1074 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1075 int len = needle_length - start;
1076 biased_suffixes_ = suffixes_ - start;
1077 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1078 for (int i = 0; i <= len; i++) {
1079 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001080 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001081 }
1082 inline int& suffix(int index) {
1083 ASSERT(biased_suffixes_ + index >= suffixes_);
1084 return biased_suffixes_[index];
1085 }
1086 inline int& shift(int index) {
1087 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1088 return biased_good_suffix_shift_[index];
1089 }
1090 private:
1091 int suffixes_[kBMMaxShift + 1];
1092 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001093 int* biased_suffixes_;
1094 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001095 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1096};
1097
1098// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001099static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001100static BMGoodSuffixBuffers bmgs_buffers;
1101
1102// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001103template <typename pchar>
1104static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1105 int start) {
1106 // Run forwards to populate bad_char_table, so that *last* instance
1107 // of character equivalence class is the one registered.
1108 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001109 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1110 : kBMAlphabetSize;
1111 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001112 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001113 } else {
1114 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001115 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001116 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001117 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001118 for (int i = start; i < pattern.length() - 1; i++) {
1119 pchar c = pattern[i];
1120 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001121 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001122 }
1123}
1124
1125template <typename pchar>
1126static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001127 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001128 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001129 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001130 // Compute Good Suffix tables.
1131 bmgs_buffers.init(m);
1132
1133 bmgs_buffers.shift(m-1) = 1;
1134 bmgs_buffers.suffix(m) = m + 1;
1135 pchar last_char = pattern[m - 1];
1136 int suffix = m + 1;
1137 for (int i = m; i > start;) {
1138 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1139 if (bmgs_buffers.shift(suffix) == len) {
1140 bmgs_buffers.shift(suffix) = suffix - i;
1141 }
1142 suffix = bmgs_buffers.suffix(suffix);
1143 }
1144 i--;
1145 suffix--;
1146 bmgs_buffers.suffix(i) = suffix;
1147 if (suffix == m) {
1148 // No suffix to extend, so we check against last_char only.
1149 while (i > start && pattern[i - 1] != last_char) {
1150 if (bmgs_buffers.shift(m) == len) {
1151 bmgs_buffers.shift(m) = m - i;
1152 }
1153 i--;
1154 bmgs_buffers.suffix(i) = m;
1155 }
1156 if (i > start) {
1157 i--;
1158 suffix--;
1159 bmgs_buffers.suffix(i) = suffix;
1160 }
1161 }
1162 }
1163 if (suffix < m) {
1164 for (int i = start; i <= m; i++) {
1165 if (bmgs_buffers.shift(i) == len) {
1166 bmgs_buffers.shift(i) = suffix - start;
1167 }
1168 if (i == suffix) {
1169 suffix = bmgs_buffers.suffix(suffix);
1170 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001171 }
1172 }
1173}
1174
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001175template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001176static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001177 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001178 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001179 }
1180 if (sizeof(pchar) == 1) {
1181 if (char_code > String::kMaxAsciiCharCode) {
1182 return -1;
1183 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001184 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001185 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001186 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001187}
1188
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001189// Restricted simplified Boyer-Moore string matching.
1190// Uses only the bad-shift table of Boyer-Moore and only uses it
1191// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001192template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001193static int BoyerMooreHorsepool(Vector<const schar> subject,
1194 Vector<const pchar> pattern,
1195 int start_index,
1196 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001197 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001198 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001199 // Only preprocess at most kBMMaxShift last characters of pattern.
1200 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001201
ager@chromium.org7c537e22008-10-16 08:43:32 +00001202 BoyerMoorePopulateBadCharTable(pattern, start);
1203
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001204 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001205 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001206 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001207 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001208 // Perform search
1209 for (idx = start_index; idx <= n - m;) {
1210 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001211 int c;
1212 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001213 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001214 int shift = j - bc_occ;
1215 idx += shift;
1216 badness += 1 - shift; // at most zero, so badness cannot increase.
1217 if (idx > n - m) {
1218 *complete = true;
1219 return -1;
1220 }
1221 }
1222 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001223 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001224 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001225 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001226 return idx;
1227 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001228 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001229 // Badness increases by the number of characters we have
1230 // checked, and decreases by the number of characters we
1231 // can skip by shifting. It's a measure of how we are doing
1232 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001233 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001234 if (badness > 0) {
1235 *complete = false;
1236 return idx;
1237 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001238 }
1239 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001240 *complete = true;
1241 return -1;
1242}
ager@chromium.org7c537e22008-10-16 08:43:32 +00001243
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001244
1245template <typename schar, typename pchar>
1246static int BoyerMooreIndexOf(Vector<const schar> subject,
1247 Vector<const pchar> pattern,
1248 int idx) {
1249 int n = subject.length();
1250 int m = pattern.length();
1251 // Only preprocess at most kBMMaxShift last characters of pattern.
1252 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
1253
1254 // Build the Good Suffix table and continue searching.
1255 BoyerMoorePopulateGoodSuffixTable(pattern, start);
1256 pchar last_char = pattern[m - 1];
1257 // Continue search from i.
1258 do {
1259 int j = m - 1;
1260 schar c;
1261 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001262 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001263 idx += shift;
1264 if (idx > n - m) {
1265 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001266 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001267 }
1268 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
1269 if (j < 0) {
1270 return idx;
1271 } else if (j < start) {
1272 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001273 // Fall back on BMH shift.
1274 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001275 } else {
1276 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001277 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001278 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001279 if (gs_shift > shift) {
1280 shift = gs_shift;
1281 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001282 idx += shift;
1283 }
1284 } while (idx <= n - m);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001285
1286 return -1;
1287}
1288
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001289
1290template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00001291static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001292 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00001293 int start_index) {
1294 for (int i = start_index, n = string.length(); i < n; i++) {
1295 if (pattern_char == string[i]) {
1296 return i;
1297 }
1298 }
1299 return -1;
1300}
1301
1302// Trivial string search for shorter strings.
1303// On return, if "complete" is set to true, the return value is the
1304// final result of searching for the patter in the subject.
1305// If "complete" is set to false, the return value is the index where
1306// further checking should start, i.e., it's guaranteed that the pattern
1307// does not occur at a position prior to the returned index.
1308template <typename pchar, typename schar>
1309static int SimpleIndexOf(Vector<const schar> subject,
1310 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001311 int idx,
1312 bool* complete) {
1313 // Badness is a count of how much work we have done. When we have
1314 // done enough work we decide it's probably worth switching to a better
1315 // algorithm.
1316 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001317 // We know our pattern is at least 2 characters, we cache the first so
1318 // the common case of the first character not matching is faster.
1319 pchar pattern_first_char = pattern[0];
1320
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001321 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
1322 badness++;
1323 if (badness > 0) {
1324 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001325 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001326 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001327 if (subject[i] != pattern_first_char) continue;
1328 int j = 1;
1329 do {
1330 if (pattern[j] != subject[i+j]) {
1331 break;
1332 }
1333 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001334 } while (j < pattern.length());
1335 if (j == pattern.length()) {
1336 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001337 return i;
1338 }
1339 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001340 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001341 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001342 return -1;
1343}
1344
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001345// Simple indexOf that never bails out. For short patterns only.
1346template <typename pchar, typename schar>
1347static int SimpleIndexOf(Vector<const schar> subject,
1348 Vector<const pchar> pattern,
1349 int idx) {
1350 pchar pattern_first_char = pattern[0];
1351 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
1352 if (subject[i] != pattern_first_char) continue;
1353 int j = 1;
1354 do {
1355 if (pattern[j] != subject[i+j]) {
1356 break;
1357 }
1358 j++;
1359 } while (j < pattern.length());
1360 if (j == pattern.length()) {
1361 return i;
1362 }
1363 }
1364 return -1;
1365}
1366
1367
1368// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001369template <typename schar, typename pchar>
1370static int StringMatchStrategy(Vector<const schar> sub,
1371 Vector<const pchar> pat,
1372 int start_index) {
1373 ASSERT(pat.length() > 1);
1374
1375 // We have an ASCII haystack and a non-ASCII needle. Check if there
1376 // really is a non-ASCII character in the needle and bail out if there
1377 // is.
1378 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
1379 for (int i = 0; i < pat.length(); i++) {
1380 uc16 c = pat[i];
1381 if (c > String::kMaxAsciiCharCode) {
1382 return -1;
1383 }
1384 }
1385 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001386 if (pat.length() < kBMMinPatternLength) {
1387 // We don't believe fancy searching can ever be more efficient.
1388 // The max shift of Boyer-Moore on a pattern of this length does
1389 // not compensate for the overhead.
1390 return SimpleIndexOf(sub, pat, start_index);
1391 }
1392 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001393 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001394 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
1395 if (complete) return idx;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001396 idx = BoyerMooreHorsepool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001397 if (complete) return idx;
1398 return BoyerMooreIndexOf(sub, pat, idx);
1399}
1400
1401// Perform string match of pattern on subject, starting at start index.
1402// Caller must ensure that 0 <= start_index <= sub->length(),
1403// and should check that pat->length() + start_index <= sub->length()
1404int Runtime::StringMatch(Handle<String> sub,
1405 Handle<String> pat,
1406 int start_index) {
1407 ASSERT(0 <= start_index);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001408 StringShape sub_shape(*sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001409 ASSERT(start_index <= sub->length(sub_shape));
ager@chromium.org7c537e22008-10-16 08:43:32 +00001410
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001411 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001412 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001413
ager@chromium.org870a0b62008-11-04 11:43:05 +00001414 int subject_length = sub->length(sub_shape);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001415 if (start_index + pattern_length > subject_length) return -1;
1416
ager@chromium.org870a0b62008-11-04 11:43:05 +00001417 if (!sub->IsFlat(sub_shape)) {
1418 FlattenString(sub);
1419 sub_shape = StringShape(*sub);
1420 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001421 StringShape pat_shape(*pat);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001422 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00001423 // character patterns linear search is necessary, so any smart
1424 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001425 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001426 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org870a0b62008-11-04 11:43:05 +00001427 if (sub_shape.IsAsciiRepresentation()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001428 uc16 pchar = pat->Get(pat_shape, 0);
1429 if (pchar > String::kMaxAsciiCharCode) {
1430 return -1;
1431 }
1432 Vector<const char> ascii_vector =
1433 sub->ToAsciiVector().SubVector(start_index, subject_length);
1434 const void* pos = memchr(ascii_vector.start(),
1435 static_cast<const char>(pchar),
1436 static_cast<size_t>(ascii_vector.length()));
1437 if (pos == NULL) {
1438 return -1;
1439 }
1440 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
1441 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001442 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00001443 return SingleCharIndexOf(sub->ToUC16Vector(),
1444 pat->Get(pat_shape, 0),
1445 start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001446 }
1447
ager@chromium.org870a0b62008-11-04 11:43:05 +00001448 if (!pat->IsFlat(pat_shape)) {
1449 FlattenString(pat);
1450 pat_shape = StringShape(*pat);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001451 sub_shape = StringShape(*sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001452 }
ager@chromium.org236ad962008-09-25 09:45:57 +00001453
ager@chromium.org7c537e22008-10-16 08:43:32 +00001454 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
1455 // dispatch on type of strings
ager@chromium.org870a0b62008-11-04 11:43:05 +00001456 if (pat_shape.IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001457 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org870a0b62008-11-04 11:43:05 +00001458 if (sub_shape.IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001459 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00001460 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001461 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00001462 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001463 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org870a0b62008-11-04 11:43:05 +00001464 if (sub_shape.IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001465 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001466 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001467 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001468}
1469
1470
1471static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001472 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001473 ASSERT(args.length() == 3);
1474
ager@chromium.org7c537e22008-10-16 08:43:32 +00001475 CONVERT_ARG_CHECKED(String, sub, 0);
1476 CONVERT_ARG_CHECKED(String, pat, 1);
1477
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001478 Object* index = args[2];
1479 uint32_t start_index;
1480 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
1481
ager@chromium.org870a0b62008-11-04 11:43:05 +00001482 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00001483 int position = Runtime::StringMatch(sub, pat, start_index);
1484 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001485}
1486
1487
1488static Object* Runtime_StringLastIndexOf(Arguments args) {
1489 NoHandleAllocation ha;
1490 ASSERT(args.length() == 3);
1491
1492 CONVERT_CHECKED(String, sub, args[0]);
1493 CONVERT_CHECKED(String, pat, args[1]);
1494 Object* index = args[2];
1495
ager@chromium.org870a0b62008-11-04 11:43:05 +00001496 sub->TryFlatten(StringShape(sub));
1497 pat->TryFlatten(StringShape(pat));
1498
1499 StringShape sub_shape(sub);
1500 StringShape pat_shape(pat);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001501
1502 uint32_t start_index;
1503 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
1504
ager@chromium.org870a0b62008-11-04 11:43:05 +00001505 uint32_t pattern_length = pat->length(pat_shape);
1506 uint32_t sub_length = sub->length(sub_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001507
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001508 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001509 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001510 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001511
1512 for (int i = start_index; i >= 0; i--) {
1513 bool found = true;
1514 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001515 if (sub->Get(sub_shape, i + j) != pat->Get(pat_shape, j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001516 found = false;
1517 break;
1518 }
1519 }
1520 if (found) return Smi::FromInt(i);
1521 }
1522
1523 return Smi::FromInt(-1);
1524}
1525
1526
1527static Object* Runtime_StringLocaleCompare(Arguments args) {
1528 NoHandleAllocation ha;
1529 ASSERT(args.length() == 2);
1530
1531 CONVERT_CHECKED(String, str1, args[0]);
1532 CONVERT_CHECKED(String, str2, args[1]);
1533
1534 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.org870a0b62008-11-04 11:43:05 +00001535 StringShape shape1(str1);
1536 StringShape shape2(str2);
1537 int str1_length = str1->length(shape1);
1538 int str2_length = str2->length(shape2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001539
1540 // Decide trivial cases without flattening.
1541 if (str1_length == 0) {
1542 if (str2_length == 0) return Smi::FromInt(0); // Equal.
1543 return Smi::FromInt(-str2_length);
1544 } else {
1545 if (str2_length == 0) return Smi::FromInt(str1_length);
1546 }
1547
1548 int end = str1_length < str2_length ? str1_length : str2_length;
1549
1550 // No need to flatten if we are going to find the answer on the first
1551 // character. At this point we know there is at least one character
1552 // in each string, due to the trivial case handling above.
ager@chromium.org870a0b62008-11-04 11:43:05 +00001553 int d = str1->Get(shape1, 0) - str2->Get(shape2, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001554 if (d != 0) return Smi::FromInt(d);
1555
ager@chromium.org870a0b62008-11-04 11:43:05 +00001556 str1->TryFlatten(shape1); // Shapes are no longer valid now!
1557 str2->TryFlatten(shape2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001558
1559 static StringInputBuffer buf1;
1560 static StringInputBuffer buf2;
1561
1562 buf1.Reset(str1);
1563 buf2.Reset(str2);
1564
1565 for (int i = 0; i < end; i++) {
1566 uint16_t char1 = buf1.GetNext();
1567 uint16_t char2 = buf2.GetNext();
1568 if (char1 != char2) return Smi::FromInt(char1 - char2);
1569 }
1570
1571 return Smi::FromInt(str1_length - str2_length);
1572}
1573
1574
1575static Object* Runtime_StringSlice(Arguments args) {
1576 NoHandleAllocation ha;
1577 ASSERT(args.length() == 3);
1578
1579 CONVERT_CHECKED(String, value, args[0]);
1580 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
1581 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
1582
1583 int start = FastD2I(from_number);
1584 int end = FastD2I(to_number);
1585
1586 RUNTIME_ASSERT(end >= start);
1587 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001588 RUNTIME_ASSERT(end <= value->length());
1589 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001590}
1591
1592
1593static Object* Runtime_NumberToRadixString(Arguments args) {
1594 NoHandleAllocation ha;
1595 ASSERT(args.length() == 2);
1596
1597 CONVERT_DOUBLE_CHECKED(value, args[0]);
1598 if (isnan(value)) {
1599 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1600 }
1601 if (isinf(value)) {
1602 if (value < 0) {
1603 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1604 }
1605 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1606 }
1607 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
1608 int radix = FastD2I(radix_number);
1609 RUNTIME_ASSERT(2 <= radix && radix <= 36);
1610 char* str = DoubleToRadixCString(value, radix);
1611 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
1612 DeleteArray(str);
1613 return result;
1614}
1615
1616
1617static Object* Runtime_NumberToFixed(Arguments args) {
1618 NoHandleAllocation ha;
1619 ASSERT(args.length() == 2);
1620
1621 CONVERT_DOUBLE_CHECKED(value, args[0]);
1622 if (isnan(value)) {
1623 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1624 }
1625 if (isinf(value)) {
1626 if (value < 0) {
1627 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1628 }
1629 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1630 }
1631 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
1632 int f = FastD2I(f_number);
1633 RUNTIME_ASSERT(f >= 0);
1634 char* str = DoubleToFixedCString(value, f);
1635 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
1636 DeleteArray(str);
1637 return res;
1638}
1639
1640
1641static Object* Runtime_NumberToExponential(Arguments args) {
1642 NoHandleAllocation ha;
1643 ASSERT(args.length() == 2);
1644
1645 CONVERT_DOUBLE_CHECKED(value, args[0]);
1646 if (isnan(value)) {
1647 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1648 }
1649 if (isinf(value)) {
1650 if (value < 0) {
1651 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1652 }
1653 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1654 }
1655 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
1656 int f = FastD2I(f_number);
1657 RUNTIME_ASSERT(f >= -1 && f <= 20);
1658 char* str = DoubleToExponentialCString(value, f);
1659 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
1660 DeleteArray(str);
1661 return res;
1662}
1663
1664
1665static Object* Runtime_NumberToPrecision(Arguments args) {
1666 NoHandleAllocation ha;
1667 ASSERT(args.length() == 2);
1668
1669 CONVERT_DOUBLE_CHECKED(value, args[0]);
1670 if (isnan(value)) {
1671 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1672 }
1673 if (isinf(value)) {
1674 if (value < 0) {
1675 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1676 }
1677 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1678 }
1679 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
1680 int f = FastD2I(f_number);
1681 RUNTIME_ASSERT(f >= 1 && f <= 21);
1682 char* str = DoubleToPrecisionCString(value, f);
1683 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
1684 DeleteArray(str);
1685 return res;
1686}
1687
1688
1689// Returns a single character string where first character equals
1690// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001691static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001692 StringShape shape(*string);
1693 if (index < static_cast<uint32_t>(string->length(shape))) {
1694 string->TryFlatten(shape); // Invalidates shape!
1695 return LookupSingleCharacterStringFromCode(
1696 string->Get(StringShape(*string), index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001697 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001698 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001699}
1700
1701
1702Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
1703 // Handle [] indexing on Strings
1704 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001705 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
1706 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001707 }
1708
1709 // Handle [] indexing on String objects
1710 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001711 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
1712 Handle<Object> result =
1713 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
1714 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001715 }
1716
1717 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001718 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001719 return prototype->GetElement(index);
1720 }
1721
1722 return object->GetElement(index);
1723}
1724
1725
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001726Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
1727 HandleScope scope;
1728
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001729 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001730 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001731 Handle<Object> error =
1732 Factory::NewTypeError("non_object_property_load",
1733 HandleVector(args, 2));
1734 return Top::Throw(*error);
1735 }
1736
1737 // Check if the given key is an array index.
1738 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001739 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001740 return GetElementOrCharAt(object, index);
1741 }
1742
1743 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001744 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001745 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001746 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001747 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001748 bool has_pending_exception = false;
1749 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001750 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001751 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001752 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001753 }
1754
ager@chromium.org32912102009-01-16 10:38:43 +00001755 // Check if the name is trivially convertible to an index and get
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001756 // the element if so.
1757 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001758 return GetElementOrCharAt(object, index);
1759 } else {
1760 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001761 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001762 }
1763}
1764
1765
1766static Object* Runtime_GetProperty(Arguments args) {
1767 NoHandleAllocation ha;
1768 ASSERT(args.length() == 2);
1769
1770 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001771 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001772
1773 return Runtime::GetObjectProperty(object, key);
1774}
1775
1776
ager@chromium.org7c537e22008-10-16 08:43:32 +00001777
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001778// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001779static Object* Runtime_KeyedGetProperty(Arguments args) {
1780 NoHandleAllocation ha;
1781 ASSERT(args.length() == 2);
1782
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001783 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00001784 // itself.
1785 //
1786 // The global proxy objects has to be excluded since LocalLookup on
ager@chromium.org32912102009-01-16 10:38:43 +00001787 // the global proxy object can return a valid result even though the
ager@chromium.org8bb60582008-12-11 12:02:20 +00001788 // global proxy object never has properties. This is the case
1789 // because the global proxy object forwards everything to its hidden
1790 // prototype including local lookups.
1791 //
1792 // Additionally, we need to make sure that we do not cache results
1793 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001794 if (args[0]->IsJSObject() &&
1795 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00001796 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001797 args[1]->IsString()) {
1798 JSObject* receiver = JSObject::cast(args[0]);
1799 String* key = String::cast(args[1]);
1800 if (receiver->HasFastProperties()) {
1801 // Attempt to use lookup cache.
1802 Object* obj = Heap::GetKeyedLookupCache();
1803 if (obj->IsFailure()) return obj;
1804 LookupCache* cache = LookupCache::cast(obj);
1805 Map* receiver_map = receiver->map();
1806 int offset = cache->Lookup(receiver_map, key);
1807 if (offset != LookupCache::kNotFound) {
1808 Object* value = receiver->FastPropertyAt(offset);
1809 return value->IsTheHole() ? Heap::undefined_value() : value;
1810 }
1811 // Lookup cache miss. Perform lookup and update the cache if
1812 // appropriate.
1813 LookupResult result;
1814 receiver->LocalLookup(key, &result);
1815 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
1816 int offset = result.GetFieldIndex();
1817 Object* obj = cache->Put(receiver_map, key, offset);
1818 if (obj->IsFailure()) return obj;
1819 Heap::SetKeyedLookupCache(LookupCache::cast(obj));
1820 Object* value = receiver->FastPropertyAt(offset);
1821 return value->IsTheHole() ? Heap::undefined_value() : value;
1822 }
1823 } else {
1824 // Attempt dictionary lookup.
1825 Dictionary* dictionary = receiver->property_dictionary();
1826 int entry = dictionary->FindStringEntry(key);
1827 if ((entry != DescriptorArray::kNotFound) &&
1828 (dictionary->DetailsAt(entry).type() == NORMAL)) {
1829 return dictionary->ValueAt(entry);
1830 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001831 }
1832 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001833
1834 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001835 return Runtime::GetObjectProperty(args.at<Object>(0),
1836 args.at<Object>(1));
1837}
1838
1839
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001840Object* Runtime::SetObjectProperty(Handle<Object> object,
1841 Handle<Object> key,
1842 Handle<Object> value,
1843 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001844 HandleScope scope;
1845
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001846 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001847 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001848 Handle<Object> error =
1849 Factory::NewTypeError("non_object_property_store",
1850 HandleVector(args, 2));
1851 return Top::Throw(*error);
1852 }
1853
1854 // If the object isn't a JavaScript object, we ignore the store.
1855 if (!object->IsJSObject()) return *value;
1856
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001857 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
1858
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001859 // Check if the given key is an array index.
1860 uint32_t index;
1861 if (Array::IndexFromObject(*key, &index)) {
1862 ASSERT(attr == NONE);
1863
1864 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
1865 // of a string using [] notation. We need to support this too in
1866 // JavaScript.
1867 // In the case of a String object we just need to redirect the assignment to
1868 // the underlying string if the index is in range. Since the underlying
1869 // string does nothing with the assignment then we can ignore such
1870 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001871 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001872 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001873 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001874
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001875 Handle<Object> result = SetElement(js_object, index, value);
1876 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001877 return *value;
1878 }
1879
1880 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001881 Handle<Object> result;
1882 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001883 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001884 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001885 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001886 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001887 key_string->TryFlatten(StringShape(*key_string));
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001888 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001889 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001890 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001891 return *value;
1892 }
1893
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001894 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001895 bool has_pending_exception = false;
1896 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
1897 if (has_pending_exception) return Failure::Exception();
1898 Handle<String> name = Handle<String>::cast(converted);
1899
1900 if (name->AsArrayIndex(&index)) {
1901 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001902 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001903 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001904 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001905 }
1906}
1907
1908
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001909static Object* Runtime_SetProperty(Arguments args) {
1910 NoHandleAllocation ha;
1911 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
1912
1913 Handle<Object> object = args.at<Object>(0);
1914 Handle<Object> key = args.at<Object>(1);
1915 Handle<Object> value = args.at<Object>(2);
1916
1917 // Compute attributes.
1918 PropertyAttributes attributes = NONE;
1919 if (args.length() == 4) {
1920 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001921 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001922 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001923 RUNTIME_ASSERT(
1924 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
1925 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001926 }
1927 return Runtime::SetObjectProperty(object, key, value, attributes);
1928}
1929
1930
1931// Set a local property, even if it is READ_ONLY. If the property does not
1932// exist, it will be added with attributes NONE.
1933static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
1934 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001935 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001936 CONVERT_CHECKED(JSObject, object, args[0]);
1937 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001938 // Compute attributes.
1939 PropertyAttributes attributes = NONE;
1940 if (args.length() == 4) {
1941 CONVERT_CHECKED(Smi, value_obj, args[3]);
1942 int unchecked_value = value_obj->value();
1943 // Only attribute bits should be set.
1944 RUNTIME_ASSERT(
1945 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
1946 attributes = static_cast<PropertyAttributes>(unchecked_value);
1947 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001948
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001949 return object->
1950 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001951}
1952
1953
1954static Object* Runtime_DeleteProperty(Arguments args) {
1955 NoHandleAllocation ha;
1956 ASSERT(args.length() == 2);
1957
1958 CONVERT_CHECKED(JSObject, object, args[0]);
1959 CONVERT_CHECKED(String, key, args[1]);
1960 return object->DeleteProperty(key);
1961}
1962
1963
1964static Object* Runtime_HasLocalProperty(Arguments args) {
1965 NoHandleAllocation ha;
1966 ASSERT(args.length() == 2);
1967 CONVERT_CHECKED(String, key, args[1]);
1968
1969 // Only JS objects can have properties.
1970 if (args[0]->IsJSObject()) {
1971 JSObject* object = JSObject::cast(args[0]);
1972 if (object->HasLocalProperty(key)) return Heap::true_value();
1973 } else if (args[0]->IsString()) {
1974 // Well, there is one exception: Handle [] on strings.
1975 uint32_t index;
1976 if (key->AsArrayIndex(&index)) {
1977 String* string = String::cast(args[0]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001978 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001979 return Heap::true_value();
1980 }
1981 }
1982 return Heap::false_value();
1983}
1984
1985
1986static Object* Runtime_HasProperty(Arguments args) {
1987 NoHandleAllocation na;
1988 ASSERT(args.length() == 2);
1989
1990 // Only JS objects can have properties.
1991 if (args[0]->IsJSObject()) {
1992 JSObject* object = JSObject::cast(args[0]);
1993 CONVERT_CHECKED(String, key, args[1]);
1994 if (object->HasProperty(key)) return Heap::true_value();
1995 }
1996 return Heap::false_value();
1997}
1998
1999
2000static Object* Runtime_HasElement(Arguments args) {
2001 NoHandleAllocation na;
2002 ASSERT(args.length() == 2);
2003
2004 // Only JS objects can have elements.
2005 if (args[0]->IsJSObject()) {
2006 JSObject* object = JSObject::cast(args[0]);
2007 CONVERT_CHECKED(Smi, index_obj, args[1]);
2008 uint32_t index = index_obj->value();
2009 if (object->HasElement(index)) return Heap::true_value();
2010 }
2011 return Heap::false_value();
2012}
2013
2014
2015static Object* Runtime_IsPropertyEnumerable(Arguments args) {
2016 NoHandleAllocation ha;
2017 ASSERT(args.length() == 2);
2018
2019 CONVERT_CHECKED(JSObject, object, args[0]);
2020 CONVERT_CHECKED(String, key, args[1]);
2021
2022 uint32_t index;
2023 if (key->AsArrayIndex(&index)) {
2024 return Heap::ToBoolean(object->HasElement(index));
2025 }
2026
ager@chromium.org870a0b62008-11-04 11:43:05 +00002027 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
2028 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002029}
2030
2031
2032static Object* Runtime_GetPropertyNames(Arguments args) {
2033 HandleScope scope;
2034 ASSERT(args.length() == 1);
2035
2036 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2037 Handle<JSObject> object(raw_object);
2038 return *GetKeysFor(object);
2039}
2040
2041
2042// Returns either a FixedArray as Runtime_GetPropertyNames,
2043// or, if the given object has an enum cache that contains
2044// all enumerable properties of the object and its prototypes
2045// have none, the map of the object. This is used to speed up
2046// the check for deletions during a for-in.
2047static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2048 ASSERT(args.length() == 1);
2049
2050 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2051
2052 if (raw_object->IsSimpleEnum()) return raw_object->map();
2053
2054 HandleScope scope;
2055 Handle<JSObject> object(raw_object);
2056 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2057
2058 // Test again, since cache may have been built by preceding call.
2059 if (object->IsSimpleEnum()) return object->map();
2060
2061 return *content;
2062}
2063
2064
2065static Object* Runtime_GetArgumentsProperty(Arguments args) {
2066 NoHandleAllocation ha;
2067 ASSERT(args.length() == 1);
2068
2069 // Compute the frame holding the arguments.
2070 JavaScriptFrameIterator it;
2071 it.AdvanceToArgumentsFrame();
2072 JavaScriptFrame* frame = it.frame();
2073
2074 // Get the actual number of provided arguments.
2075 const uint32_t n = frame->GetProvidedParametersCount();
2076
2077 // Try to convert the key to an index. If successful and within
2078 // index return the the argument from the frame.
2079 uint32_t index;
2080 if (Array::IndexFromObject(args[0], &index) && index < n) {
2081 return frame->GetParameter(index);
2082 }
2083
2084 // Convert the key to a string.
2085 HandleScope scope;
2086 bool exception = false;
2087 Handle<Object> converted =
2088 Execution::ToString(args.at<Object>(0), &exception);
2089 if (exception) return Failure::Exception();
2090 Handle<String> key = Handle<String>::cast(converted);
2091
2092 // Try to convert the string key into an array index.
2093 if (key->AsArrayIndex(&index)) {
2094 if (index < n) {
2095 return frame->GetParameter(index);
2096 } else {
2097 return Top::initial_object_prototype()->GetElement(index);
2098 }
2099 }
2100
2101 // Handle special arguments properties.
2102 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
2103 if (key->Equals(Heap::callee_symbol())) return frame->function();
2104
2105 // Lookup in the initial Object.prototype object.
2106 return Top::initial_object_prototype()->GetProperty(*key);
2107}
2108
2109
2110static Object* Runtime_ToBool(Arguments args) {
2111 NoHandleAllocation ha;
2112 ASSERT(args.length() == 1);
2113
2114 return args[0]->ToBoolean();
2115}
2116
2117
2118// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
2119// Possible optimizations: put the type string into the oddballs.
2120static Object* Runtime_Typeof(Arguments args) {
2121 NoHandleAllocation ha;
2122
2123 Object* obj = args[0];
2124 if (obj->IsNumber()) return Heap::number_symbol();
2125 HeapObject* heap_obj = HeapObject::cast(obj);
2126
2127 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002128 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002129
2130 InstanceType instance_type = heap_obj->map()->instance_type();
2131 if (instance_type < FIRST_NONSTRING_TYPE) {
2132 return Heap::string_symbol();
2133 }
2134
2135 switch (instance_type) {
2136 case ODDBALL_TYPE:
2137 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
2138 return Heap::boolean_symbol();
2139 }
2140 if (heap_obj->IsNull()) {
2141 return Heap::object_symbol();
2142 }
2143 ASSERT(heap_obj->IsUndefined());
2144 return Heap::undefined_symbol();
2145 case JS_FUNCTION_TYPE:
2146 return Heap::function_symbol();
2147 default:
2148 // For any kind of object not handled above, the spec rule for
2149 // host objects gives that it is okay to return "object"
2150 return Heap::object_symbol();
2151 }
2152}
2153
2154
2155static Object* Runtime_StringToNumber(Arguments args) {
2156 NoHandleAllocation ha;
2157 ASSERT(args.length() == 1);
2158 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002159 subject->TryFlatten(StringShape(subject));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002160 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
2161}
2162
2163
2164static Object* Runtime_StringFromCharCodeArray(Arguments args) {
2165 NoHandleAllocation ha;
2166 ASSERT(args.length() == 1);
2167
2168 CONVERT_CHECKED(JSArray, codes, args[0]);
2169 int length = Smi::cast(codes->length())->value();
2170
2171 // Check if the string can be ASCII.
2172 int i;
2173 for (i = 0; i < length; i++) {
2174 Object* element = codes->GetElement(i);
2175 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
2176 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
2177 break;
2178 }
2179
2180 Object* object = NULL;
2181 if (i == length) { // The string is ASCII.
2182 object = Heap::AllocateRawAsciiString(length);
2183 } else { // The string is not ASCII.
2184 object = Heap::AllocateRawTwoByteString(length);
2185 }
2186
2187 if (object->IsFailure()) return object;
2188 String* result = String::cast(object);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002189 StringShape result_shape(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002190 for (int i = 0; i < length; i++) {
2191 Object* element = codes->GetElement(i);
2192 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002193 result->Set(result_shape, i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002194 }
2195 return result;
2196}
2197
2198
2199// kNotEscaped is generated by the following:
2200//
2201// #!/bin/perl
2202// for (my $i = 0; $i < 256; $i++) {
2203// print "\n" if $i % 16 == 0;
2204// my $c = chr($i);
2205// my $escaped = 1;
2206// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
2207// print $escaped ? "0, " : "1, ";
2208// }
2209
2210
2211static bool IsNotEscaped(uint16_t character) {
2212 // Only for 8 bit characters, the rest are always escaped (in a different way)
2213 ASSERT(character < 256);
2214 static const char kNotEscaped[256] = {
2215 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2216 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2217 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
2218 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
2219 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2220 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
2221 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2222 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
2223 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2224 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2225 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2226 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2227 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2228 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2229 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2230 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2231 };
2232 return kNotEscaped[character] != 0;
2233}
2234
2235
2236static Object* Runtime_URIEscape(Arguments args) {
2237 const char hex_chars[] = "0123456789ABCDEF";
2238 NoHandleAllocation ha;
2239 ASSERT(args.length() == 1);
2240 CONVERT_CHECKED(String, source, args[0]);
2241
ager@chromium.org870a0b62008-11-04 11:43:05 +00002242 source->TryFlatten(StringShape(source));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002243
2244 int escaped_length = 0;
2245 int length = source->length();
2246 {
2247 Access<StringInputBuffer> buffer(&string_input_buffer);
2248 buffer->Reset(source);
2249 while (buffer->has_more()) {
2250 uint16_t character = buffer->GetNext();
2251 if (character >= 256) {
2252 escaped_length += 6;
2253 } else if (IsNotEscaped(character)) {
2254 escaped_length++;
2255 } else {
2256 escaped_length += 3;
2257 }
2258 // We don't allow strings that are longer than Smi range.
2259 if (!Smi::IsValid(escaped_length)) {
2260 Top::context()->mark_out_of_memory();
2261 return Failure::OutOfMemoryException();
2262 }
2263 }
2264 }
2265 // No length change implies no change. Return original string if no change.
2266 if (escaped_length == length) {
2267 return source;
2268 }
2269 Object* o = Heap::AllocateRawAsciiString(escaped_length);
2270 if (o->IsFailure()) return o;
2271 String* destination = String::cast(o);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002272 StringShape dshape(destination);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002273 int dest_position = 0;
2274
2275 Access<StringInputBuffer> buffer(&string_input_buffer);
2276 buffer->Rewind();
2277 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002278 uint16_t chr = buffer->GetNext();
2279 if (chr >= 256) {
2280 destination->Set(dshape, dest_position, '%');
2281 destination->Set(dshape, dest_position+1, 'u');
2282 destination->Set(dshape, dest_position+2, hex_chars[chr >> 12]);
2283 destination->Set(dshape, dest_position+3, hex_chars[(chr >> 8) & 0xf]);
2284 destination->Set(dshape, dest_position+4, hex_chars[(chr >> 4) & 0xf]);
2285 destination->Set(dshape, dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002286 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002287 } else if (IsNotEscaped(chr)) {
2288 destination->Set(dshape, dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002289 dest_position++;
2290 } else {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002291 destination->Set(dshape, dest_position, '%');
2292 destination->Set(dshape, dest_position+1, hex_chars[chr >> 4]);
2293 destination->Set(dshape, dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002294 dest_position += 3;
2295 }
2296 }
2297 return destination;
2298}
2299
2300
2301static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
2302 static const signed char kHexValue['g'] = {
2303 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2304 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2305 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2306 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
2307 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2308 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2309 -1, 10, 11, 12, 13, 14, 15 };
2310
2311 if (character1 > 'f') return -1;
2312 int hi = kHexValue[character1];
2313 if (hi == -1) return -1;
2314 if (character2 > 'f') return -1;
2315 int lo = kHexValue[character2];
2316 if (lo == -1) return -1;
2317 return (hi << 4) + lo;
2318}
2319
2320
ager@chromium.org870a0b62008-11-04 11:43:05 +00002321static inline int Unescape(String* source,
2322 StringShape shape,
2323 int i,
2324 int length,
2325 int* step) {
2326 uint16_t character = source->Get(shape, i);
2327 int32_t hi = 0;
2328 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002329 if (character == '%' &&
2330 i <= length - 6 &&
ager@chromium.org870a0b62008-11-04 11:43:05 +00002331 source->Get(shape, i + 1) == 'u' &&
2332 (hi = TwoDigitHex(source->Get(shape, i + 2),
2333 source->Get(shape, i + 3))) != -1 &&
2334 (lo = TwoDigitHex(source->Get(shape, i + 4),
2335 source->Get(shape, i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002336 *step = 6;
2337 return (hi << 8) + lo;
2338 } else if (character == '%' &&
2339 i <= length - 3 &&
ager@chromium.org870a0b62008-11-04 11:43:05 +00002340 (lo = TwoDigitHex(source->Get(shape, i + 1),
2341 source->Get(shape, i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002342 *step = 3;
2343 return lo;
2344 } else {
2345 *step = 1;
2346 return character;
2347 }
2348}
2349
2350
2351static Object* Runtime_URIUnescape(Arguments args) {
2352 NoHandleAllocation ha;
2353 ASSERT(args.length() == 1);
2354 CONVERT_CHECKED(String, source, args[0]);
2355
ager@chromium.org870a0b62008-11-04 11:43:05 +00002356 source->TryFlatten(StringShape(source));
2357 StringShape source_shape(source);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002358
2359 bool ascii = true;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002360 int length = source->length(source_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002361
2362 int unescaped_length = 0;
2363 for (int i = 0; i < length; unescaped_length++) {
2364 int step;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002365 if (Unescape(source,
2366 source_shape,
2367 i,
2368 length,
2369 &step) >
2370 String::kMaxAsciiCharCode)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002371 ascii = false;
2372 i += step;
2373 }
2374
2375 // No length change implies no change. Return original string if no change.
2376 if (unescaped_length == length)
2377 return source;
2378
2379 Object* o = ascii ?
2380 Heap::AllocateRawAsciiString(unescaped_length) :
2381 Heap::AllocateRawTwoByteString(unescaped_length);
2382 if (o->IsFailure()) return o;
2383 String* destination = String::cast(o);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002384 StringShape destination_shape(destination);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002385
2386 int dest_position = 0;
2387 for (int i = 0; i < length; dest_position++) {
2388 int step;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002389 destination->Set(destination_shape,
2390 dest_position,
2391 Unescape(source, source_shape, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002392 i += step;
2393 }
2394 return destination;
2395}
2396
2397
2398static Object* Runtime_StringParseInt(Arguments args) {
2399 NoHandleAllocation ha;
2400
2401 CONVERT_CHECKED(String, s, args[0]);
2402 CONVERT_DOUBLE_CHECKED(n, args[1]);
2403 int radix = FastD2I(n);
2404
ager@chromium.org870a0b62008-11-04 11:43:05 +00002405 s->TryFlatten(StringShape(s));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002406
ager@chromium.org870a0b62008-11-04 11:43:05 +00002407 StringShape shape(s);
2408
2409 int len = s->length(shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002410 int i;
2411
2412 // Skip leading white space.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002413 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(shape, i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002414 if (i == len) return Heap::nan_value();
2415
2416 // Compute the sign (default to +).
2417 int sign = 1;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002418 if (s->Get(shape, i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002419 sign = -1;
2420 i++;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002421 } else if (s->Get(shape, i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002422 i++;
2423 }
2424
2425 // Compute the radix if 0.
2426 if (radix == 0) {
2427 radix = 10;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002428 if (i < len && s->Get(shape, i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002429 radix = 8;
2430 if (i + 1 < len) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002431 int c = s->Get(shape, i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002432 if (c == 'x' || c == 'X') {
2433 radix = 16;
2434 i += 2;
2435 }
2436 }
2437 }
2438 } else if (radix == 16) {
2439 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002440 if (i + 1 < len && s->Get(shape, i) == '0') {
2441 int c = s->Get(shape, i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002442 if (c == 'x' || c == 'X') i += 2;
2443 }
2444 }
2445
2446 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2447 double value;
2448 int end_index = StringToInt(s, i, radix, &value);
2449 if (end_index != i) {
2450 return Heap::NumberFromDouble(sign * value);
2451 }
2452 return Heap::nan_value();
2453}
2454
2455
2456static Object* Runtime_StringParseFloat(Arguments args) {
2457 NoHandleAllocation ha;
2458 CONVERT_CHECKED(String, str, args[0]);
2459
2460 // ECMA-262 section 15.1.2.3, empty string is NaN
2461 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
2462
2463 // Create a number object from the value.
2464 return Heap::NumberFromDouble(value);
2465}
2466
2467
2468static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
2469static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
2470
2471
2472template <class Converter>
2473static Object* ConvertCase(Arguments args,
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002474 unibrow::Mapping<Converter, 128>* mapping) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002475 NoHandleAllocation ha;
2476
2477 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002478 s->TryFlatten(StringShape(s));
2479 StringShape shape(s);
2480
2481 int raw_string_length = s->length(shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002482 // Assume that the string is not empty; we need this assumption later
2483 if (raw_string_length == 0) return s;
2484 int length = raw_string_length;
2485
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002486
2487 // We try this twice, once with the assumption that the result is
2488 // no longer than the input and, if that assumption breaks, again
2489 // with the exact length. This is implemented using a goto back
2490 // to this label if we discover that the assumption doesn't hold.
2491 // I apologize sincerely for this and will give a vaffel-is to
mads.s.ager31e71382008-08-13 09:32:07 +00002492 // the first person who can implement it in a nicer way.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002493 try_convert:
2494
2495 // Allocate the resulting string.
2496 //
2497 // NOTE: This assumes that the upper/lower case of an ascii
2498 // character is also ascii. This is currently the case, but it
2499 // might break in the future if we implement more context and locale
2500 // dependent upper/lower conversions.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002501 Object* o = shape.IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002502 ? Heap::AllocateRawAsciiString(length)
2503 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002504 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002505 String* result = String::cast(o);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002506 StringShape result_shape(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002507 bool has_changed_character = false;
2508
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002509 // Convert all characters to upper case, assuming that they will fit
2510 // in the buffer
2511 Access<StringInputBuffer> buffer(&string_input_buffer);
2512 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002513 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002514 int i = 0;
2515 // We can assume that the string is not empty
2516 uc32 current = buffer->GetNext();
2517 while (i < length) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002518 bool has_next = buffer->has_more();
2519 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002520 int char_length = mapping->get(current, next, chars);
2521 if (char_length == 0) {
2522 // The case conversion of this character is the character itself.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002523 result->Set(result_shape, i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002524 i++;
2525 } else if (char_length == 1) {
2526 // Common case: converting the letter resulted in one character.
2527 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002528 result->Set(result_shape, i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002529 has_changed_character = true;
2530 i++;
2531 } else if (length == raw_string_length) {
2532 // We've assumed that the result would be as long as the
2533 // input but here is a character that converts to several
2534 // characters. No matter, we calculate the exact length
2535 // of the result and try the whole thing again.
2536 //
2537 // Note that this leaves room for optimization. We could just
2538 // memcpy what we already have to the result string. Also,
2539 // the result string is the last object allocated we could
2540 // "realloc" it and probably, in the vast majority of cases,
2541 // extend the existing string to be able to hold the full
2542 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002543 int next_length = 0;
2544 if (has_next) {
2545 next_length = mapping->get(next, 0, chars);
2546 if (next_length == 0) next_length = 1;
2547 }
2548 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002549 while (buffer->has_more()) {
2550 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002551 // NOTE: we use 0 as the next character here because, while
2552 // the next character may affect what a character converts to,
2553 // it does not in any case affect the length of what it convert
2554 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002555 int char_length = mapping->get(current, 0, chars);
2556 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002557 current_length += char_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002558 }
2559 length = current_length;
2560 goto try_convert;
2561 } else {
2562 for (int j = 0; j < char_length; j++) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002563 result->Set(result_shape, i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002564 i++;
2565 }
2566 has_changed_character = true;
2567 }
2568 current = next;
2569 }
2570 if (has_changed_character) {
2571 return result;
2572 } else {
2573 // If we didn't actually change anything in doing the conversion
2574 // we simple return the result and let the converted string
2575 // become garbage; there is no reason to keep two identical strings
2576 // alive.
2577 return s;
2578 }
2579}
2580
2581
2582static Object* Runtime_StringToLowerCase(Arguments args) {
2583 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
2584}
2585
2586
2587static Object* Runtime_StringToUpperCase(Arguments args) {
2588 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
2589}
2590
2591
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002592static Object* Runtime_NumberToString(Arguments args) {
2593 NoHandleAllocation ha;
2594 ASSERT(args.length() == 1);
2595
2596 Object* number = args[0];
2597 RUNTIME_ASSERT(number->IsNumber());
2598
2599 Object* cached = Heap::GetNumberStringCache(number);
2600 if (cached != Heap::undefined_value()) {
2601 return cached;
2602 }
2603
2604 char arr[100];
2605 Vector<char> buffer(arr, ARRAY_SIZE(arr));
2606 const char* str;
2607 if (number->IsSmi()) {
2608 int num = Smi::cast(number)->value();
2609 str = IntToCString(num, buffer);
2610 } else {
2611 double num = HeapNumber::cast(number)->value();
2612 str = DoubleToCString(num, buffer);
2613 }
2614 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2615
2616 if (!result->IsFailure()) {
2617 Heap::SetNumberStringCache(number, String::cast(result));
2618 }
2619 return result;
2620}
2621
2622
2623static Object* Runtime_NumberToInteger(Arguments args) {
2624 NoHandleAllocation ha;
2625 ASSERT(args.length() == 1);
2626
2627 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002628 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002629 CONVERT_DOUBLE_CHECKED(number, obj);
2630 return Heap::NumberFromDouble(DoubleToInteger(number));
2631}
2632
2633
2634static Object* Runtime_NumberToJSUint32(Arguments args) {
2635 NoHandleAllocation ha;
2636 ASSERT(args.length() == 1);
2637
2638 Object* obj = args[0];
2639 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
2640 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
2641 return Heap::NumberFromUint32(number);
2642}
2643
2644
2645static Object* Runtime_NumberToJSInt32(Arguments args) {
2646 NoHandleAllocation ha;
2647 ASSERT(args.length() == 1);
2648
2649 Object* obj = args[0];
2650 if (obj->IsSmi()) return obj;
2651 CONVERT_DOUBLE_CHECKED(number, obj);
2652 return Heap::NumberFromInt32(DoubleToInt32(number));
2653}
2654
2655
ager@chromium.org870a0b62008-11-04 11:43:05 +00002656// Converts a Number to a Smi, if possible. Returns NaN if the number is not
2657// a small integer.
2658static Object* Runtime_NumberToSmi(Arguments args) {
2659 NoHandleAllocation ha;
2660 ASSERT(args.length() == 1);
2661
2662 Object* obj = args[0];
2663 if (obj->IsSmi()) {
2664 return obj;
2665 }
2666 if (obj->IsHeapNumber()) {
2667 double value = HeapNumber::cast(obj)->value();
2668 int int_value = FastD2I(value);
2669 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
2670 return Smi::FromInt(int_value);
2671 }
2672 }
2673 return Heap::nan_value();
2674}
2675
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002676static Object* Runtime_NumberAdd(Arguments args) {
2677 NoHandleAllocation ha;
2678 ASSERT(args.length() == 2);
2679
2680 CONVERT_DOUBLE_CHECKED(x, args[0]);
2681 CONVERT_DOUBLE_CHECKED(y, args[1]);
2682 return Heap::AllocateHeapNumber(x + y);
2683}
2684
2685
2686static Object* Runtime_NumberSub(Arguments args) {
2687 NoHandleAllocation ha;
2688 ASSERT(args.length() == 2);
2689
2690 CONVERT_DOUBLE_CHECKED(x, args[0]);
2691 CONVERT_DOUBLE_CHECKED(y, args[1]);
2692 return Heap::AllocateHeapNumber(x - y);
2693}
2694
2695
2696static Object* Runtime_NumberMul(Arguments args) {
2697 NoHandleAllocation ha;
2698 ASSERT(args.length() == 2);
2699
2700 CONVERT_DOUBLE_CHECKED(x, args[0]);
2701 CONVERT_DOUBLE_CHECKED(y, args[1]);
2702 return Heap::AllocateHeapNumber(x * y);
2703}
2704
2705
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002706static Object* Runtime_NumberUnaryMinus(Arguments args) {
2707 NoHandleAllocation ha;
2708 ASSERT(args.length() == 1);
2709
2710 CONVERT_DOUBLE_CHECKED(x, args[0]);
2711 return Heap::AllocateHeapNumber(-x);
2712}
2713
2714
2715static Object* Runtime_NumberDiv(Arguments args) {
2716 NoHandleAllocation ha;
2717 ASSERT(args.length() == 2);
2718
2719 CONVERT_DOUBLE_CHECKED(x, args[0]);
2720 CONVERT_DOUBLE_CHECKED(y, args[1]);
2721 return Heap::NewNumberFromDouble(x / y);
2722}
2723
2724
2725static Object* Runtime_NumberMod(Arguments args) {
2726 NoHandleAllocation ha;
2727 ASSERT(args.length() == 2);
2728
2729 CONVERT_DOUBLE_CHECKED(x, args[0]);
2730 CONVERT_DOUBLE_CHECKED(y, args[1]);
2731
2732#ifdef WIN32
2733 // Workaround MS fmod bugs. ECMA-262 says:
2734 // dividend is finite and divisor is an infinity => result equals dividend
2735 // dividend is a zero and divisor is nonzero finite => result equals dividend
2736 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
2737 !(x == 0 && (y != 0 && isfinite(y))))
2738#endif
2739 x = fmod(x, y);
2740 // NewNumberFromDouble may return a Smi instead of a Number object
2741 return Heap::NewNumberFromDouble(x);
2742}
2743
2744
2745static Object* Runtime_StringAdd(Arguments args) {
2746 NoHandleAllocation ha;
2747 ASSERT(args.length() == 2);
2748
2749 CONVERT_CHECKED(String, str1, args[0]);
2750 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002751 int len1 = str1->length();
2752 int len2 = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002753 if (len1 == 0) return str2;
2754 if (len2 == 0) return str1;
2755 int length_sum = len1 + len2;
2756 // Make sure that an out of memory exception is thrown if the length
2757 // of the new cons string is too large to fit in a Smi.
2758 if (length_sum > Smi::kMaxValue || length_sum < 0) {
2759 Top::context()->mark_out_of_memory();
2760 return Failure::OutOfMemoryException();
2761 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002762 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002763}
2764
2765
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002766template<typename sinkchar>
2767static inline void StringBuilderConcatHelper(String* special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00002768 StringShape special_shape,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002769 sinkchar* sink,
2770 FixedArray* fixed_array,
2771 int array_length) {
2772 int position = 0;
2773 for (int i = 0; i < array_length; i++) {
2774 Object* element = fixed_array->get(i);
2775 if (element->IsSmi()) {
2776 int len = Smi::cast(element)->value();
2777 int pos = len >> 11;
2778 len &= 0x7ff;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002779 String::WriteToFlat(special,
2780 special_shape,
2781 sink + position,
2782 pos,
2783 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002784 position += len;
2785 } else {
2786 String* string = String::cast(element);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002787 StringShape shape(string);
2788 int element_length = string->length(shape);
2789 String::WriteToFlat(string, shape, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002790 position += element_length;
2791 }
2792 }
2793}
2794
2795
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002796static Object* Runtime_StringBuilderConcat(Arguments args) {
2797 NoHandleAllocation ha;
2798 ASSERT(args.length() == 2);
2799 CONVERT_CHECKED(JSArray, array, args[0]);
2800 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002801 StringShape special_shape(special);
2802 int special_length = special->length(special_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002803 Object* smi_array_length = array->length();
2804 if (!smi_array_length->IsSmi()) {
2805 Top::context()->mark_out_of_memory();
2806 return Failure::OutOfMemoryException();
2807 }
2808 int array_length = Smi::cast(smi_array_length)->value();
2809 if (!array->HasFastElements()) {
2810 return Top::Throw(Heap::illegal_argument_symbol());
2811 }
2812 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002813 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002814 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002815 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002816
2817 if (array_length == 0) {
2818 return Heap::empty_string();
2819 } else if (array_length == 1) {
2820 Object* first = fixed_array->get(0);
2821 if (first->IsString()) return first;
2822 }
2823
ager@chromium.org870a0b62008-11-04 11:43:05 +00002824 bool ascii = special_shape.IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002825 int position = 0;
2826 for (int i = 0; i < array_length; i++) {
2827 Object* elt = fixed_array->get(i);
2828 if (elt->IsSmi()) {
2829 int len = Smi::cast(elt)->value();
2830 int pos = len >> 11;
2831 len &= 0x7ff;
2832 if (pos + len > special_length) {
2833 return Top::Throw(Heap::illegal_argument_symbol());
2834 }
2835 position += len;
2836 } else if (elt->IsString()) {
2837 String* element = String::cast(elt);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002838 StringShape element_shape(element);
2839 int element_length = element->length(element_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002840 if (!Smi::IsValid(element_length + position)) {
2841 Top::context()->mark_out_of_memory();
2842 return Failure::OutOfMemoryException();
2843 }
2844 position += element_length;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002845 if (ascii && !element_shape.IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002846 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002847 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002848 } else {
2849 return Top::Throw(Heap::illegal_argument_symbol());
2850 }
2851 }
2852
2853 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002854 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002855
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002856 if (ascii) {
2857 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002858 if (object->IsFailure()) return object;
2859 SeqAsciiString* answer = SeqAsciiString::cast(object);
2860 StringBuilderConcatHelper(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00002861 special_shape,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002862 answer->GetChars(),
2863 fixed_array,
2864 array_length);
2865 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002866 } else {
2867 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002868 if (object->IsFailure()) return object;
2869 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
2870 StringBuilderConcatHelper(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00002871 special_shape,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002872 answer->GetChars(),
2873 fixed_array,
2874 array_length);
2875 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002876 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002877}
2878
2879
2880static Object* Runtime_NumberOr(Arguments args) {
2881 NoHandleAllocation ha;
2882 ASSERT(args.length() == 2);
2883
2884 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2885 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2886 return Heap::NumberFromInt32(x | y);
2887}
2888
2889
2890static Object* Runtime_NumberAnd(Arguments args) {
2891 NoHandleAllocation ha;
2892 ASSERT(args.length() == 2);
2893
2894 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2895 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2896 return Heap::NumberFromInt32(x & y);
2897}
2898
2899
2900static Object* Runtime_NumberXor(Arguments args) {
2901 NoHandleAllocation ha;
2902 ASSERT(args.length() == 2);
2903
2904 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2905 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2906 return Heap::NumberFromInt32(x ^ y);
2907}
2908
2909
2910static Object* Runtime_NumberNot(Arguments args) {
2911 NoHandleAllocation ha;
2912 ASSERT(args.length() == 1);
2913
2914 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2915 return Heap::NumberFromInt32(~x);
2916}
2917
2918
2919static Object* Runtime_NumberShl(Arguments args) {
2920 NoHandleAllocation ha;
2921 ASSERT(args.length() == 2);
2922
2923 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2924 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2925 return Heap::NumberFromInt32(x << (y & 0x1f));
2926}
2927
2928
2929static Object* Runtime_NumberShr(Arguments args) {
2930 NoHandleAllocation ha;
2931 ASSERT(args.length() == 2);
2932
2933 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
2934 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2935 return Heap::NumberFromUint32(x >> (y & 0x1f));
2936}
2937
2938
2939static Object* Runtime_NumberSar(Arguments args) {
2940 NoHandleAllocation ha;
2941 ASSERT(args.length() == 2);
2942
2943 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2944 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2945 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
2946}
2947
2948
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002949static Object* Runtime_NumberEquals(Arguments args) {
2950 NoHandleAllocation ha;
2951 ASSERT(args.length() == 2);
2952
2953 CONVERT_DOUBLE_CHECKED(x, args[0]);
2954 CONVERT_DOUBLE_CHECKED(y, args[1]);
2955 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
2956 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
2957 if (x == y) return Smi::FromInt(EQUAL);
2958 Object* result;
2959 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
2960 result = Smi::FromInt(EQUAL);
2961 } else {
2962 result = Smi::FromInt(NOT_EQUAL);
2963 }
2964 return result;
2965}
2966
2967
2968static Object* Runtime_StringEquals(Arguments args) {
2969 NoHandleAllocation ha;
2970 ASSERT(args.length() == 2);
2971
2972 CONVERT_CHECKED(String, x, args[0]);
2973 CONVERT_CHECKED(String, y, args[1]);
2974
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002975 bool not_equal = !x->Equals(y);
2976 // This is slightly convoluted because the value that signifies
2977 // equality is 0 and inequality is 1 so we have to negate the result
2978 // from String::Equals.
2979 ASSERT(not_equal == 0 || not_equal == 1);
2980 STATIC_CHECK(EQUAL == 0);
2981 STATIC_CHECK(NOT_EQUAL == 1);
2982 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002983}
2984
2985
2986static Object* Runtime_NumberCompare(Arguments args) {
2987 NoHandleAllocation ha;
2988 ASSERT(args.length() == 3);
2989
2990 CONVERT_DOUBLE_CHECKED(x, args[0]);
2991 CONVERT_DOUBLE_CHECKED(y, args[1]);
2992 if (isnan(x) || isnan(y)) return args[2];
2993 if (x == y) return Smi::FromInt(EQUAL);
2994 if (isless(x, y)) return Smi::FromInt(LESS);
2995 return Smi::FromInt(GREATER);
2996}
2997
2998
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002999// Compare two Smis as if they were converted to strings and then
3000// compared lexicographically.
3001static Object* Runtime_SmiLexicographicCompare(Arguments args) {
3002 NoHandleAllocation ha;
3003 ASSERT(args.length() == 2);
3004
3005 // Arrays for the individual characters of the two Smis. Smis are
3006 // 31 bit integers and 10 decimal digits are therefore enough.
3007 static int x_elms[10];
3008 static int y_elms[10];
3009
3010 // Extract the integer values from the Smis.
3011 CONVERT_CHECKED(Smi, x, args[0]);
3012 CONVERT_CHECKED(Smi, y, args[1]);
3013 int x_value = x->value();
3014 int y_value = y->value();
3015
3016 // If the integers are equal so are the string representations.
3017 if (x_value == y_value) return Smi::FromInt(EQUAL);
3018
3019 // If one of the integers are zero the normal integer order is the
3020 // same as the lexicographic order of the string representations.
3021 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
3022
ager@chromium.org32912102009-01-16 10:38:43 +00003023 // If only one of the integers is negative the negative number is
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003024 // smallest because the char code of '-' is less than the char code
3025 // of any digit. Otherwise, we make both values positive.
3026 if (x_value < 0 || y_value < 0) {
3027 if (y_value >= 0) return Smi::FromInt(LESS);
3028 if (x_value >= 0) return Smi::FromInt(GREATER);
3029 x_value = -x_value;
3030 y_value = -y_value;
3031 }
3032
3033 // Convert the integers to arrays of their decimal digits.
3034 int x_index = 0;
3035 int y_index = 0;
3036 while (x_value > 0) {
3037 x_elms[x_index++] = x_value % 10;
3038 x_value /= 10;
3039 }
3040 while (y_value > 0) {
3041 y_elms[y_index++] = y_value % 10;
3042 y_value /= 10;
3043 }
3044
3045 // Loop through the arrays of decimal digits finding the first place
3046 // where they differ.
3047 while (--x_index >= 0 && --y_index >= 0) {
3048 int diff = x_elms[x_index] - y_elms[y_index];
3049 if (diff != 0) return Smi::FromInt(diff);
3050 }
3051
3052 // If one array is a suffix of the other array, the longest array is
3053 // the representation of the largest of the Smis in the
3054 // lexicographic ordering.
3055 return Smi::FromInt(x_index - y_index);
3056}
3057
3058
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003059static Object* Runtime_StringCompare(Arguments args) {
3060 NoHandleAllocation ha;
3061 ASSERT(args.length() == 2);
3062
3063 CONVERT_CHECKED(String, x, args[0]);
3064 CONVERT_CHECKED(String, y, args[1]);
3065
ager@chromium.org870a0b62008-11-04 11:43:05 +00003066 StringShape x_shape(x);
3067 StringShape y_shape(y);
3068
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003069 // A few fast case tests before we flatten.
3070 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003071 if (y->length(y_shape) == 0) {
3072 if (x->length(x_shape) == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003073 return Smi::FromInt(GREATER);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003074 } else if (x->length(x_shape) == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003075 return Smi::FromInt(LESS);
3076 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003077
ager@chromium.org870a0b62008-11-04 11:43:05 +00003078 int d = x->Get(x_shape, 0) - y->Get(y_shape, 0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003079 if (d < 0) return Smi::FromInt(LESS);
3080 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003081
ager@chromium.org870a0b62008-11-04 11:43:05 +00003082 x->TryFlatten(x_shape); // Shapes are no longer valid!
3083 y->TryFlatten(y_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003084
3085 static StringInputBuffer bufx;
3086 static StringInputBuffer bufy;
3087 bufx.Reset(x);
3088 bufy.Reset(y);
3089 while (bufx.has_more() && bufy.has_more()) {
3090 int d = bufx.GetNext() - bufy.GetNext();
3091 if (d < 0) return Smi::FromInt(LESS);
3092 else if (d > 0) return Smi::FromInt(GREATER);
3093 }
3094
3095 // x is (non-trivial) prefix of y:
3096 if (bufy.has_more()) return Smi::FromInt(LESS);
3097 // y is prefix of x:
3098 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
3099}
3100
3101
3102static Object* Runtime_Math_abs(Arguments args) {
3103 NoHandleAllocation ha;
3104 ASSERT(args.length() == 1);
3105
3106 CONVERT_DOUBLE_CHECKED(x, args[0]);
3107 return Heap::AllocateHeapNumber(fabs(x));
3108}
3109
3110
3111static Object* Runtime_Math_acos(Arguments args) {
3112 NoHandleAllocation ha;
3113 ASSERT(args.length() == 1);
3114
3115 CONVERT_DOUBLE_CHECKED(x, args[0]);
3116 return Heap::AllocateHeapNumber(acos(x));
3117}
3118
3119
3120static Object* Runtime_Math_asin(Arguments args) {
3121 NoHandleAllocation ha;
3122 ASSERT(args.length() == 1);
3123
3124 CONVERT_DOUBLE_CHECKED(x, args[0]);
3125 return Heap::AllocateHeapNumber(asin(x));
3126}
3127
3128
3129static Object* Runtime_Math_atan(Arguments args) {
3130 NoHandleAllocation ha;
3131 ASSERT(args.length() == 1);
3132
3133 CONVERT_DOUBLE_CHECKED(x, args[0]);
3134 return Heap::AllocateHeapNumber(atan(x));
3135}
3136
3137
3138static Object* Runtime_Math_atan2(Arguments args) {
3139 NoHandleAllocation ha;
3140 ASSERT(args.length() == 2);
3141
3142 CONVERT_DOUBLE_CHECKED(x, args[0]);
3143 CONVERT_DOUBLE_CHECKED(y, args[1]);
3144 double result;
3145 if (isinf(x) && isinf(y)) {
3146 // Make sure that the result in case of two infinite arguments
3147 // is a multiple of Pi / 4. The sign of the result is determined
3148 // by the first argument (x) and the sign of the second argument
3149 // determines the multiplier: one or three.
3150 static double kPiDividedBy4 = 0.78539816339744830962;
3151 int multiplier = (x < 0) ? -1 : 1;
3152 if (y < 0) multiplier *= 3;
3153 result = multiplier * kPiDividedBy4;
3154 } else {
3155 result = atan2(x, y);
3156 }
3157 return Heap::AllocateHeapNumber(result);
3158}
3159
3160
3161static Object* Runtime_Math_ceil(Arguments args) {
3162 NoHandleAllocation ha;
3163 ASSERT(args.length() == 1);
3164
3165 CONVERT_DOUBLE_CHECKED(x, args[0]);
3166 return Heap::NumberFromDouble(ceiling(x));
3167}
3168
3169
3170static Object* Runtime_Math_cos(Arguments args) {
3171 NoHandleAllocation ha;
3172 ASSERT(args.length() == 1);
3173
3174 CONVERT_DOUBLE_CHECKED(x, args[0]);
3175 return Heap::AllocateHeapNumber(cos(x));
3176}
3177
3178
3179static Object* Runtime_Math_exp(Arguments args) {
3180 NoHandleAllocation ha;
3181 ASSERT(args.length() == 1);
3182
3183 CONVERT_DOUBLE_CHECKED(x, args[0]);
3184 return Heap::AllocateHeapNumber(exp(x));
3185}
3186
3187
3188static Object* Runtime_Math_floor(Arguments args) {
3189 NoHandleAllocation ha;
3190 ASSERT(args.length() == 1);
3191
3192 CONVERT_DOUBLE_CHECKED(x, args[0]);
3193 return Heap::NumberFromDouble(floor(x));
3194}
3195
3196
3197static Object* Runtime_Math_log(Arguments args) {
3198 NoHandleAllocation ha;
3199 ASSERT(args.length() == 1);
3200
3201 CONVERT_DOUBLE_CHECKED(x, args[0]);
3202 return Heap::AllocateHeapNumber(log(x));
3203}
3204
3205
3206static Object* Runtime_Math_pow(Arguments args) {
3207 NoHandleAllocation ha;
3208 ASSERT(args.length() == 2);
3209
3210 CONVERT_DOUBLE_CHECKED(x, args[0]);
3211 CONVERT_DOUBLE_CHECKED(y, args[1]);
3212 if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
3213 return Heap::nan_value();
3214 } else if (y == 0) {
3215 return Smi::FromInt(1);
3216 } else {
3217 return Heap::AllocateHeapNumber(pow(x, y));
3218 }
3219}
3220
3221// Returns a number value with positive sign, greater than or equal to
3222// 0 but less than 1, chosen randomly.
mads.s.ager31e71382008-08-13 09:32:07 +00003223static Object* Runtime_Math_random(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003224 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003225 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003226
3227 // To get much better precision, we combine the results of two
3228 // invocations of random(). The result is computed by normalizing a
3229 // double in the range [0, RAND_MAX + 1) obtained by adding the
3230 // high-order bits in the range [0, RAND_MAX] with the low-order
3231 // bits in the range [0, 1).
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003232 double lo = static_cast<double>(random()) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003233 double hi = static_cast<double>(random());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003234 double result = (hi + lo) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003235 ASSERT(result >= 0 && result < 1);
3236 return Heap::AllocateHeapNumber(result);
3237}
3238
3239
3240static Object* Runtime_Math_round(Arguments args) {
3241 NoHandleAllocation ha;
3242 ASSERT(args.length() == 1);
3243
3244 CONVERT_DOUBLE_CHECKED(x, args[0]);
3245 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
3246 return Heap::NumberFromDouble(floor(x + 0.5));
3247}
3248
3249
3250static Object* Runtime_Math_sin(Arguments args) {
3251 NoHandleAllocation ha;
3252 ASSERT(args.length() == 1);
3253
3254 CONVERT_DOUBLE_CHECKED(x, args[0]);
3255 return Heap::AllocateHeapNumber(sin(x));
3256}
3257
3258
3259static Object* Runtime_Math_sqrt(Arguments args) {
3260 NoHandleAllocation ha;
3261 ASSERT(args.length() == 1);
3262
3263 CONVERT_DOUBLE_CHECKED(x, args[0]);
3264 return Heap::AllocateHeapNumber(sqrt(x));
3265}
3266
3267
3268static Object* Runtime_Math_tan(Arguments args) {
3269 NoHandleAllocation ha;
3270 ASSERT(args.length() == 1);
3271
3272 CONVERT_DOUBLE_CHECKED(x, args[0]);
3273 return Heap::AllocateHeapNumber(tan(x));
3274}
3275
3276
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003277// The NewArguments function is only used when constructing the
3278// arguments array when calling non-functions from JavaScript in
3279// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003280static Object* Runtime_NewArguments(Arguments args) {
3281 NoHandleAllocation ha;
3282 ASSERT(args.length() == 1);
3283
3284 // ECMA-262, 3rd., 10.1.8, p.39
3285 CONVERT_CHECKED(JSFunction, callee, args[0]);
3286
3287 // Compute the frame holding the arguments.
3288 JavaScriptFrameIterator it;
3289 it.AdvanceToArgumentsFrame();
3290 JavaScriptFrame* frame = it.frame();
3291
3292 const int length = frame->GetProvidedParametersCount();
3293 Object* result = Heap::AllocateArgumentsObject(callee, length);
3294 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003295 if (length > 0) {
3296 Object* obj = Heap::AllocateFixedArray(length);
3297 if (obj->IsFailure()) return obj;
3298 FixedArray* array = FixedArray::cast(obj);
3299 ASSERT(array->length() == length);
3300 WriteBarrierMode mode = array->GetWriteBarrierMode();
3301 for (int i = 0; i < length; i++) {
3302 array->set(i, frame->GetParameter(i), mode);
3303 }
3304 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003305 }
3306 return result;
3307}
3308
3309
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003310static Object* Runtime_NewArgumentsFast(Arguments args) {
3311 NoHandleAllocation ha;
3312 ASSERT(args.length() == 3);
3313
3314 JSFunction* callee = JSFunction::cast(args[0]);
3315 Object** parameters = reinterpret_cast<Object**>(args[1]);
3316 const int length = Smi::cast(args[2])->value();
3317
3318 Object* result = Heap::AllocateArgumentsObject(callee, length);
3319 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003320 ASSERT(Heap::InNewSpace(result));
3321
3322 // Allocate the elements if needed.
3323 if (length > 0) {
3324 // Allocate the fixed array.
3325 Object* obj = Heap::AllocateRawFixedArray(length);
3326 if (obj->IsFailure()) return obj;
3327 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
3328 FixedArray* array = FixedArray::cast(obj);
3329 array->set_length(length);
3330 WriteBarrierMode mode = array->GetWriteBarrierMode();
3331 for (int i = 0; i < length; i++) {
3332 array->set(i, *--parameters, mode);
3333 }
3334 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
3335 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003336 }
3337 return result;
3338}
3339
3340
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003341static Object* Runtime_NewClosure(Arguments args) {
3342 HandleScope scope;
3343 ASSERT(args.length() == 2);
3344 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
3345 CONVERT_ARG_CHECKED(Context, context, 1);
3346
3347 Handle<JSFunction> result =
3348 Factory::NewFunctionFromBoilerplate(boilerplate, context);
3349 return *result;
3350}
3351
3352
3353static Object* Runtime_NewObject(Arguments args) {
3354 NoHandleAllocation ha;
3355 ASSERT(args.length() == 1);
3356
3357 Object* constructor = args[0];
3358 if (constructor->IsJSFunction()) {
3359 JSFunction* function = JSFunction::cast(constructor);
3360
ager@chromium.org32912102009-01-16 10:38:43 +00003361 // Handle stepping into constructors if step into is active.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003362 if (Debug::StepInActive()) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003363 HandleScope scope;
3364 Debug::HandleStepIn(Handle<JSFunction>(function), 0, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003365 }
3366
3367 if (function->has_initial_map() &&
3368 function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
3369 // The 'Function' function ignores the receiver object when
3370 // called using 'new' and creates a new JSFunction object that
3371 // is returned. The receiver object is only used for error
3372 // reporting if an error occurs when constructing the new
3373 // JSFunction. AllocateJSObject should not be used to allocate
3374 // JSFunctions since it does not properly initialize the shared
3375 // part of the function. Since the receiver is ignored anyway,
3376 // we use the global object as the receiver instead of a new
3377 // JSFunction object. This way, errors are reported the same
3378 // way whether or not 'Function' is called using 'new'.
3379 return Top::context()->global();
3380 }
3381 return Heap::AllocateJSObject(function);
3382 }
3383
3384 HandleScope scope;
3385 Handle<Object> cons(constructor);
3386 // The constructor is not a function; throw a type error.
3387 Handle<Object> type_error =
3388 Factory::NewTypeError("not_constructor", HandleVector(&cons, 1));
3389 return Top::Throw(*type_error);
3390}
3391
3392
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003393static Object* Runtime_LazyCompile(Arguments args) {
3394 HandleScope scope;
3395 ASSERT(args.length() == 1);
3396
3397 Handle<JSFunction> function = args.at<JSFunction>(0);
3398#ifdef DEBUG
3399 if (FLAG_trace_lazy) {
3400 PrintF("[lazy: ");
3401 function->shared()->name()->Print();
3402 PrintF("]\n");
3403 }
3404#endif
3405
3406 // Compile the target function.
3407 ASSERT(!function->is_compiled());
3408 if (!CompileLazy(function, KEEP_EXCEPTION)) {
3409 return Failure::Exception();
3410 }
3411
3412 return function->code();
3413}
3414
3415
3416static Object* Runtime_GetCalledFunction(Arguments args) {
3417 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00003418 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003419 StackFrameIterator it;
3420 // Get past the JS-to-C exit frame.
3421 ASSERT(it.frame()->is_exit());
3422 it.Advance();
3423 // Get past the CALL_NON_FUNCTION activation frame.
3424 ASSERT(it.frame()->is_java_script());
3425 it.Advance();
3426 // Argument adaptor frames do not copy the function; we have to skip
3427 // past them to get to the real calling frame.
3428 if (it.frame()->is_arguments_adaptor()) it.Advance();
3429 // Get the function from the top of the expression stack of the
3430 // calling frame.
3431 StandardFrame* frame = StandardFrame::cast(it.frame());
3432 int index = frame->ComputeExpressionsCount() - 1;
3433 Object* result = frame->GetExpression(index);
3434 return result;
3435}
3436
3437
3438static Object* Runtime_GetFunctionDelegate(Arguments args) {
3439 HandleScope scope;
3440 ASSERT(args.length() == 1);
3441 RUNTIME_ASSERT(!args[0]->IsJSFunction());
3442 return *Execution::GetFunctionDelegate(args.at<Object>(0));
3443}
3444
3445
3446static Object* Runtime_NewContext(Arguments args) {
3447 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00003448 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003449
kasper.lund7276f142008-07-30 08:49:36 +00003450 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003451 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
3452 Object* result = Heap::AllocateFunctionContext(length, function);
3453 if (result->IsFailure()) return result;
3454
3455 Top::set_context(Context::cast(result));
3456
kasper.lund7276f142008-07-30 08:49:36 +00003457 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003458}
3459
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003460static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003461 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003462 Object* js_object = object;
3463 if (!js_object->IsJSObject()) {
3464 js_object = js_object->ToObject();
3465 if (js_object->IsFailure()) {
3466 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003467 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003468 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003469 Handle<Object> result =
3470 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
3471 return Top::Throw(*result);
3472 }
3473 }
3474
3475 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003476 Heap::AllocateWithContext(Top::context(),
3477 JSObject::cast(js_object),
3478 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003479 if (result->IsFailure()) return result;
3480
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003481 Context* context = Context::cast(result);
3482 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003483
kasper.lund7276f142008-07-30 08:49:36 +00003484 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003485}
3486
3487
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003488static Object* Runtime_PushContext(Arguments args) {
3489 NoHandleAllocation ha;
3490 ASSERT(args.length() == 1);
3491 return PushContextHelper(args[0], false);
3492}
3493
3494
3495static Object* Runtime_PushCatchContext(Arguments args) {
3496 NoHandleAllocation ha;
3497 ASSERT(args.length() == 1);
3498 return PushContextHelper(args[0], true);
3499}
3500
3501
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003502static Object* Runtime_LookupContext(Arguments args) {
3503 HandleScope scope;
3504 ASSERT(args.length() == 2);
3505
3506 CONVERT_ARG_CHECKED(Context, context, 0);
3507 CONVERT_ARG_CHECKED(String, name, 1);
3508
3509 int index;
3510 PropertyAttributes attributes;
3511 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003512 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003513 context->Lookup(name, flags, &index, &attributes);
3514
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003515 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003516 ASSERT(holder->IsJSObject());
3517 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003518 }
3519
3520 // No intermediate context found. Use global object by default.
3521 return Top::context()->global();
3522}
3523
3524
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003525// A mechanism to return pairs of Object*'s. This is somewhat
3526// compiler-dependent as it assumes that a 64-bit value (a long long)
3527// is returned via two registers (edx:eax on ia32). Both the ia32 and
3528// arm platform support this; it is mostly an issue of "coaxing" the
3529// compiler to do the right thing.
3530//
3531// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003532typedef uint64_t ObjectPair;
3533static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003534 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003535 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003536}
3537
3538
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003539static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003540 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
3541 USE(attributes);
3542 return x->IsTheHole() ? Heap::undefined_value() : x;
3543}
3544
3545
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003546static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
3547 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003548 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003549 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003550 JSFunction* context_extension_function =
3551 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003552 // If the holder isn't a context extension object, we just return it
3553 // as the receiver. This allows arguments objects to be used as
3554 // receivers, but only if they are put in the context scope chain
3555 // explicitly via a with-statement.
3556 Object* constructor = holder->map()->constructor();
3557 if (constructor != context_extension_function) return holder;
3558 // Fall back to using the global object as the receiver if the
3559 // property turns out to be a local variable allocated in a context
3560 // extension object - introduced via eval.
3561 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003562}
3563
3564
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003565static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003566 HandleScope scope;
3567 ASSERT(args.length() == 2);
3568
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003569 if (!args[0]->IsContext() || !args[1]->IsString()) {
3570 return MakePair(IllegalOperation(), NULL);
3571 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003572 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003573 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003574
3575 int index;
3576 PropertyAttributes attributes;
3577 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003578 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003579 context->Lookup(name, flags, &index, &attributes);
3580
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003581 // If the index is non-negative, the slot has been found in a local
3582 // variable or a parameter. Read it from the context object or the
3583 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003584 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003585 // If the "property" we were looking for is a local variable or an
3586 // argument in a context, the receiver is the global object; see
3587 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
3588 JSObject* receiver = Top::context()->global()->global_receiver();
3589 Object* value = (holder->IsContext())
3590 ? Context::cast(*holder)->get(index)
3591 : JSObject::cast(*holder)->GetElement(index);
3592 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003593 }
3594
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003595 // If the holder is found, we read the property from it.
3596 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00003597 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003598 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003599 JSObject* receiver;
3600 if (object->IsGlobalObject()) {
3601 receiver = GlobalObject::cast(object)->global_receiver();
3602 } else if (context->is_exception_holder(*holder)) {
3603 receiver = Top::context()->global()->global_receiver();
3604 } else {
3605 receiver = ComputeReceiverForNonGlobal(object);
3606 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003607 // No need to unhole the value here. This is taken care of by the
3608 // GetProperty function.
3609 Object* value = object->GetProperty(*name);
3610 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003611 }
3612
3613 if (throw_error) {
3614 // The property doesn't exist - throw exception.
3615 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003616 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003617 return MakePair(Top::Throw(*reference_error), NULL);
3618 } else {
3619 // The property doesn't exist - return undefined
3620 return MakePair(Heap::undefined_value(), Heap::undefined_value());
3621 }
3622}
3623
3624
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003625static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003626 return LoadContextSlotHelper(args, true);
3627}
3628
3629
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003630static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003631 return LoadContextSlotHelper(args, false);
3632}
3633
3634
3635static Object* Runtime_StoreContextSlot(Arguments args) {
3636 HandleScope scope;
3637 ASSERT(args.length() == 3);
3638
3639 Handle<Object> value(args[0]);
3640 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003641 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003642
3643 int index;
3644 PropertyAttributes attributes;
3645 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003646 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003647 context->Lookup(name, flags, &index, &attributes);
3648
3649 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003650 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003651 // Ignore if read_only variable.
3652 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003653 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003654 }
3655 } else {
3656 ASSERT((attributes & READ_ONLY) == 0);
3657 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003658 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003659 USE(result);
3660 ASSERT(!result->IsFailure());
3661 }
3662 return *value;
3663 }
3664
3665 // Slow case: The property is not in a FixedArray context.
3666 // It is either in an JSObject extension context or it was not found.
3667 Handle<JSObject> context_ext;
3668
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003669 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003670 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003671 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003672 } else {
3673 // The property was not found. It needs to be stored in the global context.
3674 ASSERT(attributes == ABSENT);
3675 attributes = NONE;
3676 context_ext = Handle<JSObject>(Top::context()->global());
3677 }
3678
3679 // Set the property, but ignore if read_only variable.
3680 if ((attributes & READ_ONLY) == 0) {
3681 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
3682 if (set.is_null()) {
3683 // Failure::Exception is converted to a null handle in the
3684 // handle-based methods such as SetProperty. We therefore need
3685 // to convert null handles back to exceptions.
3686 ASSERT(Top::has_pending_exception());
3687 return Failure::Exception();
3688 }
3689 }
3690 return *value;
3691}
3692
3693
3694static Object* Runtime_Throw(Arguments args) {
3695 HandleScope scope;
3696 ASSERT(args.length() == 1);
3697
3698 return Top::Throw(args[0]);
3699}
3700
3701
3702static Object* Runtime_ReThrow(Arguments args) {
3703 HandleScope scope;
3704 ASSERT(args.length() == 1);
3705
3706 return Top::ReThrow(args[0]);
3707}
3708
3709
3710static Object* Runtime_ThrowReferenceError(Arguments args) {
3711 HandleScope scope;
3712 ASSERT(args.length() == 1);
3713
3714 Handle<Object> name(args[0]);
3715 Handle<Object> reference_error =
3716 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
3717 return Top::Throw(*reference_error);
3718}
3719
3720
3721static Object* Runtime_StackOverflow(Arguments args) {
3722 NoHandleAllocation na;
3723 return Top::StackOverflow();
3724}
3725
3726
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003727static Object* Runtime_DebugBreak(Arguments args) {
3728 ASSERT(args.length() == 0);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003729 return Execution::DebugBreakHelper();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003730}
3731
3732
3733static Object* Runtime_StackGuard(Arguments args) {
3734 ASSERT(args.length() == 1);
3735
3736 // First check if this is a real stack overflow.
3737 if (StackGuard::IsStackOverflow()) return Runtime_StackOverflow(args);
3738
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003739 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003740}
3741
3742
3743// NOTE: These PrintXXX functions are defined for all builds (not just
3744// DEBUG builds) because we may want to be able to trace function
3745// calls in all modes.
3746static void PrintString(String* str) {
3747 // not uncommon to have empty strings
3748 if (str->length() > 0) {
3749 SmartPointer<char> s =
3750 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
3751 PrintF("%s", *s);
3752 }
3753}
3754
3755
3756static void PrintObject(Object* obj) {
3757 if (obj->IsSmi()) {
3758 PrintF("%d", Smi::cast(obj)->value());
3759 } else if (obj->IsString() || obj->IsSymbol()) {
3760 PrintString(String::cast(obj));
3761 } else if (obj->IsNumber()) {
3762 PrintF("%g", obj->Number());
3763 } else if (obj->IsFailure()) {
3764 PrintF("<failure>");
3765 } else if (obj->IsUndefined()) {
3766 PrintF("<undefined>");
3767 } else if (obj->IsNull()) {
3768 PrintF("<null>");
3769 } else if (obj->IsTrue()) {
3770 PrintF("<true>");
3771 } else if (obj->IsFalse()) {
3772 PrintF("<false>");
3773 } else {
3774 PrintF("%p", obj);
3775 }
3776}
3777
3778
3779static int StackSize() {
3780 int n = 0;
3781 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
3782 return n;
3783}
3784
3785
3786static void PrintTransition(Object* result) {
3787 // indentation
3788 { const int nmax = 80;
3789 int n = StackSize();
3790 if (n <= nmax)
3791 PrintF("%4d:%*s", n, n, "");
3792 else
3793 PrintF("%4d:%*s", n, nmax, "...");
3794 }
3795
3796 if (result == NULL) {
3797 // constructor calls
3798 JavaScriptFrameIterator it;
3799 JavaScriptFrame* frame = it.frame();
3800 if (frame->IsConstructor()) PrintF("new ");
3801 // function name
3802 Object* fun = frame->function();
3803 if (fun->IsJSFunction()) {
3804 PrintObject(JSFunction::cast(fun)->shared()->name());
3805 } else {
3806 PrintObject(fun);
3807 }
3808 // function arguments
3809 // (we are intentionally only printing the actually
3810 // supplied parameters, not all parameters required)
3811 PrintF("(this=");
3812 PrintObject(frame->receiver());
3813 const int length = frame->GetProvidedParametersCount();
3814 for (int i = 0; i < length; i++) {
3815 PrintF(", ");
3816 PrintObject(frame->GetParameter(i));
3817 }
3818 PrintF(") {\n");
3819
3820 } else {
3821 // function result
3822 PrintF("} -> ");
3823 PrintObject(result);
3824 PrintF("\n");
3825 }
3826}
3827
3828
3829static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003830 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003831 NoHandleAllocation ha;
3832 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003833 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003834}
3835
3836
3837static Object* Runtime_TraceExit(Arguments args) {
3838 NoHandleAllocation ha;
3839 PrintTransition(args[0]);
3840 return args[0]; // return TOS
3841}
3842
3843
3844static Object* Runtime_DebugPrint(Arguments args) {
3845 NoHandleAllocation ha;
3846 ASSERT(args.length() == 1);
3847
3848#ifdef DEBUG
3849 if (args[0]->IsString()) {
3850 // If we have a string, assume it's a code "marker"
3851 // and print some interesting cpu debugging info.
3852 JavaScriptFrameIterator it;
3853 JavaScriptFrame* frame = it.frame();
3854 PrintF("fp = %p, sp = %p, pp = %p: ",
3855 frame->fp(), frame->sp(), frame->pp());
3856 } else {
3857 PrintF("DebugPrint: ");
3858 }
3859 args[0]->Print();
3860#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003861 // ShortPrint is available in release mode. Print is not.
3862 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003863#endif
3864 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00003865 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003866
3867 return args[0]; // return TOS
3868}
3869
3870
3871static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003872 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003873 NoHandleAllocation ha;
3874 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003875 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003876}
3877
3878
mads.s.ager31e71382008-08-13 09:32:07 +00003879static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003880 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003881 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003882
3883 // According to ECMA-262, section 15.9.1, page 117, the precision of
3884 // the number in a Date object representing a particular instant in
3885 // time is milliseconds. Therefore, we floor the result of getting
3886 // the OS time.
3887 double millis = floor(OS::TimeCurrentMillis());
3888 return Heap::NumberFromDouble(millis);
3889}
3890
3891
3892static Object* Runtime_DateParseString(Arguments args) {
3893 HandleScope scope;
3894 ASSERT(args.length() == 1);
3895
3896 CONVERT_CHECKED(String, string_object, args[0]);
3897
3898 Handle<String> str(string_object);
3899 Handle<FixedArray> output = Factory::NewFixedArray(DateParser::OUTPUT_SIZE);
3900 if (DateParser::Parse(*str, *output)) {
3901 return *Factory::NewJSArrayWithElements(output);
3902 } else {
3903 return *Factory::null_value();
3904 }
3905}
3906
3907
3908static Object* Runtime_DateLocalTimezone(Arguments args) {
3909 NoHandleAllocation ha;
3910 ASSERT(args.length() == 1);
3911
3912 CONVERT_DOUBLE_CHECKED(x, args[0]);
3913 char* zone = OS::LocalTimezone(x);
3914 return Heap::AllocateStringFromUtf8(CStrVector(zone));
3915}
3916
3917
3918static Object* Runtime_DateLocalTimeOffset(Arguments args) {
3919 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003920 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003921
3922 return Heap::NumberFromDouble(OS::LocalTimeOffset());
3923}
3924
3925
3926static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
3927 NoHandleAllocation ha;
3928 ASSERT(args.length() == 1);
3929
3930 CONVERT_DOUBLE_CHECKED(x, args[0]);
3931 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
3932}
3933
3934
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003935static Object* Runtime_NumberIsFinite(Arguments args) {
3936 NoHandleAllocation ha;
3937 ASSERT(args.length() == 1);
3938
3939 CONVERT_DOUBLE_CHECKED(value, args[0]);
3940 Object* result;
3941 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
3942 result = Heap::false_value();
3943 } else {
3944 result = Heap::true_value();
3945 }
3946 return result;
3947}
3948
3949
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003950static Object* Runtime_GlobalReceiver(Arguments args) {
3951 ASSERT(args.length() == 1);
3952 Object* global = args[0];
3953 if (!global->IsJSGlobalObject()) return Heap::null_value();
3954 return JSGlobalObject::cast(global)->global_receiver();
3955}
3956
3957
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003958static Object* Runtime_CompileString(Arguments args) {
3959 HandleScope scope;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003960 ASSERT(args.length() == 2);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003961 CONVERT_ARG_CHECKED(String, source, 0);
ager@chromium.org236ad962008-09-25 09:45:57 +00003962 CONVERT_ARG_CHECKED(Smi, line_offset, 1);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003963
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003964 // Compile source string.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003965 Handle<JSFunction> boilerplate =
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003966 Compiler::CompileEval(source, line_offset->value(), true);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003967 if (boilerplate.is_null()) return Failure::Exception();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003968 Handle<Context> context(Top::context()->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003969 Handle<JSFunction> fun =
3970 Factory::NewFunctionFromBoilerplate(boilerplate, context);
3971 return *fun;
3972}
3973
3974
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003975static Handle<JSFunction> GetBuiltinFunction(String* name) {
3976 LookupResult result;
3977 Top::global_context()->builtins()->LocalLookup(name, &result);
3978 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
3979}
3980
3981
3982static Object* CompileDirectEval(Handle<String> source) {
3983 // Compute the eval context.
3984 HandleScope scope;
3985 StackFrameLocator locator;
3986 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
3987 Handle<Context> context(Context::cast(frame->context()));
3988 bool is_global = context->IsGlobalContext();
3989
3990 // Compile source string.
3991 Handle<JSFunction> boilerplate = Compiler::CompileEval(source, 0, is_global);
3992 if (boilerplate.is_null()) return Failure::Exception();
3993 Handle<JSFunction> fun =
3994 Factory::NewFunctionFromBoilerplate(boilerplate, context);
3995 return *fun;
3996}
3997
3998
3999static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
4000 ASSERT(args.length() == 2);
4001
4002 HandleScope scope;
4003
4004 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
4005
4006 Handle<Object> receiver;
4007
4008 // Find where the 'eval' symbol is bound. It is unaliased only if
4009 // it is bound in the global context.
4010 StackFrameLocator locator;
4011 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
4012 Handle<Context> context(Context::cast(frame->context()));
4013 int index;
4014 PropertyAttributes attributes;
4015 while (!context.is_null()) {
4016 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
4017 &index, &attributes);
4018 if (attributes != ABSENT) break;
4019 if (context->is_function_context()) {
4020 context = Handle<Context>(Context::cast(context->closure()->context()));
4021 } else {
4022 context = Handle<Context>(context->previous());
4023 }
4024 }
4025
4026 if (context->IsGlobalContext()) {
4027 // 'eval' is bound in the global context, but it may have been overwritten.
4028 // Compare it to the builtin 'GlobalEval' function to make sure.
4029 Handle<JSFunction> global_eval =
4030 GetBuiltinFunction(Heap::global_eval_symbol());
4031 if (global_eval.is_identical_to(callee)) {
4032 // A direct eval call.
4033 if (args[1]->IsString()) {
4034 CONVERT_ARG_CHECKED(String, source, 1);
4035 // A normal eval call on a string. Compile it and return the
4036 // compiled function bound in the local context.
4037 Object* compiled_source = CompileDirectEval(source);
4038 if (compiled_source->IsFailure()) return compiled_source;
4039 receiver = Handle<Object>(frame->receiver());
4040 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
4041 } else {
4042 // An eval call that is not called on a string. Global eval
4043 // deals better with this.
4044 receiver = Handle<Object>(Top::global_context()->global());
4045 }
4046 } else {
4047 // 'eval' is overwritten. Just call the function with the given arguments.
4048 receiver = Handle<Object>(Top::global_context()->global());
4049 }
4050 } else {
4051 // 'eval' is not bound in the global context. Just call the function
4052 // with the given arguments. This is not necessarily the global eval.
4053 if (receiver->IsContext()) {
4054 context = Handle<Context>::cast(receiver);
4055 receiver = Handle<Object>(context->get(index));
4056 }
4057 }
4058
4059 Handle<FixedArray> call = Factory::NewFixedArray(2);
4060 call->set(0, *callee);
4061 call->set(1, *receiver);
4062 return *call;
4063}
4064
4065
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004066static Object* Runtime_CompileScript(Arguments args) {
4067 HandleScope scope;
4068 ASSERT(args.length() == 4);
4069
4070 CONVERT_ARG_CHECKED(String, source, 0);
4071 CONVERT_ARG_CHECKED(String, script, 1);
4072 CONVERT_CHECKED(Smi, line_attrs, args[2]);
4073 int line = line_attrs->value();
4074 CONVERT_CHECKED(Smi, col_attrs, args[3]);
4075 int col = col_attrs->value();
4076 Handle<JSFunction> boilerplate =
4077 Compiler::Compile(source, script, line, col, NULL, NULL);
4078 if (boilerplate.is_null()) return Failure::Exception();
4079 Handle<JSFunction> fun =
4080 Factory::NewFunctionFromBoilerplate(boilerplate,
4081 Handle<Context>(Top::context()));
4082 return *fun;
4083}
4084
4085
4086static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
4087 // This utility adjusts the property attributes for newly created Function
4088 // object ("new Function(...)") by changing the map.
4089 // All it does is changing the prototype property to enumerable
4090 // as specified in ECMA262, 15.3.5.2.
4091 HandleScope scope;
4092 ASSERT(args.length() == 1);
4093 CONVERT_ARG_CHECKED(JSFunction, func, 0);
4094 ASSERT(func->map()->instance_type() ==
4095 Top::function_instance_map()->instance_type());
4096 ASSERT(func->map()->instance_size() ==
4097 Top::function_instance_map()->instance_size());
4098 func->set_map(*Top::function_instance_map());
4099 return *func;
4100}
4101
4102
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004103// Push an array unto an array of arrays if it is not already in the
4104// array. Returns true if the element was pushed on the stack and
4105// false otherwise.
4106static Object* Runtime_PushIfAbsent(Arguments args) {
4107 ASSERT(args.length() == 2);
4108 CONVERT_CHECKED(JSArray, array, args[0]);
4109 CONVERT_CHECKED(JSArray, element, args[1]);
4110 RUNTIME_ASSERT(array->HasFastElements());
4111 int length = Smi::cast(array->length())->value();
4112 FixedArray* elements = FixedArray::cast(array->elements());
4113 for (int i = 0; i < length; i++) {
4114 if (elements->get(i) == element) return Heap::false_value();
4115 }
4116 Object* obj = array->SetFastElement(length, element);
4117 if (obj->IsFailure()) return obj;
4118 return Heap::true_value();
4119}
4120
4121
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004122/**
4123 * A simple visitor visits every element of Array's.
4124 * The backend storage can be a fixed array for fast elements case,
4125 * or a dictionary for sparse array. Since Dictionary is a subtype
4126 * of FixedArray, the class can be used by both fast and slow cases.
4127 * The second parameter of the constructor, fast_elements, specifies
4128 * whether the storage is a FixedArray or Dictionary.
4129 *
4130 * An index limit is used to deal with the situation that a result array
4131 * length overflows 32-bit non-negative integer.
4132 */
4133class ArrayConcatVisitor {
4134 public:
4135 ArrayConcatVisitor(Handle<FixedArray> storage,
4136 uint32_t index_limit,
4137 bool fast_elements) :
4138 storage_(storage), index_limit_(index_limit),
4139 fast_elements_(fast_elements), index_offset_(0) { }
4140
4141 void visit(uint32_t i, Handle<Object> elm) {
4142 uint32_t index = i + index_offset_;
4143 if (index >= index_limit_) return;
4144
4145 if (fast_elements_) {
4146 ASSERT(index < static_cast<uint32_t>(storage_->length()));
4147 storage_->set(index, *elm);
4148
4149 } else {
4150 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
4151 Handle<Dictionary> result =
4152 Factory::DictionaryAtNumberPut(dict, index, elm);
4153 if (!result.is_identical_to(dict))
4154 storage_ = result;
4155 }
4156 }
4157
4158 void increase_index_offset(uint32_t delta) {
4159 index_offset_ += delta;
4160 }
4161
4162 private:
4163 Handle<FixedArray> storage_;
4164 uint32_t index_limit_;
4165 bool fast_elements_;
4166 uint32_t index_offset_;
4167};
4168
4169
4170/**
4171 * A helper function that visits elements of a JSObject. Only elements
4172 * whose index between 0 and range (exclusive) are visited.
4173 *
4174 * If the third parameter, visitor, is not NULL, the visitor is called
4175 * with parameters, 'visitor_index_offset + element index' and the element.
4176 *
4177 * It returns the number of visisted elements.
4178 */
4179static uint32_t IterateElements(Handle<JSObject> receiver,
4180 uint32_t range,
4181 ArrayConcatVisitor* visitor) {
4182 uint32_t num_of_elements = 0;
4183
4184 if (receiver->HasFastElements()) {
4185 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
4186 uint32_t len = elements->length();
4187 if (range < len) len = range;
4188
4189 for (uint32_t j = 0; j < len; j++) {
4190 Handle<Object> e(elements->get(j));
4191 if (!e->IsTheHole()) {
4192 num_of_elements++;
4193 if (visitor)
4194 visitor->visit(j, e);
4195 }
4196 }
4197
4198 } else {
4199 Handle<Dictionary> dict(receiver->element_dictionary());
4200 uint32_t capacity = dict->Capacity();
4201 for (uint32_t j = 0; j < capacity; j++) {
4202 Handle<Object> k(dict->KeyAt(j));
4203 if (dict->IsKey(*k)) {
4204 ASSERT(k->IsNumber());
4205 uint32_t index = static_cast<uint32_t>(k->Number());
4206 if (index < range) {
4207 num_of_elements++;
4208 if (visitor) {
4209 visitor->visit(index,
4210 Handle<Object>(dict->ValueAt(j)));
4211 }
4212 }
4213 }
4214 }
4215 }
4216
4217 return num_of_elements;
4218}
4219
4220
4221/**
4222 * A helper function that visits elements of an Array object, and elements
4223 * on its prototypes.
4224 *
4225 * Elements on prototypes are visited first, and only elements whose indices
4226 * less than Array length are visited.
4227 *
4228 * If a ArrayConcatVisitor object is given, the visitor is called with
4229 * parameters, element's index + visitor_index_offset and the element.
4230 */
4231static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
4232 ArrayConcatVisitor* visitor) {
4233 uint32_t range = static_cast<uint32_t>(array->length()->Number());
4234 Handle<Object> obj = array;
4235
4236 static const int kEstimatedPrototypes = 3;
4237 List< Handle<JSObject> > objects(kEstimatedPrototypes);
4238
4239 // Visit prototype first. If an element on the prototype is shadowed by
4240 // the inheritor using the same index, the ArrayConcatVisitor visits
4241 // the prototype element before the shadowing element.
4242 // The visitor can simply overwrite the old value by new value using
4243 // the same index. This follows Array::concat semantics.
4244 while (!obj->IsNull()) {
4245 objects.Add(Handle<JSObject>::cast(obj));
4246 obj = Handle<Object>(obj->GetPrototype());
4247 }
4248
4249 uint32_t nof_elements = 0;
4250 for (int i = objects.length() - 1; i >= 0; i--) {
4251 Handle<JSObject> obj = objects[i];
4252 nof_elements +=
4253 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
4254 }
4255
4256 return nof_elements;
4257}
4258
4259
4260/**
4261 * A helper function of Runtime_ArrayConcat.
4262 *
4263 * The first argument is an Array of arrays and objects. It is the
4264 * same as the arguments array of Array::concat JS function.
4265 *
4266 * If an argument is an Array object, the function visits array
4267 * elements. If an argument is not an Array object, the function
4268 * visits the object as if it is an one-element array.
4269 *
4270 * If the result array index overflows 32-bit integer, the rounded
4271 * non-negative number is used as new length. For example, if one
4272 * array length is 2^32 - 1, second array length is 1, the
4273 * concatenated array length is 0.
4274 */
4275static uint32_t IterateArguments(Handle<JSArray> arguments,
4276 ArrayConcatVisitor* visitor) {
4277 uint32_t visited_elements = 0;
4278 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
4279
4280 for (uint32_t i = 0; i < num_of_args; i++) {
4281 Handle<Object> obj(arguments->GetElement(i));
4282 if (obj->IsJSArray()) {
4283 Handle<JSArray> array = Handle<JSArray>::cast(obj);
4284 uint32_t len = static_cast<uint32_t>(array->length()->Number());
4285 uint32_t nof_elements =
4286 IterateArrayAndPrototypeElements(array, visitor);
4287 // Total elements of array and its prototype chain can be more than
4288 // the array length, but ArrayConcat can only concatenate at most
4289 // the array length number of elements.
4290 visited_elements += (nof_elements > len) ? len : nof_elements;
4291 if (visitor) visitor->increase_index_offset(len);
4292
4293 } else {
4294 if (visitor) {
4295 visitor->visit(0, obj);
4296 visitor->increase_index_offset(1);
4297 }
4298 visited_elements++;
4299 }
4300 }
4301 return visited_elements;
4302}
4303
4304
4305/**
4306 * Array::concat implementation.
4307 * See ECMAScript 262, 15.4.4.4.
4308 */
4309static Object* Runtime_ArrayConcat(Arguments args) {
4310 ASSERT(args.length() == 1);
4311 HandleScope handle_scope;
4312
4313 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
4314 Handle<JSArray> arguments(arg_arrays);
4315
4316 // Pass 1: estimate the number of elements of the result
4317 // (it could be more than real numbers if prototype has elements).
4318 uint32_t result_length = 0;
4319 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
4320
4321 { AssertNoAllocation nogc;
4322 for (uint32_t i = 0; i < num_of_args; i++) {
4323 Object* obj = arguments->GetElement(i);
4324 if (obj->IsJSArray()) {
4325 result_length +=
4326 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
4327 } else {
4328 result_length++;
4329 }
4330 }
4331 }
4332
4333 // Allocate an empty array, will set length and content later.
4334 Handle<JSArray> result = Factory::NewJSArray(0);
4335
4336 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
4337 // If estimated number of elements is more than half of length, a
4338 // fixed array (fast case) is more time and space-efficient than a
4339 // dictionary.
4340 bool fast_case = (estimate_nof_elements * 2) >= result_length;
4341
4342 Handle<FixedArray> storage;
4343 if (fast_case) {
4344 // The backing storage array must have non-existing elements to
4345 // preserve holes across concat operations.
4346 storage = Factory::NewFixedArrayWithHoles(result_length);
4347
4348 } else {
4349 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
4350 uint32_t at_least_space_for = estimate_nof_elements +
4351 (estimate_nof_elements >> 2);
4352 storage = Handle<FixedArray>::cast(
4353 Factory::NewDictionary(at_least_space_for));
4354 }
4355
4356 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
4357
4358 ArrayConcatVisitor visitor(storage, result_length, fast_case);
4359
4360 IterateArguments(arguments, &visitor);
4361
4362 result->set_length(*len);
4363 result->set_elements(*storage);
4364
4365 return *result;
4366}
4367
4368
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004369// This will not allocate (flatten the string), but it may run
4370// very slowly for very deeply nested ConsStrings. For debugging use only.
4371static Object* Runtime_GlobalPrint(Arguments args) {
4372 NoHandleAllocation ha;
4373 ASSERT(args.length() == 1);
4374
4375 CONVERT_CHECKED(String, string, args[0]);
4376 StringInputBuffer buffer(string);
4377 while (buffer.has_more()) {
4378 uint16_t character = buffer.GetNext();
4379 PrintF("%c", character);
4380 }
4381 return string;
4382}
4383
4384
4385static Object* Runtime_RemoveArrayHoles(Arguments args) {
4386 ASSERT(args.length() == 1);
4387 // Ignore the case if this is not a JSArray.
4388 if (!args[0]->IsJSArray()) return args[0];
4389 return JSArray::cast(args[0])->RemoveHoles();
4390}
4391
4392
4393// Move contents of argument 0 (an array) to argument 1 (an array)
4394static Object* Runtime_MoveArrayContents(Arguments args) {
4395 ASSERT(args.length() == 2);
4396 CONVERT_CHECKED(JSArray, from, args[0]);
4397 CONVERT_CHECKED(JSArray, to, args[1]);
4398 to->SetContent(FixedArray::cast(from->elements()));
4399 to->set_length(from->length());
4400 from->SetContent(Heap::empty_fixed_array());
4401 from->set_length(0);
4402 return to;
4403}
4404
4405
4406// How many elements does this array have?
4407static Object* Runtime_EstimateNumberOfElements(Arguments args) {
4408 ASSERT(args.length() == 1);
4409 CONVERT_CHECKED(JSArray, array, args[0]);
4410 HeapObject* elements = array->elements();
4411 if (elements->IsDictionary()) {
4412 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
4413 } else {
4414 return array->length();
4415 }
4416}
4417
4418
4419// Returns an array that tells you where in the [0, length) interval an array
4420// might have elements. Can either return keys or intervals. Keys can have
4421// gaps in (undefined). Intervals can also span over some undefined keys.
4422static Object* Runtime_GetArrayKeys(Arguments args) {
4423 ASSERT(args.length() == 2);
4424 HandleScope scope;
4425 CONVERT_CHECKED(JSArray, raw_array, args[0]);
4426 Handle<JSArray> array(raw_array);
4427 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004428 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004429 // Create an array and get all the keys into it, then remove all the
4430 // keys that are not integers in the range 0 to length-1.
4431 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
4432 int keys_length = keys->length();
4433 for (int i = 0; i < keys_length; i++) {
4434 Object* key = keys->get(i);
4435 uint32_t index;
4436 if (!Array::IndexFromObject(key, &index) || index >= length) {
4437 // Zap invalid keys.
4438 keys->set_undefined(i);
4439 }
4440 }
4441 return *Factory::NewJSArrayWithElements(keys);
4442 } else {
4443 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
4444 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004445 single_interval->set(0,
4446 Smi::FromInt(-1),
4447 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004448 Handle<Object> length_object =
4449 Factory::NewNumber(static_cast<double>(length));
4450 single_interval->set(1, *length_object);
4451 return *Factory::NewJSArrayWithElements(single_interval);
4452 }
4453}
4454
4455
4456// DefineAccessor takes an optional final argument which is the
4457// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
4458// to the way accessors are implemented, it is set for both the getter
4459// and setter on the first call to DefineAccessor and ignored on
4460// subsequent calls.
4461static Object* Runtime_DefineAccessor(Arguments args) {
4462 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
4463 // Compute attributes.
4464 PropertyAttributes attributes = NONE;
4465 if (args.length() == 5) {
4466 CONVERT_CHECKED(Smi, attrs, args[4]);
4467 int value = attrs->value();
4468 // Only attribute bits should be set.
4469 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4470 attributes = static_cast<PropertyAttributes>(value);
4471 }
4472
4473 CONVERT_CHECKED(JSObject, obj, args[0]);
4474 CONVERT_CHECKED(String, name, args[1]);
4475 CONVERT_CHECKED(Smi, flag, args[2]);
4476 CONVERT_CHECKED(JSFunction, fun, args[3]);
4477 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
4478}
4479
4480
4481static Object* Runtime_LookupAccessor(Arguments args) {
4482 ASSERT(args.length() == 3);
4483 CONVERT_CHECKED(JSObject, obj, args[0]);
4484 CONVERT_CHECKED(String, name, args[1]);
4485 CONVERT_CHECKED(Smi, flag, args[2]);
4486 return obj->LookupAccessor(name, flag->value() == 0);
4487}
4488
4489
4490// Helper functions for wrapping and unwrapping stack frame ids.
4491static Smi* WrapFrameId(StackFrame::Id id) {
4492 ASSERT(IsAligned(OffsetFrom(id), 4));
4493 return Smi::FromInt(id >> 2);
4494}
4495
4496
4497static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
4498 return static_cast<StackFrame::Id>(wrapped->value() << 2);
4499}
4500
4501
4502// Adds a JavaScript function as a debug event listener.
4503// args[0]: debug event listener function
4504// args[1]: object supplied during callback
4505static Object* Runtime_AddDebugEventListener(Arguments args) {
4506 ASSERT(args.length() == 2);
4507 // Convert the parameters to API objects to call the API function for adding
4508 // a JavaScript function as debug event listener.
4509 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
4510 v8::Handle<v8::Function> fun(ToApi<v8::Function>(raw_fun));
4511 v8::Handle<v8::Value> data(ToApi<v8::Value>(args.at<Object>(0)));
4512 v8::Debug::AddDebugEventListener(fun, data);
4513
4514 return Heap::undefined_value();
4515}
4516
4517
4518// Removes a JavaScript function debug event listener.
4519// args[0]: debug event listener function
4520static Object* Runtime_RemoveDebugEventListener(Arguments args) {
4521 ASSERT(args.length() == 1);
4522 // Convert the parameter to an API object to call the API function for
4523 // removing a JavaScript function debug event listener.
4524 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
4525 v8::Handle<v8::Function> fun(ToApi<v8::Function>(raw_fun));
4526 v8::Debug::RemoveDebugEventListener(fun);
4527
4528 return Heap::undefined_value();
4529}
4530
4531
4532static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00004533 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004534 StackGuard::DebugBreak();
4535 return Heap::undefined_value();
4536}
4537
4538
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00004539static Object* DebugLookupResultValue(Object* receiver, LookupResult* result,
ager@chromium.org32912102009-01-16 10:38:43 +00004540 bool* caught_exception) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00004541 Object* value;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004542 switch (result->type()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00004543 case NORMAL: {
4544 Dictionary* dict =
4545 JSObject::cast(result->holder())->property_dictionary();
4546 value = dict->ValueAt(result->GetDictionaryEntry());
4547 if (value->IsTheHole()) {
4548 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004549 }
4550 return value;
4551 }
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00004552 case FIELD:
4553 value =
4554 JSObject::cast(
4555 result->holder())->FastPropertyAt(result->GetFieldIndex());
4556 if (value->IsTheHole()) {
4557 return Heap::undefined_value();
4558 }
4559 return value;
4560 case CONSTANT_FUNCTION:
4561 return result->GetConstantFunction();
4562 case CALLBACKS: {
4563 Object* structure = result->GetCallbackObject();
4564 if (structure->IsProxy()) {
4565 AccessorDescriptor* callback =
4566 reinterpret_cast<AccessorDescriptor*>(
4567 Proxy::cast(structure)->proxy());
4568 value = (callback->getter)(receiver, callback->data);
4569 if (value->IsFailure()) {
4570 value = Top::pending_exception();
4571 Top::clear_pending_exception();
4572 if (caught_exception != NULL) {
4573 *caught_exception = true;
4574 }
4575 }
4576 return value;
4577 } else {
4578 return Heap::undefined_value();
4579 }
4580 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004581 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004582 case MAP_TRANSITION:
4583 case CONSTANT_TRANSITION:
4584 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004585 return Heap::undefined_value();
4586 default:
4587 UNREACHABLE();
4588 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004589 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004590 return Heap::undefined_value();
4591}
4592
4593
ager@chromium.org32912102009-01-16 10:38:43 +00004594// Get debugger related details for an object property.
4595// args[0]: object holding property
4596// args[1]: name of the property
4597//
4598// The array returned contains the following information:
4599// 0: Property value
4600// 1: Property details
4601// 2: Property value is exception
4602// 3: Getter function if defined
4603// 4: Setter function if defined
4604// Items 2-4 are only filled if the property has either a getter or a setter
4605// defined through __defineGetter__ and/or __defineSetter__.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004606static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004607 HandleScope scope;
4608
4609 ASSERT(args.length() == 2);
4610
4611 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4612 CONVERT_ARG_CHECKED(String, name, 1);
4613
4614 // Check if the name is trivially convertible to an index and get the element
4615 // if so.
4616 uint32_t index;
4617 if (name->AsArrayIndex(&index)) {
4618 Handle<FixedArray> details = Factory::NewFixedArray(2);
4619 details->set(0, Runtime::GetElementOrCharAt(obj, index));
4620 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
4621 return *Factory::NewJSArrayWithElements(details);
4622 }
4623
4624 // Perform standard local lookup on the object.
4625 LookupResult result;
ager@chromium.org32912102009-01-16 10:38:43 +00004626 obj->LocalLookup(*name, &result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004627 if (result.IsProperty()) {
ager@chromium.org32912102009-01-16 10:38:43 +00004628 bool caught_exception = false;
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00004629 Handle<Object> value(DebugLookupResultValue(*obj, &result,
ager@chromium.org32912102009-01-16 10:38:43 +00004630 &caught_exception));
4631 // If the callback object is a fixed array then it contains JavaScript
4632 // getter and/or setter.
4633 bool hasJavaScriptAccessors = result.type() == CALLBACKS &&
4634 result.GetCallbackObject()->IsFixedArray();
4635 Handle<FixedArray> details =
4636 Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004637 details->set(0, *value);
4638 details->set(1, result.GetPropertyDetails().AsSmi());
ager@chromium.org32912102009-01-16 10:38:43 +00004639 if (hasJavaScriptAccessors) {
4640 details->set(2,
4641 caught_exception ? Heap::true_value() : Heap::false_value());
4642 details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
4643 details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
4644 }
4645
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004646 return *Factory::NewJSArrayWithElements(details);
4647 }
4648 return Heap::undefined_value();
4649}
4650
4651
4652static Object* Runtime_DebugGetProperty(Arguments args) {
4653 HandleScope scope;
4654
4655 ASSERT(args.length() == 2);
4656
4657 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4658 CONVERT_ARG_CHECKED(String, name, 1);
4659
4660 LookupResult result;
4661 obj->Lookup(*name, &result);
4662 if (result.IsProperty()) {
sgjesse@chromium.org715915b2009-01-19 16:08:47 +00004663 return DebugLookupResultValue(*obj, &result, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004664 }
4665 return Heap::undefined_value();
4666}
4667
4668
4669// Return the names of the local named properties.
4670// args[0]: object
4671static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
4672 HandleScope scope;
4673 ASSERT(args.length() == 1);
4674 if (!args[0]->IsJSObject()) {
4675 return Heap::undefined_value();
4676 }
4677 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4678
4679 int n = obj->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4680 Handle<FixedArray> names = Factory::NewFixedArray(n);
4681 obj->GetLocalPropertyNames(*names);
4682 return *Factory::NewJSArrayWithElements(names);
4683}
4684
4685
4686// Return the names of the local indexed properties.
4687// args[0]: object
4688static Object* Runtime_DebugLocalElementNames(Arguments args) {
4689 HandleScope scope;
4690 ASSERT(args.length() == 1);
4691 if (!args[0]->IsJSObject()) {
4692 return Heap::undefined_value();
4693 }
4694 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4695
4696 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4697 Handle<FixedArray> names = Factory::NewFixedArray(n);
4698 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4699 return *Factory::NewJSArrayWithElements(names);
4700}
4701
4702
4703// Return the property type calculated from the property details.
4704// args[0]: smi with property details.
4705static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
4706 ASSERT(args.length() == 1);
4707 CONVERT_CHECKED(Smi, details, args[0]);
4708 PropertyType type = PropertyDetails(details).type();
4709 return Smi::FromInt(static_cast<int>(type));
4710}
4711
4712
4713// Return the property attribute calculated from the property details.
4714// args[0]: smi with property details.
4715static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
4716 ASSERT(args.length() == 1);
4717 CONVERT_CHECKED(Smi, details, args[0]);
4718 PropertyAttributes attributes = PropertyDetails(details).attributes();
4719 return Smi::FromInt(static_cast<int>(attributes));
4720}
4721
4722
4723// Return the property insertion index calculated from the property details.
4724// args[0]: smi with property details.
4725static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
4726 ASSERT(args.length() == 1);
4727 CONVERT_CHECKED(Smi, details, args[0]);
4728 int index = PropertyDetails(details).index();
4729 return Smi::FromInt(index);
4730}
4731
4732
4733// Return information on whether an object has a named or indexed interceptor.
4734// args[0]: object
4735static Object* Runtime_DebugInterceptorInfo(Arguments args) {
4736 HandleScope scope;
4737 ASSERT(args.length() == 1);
4738 if (!args[0]->IsJSObject()) {
4739 return Smi::FromInt(0);
4740 }
4741 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4742
4743 int result = 0;
4744 if (obj->HasNamedInterceptor()) result |= 2;
4745 if (obj->HasIndexedInterceptor()) result |= 1;
4746
4747 return Smi::FromInt(result);
4748}
4749
4750
4751// Return property names from named interceptor.
4752// args[0]: object
4753static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
4754 HandleScope scope;
4755 ASSERT(args.length() == 1);
4756 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004757
ager@chromium.org32912102009-01-16 10:38:43 +00004758 if (obj->HasNamedInterceptor()) {
4759 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4760 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4761 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004762 return Heap::undefined_value();
4763}
4764
4765
4766// Return element names from indexed interceptor.
4767// args[0]: object
4768static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
4769 HandleScope scope;
4770 ASSERT(args.length() == 1);
4771 CONVERT_ARG_CHECKED(JSObject, obj, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004772
ager@chromium.org32912102009-01-16 10:38:43 +00004773 if (obj->HasIndexedInterceptor()) {
4774 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4775 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4776 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004777 return Heap::undefined_value();
4778}
4779
4780
4781// Return property value from named interceptor.
4782// args[0]: object
4783// args[1]: property name
4784static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
4785 HandleScope scope;
4786 ASSERT(args.length() == 2);
4787 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4788 RUNTIME_ASSERT(obj->HasNamedInterceptor());
4789 CONVERT_ARG_CHECKED(String, name, 1);
4790
4791 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004792 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004793}
4794
4795
4796// Return element value from indexed interceptor.
4797// args[0]: object
4798// args[1]: index
4799static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
4800 HandleScope scope;
4801 ASSERT(args.length() == 2);
4802 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4803 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
4804 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
4805
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004806 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004807}
4808
4809
4810static Object* Runtime_CheckExecutionState(Arguments args) {
4811 ASSERT(args.length() >= 1);
4812 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00004813 // Check that the break id is valid.
4814 if (Top::break_id() == 0 || break_id != Top::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004815 return Top::Throw(Heap::illegal_execution_state_symbol());
4816 }
4817
4818 return Heap::true_value();
4819}
4820
4821
4822static Object* Runtime_GetFrameCount(Arguments args) {
4823 HandleScope scope;
4824 ASSERT(args.length() == 1);
4825
4826 // Check arguments.
4827 Object* result = Runtime_CheckExecutionState(args);
4828 if (result->IsFailure()) return result;
4829
4830 // Count all frames which are relevant to debugging stack trace.
4831 int n = 0;
4832 StackFrame::Id id = Top::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00004833 if (id == StackFrame::NO_ID) {
4834 // If there is no JavaScript stack frame count is 0.
4835 return Smi::FromInt(0);
4836 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004837 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
4838 return Smi::FromInt(n);
4839}
4840
4841
4842static const int kFrameDetailsFrameIdIndex = 0;
4843static const int kFrameDetailsReceiverIndex = 1;
4844static const int kFrameDetailsFunctionIndex = 2;
4845static const int kFrameDetailsArgumentCountIndex = 3;
4846static const int kFrameDetailsLocalCountIndex = 4;
4847static const int kFrameDetailsSourcePositionIndex = 5;
4848static const int kFrameDetailsConstructCallIndex = 6;
4849static const int kFrameDetailsDebuggerFrameIndex = 7;
4850static const int kFrameDetailsFirstDynamicIndex = 8;
4851
4852// Return an array with frame details
4853// args[0]: number: break id
4854// args[1]: number: frame index
4855//
4856// The array returned contains the following information:
4857// 0: Frame id
4858// 1: Receiver
4859// 2: Function
4860// 3: Argument count
4861// 4: Local count
4862// 5: Source position
4863// 6: Constructor call
4864// 7: Debugger frame
4865// Arguments name, value
4866// Locals name, value
4867static Object* Runtime_GetFrameDetails(Arguments args) {
4868 HandleScope scope;
4869 ASSERT(args.length() == 2);
4870
4871 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004872 Object* check = Runtime_CheckExecutionState(args);
4873 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004874 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
4875
4876 // Find the relevant frame with the requested index.
4877 StackFrame::Id id = Top::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00004878 if (id == StackFrame::NO_ID) {
4879 // If there are no JavaScript stack frames return undefined.
4880 return Heap::undefined_value();
4881 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004882 int count = 0;
4883 JavaScriptFrameIterator it(id);
4884 for (; !it.done(); it.Advance()) {
4885 if (count == index) break;
4886 count++;
4887 }
4888 if (it.done()) return Heap::undefined_value();
4889
4890 // Traverse the saved contexts chain to find the active context for the
4891 // selected frame.
4892 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004893 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004894 save = save->prev();
4895 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004896 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004897
4898 // Get the frame id.
4899 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
4900
4901 // Find source position.
4902 int position = it.frame()->FindCode()->SourcePosition(it.frame()->pc());
4903
4904 // Check for constructor frame.
4905 bool constructor = it.frame()->IsConstructor();
4906
4907 // Get code and read scope info from it for local variable information.
4908 Handle<Code> code(it.frame()->FindCode());
4909 ScopeInfo<> info(*code);
4910
4911 // Get the context.
4912 Handle<Context> context(Context::cast(it.frame()->context()));
4913
4914 // Get the locals names and values into a temporary array.
4915 //
4916 // TODO(1240907): Hide compiler-introduced stack variables
4917 // (e.g. .result)? For users of the debugger, they will probably be
4918 // confusing.
4919 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
4920 for (int i = 0; i < info.NumberOfLocals(); i++) {
4921 // Name of the local.
4922 locals->set(i * 2, *info.LocalName(i));
4923
4924 // Fetch the value of the local - either from the stack or from a
4925 // heap-allocated context.
4926 if (i < info.number_of_stack_slots()) {
4927 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
4928 } else {
4929 Handle<String> name = info.LocalName(i);
4930 // Traverse the context chain to the function context as all local
4931 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004932 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004933 context = Handle<Context>(context->previous());
4934 }
4935 ASSERT(context->is_function_context());
4936 locals->set(i * 2 + 1,
4937 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
4938 NULL)));
4939 }
4940 }
4941
4942 // Now advance to the arguments adapter frame (if any). If contains all
4943 // the provided parameters and
4944
4945 // Now advance to the arguments adapter frame (if any). It contains all
4946 // the provided parameters whereas the function frame always have the number
4947 // of arguments matching the functions parameters. The rest of the
4948 // information (except for what is collected above) is the same.
4949 it.AdvanceToArgumentsFrame();
4950
4951 // Find the number of arguments to fill. At least fill the number of
4952 // parameters for the function and fill more if more parameters are provided.
4953 int argument_count = info.number_of_parameters();
4954 if (argument_count < it.frame()->GetProvidedParametersCount()) {
4955 argument_count = it.frame()->GetProvidedParametersCount();
4956 }
4957
4958 // Calculate the size of the result.
4959 int details_size = kFrameDetailsFirstDynamicIndex +
4960 2 * (argument_count + info.NumberOfLocals());
4961 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
4962
4963 // Add the frame id.
4964 details->set(kFrameDetailsFrameIdIndex, *frame_id);
4965
4966 // Add the function (same as in function frame).
4967 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
4968
4969 // Add the arguments count.
4970 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
4971
4972 // Add the locals count
4973 details->set(kFrameDetailsLocalCountIndex,
4974 Smi::FromInt(info.NumberOfLocals()));
4975
4976 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00004977 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004978 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
4979 } else {
4980 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
4981 }
4982
4983 // Add the constructor information.
4984 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
4985
4986 // Add information on whether this frame is invoked in the debugger context.
4987 details->set(kFrameDetailsDebuggerFrameIndex,
4988 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
4989
4990 // Fill the dynamic part.
4991 int details_index = kFrameDetailsFirstDynamicIndex;
4992
4993 // Add arguments name and value.
4994 for (int i = 0; i < argument_count; i++) {
4995 // Name of the argument.
4996 if (i < info.number_of_parameters()) {
4997 details->set(details_index++, *info.parameter_name(i));
4998 } else {
4999 details->set(details_index++, Heap::undefined_value());
5000 }
5001
5002 // Parameter value.
5003 if (i < it.frame()->GetProvidedParametersCount()) {
5004 details->set(details_index++, it.frame()->GetParameter(i));
5005 } else {
5006 details->set(details_index++, Heap::undefined_value());
5007 }
5008 }
5009
5010 // Add locals name and value from the temporary copy from the function frame.
5011 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
5012 details->set(details_index++, locals->get(i));
5013 }
5014
5015 // Add the receiver (same as in function frame).
5016 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
5017 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
5018 Handle<Object> receiver(it.frame()->receiver());
5019 if (!receiver->IsJSObject()) {
5020 // If the receiver is NOT a JSObject we have hit an optimization
5021 // where a value object is not converted into a wrapped JS objects.
5022 // To hide this optimization from the debugger, we wrap the receiver
5023 // by creating correct wrapper object based on the calling frame's
5024 // global context.
5025 it.Advance();
5026 Handle<Context> calling_frames_global_context(
5027 Context::cast(Context::cast(it.frame()->context())->global_context()));
5028 receiver = Factory::ToObject(receiver, calling_frames_global_context);
5029 }
5030 details->set(kFrameDetailsReceiverIndex, *receiver);
5031
5032 ASSERT_EQ(details_size, details_index);
5033 return *Factory::NewJSArrayWithElements(details);
5034}
5035
5036
5037static Object* Runtime_GetCFrames(Arguments args) {
5038 HandleScope scope;
5039 ASSERT(args.length() == 1);
5040 Object* result = Runtime_CheckExecutionState(args);
5041 if (result->IsFailure()) return result;
5042
5043 static const int kMaxCFramesSize = 200;
5044 OS::StackFrame frames[kMaxCFramesSize];
5045 int frames_count = OS::StackWalk(frames, kMaxCFramesSize);
5046 if (frames_count == OS::kStackWalkError) {
5047 return Heap::undefined_value();
5048 }
5049
5050 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
5051 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
5052 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
5053 for (int i = 0; i < frames_count; i++) {
5054 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
5055 frame_value->SetProperty(
5056 *address_str,
5057 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
5058 NONE);
5059
5060 // Get the stack walk text for this frame.
5061 Handle<String> frame_text;
5062 if (strlen(frames[i].text) > 0) {
5063 Vector<const char> str(frames[i].text, strlen(frames[i].text));
5064 frame_text = Factory::NewStringFromAscii(str);
5065 }
5066
5067 if (!frame_text.is_null()) {
5068 frame_value->SetProperty(*text_str, *frame_text, NONE);
5069 }
5070
5071 frames_array->set(i, *frame_value);
5072 }
5073 return *Factory::NewJSArrayWithElements(frames_array);
5074}
5075
5076
5077static Object* Runtime_GetBreakLocations(Arguments args) {
5078 HandleScope scope;
5079 ASSERT(args.length() == 1);
5080
5081 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
5082 Handle<SharedFunctionInfo> shared(raw_fun->shared());
5083 // Find the number of break points
5084 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
5085 if (break_locations->IsUndefined()) return Heap::undefined_value();
5086 // Return array as JS array
5087 return *Factory::NewJSArrayWithElements(
5088 Handle<FixedArray>::cast(break_locations));
5089}
5090
5091
5092// Set a break point in a function
5093// args[0]: function
5094// args[1]: number: break source position (within the function source)
5095// args[2]: number: break point object
5096static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
5097 HandleScope scope;
5098 ASSERT(args.length() == 3);
5099 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
5100 Handle<SharedFunctionInfo> shared(raw_fun->shared());
5101 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
5102 RUNTIME_ASSERT(source_position >= 0);
5103 Handle<Object> break_point_object_arg = args.at<Object>(2);
5104
5105 // Set break point.
5106 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
5107
5108 return Heap::undefined_value();
5109}
5110
5111
5112static Object* FindSharedFunctionInfoInScript(Handle<Script> script,
5113 int position) {
5114 // Iterate the heap looking for SharedFunctionInfo generated from the
5115 // script. The inner most SharedFunctionInfo containing the source position
5116 // for the requested break point is found.
5117 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
5118 // which is found is not compiled it is compiled and the heap is iterated
5119 // again as the compilation might create inner functions from the newly
5120 // compiled function and the actual requested break point might be in one of
5121 // these functions.
5122 bool done = false;
5123 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00005124 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005125 Handle<SharedFunctionInfo> target;
5126 // The current candidate for the last function in script:
5127 Handle<SharedFunctionInfo> last;
5128 while (!done) {
5129 HeapIterator iterator;
5130 while (iterator.has_next()) {
5131 HeapObject* obj = iterator.next();
5132 ASSERT(obj != NULL);
5133 if (obj->IsSharedFunctionInfo()) {
5134 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
5135 if (shared->script() == *script) {
5136 // If the SharedFunctionInfo found has the requested script data and
5137 // contains the source position it is a candidate.
5138 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00005139 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005140 start_position = shared->start_position();
5141 }
5142 if (start_position <= position &&
5143 position <= shared->end_position()) {
ager@chromium.org32912102009-01-16 10:38:43 +00005144 // If there is no candidate or this function is within the current
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005145 // candidate this is the new candidate.
5146 if (target.is_null()) {
5147 target_start_position = start_position;
5148 target = shared;
5149 } else {
5150 if (target_start_position < start_position &&
5151 shared->end_position() < target->end_position()) {
5152 target_start_position = start_position;
5153 target = shared;
5154 }
5155 }
5156 }
5157
5158 // Keep track of the last function in the script.
5159 if (last.is_null() ||
5160 shared->end_position() > last->start_position()) {
5161 last = shared;
5162 }
5163 }
5164 }
5165 }
5166
5167 // Make sure some candidate is selected.
5168 if (target.is_null()) {
5169 if (!last.is_null()) {
5170 // Position after the last function - use last.
5171 target = last;
5172 } else {
5173 // Unable to find function - possibly script without any function.
5174 return Heap::undefined_value();
5175 }
5176 }
5177
5178 // If the candidate found is compiled we are done. NOTE: when lazy
5179 // compilation of inner functions is introduced some additional checking
5180 // needs to be done here to compile inner functions.
5181 done = target->is_compiled();
5182 if (!done) {
5183 // If the candidate is not compiled compile it to reveal any inner
5184 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005185 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005186 }
5187 }
5188
5189 return *target;
5190}
5191
5192
5193// Change the state of a break point in a script. NOTE: Regarding performance
5194// see the NOTE for GetScriptFromScriptData.
5195// args[0]: script to set break point in
5196// args[1]: number: break source position (within the script source)
5197// args[2]: number: break point object
5198static Object* Runtime_SetScriptBreakPoint(Arguments args) {
5199 HandleScope scope;
5200 ASSERT(args.length() == 3);
5201 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
5202 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
5203 RUNTIME_ASSERT(source_position >= 0);
5204 Handle<Object> break_point_object_arg = args.at<Object>(2);
5205
5206 // Get the script from the script wrapper.
5207 RUNTIME_ASSERT(wrapper->value()->IsScript());
5208 Handle<Script> script(Script::cast(wrapper->value()));
5209
5210 Object* result = FindSharedFunctionInfoInScript(script, source_position);
5211 if (!result->IsUndefined()) {
5212 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
5213 // Find position within function. The script position might be before the
5214 // source position of the first function.
5215 int position;
5216 if (shared->start_position() > source_position) {
5217 position = 0;
5218 } else {
5219 position = source_position - shared->start_position();
5220 }
5221 Debug::SetBreakPoint(shared, position, break_point_object_arg);
5222 }
5223 return Heap::undefined_value();
5224}
5225
5226
5227// Clear a break point
5228// args[0]: number: break point object
5229static Object* Runtime_ClearBreakPoint(Arguments args) {
5230 HandleScope scope;
5231 ASSERT(args.length() == 1);
5232 Handle<Object> break_point_object_arg = args.at<Object>(0);
5233
5234 // Clear break point.
5235 Debug::ClearBreakPoint(break_point_object_arg);
5236
5237 return Heap::undefined_value();
5238}
5239
5240
5241// Change the state of break on exceptions
5242// args[0]: boolean indicating uncaught exceptions
5243// args[1]: boolean indicating on/off
5244static Object* Runtime_ChangeBreakOnException(Arguments args) {
5245 HandleScope scope;
5246 ASSERT(args.length() == 2);
5247 ASSERT(args[0]->IsNumber());
5248 ASSERT(args[1]->IsBoolean());
5249
5250 // Update break point state
5251 ExceptionBreakType type =
5252 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
5253 bool enable = args[1]->ToBoolean()->IsTrue();
5254 Debug::ChangeBreakOnException(type, enable);
5255 return Heap::undefined_value();
5256}
5257
5258
5259// Prepare for stepping
5260// args[0]: break id for checking execution state
5261// args[1]: step action from the enumeration StepAction
5262// args[2]: number of times to perform the step
5263static Object* Runtime_PrepareStep(Arguments args) {
5264 HandleScope scope;
5265 ASSERT(args.length() == 3);
5266 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005267 Object* check = Runtime_CheckExecutionState(args);
5268 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005269 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
5270 return Top::Throw(Heap::illegal_argument_symbol());
5271 }
5272
5273 // Get the step action and check validity.
5274 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
5275 if (step_action != StepIn &&
5276 step_action != StepNext &&
5277 step_action != StepOut &&
5278 step_action != StepInMin &&
5279 step_action != StepMin) {
5280 return Top::Throw(Heap::illegal_argument_symbol());
5281 }
5282
5283 // Get the number of steps.
5284 int step_count = NumberToInt32(args[2]);
5285 if (step_count < 1) {
5286 return Top::Throw(Heap::illegal_argument_symbol());
5287 }
5288
5289 // Prepare step.
5290 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
5291 return Heap::undefined_value();
5292}
5293
5294
5295// Clear all stepping set by PrepareStep.
5296static Object* Runtime_ClearStepping(Arguments args) {
5297 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00005298 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005299 Debug::ClearStepping();
5300 return Heap::undefined_value();
5301}
5302
5303
5304// Creates a copy of the with context chain. The copy of the context chain is
5305// is linked to the function context supplied.
5306static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
5307 Handle<Context> function_context) {
5308 // At the bottom of the chain. Return the function context to link to.
5309 if (context_chain->is_function_context()) {
5310 return function_context;
5311 }
5312
5313 // Recursively copy the with contexts.
5314 Handle<Context> previous(context_chain->previous());
5315 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
5316 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005317 CopyWithContextChain(function_context, previous),
5318 extension,
5319 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005320}
5321
5322
5323// Helper function to find or create the arguments object for
5324// Runtime_DebugEvaluate.
5325static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
5326 Handle<JSFunction> function,
5327 Handle<Code> code,
5328 const ScopeInfo<>* sinfo,
5329 Handle<Context> function_context) {
5330 // Try to find the value of 'arguments' to pass as parameter. If it is not
5331 // found (that is the debugged function does not reference 'arguments' and
5332 // does not support eval) then create an 'arguments' object.
5333 int index;
5334 if (sinfo->number_of_stack_slots() > 0) {
5335 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
5336 if (index != -1) {
5337 return Handle<Object>(frame->GetExpression(index));
5338 }
5339 }
5340
5341 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
5342 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
5343 NULL);
5344 if (index != -1) {
5345 return Handle<Object>(function_context->get(index));
5346 }
5347 }
5348
5349 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005350 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
5351 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005352 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005353 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005354 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005355 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005356 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005357 return arguments;
5358}
5359
5360
5361// Evaluate a piece of JavaScript in the context of a stack frame for
ager@chromium.org32912102009-01-16 10:38:43 +00005362// debugging. This is accomplished by creating a new context which in its
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005363// extension part has all the parameters and locals of the function on the
5364// stack frame. A function which calls eval with the code to evaluate is then
5365// compiled in this context and called in this context. As this context
5366// replaces the context of the function on the stack frame a new (empty)
5367// function is created as well to be used as the closure for the context.
5368// This function and the context acts as replacements for the function on the
5369// stack frame presenting the same view of the values of parameters and
5370// local variables as if the piece of JavaScript was evaluated at the point
5371// where the function on the stack frame is currently stopped.
5372static Object* Runtime_DebugEvaluate(Arguments args) {
5373 HandleScope scope;
5374
5375 // Check the execution state and decode arguments frame and source to be
5376 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005377 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005378 Object* check_result = Runtime_CheckExecutionState(args);
5379 if (check_result->IsFailure()) return check_result;
5380 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
5381 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005382 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
5383
5384 // Handle the processing of break.
5385 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005386
5387 // Get the frame where the debugging is performed.
5388 StackFrame::Id id = UnwrapFrameId(wrapped_id);
5389 JavaScriptFrameIterator it(id);
5390 JavaScriptFrame* frame = it.frame();
5391 Handle<JSFunction> function(JSFunction::cast(frame->function()));
5392 Handle<Code> code(function->code());
5393 ScopeInfo<> sinfo(*code);
5394
5395 // Traverse the saved contexts chain to find the active context for the
5396 // selected frame.
5397 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005398 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005399 save = save->prev();
5400 }
5401 ASSERT(save != NULL);
5402 SaveContext savex;
5403 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005404
5405 // Create the (empty) function replacing the function on the stack frame for
5406 // the purpose of evaluating in the context created below. It is important
5407 // that this function does not describe any parameters and local variables
5408 // in the context. If it does then this will cause problems with the lookup
5409 // in Context::Lookup, where context slots for parameters and local variables
5410 // are looked at before the extension object.
5411 Handle<JSFunction> go_between =
5412 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
5413 go_between->set_context(function->context());
5414#ifdef DEBUG
5415 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
5416 ASSERT(go_between_sinfo.number_of_parameters() == 0);
5417 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
5418#endif
5419
5420 // Allocate and initialize a context extension object with all the
5421 // arguments, stack locals heap locals and extension properties of the
5422 // debugged function.
5423 Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function());
5424 // First fill all parameters to the context extension.
5425 for (int i = 0; i < sinfo.number_of_parameters(); ++i) {
5426 SetProperty(context_ext,
5427 sinfo.parameter_name(i),
5428 Handle<Object>(frame->GetParameter(i)), NONE);
5429 }
5430 // Second fill all stack locals to the context extension.
5431 for (int i = 0; i < sinfo.number_of_stack_slots(); i++) {
5432 SetProperty(context_ext,
5433 sinfo.stack_slot_name(i),
5434 Handle<Object>(frame->GetExpression(i)), NONE);
5435 }
5436 // Third fill all context locals to the context extension.
5437 Handle<Context> frame_context(Context::cast(frame->context()));
5438 Handle<Context> function_context(frame_context->fcontext());
5439 for (int i = Context::MIN_CONTEXT_SLOTS;
5440 i < sinfo.number_of_context_slots();
5441 ++i) {
5442 int context_index =
5443 ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL);
5444 SetProperty(context_ext,
5445 sinfo.context_slot_name(i),
5446 Handle<Object>(function_context->get(context_index)), NONE);
5447 }
5448 // Finally copy any properties from the function context extension. This will
5449 // be variables introduced by eval.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005450 if (function_context->has_extension() &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005451 !function_context->IsGlobalContext()) {
5452 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
5453 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
5454 for (int i = 0; i < keys->length(); i++) {
5455 // Names of variables introduced by eval are strings.
5456 ASSERT(keys->get(i)->IsString());
5457 Handle<String> key(String::cast(keys->get(i)));
5458 SetProperty(context_ext, key, GetProperty(ext, key), NONE);
5459 }
5460 }
5461
5462 // Allocate a new context for the debug evaluation and set the extension
5463 // object build.
5464 Handle<Context> context =
5465 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
5466 context->set_extension(*context_ext);
5467 // Copy any with contexts present and chain them in front of this context.
5468 context = CopyWithContextChain(frame_context, context);
5469
5470 // Wrap the evaluation statement in a new function compiled in the newly
5471 // created context. The function has one parameter which has to be called
5472 // 'arguments'. This it to have access to what would have been 'arguments' in
ager@chromium.org32912102009-01-16 10:38:43 +00005473 // the function being debugged.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005474 // function(arguments,__source__) {return eval(__source__);}
5475 static const char* source_str =
5476 "function(arguments,__source__){return eval(__source__);}";
5477 static const int source_str_length = strlen(source_str);
5478 Handle<String> function_source =
5479 Factory::NewStringFromAscii(Vector<const char>(source_str,
5480 source_str_length));
5481 Handle<JSFunction> boilerplate =
ager@chromium.org236ad962008-09-25 09:45:57 +00005482 Compiler::CompileEval(function_source, 0, context->IsGlobalContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005483 if (boilerplate.is_null()) return Failure::Exception();
5484 Handle<JSFunction> compiled_function =
5485 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5486
5487 // Invoke the result of the compilation to get the evaluation function.
5488 bool has_pending_exception;
5489 Handle<Object> receiver(frame->receiver());
5490 Handle<Object> evaluation_function =
5491 Execution::Call(compiled_function, receiver, 0, NULL,
5492 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005493 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005494
5495 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
5496 function_context);
5497
5498 // Invoke the evaluation function and return the result.
5499 const int argc = 2;
5500 Object** argv[argc] = { arguments.location(),
5501 Handle<Object>::cast(source).location() };
5502 Handle<Object> result =
5503 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
5504 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005505 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005506 return *result;
5507}
5508
5509
5510static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
5511 HandleScope scope;
5512
5513 // Check the execution state and decode arguments frame and source to be
5514 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005515 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005516 Object* check_result = Runtime_CheckExecutionState(args);
5517 if (check_result->IsFailure()) return check_result;
5518 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005519 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
5520
5521 // Handle the processing of break.
5522 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005523
5524 // Enter the top context from before the debugger was invoked.
5525 SaveContext save;
5526 SaveContext* top = &save;
5527 while (top != NULL && *top->context() == *Debug::debug_context()) {
5528 top = top->prev();
5529 }
5530 if (top != NULL) {
5531 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005532 }
5533
5534 // Get the global context now set to the top context from before the
5535 // debugger was invoked.
5536 Handle<Context> context = Top::global_context();
5537
5538 // Compile the source to be evaluated.
ager@chromium.org236ad962008-09-25 09:45:57 +00005539 Handle<JSFunction> boilerplate(Compiler::CompileEval(source, 0, true));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005540 if (boilerplate.is_null()) return Failure::Exception();
5541 Handle<JSFunction> compiled_function =
5542 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
5543 context));
5544
5545 // Invoke the result of the compilation to get the evaluation function.
5546 bool has_pending_exception;
5547 Handle<Object> receiver = Top::global();
5548 Handle<Object> result =
5549 Execution::Call(compiled_function, receiver, 0, NULL,
5550 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005551 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005552 return *result;
5553}
5554
5555
5556// Helper function used by Runtime_DebugGetLoadedScripts below.
5557static int DebugGetLoadedScripts(FixedArray* instances, int instances_size) {
5558 NoHandleAllocation ha;
5559 AssertNoAllocation no_alloc;
5560
5561 // Get hold of the current empty script.
5562 Context* context = Top::context()->global_context();
5563 Script* empty = context->empty_script();
5564
5565 // Scan heap for Script objects.
5566 int count = 0;
5567 HeapIterator iterator;
5568 while (iterator.has_next()) {
5569 HeapObject* obj = iterator.next();
5570 ASSERT(obj != NULL);
5571 if (obj->IsScript() && obj != empty) {
5572 if (instances != NULL && count < instances_size) {
5573 instances->set(count, obj);
5574 }
5575 count++;
5576 }
5577 }
5578
5579 return count;
5580}
5581
5582
5583static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
5584 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00005585 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005586
5587 // Perform two GCs to get rid of all unreferenced scripts. The first GC gets
ager@chromium.org32912102009-01-16 10:38:43 +00005588 // rid of all the cached script wrappers and the second gets rid of the
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005589 // scripts which is no longer referenced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005590 Heap::CollectAllGarbage();
5591 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005592
5593 // Get the number of scripts.
5594 int count;
5595 count = DebugGetLoadedScripts(NULL, 0);
5596
5597 // Allocate an array to hold the result.
5598 Handle<FixedArray> instances = Factory::NewFixedArray(count);
5599
5600 // Fill the script objects.
5601 count = DebugGetLoadedScripts(*instances, count);
5602
5603 // Convert the script objects to proper JS objects.
5604 for (int i = 0; i < count; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005605 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
5606 // Get the script wrapper in a local handle before calling GetScriptWrapper,
5607 // because using
5608 // instances->set(i, *GetScriptWrapper(script))
5609 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
5610 // already have deferenced the instances handle.
5611 Handle<JSValue> wrapper = GetScriptWrapper(script);
5612 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005613 }
5614
5615 // Return result as a JS array.
5616 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
5617 Handle<JSArray>::cast(result)->SetContent(*instances);
5618 return *result;
5619}
5620
5621
5622// Helper function used by Runtime_DebugReferencedBy below.
5623static int DebugReferencedBy(JSObject* target,
5624 Object* instance_filter, int max_references,
5625 FixedArray* instances, int instances_size,
5626 JSFunction* context_extension_function,
5627 JSFunction* arguments_function) {
5628 NoHandleAllocation ha;
5629 AssertNoAllocation no_alloc;
5630
5631 // Iterate the heap.
5632 int count = 0;
5633 JSObject* last = NULL;
5634 HeapIterator iterator;
5635 while (iterator.has_next() &&
5636 (max_references == 0 || count < max_references)) {
5637 // Only look at all JSObjects.
5638 HeapObject* heap_obj = iterator.next();
5639 if (heap_obj->IsJSObject()) {
5640 // Skip context extension objects and argument arrays as these are
5641 // checked in the context of functions using them.
5642 JSObject* obj = JSObject::cast(heap_obj);
5643 if (obj->map()->constructor() == context_extension_function ||
5644 obj->map()->constructor() == arguments_function) {
5645 continue;
5646 }
5647
5648 // Check if the JS object has a reference to the object looked for.
5649 if (obj->ReferencesObject(target)) {
5650 // Check instance filter if supplied. This is normally used to avoid
5651 // references from mirror objects (see Runtime_IsInPrototypeChain).
5652 if (!instance_filter->IsUndefined()) {
5653 Object* V = obj;
5654 while (true) {
5655 Object* prototype = V->GetPrototype();
5656 if (prototype->IsNull()) {
5657 break;
5658 }
5659 if (instance_filter == prototype) {
5660 obj = NULL; // Don't add this object.
5661 break;
5662 }
5663 V = prototype;
5664 }
5665 }
5666
5667 if (obj != NULL) {
5668 // Valid reference found add to instance array if supplied an update
5669 // count.
5670 if (instances != NULL && count < instances_size) {
5671 instances->set(count, obj);
5672 }
5673 last = obj;
5674 count++;
5675 }
5676 }
5677 }
5678 }
5679
5680 // Check for circular reference only. This can happen when the object is only
5681 // referenced from mirrors and has a circular reference in which case the
5682 // object is not really alive and would have been garbage collected if not
5683 // referenced from the mirror.
5684 if (count == 1 && last == target) {
5685 count = 0;
5686 }
5687
5688 // Return the number of referencing objects found.
5689 return count;
5690}
5691
5692
5693// Scan the heap for objects with direct references to an object
5694// args[0]: the object to find references to
5695// args[1]: constructor function for instances to exclude (Mirror)
5696// args[2]: the the maximum number of objects to return
5697static Object* Runtime_DebugReferencedBy(Arguments args) {
5698 ASSERT(args.length() == 3);
5699
5700 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005701 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005702
5703 // Check parameters.
5704 CONVERT_CHECKED(JSObject, target, args[0]);
5705 Object* instance_filter = args[1];
5706 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
5707 instance_filter->IsJSObject());
5708 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
5709 RUNTIME_ASSERT(max_references >= 0);
5710
5711 // Get the constructor function for context extension and arguments array.
5712 JSFunction* context_extension_function =
5713 Top::context()->global_context()->context_extension_function();
5714 JSObject* arguments_boilerplate =
5715 Top::context()->global_context()->arguments_boilerplate();
5716 JSFunction* arguments_function =
5717 JSFunction::cast(arguments_boilerplate->map()->constructor());
5718
5719 // Get the number of referencing objects.
5720 int count;
5721 count = DebugReferencedBy(target, instance_filter, max_references,
5722 NULL, 0,
5723 context_extension_function, arguments_function);
5724
5725 // Allocate an array to hold the result.
5726 Object* object = Heap::AllocateFixedArray(count);
5727 if (object->IsFailure()) return object;
5728 FixedArray* instances = FixedArray::cast(object);
5729
5730 // Fill the referencing objects.
5731 count = DebugReferencedBy(target, instance_filter, max_references,
5732 instances, count,
5733 context_extension_function, arguments_function);
5734
5735 // Return result as JS array.
5736 Object* result =
5737 Heap::AllocateJSObject(
5738 Top::context()->global_context()->array_function());
5739 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
5740 return result;
5741}
5742
5743
5744// Helper function used by Runtime_DebugConstructedBy below.
5745static int DebugConstructedBy(JSFunction* constructor, int max_references,
5746 FixedArray* instances, int instances_size) {
5747 AssertNoAllocation no_alloc;
5748
5749 // Iterate the heap.
5750 int count = 0;
5751 HeapIterator iterator;
5752 while (iterator.has_next() &&
5753 (max_references == 0 || count < max_references)) {
5754 // Only look at all JSObjects.
5755 HeapObject* heap_obj = iterator.next();
5756 if (heap_obj->IsJSObject()) {
5757 JSObject* obj = JSObject::cast(heap_obj);
5758 if (obj->map()->constructor() == constructor) {
5759 // Valid reference found add to instance array if supplied an update
5760 // count.
5761 if (instances != NULL && count < instances_size) {
5762 instances->set(count, obj);
5763 }
5764 count++;
5765 }
5766 }
5767 }
5768
5769 // Return the number of referencing objects found.
5770 return count;
5771}
5772
5773
5774// Scan the heap for objects constructed by a specific function.
5775// args[0]: the constructor to find instances of
5776// args[1]: the the maximum number of objects to return
5777static Object* Runtime_DebugConstructedBy(Arguments args) {
5778 ASSERT(args.length() == 2);
5779
5780 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005781 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005782
5783 // Check parameters.
5784 CONVERT_CHECKED(JSFunction, constructor, args[0]);
5785 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
5786 RUNTIME_ASSERT(max_references >= 0);
5787
5788 // Get the number of referencing objects.
5789 int count;
5790 count = DebugConstructedBy(constructor, max_references, NULL, 0);
5791
5792 // Allocate an array to hold the result.
5793 Object* object = Heap::AllocateFixedArray(count);
5794 if (object->IsFailure()) return object;
5795 FixedArray* instances = FixedArray::cast(object);
5796
5797 // Fill the referencing objects.
5798 count = DebugConstructedBy(constructor, max_references, instances, count);
5799
5800 // Return result as JS array.
5801 Object* result =
5802 Heap::AllocateJSObject(
5803 Top::context()->global_context()->array_function());
5804 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
5805 return result;
5806}
5807
5808
5809static Object* Runtime_GetPrototype(Arguments args) {
5810 ASSERT(args.length() == 1);
5811
5812 CONVERT_CHECKED(JSObject, obj, args[0]);
5813
5814 return obj->GetPrototype();
5815}
5816
5817
5818static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005819 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005820 CPU::DebugBreak();
5821 return Heap::undefined_value();
5822}
5823
5824
5825// Finds the script object from the script data. NOTE: This operation uses
5826// heap traversal to find the function generated for the source position
5827// for the requested break point. For lazily compiled functions several heap
5828// traversals might be required rendering this operation as a rather slow
5829// operation. However for setting break points which is normally done through
5830// some kind of user interaction the performance is not crucial.
5831static Handle<Object> Runtime_GetScriptFromScriptName(
5832 Handle<String> script_name) {
5833 // Scan the heap for Script objects to find the script with the requested
5834 // script data.
5835 Handle<Script> script;
5836 HeapIterator iterator;
5837 while (script.is_null() && iterator.has_next()) {
5838 HeapObject* obj = iterator.next();
5839 // If a script is found check if it has the script data requested.
5840 if (obj->IsScript()) {
5841 if (Script::cast(obj)->name()->IsString()) {
5842 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
5843 script = Handle<Script>(Script::cast(obj));
5844 }
5845 }
5846 }
5847 }
5848
5849 // If no script with the requested script data is found return undefined.
5850 if (script.is_null()) return Factory::undefined_value();
5851
5852 // Return the script found.
5853 return GetScriptWrapper(script);
5854}
5855
5856
5857// Get the script object from script data. NOTE: Regarding performance
5858// see the NOTE for GetScriptFromScriptData.
5859// args[0]: script data for the script to find the source for
5860static Object* Runtime_GetScript(Arguments args) {
5861 HandleScope scope;
5862
5863 ASSERT(args.length() == 1);
5864
5865 CONVERT_CHECKED(String, script_name, args[0]);
5866
5867 // Find the requested script.
5868 Handle<Object> result =
5869 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
5870 return *result;
5871}
5872
5873
5874static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
5875#ifdef DEBUG
5876 HandleScope scope;
5877 ASSERT(args.length() == 1);
5878 // Get the function and make sure it is compiled.
5879 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5880 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
5881 return Failure::Exception();
5882 }
5883 func->code()->PrintLn();
5884#endif // DEBUG
5885 return Heap::undefined_value();
5886}
5887
5888
5889static Object* Runtime_Abort(Arguments args) {
5890 ASSERT(args.length() == 2);
5891 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
5892 Smi::cast(args[1])->value());
5893 Top::PrintStack();
5894 OS::Abort();
5895 UNREACHABLE();
5896 return NULL;
5897}
5898
5899
kasper.lund44510672008-07-25 07:37:58 +00005900#ifdef DEBUG
5901// ListNatives is ONLY used by the fuzz-natives.js in debug mode
5902// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005903static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005904 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005905 HandleScope scope;
5906 Handle<JSArray> result = Factory::NewJSArray(0);
5907 int index = 0;
5908#define ADD_ENTRY(Name, argc) \
5909 { \
5910 HandleScope inner; \
5911 Handle<String> name = \
5912 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
5913 Handle<JSArray> pair = Factory::NewJSArray(0); \
5914 SetElement(pair, 0, name); \
5915 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
5916 SetElement(result, index++, pair); \
5917 }
5918 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
5919#undef ADD_ENTRY
5920 return *result;
5921}
kasper.lund44510672008-07-25 07:37:58 +00005922#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005923
5924
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005925static Object* Runtime_Log(Arguments args) {
5926 ASSERT(args.length() == 2);
5927 String* format = String::cast(args[0]);
5928 Vector<const char> chars = format->ToAsciiVector();
5929 JSArray* elms = JSArray::cast(args[1]);
5930 Logger::LogRuntime(chars, elms);
5931 return Heap::undefined_value();
5932}
5933
5934
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005935static Object* Runtime_IS_VAR(Arguments args) {
5936 UNREACHABLE(); // implemented as macro in the parser
5937 return NULL;
5938}
5939
5940
5941// ----------------------------------------------------------------------------
5942// Implementation of Runtime
5943
5944#define F(name, nargs) \
5945 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
5946 static_cast<int>(Runtime::k##name) },
5947
5948static Runtime::Function Runtime_functions[] = {
5949 RUNTIME_FUNCTION_LIST(F)
5950 { NULL, NULL, NULL, 0, -1 }
5951};
5952
5953#undef F
5954
5955
5956Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
5957 ASSERT(0 <= fid && fid < kNofFunctions);
5958 return &Runtime_functions[fid];
5959}
5960
5961
5962Runtime::Function* Runtime::FunctionForName(const char* name) {
5963 for (Function* f = Runtime_functions; f->name != NULL; f++) {
5964 if (strcmp(f->name, name) == 0) {
5965 return f;
5966 }
5967 }
5968 return NULL;
5969}
5970
5971
5972void Runtime::PerformGC(Object* result) {
5973 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005974 if (failure->IsRetryAfterGC()) {
5975 // Try to do a garbage collection; ignore it if it fails. The C
5976 // entry stub will throw an out-of-memory exception in that case.
5977 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
5978 } else {
5979 // Handle last resort GC and make sure to allow future allocations
5980 // to grow the heap without causing GCs (if possible).
5981 Counters::gc_last_resort_from_js.Increment();
5982 Heap::CollectAllGarbage();
5983 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005984}
5985
5986
5987} } // namespace v8::internal