blob: 1949cc44a3ec55e6867afb5b2127931b2381ed3b [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.org236ad962008-09-25 09:45:57 +0000105 if (FLAG_canonicalize_object_literal_maps) {
106 // First find prefix of consecutive symbol keys.
107 int number_of_properties = constant_properties->length()/2;
108 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.org236ad962008-09-25 09:45:57 +0000128 return Handle<Map>(context->object_function()->initial_map());
129}
130
131
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000132static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
133 HandleScope scope;
134 ASSERT(args.length() == 3);
135 // Copy the arguments.
136 Handle<FixedArray> literals = args.at<FixedArray>(0);
137 int literals_index = Smi::cast(args[1])->value();
138 Handle<FixedArray> constant_properties = args.at<FixedArray>(2);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000139
140 // Get the global context from the literals array. This is the
141 // context in which the function was created and we use the object
142 // function from this context to create the object literal. We do
143 // not use the object function from the current global context
144 // because this might be the object function from another context
145 // which we should not have access to.
ager@chromium.org236ad962008-09-25 09:45:57 +0000146 Handle<Context> context =
147 Handle<Context>(JSFunction::GlobalContextFromLiterals(*literals));
148
149 bool is_result_from_cache;
150 Handle<Map> map = ComputeObjectLiteralMap(context,
151 constant_properties,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000152 &is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000153
ager@chromium.org236ad962008-09-25 09:45:57 +0000154 Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000155 { // Add the constant propeties to the boilerplate.
156 int length = constant_properties->length();
ager@chromium.org236ad962008-09-25 09:45:57 +0000157 OptimizedObjectForAddingMultipleProperties opt(boilerplate,
158 !is_result_from_cache);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000159 for (int index = 0; index < length; index +=2) {
160 Handle<Object> key(constant_properties->get(index+0));
161 Handle<Object> value(constant_properties->get(index+1));
162 uint32_t element_index = 0;
163 if (key->IsSymbol()) {
164 // If key is a symbol it is not an array element.
165 Handle<String> name(String::cast(*key));
166 ASSERT(!name->AsArrayIndex(&element_index));
167 SetProperty(boilerplate, name, value, NONE);
168 } else if (Array::IndexFromObject(*key, &element_index)) {
169 // Array index (uint32).
170 SetElement(boilerplate, element_index, value);
171 } else {
172 // Non-uint32 number.
173 ASSERT(key->IsNumber());
174 double num = key->Number();
175 char arr[100];
176 Vector<char> buffer(arr, ARRAY_SIZE(arr));
177 const char* str = DoubleToCString(num, buffer);
178 Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
179 SetProperty(boilerplate, name, value, NONE);
180 }
181 }
182 }
183
184 // Update the functions literal and return the boilerplate.
185 literals->set(literals_index, *boilerplate);
186
187 return *boilerplate;
188}
189
190
191static Object* Runtime_CreateArrayLiteral(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000192 // Takes a FixedArray of elements containing the literal elements of
193 // the array literal and produces JSArray with those elements.
194 // Additionally takes the literals array of the surrounding function
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000195 // which contains the context from which to get the Array function
196 // to use for creating the array literal.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000197 ASSERT(args.length() == 2);
198 CONVERT_CHECKED(FixedArray, elements, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000199 CONVERT_CHECKED(FixedArray, literals, args[1]);
ager@chromium.org236ad962008-09-25 09:45:57 +0000200 JSFunction* constructor =
201 JSFunction::GlobalContextFromLiterals(literals)->array_function();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000202 // Create the JSArray.
203 Object* object = Heap::AllocateJSObject(constructor);
204 if (object->IsFailure()) return object;
205
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000206 // Copy the elements.
207 Object* content = elements->Copy();
208 if (content->IsFailure()) return content;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000209
210 // Set the elements.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000211 JSArray::cast(object)->SetContent(FixedArray::cast(content));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000212 return object;
213}
214
215
216static Object* Runtime_ClassOf(Arguments args) {
217 NoHandleAllocation ha;
218 ASSERT(args.length() == 1);
219 Object* obj = args[0];
220 if (!obj->IsJSObject()) return Heap::null_value();
221 return JSObject::cast(obj)->class_name();
222}
223
ager@chromium.org7c537e22008-10-16 08:43:32 +0000224
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000225static Object* Runtime_HasStringClass(Arguments args) {
226 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::String_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000227}
228
229
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000230static Object* Runtime_HasDateClass(Arguments args) {
231 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Date_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000232}
233
234
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000235static Object* Runtime_HasArrayClass(Arguments args) {
236 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Array_symbol()));
237}
238
239
240static Object* Runtime_HasFunctionClass(Arguments args) {
241 return Heap::ToBoolean(
242 args[0]->HasSpecificClassOf(Heap::function_class_symbol()));
243}
244
245
246static Object* Runtime_HasNumberClass(Arguments args) {
247 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Number_symbol()));
248}
249
250
251static Object* Runtime_HasBooleanClass(Arguments args) {
252 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Boolean_symbol()));
253}
254
255
256static Object* Runtime_HasArgumentsClass(Arguments args) {
257 return Heap::ToBoolean(
258 args[0]->HasSpecificClassOf(Heap::Arguments_symbol()));
259}
260
261
262static Object* Runtime_HasRegExpClass(Arguments args) {
263 return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::RegExp_symbol()));
ager@chromium.org7c537e22008-10-16 08:43:32 +0000264}
265
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000266
267static Object* Runtime_IsInPrototypeChain(Arguments args) {
268 NoHandleAllocation ha;
269 ASSERT(args.length() == 2);
270 // See ECMA-262, section 15.3.5.3, page 88 (steps 5 - 8).
271 Object* O = args[0];
272 Object* V = args[1];
273 while (true) {
274 Object* prototype = V->GetPrototype();
275 if (prototype->IsNull()) return Heap::false_value();
276 if (O == prototype) return Heap::true_value();
277 V = prototype;
278 }
279}
280
281
282static Object* Runtime_IsConstructCall(Arguments args) {
283 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +0000284 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000285 JavaScriptFrameIterator it;
286 return Heap::ToBoolean(it.frame()->IsConstructor());
287}
288
289
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000290static Object* Runtime_RegExpCompile(Arguments args) {
291 HandleScope scope; // create a new handle scope
292 ASSERT(args.length() == 3);
ager@chromium.org236ad962008-09-25 09:45:57 +0000293 CONVERT_CHECKED(JSRegExp, raw_re, args[0]);
294 Handle<JSRegExp> re(raw_re);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295 CONVERT_CHECKED(String, raw_pattern, args[1]);
296 Handle<String> pattern(raw_pattern);
297 CONVERT_CHECKED(String, raw_flags, args[2]);
298 Handle<String> flags(raw_flags);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000299 return *RegExpImpl::Compile(re, pattern, flags);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000300}
301
302
303static Object* Runtime_CreateApiFunction(Arguments args) {
304 HandleScope scope;
305 ASSERT(args.length() == 1);
306 CONVERT_CHECKED(FunctionTemplateInfo, raw_data, args[0]);
307 Handle<FunctionTemplateInfo> data(raw_data);
308 return *Factory::CreateApiFunction(data);
309}
310
311
312static Object* Runtime_IsTemplate(Arguments args) {
313 ASSERT(args.length() == 1);
314 Object* arg = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000315 bool result = arg->IsObjectTemplateInfo() || arg->IsFunctionTemplateInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000316 return Heap::ToBoolean(result);
317}
318
319
320static Object* Runtime_GetTemplateField(Arguments args) {
321 ASSERT(args.length() == 2);
322 CONVERT_CHECKED(HeapObject, templ, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000323 CONVERT_CHECKED(Smi, field, args[1]);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +0000324 int index = field->value();
325 int offset = index * kPointerSize + HeapObject::kHeaderSize;
326 InstanceType type = templ->map()->instance_type();
327 RUNTIME_ASSERT(type == FUNCTION_TEMPLATE_INFO_TYPE ||
328 type == OBJECT_TEMPLATE_INFO_TYPE);
329 RUNTIME_ASSERT(offset > 0);
330 if (type == FUNCTION_TEMPLATE_INFO_TYPE) {
331 RUNTIME_ASSERT(offset < FunctionTemplateInfo::kSize);
332 } else {
333 RUNTIME_ASSERT(offset < ObjectTemplateInfo::kSize);
334 }
335 return *HeapObject::RawField(templ, offset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000336}
337
338
ager@chromium.org870a0b62008-11-04 11:43:05 +0000339static Object* Runtime_DisableAccessChecks(Arguments args) {
340 ASSERT(args.length() == 1);
341 CONVERT_CHECKED(HeapObject, object, args[0]);
342 bool needs_access_checks = object->map()->is_access_check_needed();
343 object->map()->set_is_access_check_needed(false);
344 return needs_access_checks ? Heap::true_value() : Heap::false_value();
345}
346
347
348static Object* Runtime_EnableAccessChecks(Arguments args) {
349 ASSERT(args.length() == 1);
350 CONVERT_CHECKED(HeapObject, object, args[0]);
351 object->map()->set_is_access_check_needed(true);
352 return Heap::undefined_value();
353}
354
355
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000356static Object* ThrowRedeclarationError(const char* type, Handle<String> name) {
357 HandleScope scope;
358 Handle<Object> type_handle = Factory::NewStringFromAscii(CStrVector(type));
359 Handle<Object> args[2] = { type_handle, name };
360 Handle<Object> error =
361 Factory::NewTypeError("redeclaration", HandleVector(args, 2));
362 return Top::Throw(*error);
363}
364
365
366static Object* Runtime_DeclareGlobals(Arguments args) {
367 HandleScope scope;
368 Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
369
370 CONVERT_ARG_CHECKED(FixedArray, pairs, 0);
371 Handle<Context> context = args.at<Context>(1);
372 bool is_eval = Smi::cast(args[2])->value() == 1;
373
374 // Compute the property attributes. According to ECMA-262, section
375 // 13, page 71, the property must be read-only and
376 // non-deletable. However, neither SpiderMonkey nor KJS creates the
377 // property as read-only, so we don't either.
378 PropertyAttributes base = is_eval ? NONE : DONT_DELETE;
379
380 // Only optimize the object if we intend to add more than 5 properties.
381 OptimizedObjectForAddingMultipleProperties ba(global, pairs->length()/2 > 5);
382
383 // Traverse the name/value pairs and set the properties.
384 int length = pairs->length();
385 for (int i = 0; i < length; i += 2) {
386 HandleScope scope;
387 Handle<String> name(String::cast(pairs->get(i)));
388 Handle<Object> value(pairs->get(i + 1));
389
390 // We have to declare a global const property. To capture we only
391 // assign to it when evaluating the assignment for "const x =
392 // <expr>" the initial value is the hole.
393 bool is_const_property = value->IsTheHole();
394
395 if (value->IsUndefined() || is_const_property) {
396 // Lookup the property in the global object, and don't set the
397 // value of the variable if the property is already there.
398 LookupResult lookup;
399 global->Lookup(*name, &lookup);
400 if (lookup.IsProperty()) {
401 // Determine if the property is local by comparing the holder
402 // against the global object. The information will be used to
403 // avoid throwing re-declaration errors when declaring
404 // variables or constants that exist in the prototype chain.
405 bool is_local = (*global == lookup.holder());
406 // Get the property attributes and determine if the property is
407 // read-only.
408 PropertyAttributes attributes = global->GetPropertyAttribute(*name);
409 bool is_read_only = (attributes & READ_ONLY) != 0;
410 if (lookup.type() == INTERCEPTOR) {
411 // If the interceptor says the property is there, we
412 // just return undefined without overwriting the property.
413 // Otherwise, we continue to setting the property.
414 if (attributes != ABSENT) {
415 // Check if the existing property conflicts with regards to const.
416 if (is_local && (is_read_only || is_const_property)) {
417 const char* type = (is_read_only) ? "const" : "var";
418 return ThrowRedeclarationError(type, name);
419 };
420 // The property already exists without conflicting: Go to
421 // the next declaration.
422 continue;
423 }
424 // Fall-through and introduce the absent property by using
425 // SetProperty.
426 } else {
427 if (is_local && (is_read_only || is_const_property)) {
428 const char* type = (is_read_only) ? "const" : "var";
429 return ThrowRedeclarationError(type, name);
430 }
431 // The property already exists without conflicting: Go to
432 // the next declaration.
433 continue;
434 }
435 }
436 } else {
437 // Copy the function and update its context. Use it as value.
438 Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
439 Handle<JSFunction> function =
440 Factory::NewFunctionFromBoilerplate(boilerplate, context);
441 value = function;
442 }
443
444 LookupResult lookup;
445 global->LocalLookup(*name, &lookup);
446
447 PropertyAttributes attributes = is_const_property
448 ? static_cast<PropertyAttributes>(base | READ_ONLY)
449 : base;
450
451 if (lookup.IsProperty()) {
452 // There's a local property that we need to overwrite because
453 // we're either declaring a function or there's an interceptor
454 // that claims the property is absent.
455
456 // Check for conflicting re-declarations. We cannot have
457 // conflicting types in case of intercepted properties because
458 // they are absent.
459 if (lookup.type() != INTERCEPTOR &&
460 (lookup.IsReadOnly() || is_const_property)) {
461 const char* type = (lookup.IsReadOnly()) ? "const" : "var";
462 return ThrowRedeclarationError(type, name);
463 }
464 SetProperty(global, name, value, attributes);
465 } else {
466 // If a property with this name does not already exist on the
467 // global object add the property locally. We take special
468 // precautions to always add it as a local property even in case
469 // of callbacks in the prototype chain (this rules out using
470 // SetProperty). Also, we must use the handle-based version to
471 // avoid GC issues.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000472 IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000473 }
474 }
ager@chromium.org7c537e22008-10-16 08:43:32 +0000475
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000476 return Heap::undefined_value();
477}
478
479
480static Object* Runtime_DeclareContextSlot(Arguments args) {
481 HandleScope scope;
ager@chromium.org7c537e22008-10-16 08:43:32 +0000482 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000483
ager@chromium.org7c537e22008-10-16 08:43:32 +0000484 CONVERT_ARG_CHECKED(Context, context, 0);
485 Handle<String> name(String::cast(args[1]));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000486 PropertyAttributes mode =
ager@chromium.org7c537e22008-10-16 08:43:32 +0000487 static_cast<PropertyAttributes>(Smi::cast(args[2])->value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000488 ASSERT(mode == READ_ONLY || mode == NONE);
ager@chromium.org7c537e22008-10-16 08:43:32 +0000489 Handle<Object> initial_value(args[3]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000490
491 // Declarations are always done in the function context.
492 context = Handle<Context>(context->fcontext());
493
494 int index;
495 PropertyAttributes attributes;
496 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000497 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000498 context->Lookup(name, flags, &index, &attributes);
499
500 if (attributes != ABSENT) {
501 // The name was declared before; check for conflicting
502 // re-declarations: This is similar to the code in parser.cc in
503 // the AstBuildingParser::Declare function.
504 if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
505 // Functions are not read-only.
506 ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
507 const char* type = ((attributes & READ_ONLY) != 0) ? "const" : "var";
508 return ThrowRedeclarationError(type, name);
509 }
510
511 // Initialize it if necessary.
512 if (*initial_value != NULL) {
513 if (index >= 0) {
514 // The variable or constant context slot should always be in
515 // the function context; not in any outer context nor in the
516 // arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000517 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000518 if (((attributes & READ_ONLY) == 0) ||
519 context->get(index)->IsTheHole()) {
520 context->set(index, *initial_value);
521 }
522 } else {
523 // Slow case: The property is not in the FixedArray part of the context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000524 Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000525 SetProperty(context_ext, name, initial_value, mode);
526 }
527 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000528
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000529 } else {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000530 // The property is not in the function context. It needs to be
531 // "declared" in the function context's extension context, or in the
532 // global context.
533 Handle<JSObject> context_ext;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000534 if (context->has_extension()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +0000535 // The function context's extension context exists - use it.
536 context_ext = Handle<JSObject>(context->extension());
537 } else {
538 // The function context's extension context does not exists - allocate
539 // it.
540 context_ext = Factory::NewJSObject(Top::context_extension_function());
541 // And store it in the extension slot.
542 context->set_extension(*context_ext);
543 }
544 ASSERT(*context_ext != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000545
ager@chromium.org7c537e22008-10-16 08:43:32 +0000546 // Declare the property by setting it to the initial value if provided,
547 // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
548 // constant declarations).
549 ASSERT(!context_ext->HasLocalProperty(*name));
550 Handle<Object> value(Heap::undefined_value());
551 if (*initial_value != NULL) value = initial_value;
552 SetProperty(context_ext, name, value, mode);
553 ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
554 }
555
556 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000557}
558
559
560static Object* Runtime_InitializeVarGlobal(Arguments args) {
561 NoHandleAllocation nha;
562
563 // Determine if we need to assign to the variable if it already
564 // exists (based on the number of arguments).
565 RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
566 bool assign = args.length() == 2;
567
568 CONVERT_ARG_CHECKED(String, name, 0);
569 GlobalObject* global = Top::context()->global();
570
571 // According to ECMA-262, section 12.2, page 62, the property must
572 // not be deletable.
573 PropertyAttributes attributes = DONT_DELETE;
574
575 // Lookup the property locally in the global object. If it isn't
576 // there, we add the property and take special precautions to always
577 // add it as a local property even in case of callbacks in the
578 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000579 // We have IgnoreAttributesAndSetLocalProperty for this.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000580 LookupResult lookup;
581 global->LocalLookup(*name, &lookup);
582 if (!lookup.IsProperty()) {
583 Object* value = (assign) ? args[1] : Heap::undefined_value();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000584 return global->IgnoreAttributesAndSetLocalProperty(*name,
585 value,
586 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000587 }
588
589 // Determine if this is a redeclaration of something read-only.
590 if (lookup.IsReadOnly()) {
591 return ThrowRedeclarationError("const", name);
592 }
593
594 // Determine if this is a redeclaration of an intercepted read-only
595 // property and figure out if the property exists at all.
596 bool found = true;
597 PropertyType type = lookup.type();
598 if (type == INTERCEPTOR) {
599 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
600 if (intercepted == ABSENT) {
601 // The interceptor claims the property isn't there. We need to
602 // make sure to introduce it.
603 found = false;
604 } else if ((intercepted & READ_ONLY) != 0) {
605 // The property is present, but read-only. Since we're trying to
606 // overwrite it with a variable declaration we must throw a
607 // re-declaration error.
608 return ThrowRedeclarationError("const", name);
609 }
610 // Restore global object from context (in case of GC).
611 global = Top::context()->global();
612 }
613
614 if (found && !assign) {
615 // The global property is there and we're not assigning any value
616 // to it. Just return.
617 return Heap::undefined_value();
618 }
619
620 // Assign the value (or undefined) to the property.
621 Object* value = (assign) ? args[1] : Heap::undefined_value();
622 return global->SetProperty(&lookup, *name, value, attributes);
623}
624
625
626static Object* Runtime_InitializeConstGlobal(Arguments args) {
627 // All constants are declared with an initial value. The name
628 // of the constant is the first argument and the initial value
629 // is the second.
630 RUNTIME_ASSERT(args.length() == 2);
631 CONVERT_ARG_CHECKED(String, name, 0);
632 Handle<Object> value = args.at<Object>(1);
633
634 // Get the current global object from top.
635 GlobalObject* global = Top::context()->global();
636
637 // According to ECMA-262, section 12.2, page 62, the property must
638 // not be deletable. Since it's a const, it must be READ_ONLY too.
639 PropertyAttributes attributes =
640 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
641
642 // Lookup the property locally in the global object. If it isn't
643 // there, we add the property and take special precautions to always
644 // add it as a local property even in case of callbacks in the
645 // prototype chain (this rules out using SetProperty).
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000646 // We use IgnoreAttributesAndSetLocalProperty instead
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000647 LookupResult lookup;
648 global->LocalLookup(*name, &lookup);
649 if (!lookup.IsProperty()) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000650 return global->IgnoreAttributesAndSetLocalProperty(*name,
651 *value,
652 attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000653 }
654
655 // Determine if this is a redeclaration of something not
656 // read-only. In case the result is hidden behind an interceptor we
657 // need to ask it for the property attributes.
658 if (!lookup.IsReadOnly()) {
659 if (lookup.type() != INTERCEPTOR) {
660 return ThrowRedeclarationError("var", name);
661 }
662
663 PropertyAttributes intercepted = global->GetPropertyAttribute(*name);
664
665 // Throw re-declaration error if the intercepted property is present
666 // but not read-only.
667 if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
668 return ThrowRedeclarationError("var", name);
669 }
670
671 // Restore global object from context (in case of GC) and continue
672 // with setting the value because the property is either absent or
673 // read-only. We also have to do redo the lookup.
674 global = Top::context()->global();
675
676 // BUG 1213579: Handle the case where we have to set a read-only
677 // property through an interceptor and only do it if it's
678 // uninitialized, e.g. the hole. Nirk...
679 global->SetProperty(*name, *value, attributes);
680 return *value;
681 }
682
683 // Set the value, but only we're assigning the initial value to a
684 // constant. For now, we determine this by checking if the
685 // current value is the hole.
686 PropertyType type = lookup.type();
687 if (type == FIELD) {
688 FixedArray* properties = global->properties();
689 int index = lookup.GetFieldIndex();
690 if (properties->get(index)->IsTheHole()) {
691 properties->set(index, *value);
692 }
693 } else if (type == NORMAL) {
694 Dictionary* dictionary = global->property_dictionary();
695 int entry = lookup.GetDictionaryEntry();
696 if (dictionary->ValueAt(entry)->IsTheHole()) {
697 dictionary->ValueAtPut(entry, *value);
698 }
699 } else {
700 // Ignore re-initialization of constants that have already been
701 // assigned a function value.
702 ASSERT(lookup.IsReadOnly() && type == CONSTANT_FUNCTION);
703 }
704
705 // Use the set value as the result of the operation.
706 return *value;
707}
708
709
710static Object* Runtime_InitializeConstContextSlot(Arguments args) {
711 HandleScope scope;
712 ASSERT(args.length() == 3);
713
714 Handle<Object> value(args[0]);
715 ASSERT(!value->IsTheHole());
716 CONVERT_ARG_CHECKED(Context, context, 1);
717 Handle<String> name(String::cast(args[2]));
718
719 // Initializations are always done in the function context.
720 context = Handle<Context>(context->fcontext());
721
722 int index;
723 PropertyAttributes attributes;
724 ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000725 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000726 context->Lookup(name, flags, &index, &attributes);
727
728 // The property should always be present. It is always declared
729 // before being initialized through DeclareContextSlot.
730 ASSERT(attributes != ABSENT && (attributes & READ_ONLY) != 0);
731
732 // If the slot is in the context, we set it but only if it hasn't
733 // been set before.
734 if (index >= 0) {
735 // The constant context slot should always be in the function
736 // context; not in any outer context nor in the arguments object.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000737 ASSERT(holder.is_identical_to(context));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000738 if (context->get(index)->IsTheHole()) {
739 context->set(index, *value);
740 }
741 return *value;
742 }
743
744 // Otherwise, the slot must be in a JS object extension.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000745 Handle<JSObject> context_ext(JSObject::cast(*holder));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000746
747 // We must initialize the value only if it wasn't initialized
748 // before, e.g. for const declarations in a loop. The property has
749 // the hole value if it wasn't initialized yet. NOTE: We cannot use
750 // GetProperty() to get the current value as it 'unholes' the value.
751 LookupResult lookup;
752 context_ext->LocalLookupRealNamedProperty(*name, &lookup);
753 ASSERT(lookup.IsProperty()); // the property was declared
754 ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
755
756 PropertyType type = lookup.type();
757 if (type == FIELD) {
758 FixedArray* properties = context_ext->properties();
759 int index = lookup.GetFieldIndex();
760 if (properties->get(index)->IsTheHole()) {
761 properties->set(index, *value);
762 }
763 } else if (type == NORMAL) {
764 Dictionary* dictionary = context_ext->property_dictionary();
765 int entry = lookup.GetDictionaryEntry();
766 if (dictionary->ValueAt(entry)->IsTheHole()) {
767 dictionary->ValueAtPut(entry, *value);
768 }
769 } else {
770 // We should not reach here. Any real, named property should be
771 // either a field or a dictionary slot.
772 UNREACHABLE();
773 }
774 return *value;
775}
776
777
778static Object* Runtime_RegExpExec(Arguments args) {
779 HandleScope scope;
780 ASSERT(args.length() == 3);
ager@chromium.org236ad962008-09-25 09:45:57 +0000781 CONVERT_CHECKED(JSRegExp, raw_regexp, args[0]);
782 Handle<JSRegExp> regexp(raw_regexp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000783 CONVERT_CHECKED(String, raw_subject, args[1]);
784 Handle<String> subject(raw_subject);
785 Handle<Object> index(args[2]);
786 ASSERT(index->IsNumber());
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000787 return *RegExpImpl::Exec(regexp, subject, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000788}
789
790
791static Object* Runtime_RegExpExecGlobal(Arguments args) {
792 HandleScope scope;
793 ASSERT(args.length() == 2);
ager@chromium.org236ad962008-09-25 09:45:57 +0000794 CONVERT_CHECKED(JSRegExp, raw_regexp, args[0]);
795 Handle<JSRegExp> regexp(raw_regexp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000796 CONVERT_CHECKED(String, raw_subject, args[1]);
797 Handle<String> subject(raw_subject);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000798 return *RegExpImpl::ExecGlobal(regexp, subject);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000799}
800
801
802static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
803 HandleScope scope;
804 ASSERT(args.length() == 4);
805 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
806 int index = Smi::cast(args[1])->value();
807 Handle<String> pattern = args.at<String>(2);
808 Handle<String> flags = args.at<String>(3);
809
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000810 // Get the RegExp function from the context in the literals array.
811 // This is the RegExp function from the context in which the
812 // function was created. We do not use the RegExp function from the
813 // current global context because this might be the RegExp function
814 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000815 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +0000816 Handle<JSFunction>(
817 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000818 // Compute the regular expression literal.
819 bool has_pending_exception;
820 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000821 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
822 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000823 if (has_pending_exception) {
824 ASSERT(Top::has_pending_exception());
825 return Failure::Exception();
826 }
827 literals->set(index, *regexp);
828 return *regexp;
829}
830
831
832static Object* Runtime_FunctionGetName(Arguments args) {
833 NoHandleAllocation ha;
834 ASSERT(args.length() == 1);
835
836 CONVERT_CHECKED(JSFunction, f, args[0]);
837 return f->shared()->name();
838}
839
840
ager@chromium.org236ad962008-09-25 09:45:57 +0000841static Object* Runtime_FunctionSetName(Arguments args) {
842 NoHandleAllocation ha;
843 ASSERT(args.length() == 2);
844
845 CONVERT_CHECKED(JSFunction, f, args[0]);
846 CONVERT_CHECKED(String, name, args[1]);
847 f->shared()->set_name(name);
848 return Heap::undefined_value();
849}
850
851
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000852static Object* Runtime_FunctionGetScript(Arguments args) {
853 HandleScope scope;
854 ASSERT(args.length() == 1);
855
856 CONVERT_CHECKED(JSFunction, fun, args[0]);
857 Handle<Object> script = Handle<Object>(fun->shared()->script());
858 if (!script->IsScript()) return Heap::undefined_value();
859
860 return *GetScriptWrapper(Handle<Script>::cast(script));
861}
862
863
864static Object* Runtime_FunctionGetSourceCode(Arguments args) {
865 NoHandleAllocation ha;
866 ASSERT(args.length() == 1);
867
868 CONVERT_CHECKED(JSFunction, f, args[0]);
869 return f->shared()->GetSourceCode();
870}
871
872
873static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
874 NoHandleAllocation ha;
875 ASSERT(args.length() == 1);
876
877 CONVERT_CHECKED(JSFunction, fun, args[0]);
878 int pos = fun->shared()->start_position();
879 return Smi::FromInt(pos);
880}
881
882
883static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
884 NoHandleAllocation ha;
885 ASSERT(args.length() == 2);
886
887 CONVERT_CHECKED(JSFunction, fun, args[0]);
888 CONVERT_CHECKED(String, name, args[1]);
889 fun->SetInstanceClassName(name);
890 return Heap::undefined_value();
891}
892
893
894static Object* Runtime_FunctionSetLength(Arguments args) {
895 NoHandleAllocation ha;
896 ASSERT(args.length() == 2);
897
898 CONVERT_CHECKED(JSFunction, fun, args[0]);
899 CONVERT_CHECKED(Smi, length, args[1]);
900 fun->shared()->set_length(length->value());
901 return length;
902}
903
904
905static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000906 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000907 ASSERT(args.length() == 2);
908
909 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000910 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
911 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000912 return args[0]; // return TOS
913}
914
915
916static Object* Runtime_SetCode(Arguments args) {
917 HandleScope scope;
918 ASSERT(args.length() == 2);
919
920 CONVERT_CHECKED(JSFunction, raw_target, args[0]);
921 Handle<JSFunction> target(raw_target);
922 Handle<Object> code = args.at<Object>(1);
923
924 Handle<Context> context(target->context());
925
926 if (!code->IsNull()) {
927 RUNTIME_ASSERT(code->IsJSFunction());
928 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
929 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
930 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
931 return Failure::Exception();
932 }
933 // Set the code, formal parameter count, and the length of the target
934 // function.
935 target->set_code(fun->code());
936 target->shared()->set_length(fun->shared()->length());
937 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000938 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +0000939 // Set the source code of the target function to undefined.
940 // SetCode is only used for built-in constructors like String,
941 // Array, and Object, and some web code
942 // doesn't like seeing source code for constructors.
943 target->shared()->set_script(Heap::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000944 context = Handle<Context>(fun->context());
945
946 // Make sure we get a fresh copy of the literal vector to avoid
947 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000948 int number_of_literals = fun->NumberOfLiterals();
949 Handle<FixedArray> literals =
950 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000951 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000952 // Insert the object, regexp and array functions in the literals
953 // array prefix. These are the functions that will be used when
954 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +0000955 literals->set(JSFunction::kLiteralGlobalContextIndex,
956 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000957 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000958 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000959 }
960
961 target->set_context(*context);
962 return *target;
963}
964
965
966static Object* CharCodeAt(String* subject, Object* index) {
967 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000968 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000969 // Flatten the string. If someone wants to get a char at an index
970 // in a cons string, it is likely that more indices will be
971 // accessed.
ager@chromium.org870a0b62008-11-04 11:43:05 +0000972 StringShape shape(subject);
973 subject->TryFlatten(shape); // shape no longer valid!
974 if (i >= static_cast<uint32_t>(subject->length(StringShape(subject)))) {
975 return Heap::nan_value();
976 }
977 return Smi::FromInt(subject->Get(StringShape(subject), i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000978}
979
980
981static Object* Runtime_StringCharCodeAt(Arguments args) {
982 NoHandleAllocation ha;
983 ASSERT(args.length() == 2);
984
985 CONVERT_CHECKED(String, subject, args[0]);
986 Object* index = args[1];
987 return CharCodeAt(subject, index);
988}
989
990
991static Object* Runtime_CharFromCode(Arguments args) {
992 NoHandleAllocation ha;
993 ASSERT(args.length() == 1);
994 uint32_t code;
995 if (Array::IndexFromObject(args[0], &code)) {
996 if (code <= 0xffff) {
997 return Heap::LookupSingleCharacterStringFromCode(code);
998 }
999 }
1000 return Heap::empty_string();
1001}
1002
1003
ager@chromium.org7c537e22008-10-16 08:43:32 +00001004// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1005// limit, we can fix the size of tables.
1006static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001007// Reduce alphabet to this size.
1008static const int kBMAlphabetSize = 0x100;
1009// For patterns below this length, the skip length of Boyer-Moore is too short
1010// to compensate for the algorithmic overhead compared to simple brute force.
1011static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001012
ager@chromium.org7c537e22008-10-16 08:43:32 +00001013// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1014// shift. Only allows the last kBMMaxShift characters of the needle
1015// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001016class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001017 public:
1018 BMGoodSuffixBuffers() {}
1019 inline void init(int needle_length) {
1020 ASSERT(needle_length > 1);
1021 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1022 int len = needle_length - start;
1023 biased_suffixes_ = suffixes_ - start;
1024 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1025 for (int i = 0; i <= len; i++) {
1026 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001027 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001028 }
1029 inline int& suffix(int index) {
1030 ASSERT(biased_suffixes_ + index >= suffixes_);
1031 return biased_suffixes_[index];
1032 }
1033 inline int& shift(int index) {
1034 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1035 return biased_good_suffix_shift_[index];
1036 }
1037 private:
1038 int suffixes_[kBMMaxShift + 1];
1039 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001040 int* biased_suffixes_;
1041 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001042 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1043};
1044
1045// buffers reused by BoyerMoore
1046static int bad_char_occurence[kBMAlphabetSize];
1047static BMGoodSuffixBuffers bmgs_buffers;
1048
1049// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001050template <typename pchar>
1051static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1052 int start) {
1053 // Run forwards to populate bad_char_table, so that *last* instance
1054 // of character equivalence class is the one registered.
1055 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001056 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1057 : kBMAlphabetSize;
1058 if (start == 0) { // All patterns less than kBMMaxShift in length.
1059 memset(bad_char_occurence, -1, table_size * sizeof(*bad_char_occurence));
1060 } else {
1061 for (int i = 0; i < table_size; i++) {
1062 bad_char_occurence[i] = start - 1;
1063 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001064 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001065 for (int i = start; i < pattern.length() - 1; i++) {
1066 pchar c = pattern[i];
1067 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
1068 bad_char_occurence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001069 }
1070}
1071
1072template <typename pchar>
1073static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001074 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001075 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001076 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001077 // Compute Good Suffix tables.
1078 bmgs_buffers.init(m);
1079
1080 bmgs_buffers.shift(m-1) = 1;
1081 bmgs_buffers.suffix(m) = m + 1;
1082 pchar last_char = pattern[m - 1];
1083 int suffix = m + 1;
1084 for (int i = m; i > start;) {
1085 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1086 if (bmgs_buffers.shift(suffix) == len) {
1087 bmgs_buffers.shift(suffix) = suffix - i;
1088 }
1089 suffix = bmgs_buffers.suffix(suffix);
1090 }
1091 i--;
1092 suffix--;
1093 bmgs_buffers.suffix(i) = suffix;
1094 if (suffix == m) {
1095 // No suffix to extend, so we check against last_char only.
1096 while (i > start && pattern[i - 1] != last_char) {
1097 if (bmgs_buffers.shift(m) == len) {
1098 bmgs_buffers.shift(m) = m - i;
1099 }
1100 i--;
1101 bmgs_buffers.suffix(i) = m;
1102 }
1103 if (i > start) {
1104 i--;
1105 suffix--;
1106 bmgs_buffers.suffix(i) = suffix;
1107 }
1108 }
1109 }
1110 if (suffix < m) {
1111 for (int i = start; i <= m; i++) {
1112 if (bmgs_buffers.shift(i) == len) {
1113 bmgs_buffers.shift(i) = suffix - start;
1114 }
1115 if (i == suffix) {
1116 suffix = bmgs_buffers.suffix(suffix);
1117 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001118 }
1119 }
1120}
1121
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001122template <typename schar, typename pchar>
1123static inline int CharOccurence(int char_code) {
1124 if (sizeof(schar) == 1) {
1125 return bad_char_occurence[char_code];
1126 }
1127 if (sizeof(pchar) == 1) {
1128 if (char_code > String::kMaxAsciiCharCode) {
1129 return -1;
1130 }
1131 return bad_char_occurence[char_code];
1132 }
1133 return bad_char_occurence[char_code % kBMAlphabetSize];
1134}
1135
1136// Restricted simplified Boyer-Moore string matching. Restricts tables to a
ager@chromium.org7c537e22008-10-16 08:43:32 +00001137// suffix of long pattern strings and handles only equivalence classes
1138// of the full alphabet. This allows us to ensure that tables take only
1139// a fixed amount of space.
1140template <typename schar, typename pchar>
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001141static int BoyerMooreSimplified(Vector<const schar> subject,
1142 Vector<const pchar> pattern,
1143 int start_index,
1144 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001145 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001146 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001147 // Only preprocess at most kBMMaxShift last characters of pattern.
1148 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001149
ager@chromium.org7c537e22008-10-16 08:43:32 +00001150 BoyerMoorePopulateBadCharTable(pattern, start);
1151
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001152 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001153 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001154 pchar last_char = pattern[m - 1];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001155 // Perform search
1156 for (idx = start_index; idx <= n - m;) {
1157 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001158 int c;
1159 while (last_char != (c = subject[idx + j])) {
1160 int bc_occ = CharOccurence<schar, pchar>(c);
1161 int shift = j - bc_occ;
1162 idx += shift;
1163 badness += 1 - shift; // at most zero, so badness cannot increase.
1164 if (idx > n - m) {
1165 *complete = true;
1166 return -1;
1167 }
1168 }
1169 j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001170 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
1171 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001172 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001173 return idx;
1174 } else {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001175 int bc_occ = CharOccurence<schar, pchar>(c);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001176 int shift = bc_occ < j ? j - bc_occ : 1;
1177 idx += shift;
1178 // Badness increases by the number of characters we have
1179 // checked, and decreases by the number of characters we
1180 // can skip by shifting. It's a measure of how we are doing
1181 // compared to reading each character exactly once.
1182 badness += (m - j) - shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001183 if (badness > 0) {
1184 *complete = false;
1185 return idx;
1186 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001187 }
1188 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001189 *complete = true;
1190 return -1;
1191}
ager@chromium.org7c537e22008-10-16 08:43:32 +00001192
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001193
1194template <typename schar, typename pchar>
1195static int BoyerMooreIndexOf(Vector<const schar> subject,
1196 Vector<const pchar> pattern,
1197 int idx) {
1198 int n = subject.length();
1199 int m = pattern.length();
1200 // Only preprocess at most kBMMaxShift last characters of pattern.
1201 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
1202
1203 // Build the Good Suffix table and continue searching.
1204 BoyerMoorePopulateGoodSuffixTable(pattern, start);
1205 pchar last_char = pattern[m - 1];
1206 // Continue search from i.
1207 do {
1208 int j = m - 1;
1209 schar c;
1210 while (last_char != (c = subject[idx + j])) {
1211 int shift = j - CharOccurence<schar, pchar>(c);
1212 idx += shift;
1213 if (idx > n - m) {
1214 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001215 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001216 }
1217 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
1218 if (j < 0) {
1219 return idx;
1220 } else if (j < start) {
1221 // we have matched more than our tables allow us to be smart about.
1222 idx += 1;
1223 } else {
1224 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
1225 int bc_occ = CharOccurence<schar, pchar>(c);
1226 int shift = j - bc_occ; // Bad-char shift.
1227 shift = (gs_shift > shift) ? gs_shift : shift;
1228 idx += shift;
1229 }
1230 } while (idx <= n - m);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001231
1232 return -1;
1233}
1234
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001235
1236template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00001237static int SingleCharIndexOf(Vector<const schar> string,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001238 uc16 pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00001239 int start_index) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001240 if (sizeof(schar) == 1 && pattern_char > String::kMaxAsciiCharCode) {
1241 return -1;
1242 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001243 for (int i = start_index, n = string.length(); i < n; i++) {
1244 if (pattern_char == string[i]) {
1245 return i;
1246 }
1247 }
1248 return -1;
1249}
1250
1251// Trivial string search for shorter strings.
1252// On return, if "complete" is set to true, the return value is the
1253// final result of searching for the patter in the subject.
1254// If "complete" is set to false, the return value is the index where
1255// further checking should start, i.e., it's guaranteed that the pattern
1256// does not occur at a position prior to the returned index.
1257template <typename pchar, typename schar>
1258static int SimpleIndexOf(Vector<const schar> subject,
1259 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001260 int idx,
1261 bool* complete) {
1262 // Badness is a count of how much work we have done. When we have
1263 // done enough work we decide it's probably worth switching to a better
1264 // algorithm.
1265 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001266 // We know our pattern is at least 2 characters, we cache the first so
1267 // the common case of the first character not matching is faster.
1268 pchar pattern_first_char = pattern[0];
1269
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001270 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
1271 badness++;
1272 if (badness > 0) {
1273 *complete = false;
1274 return (i);
1275 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001276 if (subject[i] != pattern_first_char) continue;
1277 int j = 1;
1278 do {
1279 if (pattern[j] != subject[i+j]) {
1280 break;
1281 }
1282 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001283 } while (j < pattern.length());
1284 if (j == pattern.length()) {
1285 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001286 return i;
1287 }
1288 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001289 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001290 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001291 return -1;
1292}
1293
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001294// Simple indexOf that never bails out. For short patterns only.
1295template <typename pchar, typename schar>
1296static int SimpleIndexOf(Vector<const schar> subject,
1297 Vector<const pchar> pattern,
1298 int idx) {
1299 pchar pattern_first_char = pattern[0];
1300 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
1301 if (subject[i] != pattern_first_char) continue;
1302 int j = 1;
1303 do {
1304 if (pattern[j] != subject[i+j]) {
1305 break;
1306 }
1307 j++;
1308 } while (j < pattern.length());
1309 if (j == pattern.length()) {
1310 return i;
1311 }
1312 }
1313 return -1;
1314}
1315
1316
1317// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001318template <typename schar, typename pchar>
1319static int StringMatchStrategy(Vector<const schar> sub,
1320 Vector<const pchar> pat,
1321 int start_index) {
1322 ASSERT(pat.length() > 1);
1323
1324 // We have an ASCII haystack and a non-ASCII needle. Check if there
1325 // really is a non-ASCII character in the needle and bail out if there
1326 // is.
1327 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
1328 for (int i = 0; i < pat.length(); i++) {
1329 uc16 c = pat[i];
1330 if (c > String::kMaxAsciiCharCode) {
1331 return -1;
1332 }
1333 }
1334 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001335 if (pat.length() < kBMMinPatternLength) {
1336 // We don't believe fancy searching can ever be more efficient.
1337 // The max shift of Boyer-Moore on a pattern of this length does
1338 // not compensate for the overhead.
1339 return SimpleIndexOf(sub, pat, start_index);
1340 }
1341 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001342 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001343 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
1344 if (complete) return idx;
1345 idx = BoyerMooreSimplified(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001346 if (complete) return idx;
1347 return BoyerMooreIndexOf(sub, pat, idx);
1348}
1349
1350// Perform string match of pattern on subject, starting at start index.
1351// Caller must ensure that 0 <= start_index <= sub->length(),
1352// and should check that pat->length() + start_index <= sub->length()
1353int Runtime::StringMatch(Handle<String> sub,
1354 Handle<String> pat,
1355 int start_index) {
1356 ASSERT(0 <= start_index);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001357 StringShape sub_shape(*sub);
1358 StringShape pat_shape(*pat);
1359 ASSERT(start_index <= sub->length(sub_shape));
ager@chromium.org7c537e22008-10-16 08:43:32 +00001360
ager@chromium.org870a0b62008-11-04 11:43:05 +00001361 int pattern_length = pat->length(pat_shape);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001362 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001363
ager@chromium.org870a0b62008-11-04 11:43:05 +00001364 int subject_length = sub->length(sub_shape);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001365 if (start_index + pattern_length > subject_length) return -1;
1366
ager@chromium.org870a0b62008-11-04 11:43:05 +00001367 if (!sub->IsFlat(sub_shape)) {
1368 FlattenString(sub);
1369 sub_shape = StringShape(*sub);
1370 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001371 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00001372 // character patterns linear search is necessary, so any smart
1373 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001374 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001375 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org870a0b62008-11-04 11:43:05 +00001376 if (sub_shape.IsAsciiRepresentation()) {
1377 return SingleCharIndexOf(sub->ToAsciiVector(),
1378 pat->Get(pat_shape, 0),
1379 start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001380 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00001381 return SingleCharIndexOf(sub->ToUC16Vector(),
1382 pat->Get(pat_shape, 0),
1383 start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001384 }
1385
ager@chromium.org870a0b62008-11-04 11:43:05 +00001386 if (!pat->IsFlat(pat_shape)) {
1387 FlattenString(pat);
1388 pat_shape = StringShape(*pat);
1389 }
ager@chromium.org236ad962008-09-25 09:45:57 +00001390
ager@chromium.org7c537e22008-10-16 08:43:32 +00001391 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
1392 // dispatch on type of strings
ager@chromium.org870a0b62008-11-04 11:43:05 +00001393 if (pat_shape.IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001394 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org870a0b62008-11-04 11:43:05 +00001395 if (sub_shape.IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001396 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00001397 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001398 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00001399 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001400 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org870a0b62008-11-04 11:43:05 +00001401 if (sub_shape.IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001402 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001403 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001404 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001405}
1406
1407
1408static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001409 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001410 ASSERT(args.length() == 3);
1411
ager@chromium.org7c537e22008-10-16 08:43:32 +00001412 CONVERT_ARG_CHECKED(String, sub, 0);
1413 CONVERT_ARG_CHECKED(String, pat, 1);
1414
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001415 Object* index = args[2];
1416 uint32_t start_index;
1417 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
1418
ager@chromium.org870a0b62008-11-04 11:43:05 +00001419 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00001420 int position = Runtime::StringMatch(sub, pat, start_index);
1421 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001422}
1423
1424
1425static Object* Runtime_StringLastIndexOf(Arguments args) {
1426 NoHandleAllocation ha;
1427 ASSERT(args.length() == 3);
1428
1429 CONVERT_CHECKED(String, sub, args[0]);
1430 CONVERT_CHECKED(String, pat, args[1]);
1431 Object* index = args[2];
1432
ager@chromium.org870a0b62008-11-04 11:43:05 +00001433 sub->TryFlatten(StringShape(sub));
1434 pat->TryFlatten(StringShape(pat));
1435
1436 StringShape sub_shape(sub);
1437 StringShape pat_shape(pat);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001438
1439 uint32_t start_index;
1440 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
1441
ager@chromium.org870a0b62008-11-04 11:43:05 +00001442 uint32_t pattern_length = pat->length(pat_shape);
1443 uint32_t sub_length = sub->length(sub_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001444
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001445 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001446 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001447 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001448
1449 for (int i = start_index; i >= 0; i--) {
1450 bool found = true;
1451 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001452 if (sub->Get(sub_shape, i + j) != pat->Get(pat_shape, j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001453 found = false;
1454 break;
1455 }
1456 }
1457 if (found) return Smi::FromInt(i);
1458 }
1459
1460 return Smi::FromInt(-1);
1461}
1462
1463
1464static Object* Runtime_StringLocaleCompare(Arguments args) {
1465 NoHandleAllocation ha;
1466 ASSERT(args.length() == 2);
1467
1468 CONVERT_CHECKED(String, str1, args[0]);
1469 CONVERT_CHECKED(String, str2, args[1]);
1470
1471 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.org870a0b62008-11-04 11:43:05 +00001472 StringShape shape1(str1);
1473 StringShape shape2(str2);
1474 int str1_length = str1->length(shape1);
1475 int str2_length = str2->length(shape2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001476
1477 // Decide trivial cases without flattening.
1478 if (str1_length == 0) {
1479 if (str2_length == 0) return Smi::FromInt(0); // Equal.
1480 return Smi::FromInt(-str2_length);
1481 } else {
1482 if (str2_length == 0) return Smi::FromInt(str1_length);
1483 }
1484
1485 int end = str1_length < str2_length ? str1_length : str2_length;
1486
1487 // No need to flatten if we are going to find the answer on the first
1488 // character. At this point we know there is at least one character
1489 // in each string, due to the trivial case handling above.
ager@chromium.org870a0b62008-11-04 11:43:05 +00001490 int d = str1->Get(shape1, 0) - str2->Get(shape2, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001491 if (d != 0) return Smi::FromInt(d);
1492
ager@chromium.org870a0b62008-11-04 11:43:05 +00001493 str1->TryFlatten(shape1); // Shapes are no longer valid now!
1494 str2->TryFlatten(shape2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001495
1496 static StringInputBuffer buf1;
1497 static StringInputBuffer buf2;
1498
1499 buf1.Reset(str1);
1500 buf2.Reset(str2);
1501
1502 for (int i = 0; i < end; i++) {
1503 uint16_t char1 = buf1.GetNext();
1504 uint16_t char2 = buf2.GetNext();
1505 if (char1 != char2) return Smi::FromInt(char1 - char2);
1506 }
1507
1508 return Smi::FromInt(str1_length - str2_length);
1509}
1510
1511
1512static Object* Runtime_StringSlice(Arguments args) {
1513 NoHandleAllocation ha;
1514 ASSERT(args.length() == 3);
1515
1516 CONVERT_CHECKED(String, value, args[0]);
1517 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
1518 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
1519
1520 int start = FastD2I(from_number);
1521 int end = FastD2I(to_number);
1522
ager@chromium.org870a0b62008-11-04 11:43:05 +00001523 StringShape shape(value);
1524
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001525 RUNTIME_ASSERT(end >= start);
1526 RUNTIME_ASSERT(start >= 0);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001527 RUNTIME_ASSERT(end <= value->length(shape));
1528 return value->Slice(shape, start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001529}
1530
1531
1532static Object* Runtime_NumberToRadixString(Arguments args) {
1533 NoHandleAllocation ha;
1534 ASSERT(args.length() == 2);
1535
1536 CONVERT_DOUBLE_CHECKED(value, args[0]);
1537 if (isnan(value)) {
1538 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1539 }
1540 if (isinf(value)) {
1541 if (value < 0) {
1542 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1543 }
1544 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1545 }
1546 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
1547 int radix = FastD2I(radix_number);
1548 RUNTIME_ASSERT(2 <= radix && radix <= 36);
1549 char* str = DoubleToRadixCString(value, radix);
1550 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
1551 DeleteArray(str);
1552 return result;
1553}
1554
1555
1556static Object* Runtime_NumberToFixed(Arguments args) {
1557 NoHandleAllocation ha;
1558 ASSERT(args.length() == 2);
1559
1560 CONVERT_DOUBLE_CHECKED(value, args[0]);
1561 if (isnan(value)) {
1562 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1563 }
1564 if (isinf(value)) {
1565 if (value < 0) {
1566 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1567 }
1568 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1569 }
1570 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
1571 int f = FastD2I(f_number);
1572 RUNTIME_ASSERT(f >= 0);
1573 char* str = DoubleToFixedCString(value, f);
1574 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
1575 DeleteArray(str);
1576 return res;
1577}
1578
1579
1580static Object* Runtime_NumberToExponential(Arguments args) {
1581 NoHandleAllocation ha;
1582 ASSERT(args.length() == 2);
1583
1584 CONVERT_DOUBLE_CHECKED(value, args[0]);
1585 if (isnan(value)) {
1586 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1587 }
1588 if (isinf(value)) {
1589 if (value < 0) {
1590 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1591 }
1592 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1593 }
1594 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
1595 int f = FastD2I(f_number);
1596 RUNTIME_ASSERT(f >= -1 && f <= 20);
1597 char* str = DoubleToExponentialCString(value, f);
1598 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
1599 DeleteArray(str);
1600 return res;
1601}
1602
1603
1604static Object* Runtime_NumberToPrecision(Arguments args) {
1605 NoHandleAllocation ha;
1606 ASSERT(args.length() == 2);
1607
1608 CONVERT_DOUBLE_CHECKED(value, args[0]);
1609 if (isnan(value)) {
1610 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1611 }
1612 if (isinf(value)) {
1613 if (value < 0) {
1614 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1615 }
1616 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1617 }
1618 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
1619 int f = FastD2I(f_number);
1620 RUNTIME_ASSERT(f >= 1 && f <= 21);
1621 char* str = DoubleToPrecisionCString(value, f);
1622 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
1623 DeleteArray(str);
1624 return res;
1625}
1626
1627
1628// Returns a single character string where first character equals
1629// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001630static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001631 StringShape shape(*string);
1632 if (index < static_cast<uint32_t>(string->length(shape))) {
1633 string->TryFlatten(shape); // Invalidates shape!
1634 return LookupSingleCharacterStringFromCode(
1635 string->Get(StringShape(*string), index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001636 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001637 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001638}
1639
1640
1641Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
1642 // Handle [] indexing on Strings
1643 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001644 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
1645 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001646 }
1647
1648 // Handle [] indexing on String objects
1649 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001650 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
1651 Handle<Object> result =
1652 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
1653 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001654 }
1655
1656 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001657 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001658 return prototype->GetElement(index);
1659 }
1660
1661 return object->GetElement(index);
1662}
1663
1664
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001665Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
1666 HandleScope scope;
1667
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001668 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001669 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001670 Handle<Object> error =
1671 Factory::NewTypeError("non_object_property_load",
1672 HandleVector(args, 2));
1673 return Top::Throw(*error);
1674 }
1675
1676 // Check if the given key is an array index.
1677 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001678 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001679 return GetElementOrCharAt(object, index);
1680 }
1681
1682 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001683 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001684 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001685 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001686 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001687 bool has_pending_exception = false;
1688 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001689 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001690 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001691 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001692 }
1693
1694 // Check if the name is trivially convertable to an index and get
1695 // the element if so.
1696 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001697 return GetElementOrCharAt(object, index);
1698 } else {
1699 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001700 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001701 }
1702}
1703
1704
1705static Object* Runtime_GetProperty(Arguments args) {
1706 NoHandleAllocation ha;
1707 ASSERT(args.length() == 2);
1708
1709 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001710 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001711
1712 return Runtime::GetObjectProperty(object, key);
1713}
1714
1715
ager@chromium.org7c537e22008-10-16 08:43:32 +00001716
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001717// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001718static Object* Runtime_KeyedGetProperty(Arguments args) {
1719 NoHandleAllocation ha;
1720 ASSERT(args.length() == 2);
1721
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001722 // Fast cases for getting named properties of the receiver JSObject
1723 // itself. The global proxy objects has to be excluded since
1724 // LocalLookup on the global proxy object can return a valid result
1725 // eventhough the global proxy object never has properties. This is
1726 // the case because the global proxy object forwards everything to
1727 // its hidden prototype including local lookups.
1728 if (args[0]->IsJSObject() &&
1729 !args[0]->IsJSGlobalProxy() &&
1730 args[1]->IsString()) {
1731 JSObject* receiver = JSObject::cast(args[0]);
1732 String* key = String::cast(args[1]);
1733 if (receiver->HasFastProperties()) {
1734 // Attempt to use lookup cache.
1735 Object* obj = Heap::GetKeyedLookupCache();
1736 if (obj->IsFailure()) return obj;
1737 LookupCache* cache = LookupCache::cast(obj);
1738 Map* receiver_map = receiver->map();
1739 int offset = cache->Lookup(receiver_map, key);
1740 if (offset != LookupCache::kNotFound) {
1741 Object* value = receiver->FastPropertyAt(offset);
1742 return value->IsTheHole() ? Heap::undefined_value() : value;
1743 }
1744 // Lookup cache miss. Perform lookup and update the cache if
1745 // appropriate.
1746 LookupResult result;
1747 receiver->LocalLookup(key, &result);
1748 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
1749 int offset = result.GetFieldIndex();
1750 Object* obj = cache->Put(receiver_map, key, offset);
1751 if (obj->IsFailure()) return obj;
1752 Heap::SetKeyedLookupCache(LookupCache::cast(obj));
1753 Object* value = receiver->FastPropertyAt(offset);
1754 return value->IsTheHole() ? Heap::undefined_value() : value;
1755 }
1756 } else {
1757 // Attempt dictionary lookup.
1758 Dictionary* dictionary = receiver->property_dictionary();
1759 int entry = dictionary->FindStringEntry(key);
1760 if ((entry != DescriptorArray::kNotFound) &&
1761 (dictionary->DetailsAt(entry).type() == NORMAL)) {
1762 return dictionary->ValueAt(entry);
1763 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001764 }
1765 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001766
1767 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001768 return Runtime::GetObjectProperty(args.at<Object>(0),
1769 args.at<Object>(1));
1770}
1771
1772
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001773Object* Runtime::SetObjectProperty(Handle<Object> object,
1774 Handle<Object> key,
1775 Handle<Object> value,
1776 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001777 HandleScope scope;
1778
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001779 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001780 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001781 Handle<Object> error =
1782 Factory::NewTypeError("non_object_property_store",
1783 HandleVector(args, 2));
1784 return Top::Throw(*error);
1785 }
1786
1787 // If the object isn't a JavaScript object, we ignore the store.
1788 if (!object->IsJSObject()) return *value;
1789
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001790 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
1791
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001792 // Check if the given key is an array index.
1793 uint32_t index;
1794 if (Array::IndexFromObject(*key, &index)) {
1795 ASSERT(attr == NONE);
1796
1797 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
1798 // of a string using [] notation. We need to support this too in
1799 // JavaScript.
1800 // In the case of a String object we just need to redirect the assignment to
1801 // the underlying string if the index is in range. Since the underlying
1802 // string does nothing with the assignment then we can ignore such
1803 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001804 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001805 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001806 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001807
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001808 Handle<Object> result = SetElement(js_object, index, value);
1809 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001810 return *value;
1811 }
1812
1813 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001814 Handle<Object> result;
1815 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001816 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001817 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001818 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001819 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001820 key_string->TryFlatten(StringShape(*key_string));
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001821 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001822 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001823 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001824 return *value;
1825 }
1826
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001827 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001828 bool has_pending_exception = false;
1829 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
1830 if (has_pending_exception) return Failure::Exception();
1831 Handle<String> name = Handle<String>::cast(converted);
1832
1833 if (name->AsArrayIndex(&index)) {
1834 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001835 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001836 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001837 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001838 }
1839}
1840
1841
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001842static Object* Runtime_SetProperty(Arguments args) {
1843 NoHandleAllocation ha;
1844 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
1845
1846 Handle<Object> object = args.at<Object>(0);
1847 Handle<Object> key = args.at<Object>(1);
1848 Handle<Object> value = args.at<Object>(2);
1849
1850 // Compute attributes.
1851 PropertyAttributes attributes = NONE;
1852 if (args.length() == 4) {
1853 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001854 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001855 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001856 RUNTIME_ASSERT(
1857 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
1858 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001859 }
1860 return Runtime::SetObjectProperty(object, key, value, attributes);
1861}
1862
1863
1864// Set a local property, even if it is READ_ONLY. If the property does not
1865// exist, it will be added with attributes NONE.
1866static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
1867 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001868 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001869 CONVERT_CHECKED(JSObject, object, args[0]);
1870 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001871 // Compute attributes.
1872 PropertyAttributes attributes = NONE;
1873 if (args.length() == 4) {
1874 CONVERT_CHECKED(Smi, value_obj, args[3]);
1875 int unchecked_value = value_obj->value();
1876 // Only attribute bits should be set.
1877 RUNTIME_ASSERT(
1878 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
1879 attributes = static_cast<PropertyAttributes>(unchecked_value);
1880 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001881
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001882 return object->
1883 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001884}
1885
1886
1887static Object* Runtime_DeleteProperty(Arguments args) {
1888 NoHandleAllocation ha;
1889 ASSERT(args.length() == 2);
1890
1891 CONVERT_CHECKED(JSObject, object, args[0]);
1892 CONVERT_CHECKED(String, key, args[1]);
1893 return object->DeleteProperty(key);
1894}
1895
1896
1897static Object* Runtime_HasLocalProperty(Arguments args) {
1898 NoHandleAllocation ha;
1899 ASSERT(args.length() == 2);
1900 CONVERT_CHECKED(String, key, args[1]);
1901
1902 // Only JS objects can have properties.
1903 if (args[0]->IsJSObject()) {
1904 JSObject* object = JSObject::cast(args[0]);
1905 if (object->HasLocalProperty(key)) return Heap::true_value();
1906 } else if (args[0]->IsString()) {
1907 // Well, there is one exception: Handle [] on strings.
1908 uint32_t index;
1909 if (key->AsArrayIndex(&index)) {
1910 String* string = String::cast(args[0]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001911 StringShape shape(string);
1912 if (index < static_cast<uint32_t>(string->length(shape)))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001913 return Heap::true_value();
1914 }
1915 }
1916 return Heap::false_value();
1917}
1918
1919
1920static Object* Runtime_HasProperty(Arguments args) {
1921 NoHandleAllocation na;
1922 ASSERT(args.length() == 2);
1923
1924 // Only JS objects can have properties.
1925 if (args[0]->IsJSObject()) {
1926 JSObject* object = JSObject::cast(args[0]);
1927 CONVERT_CHECKED(String, key, args[1]);
1928 if (object->HasProperty(key)) return Heap::true_value();
1929 }
1930 return Heap::false_value();
1931}
1932
1933
1934static Object* Runtime_HasElement(Arguments args) {
1935 NoHandleAllocation na;
1936 ASSERT(args.length() == 2);
1937
1938 // Only JS objects can have elements.
1939 if (args[0]->IsJSObject()) {
1940 JSObject* object = JSObject::cast(args[0]);
1941 CONVERT_CHECKED(Smi, index_obj, args[1]);
1942 uint32_t index = index_obj->value();
1943 if (object->HasElement(index)) return Heap::true_value();
1944 }
1945 return Heap::false_value();
1946}
1947
1948
1949static Object* Runtime_IsPropertyEnumerable(Arguments args) {
1950 NoHandleAllocation ha;
1951 ASSERT(args.length() == 2);
1952
1953 CONVERT_CHECKED(JSObject, object, args[0]);
1954 CONVERT_CHECKED(String, key, args[1]);
1955
1956 uint32_t index;
1957 if (key->AsArrayIndex(&index)) {
1958 return Heap::ToBoolean(object->HasElement(index));
1959 }
1960
ager@chromium.org870a0b62008-11-04 11:43:05 +00001961 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
1962 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001963}
1964
1965
1966static Object* Runtime_GetPropertyNames(Arguments args) {
1967 HandleScope scope;
1968 ASSERT(args.length() == 1);
1969
1970 CONVERT_CHECKED(JSObject, raw_object, args[0]);
1971 Handle<JSObject> object(raw_object);
1972 return *GetKeysFor(object);
1973}
1974
1975
1976// Returns either a FixedArray as Runtime_GetPropertyNames,
1977// or, if the given object has an enum cache that contains
1978// all enumerable properties of the object and its prototypes
1979// have none, the map of the object. This is used to speed up
1980// the check for deletions during a for-in.
1981static Object* Runtime_GetPropertyNamesFast(Arguments args) {
1982 ASSERT(args.length() == 1);
1983
1984 CONVERT_CHECKED(JSObject, raw_object, args[0]);
1985
1986 if (raw_object->IsSimpleEnum()) return raw_object->map();
1987
1988 HandleScope scope;
1989 Handle<JSObject> object(raw_object);
1990 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
1991
1992 // Test again, since cache may have been built by preceding call.
1993 if (object->IsSimpleEnum()) return object->map();
1994
1995 return *content;
1996}
1997
1998
1999static Object* Runtime_GetArgumentsProperty(Arguments args) {
2000 NoHandleAllocation ha;
2001 ASSERT(args.length() == 1);
2002
2003 // Compute the frame holding the arguments.
2004 JavaScriptFrameIterator it;
2005 it.AdvanceToArgumentsFrame();
2006 JavaScriptFrame* frame = it.frame();
2007
2008 // Get the actual number of provided arguments.
2009 const uint32_t n = frame->GetProvidedParametersCount();
2010
2011 // Try to convert the key to an index. If successful and within
2012 // index return the the argument from the frame.
2013 uint32_t index;
2014 if (Array::IndexFromObject(args[0], &index) && index < n) {
2015 return frame->GetParameter(index);
2016 }
2017
2018 // Convert the key to a string.
2019 HandleScope scope;
2020 bool exception = false;
2021 Handle<Object> converted =
2022 Execution::ToString(args.at<Object>(0), &exception);
2023 if (exception) return Failure::Exception();
2024 Handle<String> key = Handle<String>::cast(converted);
2025
2026 // Try to convert the string key into an array index.
2027 if (key->AsArrayIndex(&index)) {
2028 if (index < n) {
2029 return frame->GetParameter(index);
2030 } else {
2031 return Top::initial_object_prototype()->GetElement(index);
2032 }
2033 }
2034
2035 // Handle special arguments properties.
2036 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
2037 if (key->Equals(Heap::callee_symbol())) return frame->function();
2038
2039 // Lookup in the initial Object.prototype object.
2040 return Top::initial_object_prototype()->GetProperty(*key);
2041}
2042
2043
2044static Object* Runtime_ToBool(Arguments args) {
2045 NoHandleAllocation ha;
2046 ASSERT(args.length() == 1);
2047
2048 return args[0]->ToBoolean();
2049}
2050
2051
2052// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
2053// Possible optimizations: put the type string into the oddballs.
2054static Object* Runtime_Typeof(Arguments args) {
2055 NoHandleAllocation ha;
2056
2057 Object* obj = args[0];
2058 if (obj->IsNumber()) return Heap::number_symbol();
2059 HeapObject* heap_obj = HeapObject::cast(obj);
2060
2061 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002062 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002063
2064 InstanceType instance_type = heap_obj->map()->instance_type();
2065 if (instance_type < FIRST_NONSTRING_TYPE) {
2066 return Heap::string_symbol();
2067 }
2068
2069 switch (instance_type) {
2070 case ODDBALL_TYPE:
2071 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
2072 return Heap::boolean_symbol();
2073 }
2074 if (heap_obj->IsNull()) {
2075 return Heap::object_symbol();
2076 }
2077 ASSERT(heap_obj->IsUndefined());
2078 return Heap::undefined_symbol();
2079 case JS_FUNCTION_TYPE:
2080 return Heap::function_symbol();
2081 default:
2082 // For any kind of object not handled above, the spec rule for
2083 // host objects gives that it is okay to return "object"
2084 return Heap::object_symbol();
2085 }
2086}
2087
2088
2089static Object* Runtime_StringToNumber(Arguments args) {
2090 NoHandleAllocation ha;
2091 ASSERT(args.length() == 1);
2092 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002093 subject->TryFlatten(StringShape(subject));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002094 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
2095}
2096
2097
2098static Object* Runtime_StringFromCharCodeArray(Arguments args) {
2099 NoHandleAllocation ha;
2100 ASSERT(args.length() == 1);
2101
2102 CONVERT_CHECKED(JSArray, codes, args[0]);
2103 int length = Smi::cast(codes->length())->value();
2104
2105 // Check if the string can be ASCII.
2106 int i;
2107 for (i = 0; i < length; i++) {
2108 Object* element = codes->GetElement(i);
2109 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
2110 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
2111 break;
2112 }
2113
2114 Object* object = NULL;
2115 if (i == length) { // The string is ASCII.
2116 object = Heap::AllocateRawAsciiString(length);
2117 } else { // The string is not ASCII.
2118 object = Heap::AllocateRawTwoByteString(length);
2119 }
2120
2121 if (object->IsFailure()) return object;
2122 String* result = String::cast(object);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002123 StringShape result_shape(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002124 for (int i = 0; i < length; i++) {
2125 Object* element = codes->GetElement(i);
2126 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002127 result->Set(result_shape, i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002128 }
2129 return result;
2130}
2131
2132
2133// kNotEscaped is generated by the following:
2134//
2135// #!/bin/perl
2136// for (my $i = 0; $i < 256; $i++) {
2137// print "\n" if $i % 16 == 0;
2138// my $c = chr($i);
2139// my $escaped = 1;
2140// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
2141// print $escaped ? "0, " : "1, ";
2142// }
2143
2144
2145static bool IsNotEscaped(uint16_t character) {
2146 // Only for 8 bit characters, the rest are always escaped (in a different way)
2147 ASSERT(character < 256);
2148 static const char kNotEscaped[256] = {
2149 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2150 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2151 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
2152 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
2153 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2154 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
2155 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2156 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
2157 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2158 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2159 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2160 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2161 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2162 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2163 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2165 };
2166 return kNotEscaped[character] != 0;
2167}
2168
2169
2170static Object* Runtime_URIEscape(Arguments args) {
2171 const char hex_chars[] = "0123456789ABCDEF";
2172 NoHandleAllocation ha;
2173 ASSERT(args.length() == 1);
2174 CONVERT_CHECKED(String, source, args[0]);
2175
ager@chromium.org870a0b62008-11-04 11:43:05 +00002176 source->TryFlatten(StringShape(source));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002177
2178 int escaped_length = 0;
2179 int length = source->length();
2180 {
2181 Access<StringInputBuffer> buffer(&string_input_buffer);
2182 buffer->Reset(source);
2183 while (buffer->has_more()) {
2184 uint16_t character = buffer->GetNext();
2185 if (character >= 256) {
2186 escaped_length += 6;
2187 } else if (IsNotEscaped(character)) {
2188 escaped_length++;
2189 } else {
2190 escaped_length += 3;
2191 }
2192 // We don't allow strings that are longer than Smi range.
2193 if (!Smi::IsValid(escaped_length)) {
2194 Top::context()->mark_out_of_memory();
2195 return Failure::OutOfMemoryException();
2196 }
2197 }
2198 }
2199 // No length change implies no change. Return original string if no change.
2200 if (escaped_length == length) {
2201 return source;
2202 }
2203 Object* o = Heap::AllocateRawAsciiString(escaped_length);
2204 if (o->IsFailure()) return o;
2205 String* destination = String::cast(o);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002206 StringShape dshape(destination);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002207 int dest_position = 0;
2208
2209 Access<StringInputBuffer> buffer(&string_input_buffer);
2210 buffer->Rewind();
2211 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002212 uint16_t chr = buffer->GetNext();
2213 if (chr >= 256) {
2214 destination->Set(dshape, dest_position, '%');
2215 destination->Set(dshape, dest_position+1, 'u');
2216 destination->Set(dshape, dest_position+2, hex_chars[chr >> 12]);
2217 destination->Set(dshape, dest_position+3, hex_chars[(chr >> 8) & 0xf]);
2218 destination->Set(dshape, dest_position+4, hex_chars[(chr >> 4) & 0xf]);
2219 destination->Set(dshape, dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002220 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002221 } else if (IsNotEscaped(chr)) {
2222 destination->Set(dshape, dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002223 dest_position++;
2224 } else {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002225 destination->Set(dshape, dest_position, '%');
2226 destination->Set(dshape, dest_position+1, hex_chars[chr >> 4]);
2227 destination->Set(dshape, dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002228 dest_position += 3;
2229 }
2230 }
2231 return destination;
2232}
2233
2234
2235static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
2236 static const signed char kHexValue['g'] = {
2237 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2238 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2239 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2240 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
2241 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2242 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2243 -1, 10, 11, 12, 13, 14, 15 };
2244
2245 if (character1 > 'f') return -1;
2246 int hi = kHexValue[character1];
2247 if (hi == -1) return -1;
2248 if (character2 > 'f') return -1;
2249 int lo = kHexValue[character2];
2250 if (lo == -1) return -1;
2251 return (hi << 4) + lo;
2252}
2253
2254
ager@chromium.org870a0b62008-11-04 11:43:05 +00002255static inline int Unescape(String* source,
2256 StringShape shape,
2257 int i,
2258 int length,
2259 int* step) {
2260 uint16_t character = source->Get(shape, i);
2261 int32_t hi = 0;
2262 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002263 if (character == '%' &&
2264 i <= length - 6 &&
ager@chromium.org870a0b62008-11-04 11:43:05 +00002265 source->Get(shape, i + 1) == 'u' &&
2266 (hi = TwoDigitHex(source->Get(shape, i + 2),
2267 source->Get(shape, i + 3))) != -1 &&
2268 (lo = TwoDigitHex(source->Get(shape, i + 4),
2269 source->Get(shape, i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002270 *step = 6;
2271 return (hi << 8) + lo;
2272 } else if (character == '%' &&
2273 i <= length - 3 &&
ager@chromium.org870a0b62008-11-04 11:43:05 +00002274 (lo = TwoDigitHex(source->Get(shape, i + 1),
2275 source->Get(shape, i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002276 *step = 3;
2277 return lo;
2278 } else {
2279 *step = 1;
2280 return character;
2281 }
2282}
2283
2284
2285static Object* Runtime_URIUnescape(Arguments args) {
2286 NoHandleAllocation ha;
2287 ASSERT(args.length() == 1);
2288 CONVERT_CHECKED(String, source, args[0]);
2289
ager@chromium.org870a0b62008-11-04 11:43:05 +00002290 source->TryFlatten(StringShape(source));
2291 StringShape source_shape(source);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002292
2293 bool ascii = true;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002294 int length = source->length(source_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002295
2296 int unescaped_length = 0;
2297 for (int i = 0; i < length; unescaped_length++) {
2298 int step;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002299 if (Unescape(source,
2300 source_shape,
2301 i,
2302 length,
2303 &step) >
2304 String::kMaxAsciiCharCode)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002305 ascii = false;
2306 i += step;
2307 }
2308
2309 // No length change implies no change. Return original string if no change.
2310 if (unescaped_length == length)
2311 return source;
2312
2313 Object* o = ascii ?
2314 Heap::AllocateRawAsciiString(unescaped_length) :
2315 Heap::AllocateRawTwoByteString(unescaped_length);
2316 if (o->IsFailure()) return o;
2317 String* destination = String::cast(o);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002318 StringShape destination_shape(destination);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002319
2320 int dest_position = 0;
2321 for (int i = 0; i < length; dest_position++) {
2322 int step;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002323 destination->Set(destination_shape,
2324 dest_position,
2325 Unescape(source, source_shape, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002326 i += step;
2327 }
2328 return destination;
2329}
2330
2331
2332static Object* Runtime_StringParseInt(Arguments args) {
2333 NoHandleAllocation ha;
2334
2335 CONVERT_CHECKED(String, s, args[0]);
2336 CONVERT_DOUBLE_CHECKED(n, args[1]);
2337 int radix = FastD2I(n);
2338
ager@chromium.org870a0b62008-11-04 11:43:05 +00002339 s->TryFlatten(StringShape(s));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002340
ager@chromium.org870a0b62008-11-04 11:43:05 +00002341 StringShape shape(s);
2342
2343 int len = s->length(shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002344 int i;
2345
2346 // Skip leading white space.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002347 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(shape, i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002348 if (i == len) return Heap::nan_value();
2349
2350 // Compute the sign (default to +).
2351 int sign = 1;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002352 if (s->Get(shape, i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002353 sign = -1;
2354 i++;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002355 } else if (s->Get(shape, i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002356 i++;
2357 }
2358
2359 // Compute the radix if 0.
2360 if (radix == 0) {
2361 radix = 10;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002362 if (i < len && s->Get(shape, i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002363 radix = 8;
2364 if (i + 1 < len) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002365 int c = s->Get(shape, i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002366 if (c == 'x' || c == 'X') {
2367 radix = 16;
2368 i += 2;
2369 }
2370 }
2371 }
2372 } else if (radix == 16) {
2373 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002374 if (i + 1 < len && s->Get(shape, i) == '0') {
2375 int c = s->Get(shape, i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002376 if (c == 'x' || c == 'X') i += 2;
2377 }
2378 }
2379
2380 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2381 double value;
2382 int end_index = StringToInt(s, i, radix, &value);
2383 if (end_index != i) {
2384 return Heap::NumberFromDouble(sign * value);
2385 }
2386 return Heap::nan_value();
2387}
2388
2389
2390static Object* Runtime_StringParseFloat(Arguments args) {
2391 NoHandleAllocation ha;
2392 CONVERT_CHECKED(String, str, args[0]);
2393
2394 // ECMA-262 section 15.1.2.3, empty string is NaN
2395 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
2396
2397 // Create a number object from the value.
2398 return Heap::NumberFromDouble(value);
2399}
2400
2401
2402static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
2403static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
2404
2405
2406template <class Converter>
2407static Object* ConvertCase(Arguments args,
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002408 unibrow::Mapping<Converter, 128>* mapping) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002409 NoHandleAllocation ha;
2410
2411 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002412 s->TryFlatten(StringShape(s));
2413 StringShape shape(s);
2414
2415 int raw_string_length = s->length(shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002416 // Assume that the string is not empty; we need this assumption later
2417 if (raw_string_length == 0) return s;
2418 int length = raw_string_length;
2419
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002420
2421 // We try this twice, once with the assumption that the result is
2422 // no longer than the input and, if that assumption breaks, again
2423 // with the exact length. This is implemented using a goto back
2424 // to this label if we discover that the assumption doesn't hold.
2425 // I apologize sincerely for this and will give a vaffel-is to
mads.s.ager31e71382008-08-13 09:32:07 +00002426 // the first person who can implement it in a nicer way.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002427 try_convert:
2428
2429 // Allocate the resulting string.
2430 //
2431 // NOTE: This assumes that the upper/lower case of an ascii
2432 // character is also ascii. This is currently the case, but it
2433 // might break in the future if we implement more context and locale
2434 // dependent upper/lower conversions.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002435 Object* o = shape.IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002436 ? Heap::AllocateRawAsciiString(length)
2437 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002438 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002439 String* result = String::cast(o);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002440 StringShape result_shape(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002441 bool has_changed_character = false;
2442
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002443 // Convert all characters to upper case, assuming that they will fit
2444 // in the buffer
2445 Access<StringInputBuffer> buffer(&string_input_buffer);
2446 buffer->Reset(s);
2447 unibrow::uchar chars[unibrow::kMaxCaseConvertedSize];
2448 int i = 0;
2449 // We can assume that the string is not empty
2450 uc32 current = buffer->GetNext();
2451 while (i < length) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002452 bool has_next = buffer->has_more();
2453 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002454 int char_length = mapping->get(current, next, chars);
2455 if (char_length == 0) {
2456 // The case conversion of this character is the character itself.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002457 result->Set(result_shape, i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002458 i++;
2459 } else if (char_length == 1) {
2460 // Common case: converting the letter resulted in one character.
2461 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002462 result->Set(result_shape, i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002463 has_changed_character = true;
2464 i++;
2465 } else if (length == raw_string_length) {
2466 // We've assumed that the result would be as long as the
2467 // input but here is a character that converts to several
2468 // characters. No matter, we calculate the exact length
2469 // of the result and try the whole thing again.
2470 //
2471 // Note that this leaves room for optimization. We could just
2472 // memcpy what we already have to the result string. Also,
2473 // the result string is the last object allocated we could
2474 // "realloc" it and probably, in the vast majority of cases,
2475 // extend the existing string to be able to hold the full
2476 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002477 int next_length = 0;
2478 if (has_next) {
2479 next_length = mapping->get(next, 0, chars);
2480 if (next_length == 0) next_length = 1;
2481 }
2482 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002483 while (buffer->has_more()) {
2484 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002485 // NOTE: we use 0 as the next character here because, while
2486 // the next character may affect what a character converts to,
2487 // it does not in any case affect the length of what it convert
2488 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002489 int char_length = mapping->get(current, 0, chars);
2490 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002491 current_length += char_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002492 }
2493 length = current_length;
2494 goto try_convert;
2495 } else {
2496 for (int j = 0; j < char_length; j++) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002497 result->Set(result_shape, i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002498 i++;
2499 }
2500 has_changed_character = true;
2501 }
2502 current = next;
2503 }
2504 if (has_changed_character) {
2505 return result;
2506 } else {
2507 // If we didn't actually change anything in doing the conversion
2508 // we simple return the result and let the converted string
2509 // become garbage; there is no reason to keep two identical strings
2510 // alive.
2511 return s;
2512 }
2513}
2514
2515
2516static Object* Runtime_StringToLowerCase(Arguments args) {
2517 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
2518}
2519
2520
2521static Object* Runtime_StringToUpperCase(Arguments args) {
2522 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
2523}
2524
2525
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002526static Object* Runtime_NumberToString(Arguments args) {
2527 NoHandleAllocation ha;
2528 ASSERT(args.length() == 1);
2529
2530 Object* number = args[0];
2531 RUNTIME_ASSERT(number->IsNumber());
2532
2533 Object* cached = Heap::GetNumberStringCache(number);
2534 if (cached != Heap::undefined_value()) {
2535 return cached;
2536 }
2537
2538 char arr[100];
2539 Vector<char> buffer(arr, ARRAY_SIZE(arr));
2540 const char* str;
2541 if (number->IsSmi()) {
2542 int num = Smi::cast(number)->value();
2543 str = IntToCString(num, buffer);
2544 } else {
2545 double num = HeapNumber::cast(number)->value();
2546 str = DoubleToCString(num, buffer);
2547 }
2548 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2549
2550 if (!result->IsFailure()) {
2551 Heap::SetNumberStringCache(number, String::cast(result));
2552 }
2553 return result;
2554}
2555
2556
2557static Object* Runtime_NumberToInteger(Arguments args) {
2558 NoHandleAllocation ha;
2559 ASSERT(args.length() == 1);
2560
2561 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002562 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002563 CONVERT_DOUBLE_CHECKED(number, obj);
2564 return Heap::NumberFromDouble(DoubleToInteger(number));
2565}
2566
2567
2568static Object* Runtime_NumberToJSUint32(Arguments args) {
2569 NoHandleAllocation ha;
2570 ASSERT(args.length() == 1);
2571
2572 Object* obj = args[0];
2573 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
2574 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
2575 return Heap::NumberFromUint32(number);
2576}
2577
2578
2579static Object* Runtime_NumberToJSInt32(Arguments args) {
2580 NoHandleAllocation ha;
2581 ASSERT(args.length() == 1);
2582
2583 Object* obj = args[0];
2584 if (obj->IsSmi()) return obj;
2585 CONVERT_DOUBLE_CHECKED(number, obj);
2586 return Heap::NumberFromInt32(DoubleToInt32(number));
2587}
2588
2589
ager@chromium.org870a0b62008-11-04 11:43:05 +00002590// Converts a Number to a Smi, if possible. Returns NaN if the number is not
2591// a small integer.
2592static Object* Runtime_NumberToSmi(Arguments args) {
2593 NoHandleAllocation ha;
2594 ASSERT(args.length() == 1);
2595
2596 Object* obj = args[0];
2597 if (obj->IsSmi()) {
2598 return obj;
2599 }
2600 if (obj->IsHeapNumber()) {
2601 double value = HeapNumber::cast(obj)->value();
2602 int int_value = FastD2I(value);
2603 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
2604 return Smi::FromInt(int_value);
2605 }
2606 }
2607 return Heap::nan_value();
2608}
2609
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002610static Object* Runtime_NumberAdd(Arguments args) {
2611 NoHandleAllocation ha;
2612 ASSERT(args.length() == 2);
2613
2614 CONVERT_DOUBLE_CHECKED(x, args[0]);
2615 CONVERT_DOUBLE_CHECKED(y, args[1]);
2616 return Heap::AllocateHeapNumber(x + y);
2617}
2618
2619
2620static Object* Runtime_NumberSub(Arguments args) {
2621 NoHandleAllocation ha;
2622 ASSERT(args.length() == 2);
2623
2624 CONVERT_DOUBLE_CHECKED(x, args[0]);
2625 CONVERT_DOUBLE_CHECKED(y, args[1]);
2626 return Heap::AllocateHeapNumber(x - y);
2627}
2628
2629
2630static Object* Runtime_NumberMul(Arguments args) {
2631 NoHandleAllocation ha;
2632 ASSERT(args.length() == 2);
2633
2634 CONVERT_DOUBLE_CHECKED(x, args[0]);
2635 CONVERT_DOUBLE_CHECKED(y, args[1]);
2636 return Heap::AllocateHeapNumber(x * y);
2637}
2638
2639
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002640static Object* Runtime_NumberUnaryMinus(Arguments args) {
2641 NoHandleAllocation ha;
2642 ASSERT(args.length() == 1);
2643
2644 CONVERT_DOUBLE_CHECKED(x, args[0]);
2645 return Heap::AllocateHeapNumber(-x);
2646}
2647
2648
2649static Object* Runtime_NumberDiv(Arguments args) {
2650 NoHandleAllocation ha;
2651 ASSERT(args.length() == 2);
2652
2653 CONVERT_DOUBLE_CHECKED(x, args[0]);
2654 CONVERT_DOUBLE_CHECKED(y, args[1]);
2655 return Heap::NewNumberFromDouble(x / y);
2656}
2657
2658
2659static Object* Runtime_NumberMod(Arguments args) {
2660 NoHandleAllocation ha;
2661 ASSERT(args.length() == 2);
2662
2663 CONVERT_DOUBLE_CHECKED(x, args[0]);
2664 CONVERT_DOUBLE_CHECKED(y, args[1]);
2665
2666#ifdef WIN32
2667 // Workaround MS fmod bugs. ECMA-262 says:
2668 // dividend is finite and divisor is an infinity => result equals dividend
2669 // dividend is a zero and divisor is nonzero finite => result equals dividend
2670 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
2671 !(x == 0 && (y != 0 && isfinite(y))))
2672#endif
2673 x = fmod(x, y);
2674 // NewNumberFromDouble may return a Smi instead of a Number object
2675 return Heap::NewNumberFromDouble(x);
2676}
2677
2678
2679static Object* Runtime_StringAdd(Arguments args) {
2680 NoHandleAllocation ha;
2681 ASSERT(args.length() == 2);
2682
2683 CONVERT_CHECKED(String, str1, args[0]);
2684 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002685 StringShape shape1(str1);
2686 StringShape shape2(str2);
2687 int len1 = str1->length(shape1);
2688 int len2 = str2->length(shape2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002689 if (len1 == 0) return str2;
2690 if (len2 == 0) return str1;
2691 int length_sum = len1 + len2;
2692 // Make sure that an out of memory exception is thrown if the length
2693 // of the new cons string is too large to fit in a Smi.
2694 if (length_sum > Smi::kMaxValue || length_sum < 0) {
2695 Top::context()->mark_out_of_memory();
2696 return Failure::OutOfMemoryException();
2697 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00002698 return Heap::AllocateConsString(str1, shape1, str2, shape2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002699}
2700
2701
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002702template<typename sinkchar>
2703static inline void StringBuilderConcatHelper(String* special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00002704 StringShape special_shape,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002705 sinkchar* sink,
2706 FixedArray* fixed_array,
2707 int array_length) {
2708 int position = 0;
2709 for (int i = 0; i < array_length; i++) {
2710 Object* element = fixed_array->get(i);
2711 if (element->IsSmi()) {
2712 int len = Smi::cast(element)->value();
2713 int pos = len >> 11;
2714 len &= 0x7ff;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002715 String::WriteToFlat(special,
2716 special_shape,
2717 sink + position,
2718 pos,
2719 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002720 position += len;
2721 } else {
2722 String* string = String::cast(element);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002723 StringShape shape(string);
2724 int element_length = string->length(shape);
2725 String::WriteToFlat(string, shape, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002726 position += element_length;
2727 }
2728 }
2729}
2730
2731
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002732static Object* Runtime_StringBuilderConcat(Arguments args) {
2733 NoHandleAllocation ha;
2734 ASSERT(args.length() == 2);
2735 CONVERT_CHECKED(JSArray, array, args[0]);
2736 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002737 StringShape special_shape(special);
2738 int special_length = special->length(special_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002739 Object* smi_array_length = array->length();
2740 if (!smi_array_length->IsSmi()) {
2741 Top::context()->mark_out_of_memory();
2742 return Failure::OutOfMemoryException();
2743 }
2744 int array_length = Smi::cast(smi_array_length)->value();
2745 if (!array->HasFastElements()) {
2746 return Top::Throw(Heap::illegal_argument_symbol());
2747 }
2748 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002749 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002750 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002751 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002752
2753 if (array_length == 0) {
2754 return Heap::empty_string();
2755 } else if (array_length == 1) {
2756 Object* first = fixed_array->get(0);
2757 if (first->IsString()) return first;
2758 }
2759
ager@chromium.org870a0b62008-11-04 11:43:05 +00002760 bool ascii = special_shape.IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002761 int position = 0;
2762 for (int i = 0; i < array_length; i++) {
2763 Object* elt = fixed_array->get(i);
2764 if (elt->IsSmi()) {
2765 int len = Smi::cast(elt)->value();
2766 int pos = len >> 11;
2767 len &= 0x7ff;
2768 if (pos + len > special_length) {
2769 return Top::Throw(Heap::illegal_argument_symbol());
2770 }
2771 position += len;
2772 } else if (elt->IsString()) {
2773 String* element = String::cast(elt);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002774 StringShape element_shape(element);
2775 int element_length = element->length(element_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002776 if (!Smi::IsValid(element_length + position)) {
2777 Top::context()->mark_out_of_memory();
2778 return Failure::OutOfMemoryException();
2779 }
2780 position += element_length;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002781 if (ascii && !element_shape.IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002782 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002783 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002784 } else {
2785 return Top::Throw(Heap::illegal_argument_symbol());
2786 }
2787 }
2788
2789 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002790 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002791
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002792 if (ascii) {
2793 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002794 if (object->IsFailure()) return object;
2795 SeqAsciiString* answer = SeqAsciiString::cast(object);
2796 StringBuilderConcatHelper(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00002797 special_shape,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002798 answer->GetChars(),
2799 fixed_array,
2800 array_length);
2801 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002802 } else {
2803 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002804 if (object->IsFailure()) return object;
2805 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
2806 StringBuilderConcatHelper(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00002807 special_shape,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002808 answer->GetChars(),
2809 fixed_array,
2810 array_length);
2811 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002812 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002813}
2814
2815
2816static Object* Runtime_NumberOr(Arguments args) {
2817 NoHandleAllocation ha;
2818 ASSERT(args.length() == 2);
2819
2820 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2821 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2822 return Heap::NumberFromInt32(x | y);
2823}
2824
2825
2826static Object* Runtime_NumberAnd(Arguments args) {
2827 NoHandleAllocation ha;
2828 ASSERT(args.length() == 2);
2829
2830 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2831 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2832 return Heap::NumberFromInt32(x & y);
2833}
2834
2835
2836static Object* Runtime_NumberXor(Arguments args) {
2837 NoHandleAllocation ha;
2838 ASSERT(args.length() == 2);
2839
2840 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2841 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2842 return Heap::NumberFromInt32(x ^ y);
2843}
2844
2845
2846static Object* Runtime_NumberNot(Arguments args) {
2847 NoHandleAllocation ha;
2848 ASSERT(args.length() == 1);
2849
2850 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2851 return Heap::NumberFromInt32(~x);
2852}
2853
2854
2855static Object* Runtime_NumberShl(Arguments args) {
2856 NoHandleAllocation ha;
2857 ASSERT(args.length() == 2);
2858
2859 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2860 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2861 return Heap::NumberFromInt32(x << (y & 0x1f));
2862}
2863
2864
2865static Object* Runtime_NumberShr(Arguments args) {
2866 NoHandleAllocation ha;
2867 ASSERT(args.length() == 2);
2868
2869 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
2870 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2871 return Heap::NumberFromUint32(x >> (y & 0x1f));
2872}
2873
2874
2875static Object* Runtime_NumberSar(Arguments args) {
2876 NoHandleAllocation ha;
2877 ASSERT(args.length() == 2);
2878
2879 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2880 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2881 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
2882}
2883
2884
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002885static Object* Runtime_NumberEquals(Arguments args) {
2886 NoHandleAllocation ha;
2887 ASSERT(args.length() == 2);
2888
2889 CONVERT_DOUBLE_CHECKED(x, args[0]);
2890 CONVERT_DOUBLE_CHECKED(y, args[1]);
2891 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
2892 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
2893 if (x == y) return Smi::FromInt(EQUAL);
2894 Object* result;
2895 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
2896 result = Smi::FromInt(EQUAL);
2897 } else {
2898 result = Smi::FromInt(NOT_EQUAL);
2899 }
2900 return result;
2901}
2902
2903
2904static Object* Runtime_StringEquals(Arguments args) {
2905 NoHandleAllocation ha;
2906 ASSERT(args.length() == 2);
2907
2908 CONVERT_CHECKED(String, x, args[0]);
2909 CONVERT_CHECKED(String, y, args[1]);
2910
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002911 bool not_equal = !x->Equals(y);
2912 // This is slightly convoluted because the value that signifies
2913 // equality is 0 and inequality is 1 so we have to negate the result
2914 // from String::Equals.
2915 ASSERT(not_equal == 0 || not_equal == 1);
2916 STATIC_CHECK(EQUAL == 0);
2917 STATIC_CHECK(NOT_EQUAL == 1);
2918 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002919}
2920
2921
2922static Object* Runtime_NumberCompare(Arguments args) {
2923 NoHandleAllocation ha;
2924 ASSERT(args.length() == 3);
2925
2926 CONVERT_DOUBLE_CHECKED(x, args[0]);
2927 CONVERT_DOUBLE_CHECKED(y, args[1]);
2928 if (isnan(x) || isnan(y)) return args[2];
2929 if (x == y) return Smi::FromInt(EQUAL);
2930 if (isless(x, y)) return Smi::FromInt(LESS);
2931 return Smi::FromInt(GREATER);
2932}
2933
2934
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002935// Compare two Smis as if they were converted to strings and then
2936// compared lexicographically.
2937static Object* Runtime_SmiLexicographicCompare(Arguments args) {
2938 NoHandleAllocation ha;
2939 ASSERT(args.length() == 2);
2940
2941 // Arrays for the individual characters of the two Smis. Smis are
2942 // 31 bit integers and 10 decimal digits are therefore enough.
2943 static int x_elms[10];
2944 static int y_elms[10];
2945
2946 // Extract the integer values from the Smis.
2947 CONVERT_CHECKED(Smi, x, args[0]);
2948 CONVERT_CHECKED(Smi, y, args[1]);
2949 int x_value = x->value();
2950 int y_value = y->value();
2951
2952 // If the integers are equal so are the string representations.
2953 if (x_value == y_value) return Smi::FromInt(EQUAL);
2954
2955 // If one of the integers are zero the normal integer order is the
2956 // same as the lexicographic order of the string representations.
2957 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
2958
2959 // If only one of the intergers is negative the negative number is
2960 // smallest because the char code of '-' is less than the char code
2961 // of any digit. Otherwise, we make both values positive.
2962 if (x_value < 0 || y_value < 0) {
2963 if (y_value >= 0) return Smi::FromInt(LESS);
2964 if (x_value >= 0) return Smi::FromInt(GREATER);
2965 x_value = -x_value;
2966 y_value = -y_value;
2967 }
2968
2969 // Convert the integers to arrays of their decimal digits.
2970 int x_index = 0;
2971 int y_index = 0;
2972 while (x_value > 0) {
2973 x_elms[x_index++] = x_value % 10;
2974 x_value /= 10;
2975 }
2976 while (y_value > 0) {
2977 y_elms[y_index++] = y_value % 10;
2978 y_value /= 10;
2979 }
2980
2981 // Loop through the arrays of decimal digits finding the first place
2982 // where they differ.
2983 while (--x_index >= 0 && --y_index >= 0) {
2984 int diff = x_elms[x_index] - y_elms[y_index];
2985 if (diff != 0) return Smi::FromInt(diff);
2986 }
2987
2988 // If one array is a suffix of the other array, the longest array is
2989 // the representation of the largest of the Smis in the
2990 // lexicographic ordering.
2991 return Smi::FromInt(x_index - y_index);
2992}
2993
2994
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002995static Object* Runtime_StringCompare(Arguments args) {
2996 NoHandleAllocation ha;
2997 ASSERT(args.length() == 2);
2998
2999 CONVERT_CHECKED(String, x, args[0]);
3000 CONVERT_CHECKED(String, y, args[1]);
3001
ager@chromium.org870a0b62008-11-04 11:43:05 +00003002 StringShape x_shape(x);
3003 StringShape y_shape(y);
3004
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003005 // A few fast case tests before we flatten.
3006 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003007 if (y->length(y_shape) == 0) {
3008 if (x->length(x_shape) == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003009 return Smi::FromInt(GREATER);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003010 } else if (x->length(x_shape) == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003011 return Smi::FromInt(LESS);
3012 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003013
ager@chromium.org870a0b62008-11-04 11:43:05 +00003014 int d = x->Get(x_shape, 0) - y->Get(y_shape, 0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003015 if (d < 0) return Smi::FromInt(LESS);
3016 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003017
ager@chromium.org870a0b62008-11-04 11:43:05 +00003018 x->TryFlatten(x_shape); // Shapes are no longer valid!
3019 y->TryFlatten(y_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003020
3021 static StringInputBuffer bufx;
3022 static StringInputBuffer bufy;
3023 bufx.Reset(x);
3024 bufy.Reset(y);
3025 while (bufx.has_more() && bufy.has_more()) {
3026 int d = bufx.GetNext() - bufy.GetNext();
3027 if (d < 0) return Smi::FromInt(LESS);
3028 else if (d > 0) return Smi::FromInt(GREATER);
3029 }
3030
3031 // x is (non-trivial) prefix of y:
3032 if (bufy.has_more()) return Smi::FromInt(LESS);
3033 // y is prefix of x:
3034 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
3035}
3036
3037
3038static Object* Runtime_Math_abs(Arguments args) {
3039 NoHandleAllocation ha;
3040 ASSERT(args.length() == 1);
3041
3042 CONVERT_DOUBLE_CHECKED(x, args[0]);
3043 return Heap::AllocateHeapNumber(fabs(x));
3044}
3045
3046
3047static Object* Runtime_Math_acos(Arguments args) {
3048 NoHandleAllocation ha;
3049 ASSERT(args.length() == 1);
3050
3051 CONVERT_DOUBLE_CHECKED(x, args[0]);
3052 return Heap::AllocateHeapNumber(acos(x));
3053}
3054
3055
3056static Object* Runtime_Math_asin(Arguments args) {
3057 NoHandleAllocation ha;
3058 ASSERT(args.length() == 1);
3059
3060 CONVERT_DOUBLE_CHECKED(x, args[0]);
3061 return Heap::AllocateHeapNumber(asin(x));
3062}
3063
3064
3065static Object* Runtime_Math_atan(Arguments args) {
3066 NoHandleAllocation ha;
3067 ASSERT(args.length() == 1);
3068
3069 CONVERT_DOUBLE_CHECKED(x, args[0]);
3070 return Heap::AllocateHeapNumber(atan(x));
3071}
3072
3073
3074static Object* Runtime_Math_atan2(Arguments args) {
3075 NoHandleAllocation ha;
3076 ASSERT(args.length() == 2);
3077
3078 CONVERT_DOUBLE_CHECKED(x, args[0]);
3079 CONVERT_DOUBLE_CHECKED(y, args[1]);
3080 double result;
3081 if (isinf(x) && isinf(y)) {
3082 // Make sure that the result in case of two infinite arguments
3083 // is a multiple of Pi / 4. The sign of the result is determined
3084 // by the first argument (x) and the sign of the second argument
3085 // determines the multiplier: one or three.
3086 static double kPiDividedBy4 = 0.78539816339744830962;
3087 int multiplier = (x < 0) ? -1 : 1;
3088 if (y < 0) multiplier *= 3;
3089 result = multiplier * kPiDividedBy4;
3090 } else {
3091 result = atan2(x, y);
3092 }
3093 return Heap::AllocateHeapNumber(result);
3094}
3095
3096
3097static Object* Runtime_Math_ceil(Arguments args) {
3098 NoHandleAllocation ha;
3099 ASSERT(args.length() == 1);
3100
3101 CONVERT_DOUBLE_CHECKED(x, args[0]);
3102 return Heap::NumberFromDouble(ceiling(x));
3103}
3104
3105
3106static Object* Runtime_Math_cos(Arguments args) {
3107 NoHandleAllocation ha;
3108 ASSERT(args.length() == 1);
3109
3110 CONVERT_DOUBLE_CHECKED(x, args[0]);
3111 return Heap::AllocateHeapNumber(cos(x));
3112}
3113
3114
3115static Object* Runtime_Math_exp(Arguments args) {
3116 NoHandleAllocation ha;
3117 ASSERT(args.length() == 1);
3118
3119 CONVERT_DOUBLE_CHECKED(x, args[0]);
3120 return Heap::AllocateHeapNumber(exp(x));
3121}
3122
3123
3124static Object* Runtime_Math_floor(Arguments args) {
3125 NoHandleAllocation ha;
3126 ASSERT(args.length() == 1);
3127
3128 CONVERT_DOUBLE_CHECKED(x, args[0]);
3129 return Heap::NumberFromDouble(floor(x));
3130}
3131
3132
3133static Object* Runtime_Math_log(Arguments args) {
3134 NoHandleAllocation ha;
3135 ASSERT(args.length() == 1);
3136
3137 CONVERT_DOUBLE_CHECKED(x, args[0]);
3138 return Heap::AllocateHeapNumber(log(x));
3139}
3140
3141
3142static Object* Runtime_Math_pow(Arguments args) {
3143 NoHandleAllocation ha;
3144 ASSERT(args.length() == 2);
3145
3146 CONVERT_DOUBLE_CHECKED(x, args[0]);
3147 CONVERT_DOUBLE_CHECKED(y, args[1]);
3148 if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
3149 return Heap::nan_value();
3150 } else if (y == 0) {
3151 return Smi::FromInt(1);
3152 } else {
3153 return Heap::AllocateHeapNumber(pow(x, y));
3154 }
3155}
3156
3157// Returns a number value with positive sign, greater than or equal to
3158// 0 but less than 1, chosen randomly.
mads.s.ager31e71382008-08-13 09:32:07 +00003159static Object* Runtime_Math_random(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003160 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003161 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003162
3163 // To get much better precision, we combine the results of two
3164 // invocations of random(). The result is computed by normalizing a
3165 // double in the range [0, RAND_MAX + 1) obtained by adding the
3166 // high-order bits in the range [0, RAND_MAX] with the low-order
3167 // bits in the range [0, 1).
3168 double lo = static_cast<double>(random()) / (RAND_MAX + 1.0);
3169 double hi = static_cast<double>(random());
3170 double result = (hi + lo) / (RAND_MAX + 1.0);
3171 ASSERT(result >= 0 && result < 1);
3172 return Heap::AllocateHeapNumber(result);
3173}
3174
3175
3176static Object* Runtime_Math_round(Arguments args) {
3177 NoHandleAllocation ha;
3178 ASSERT(args.length() == 1);
3179
3180 CONVERT_DOUBLE_CHECKED(x, args[0]);
3181 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
3182 return Heap::NumberFromDouble(floor(x + 0.5));
3183}
3184
3185
3186static Object* Runtime_Math_sin(Arguments args) {
3187 NoHandleAllocation ha;
3188 ASSERT(args.length() == 1);
3189
3190 CONVERT_DOUBLE_CHECKED(x, args[0]);
3191 return Heap::AllocateHeapNumber(sin(x));
3192}
3193
3194
3195static Object* Runtime_Math_sqrt(Arguments args) {
3196 NoHandleAllocation ha;
3197 ASSERT(args.length() == 1);
3198
3199 CONVERT_DOUBLE_CHECKED(x, args[0]);
3200 return Heap::AllocateHeapNumber(sqrt(x));
3201}
3202
3203
3204static Object* Runtime_Math_tan(Arguments args) {
3205 NoHandleAllocation ha;
3206 ASSERT(args.length() == 1);
3207
3208 CONVERT_DOUBLE_CHECKED(x, args[0]);
3209 return Heap::AllocateHeapNumber(tan(x));
3210}
3211
3212
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003213// The NewArguments function is only used when constructing the
3214// arguments array when calling non-functions from JavaScript in
3215// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003216static Object* Runtime_NewArguments(Arguments args) {
3217 NoHandleAllocation ha;
3218 ASSERT(args.length() == 1);
3219
3220 // ECMA-262, 3rd., 10.1.8, p.39
3221 CONVERT_CHECKED(JSFunction, callee, args[0]);
3222
3223 // Compute the frame holding the arguments.
3224 JavaScriptFrameIterator it;
3225 it.AdvanceToArgumentsFrame();
3226 JavaScriptFrame* frame = it.frame();
3227
3228 const int length = frame->GetProvidedParametersCount();
3229 Object* result = Heap::AllocateArgumentsObject(callee, length);
3230 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003231 if (length > 0) {
3232 Object* obj = Heap::AllocateFixedArray(length);
3233 if (obj->IsFailure()) return obj;
3234 FixedArray* array = FixedArray::cast(obj);
3235 ASSERT(array->length() == length);
3236 WriteBarrierMode mode = array->GetWriteBarrierMode();
3237 for (int i = 0; i < length; i++) {
3238 array->set(i, frame->GetParameter(i), mode);
3239 }
3240 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003241 }
3242 return result;
3243}
3244
3245
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003246static Object* Runtime_NewArgumentsFast(Arguments args) {
3247 NoHandleAllocation ha;
3248 ASSERT(args.length() == 3);
3249
3250 JSFunction* callee = JSFunction::cast(args[0]);
3251 Object** parameters = reinterpret_cast<Object**>(args[1]);
3252 const int length = Smi::cast(args[2])->value();
3253
3254 Object* result = Heap::AllocateArgumentsObject(callee, length);
3255 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003256 ASSERT(Heap::InNewSpace(result));
3257
3258 // Allocate the elements if needed.
3259 if (length > 0) {
3260 // Allocate the fixed array.
3261 Object* obj = Heap::AllocateRawFixedArray(length);
3262 if (obj->IsFailure()) return obj;
3263 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
3264 FixedArray* array = FixedArray::cast(obj);
3265 array->set_length(length);
3266 WriteBarrierMode mode = array->GetWriteBarrierMode();
3267 for (int i = 0; i < length; i++) {
3268 array->set(i, *--parameters, mode);
3269 }
3270 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
3271 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003272 }
3273 return result;
3274}
3275
3276
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003277static Object* Runtime_NewClosure(Arguments args) {
3278 HandleScope scope;
3279 ASSERT(args.length() == 2);
3280 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
3281 CONVERT_ARG_CHECKED(Context, context, 1);
3282
3283 Handle<JSFunction> result =
3284 Factory::NewFunctionFromBoilerplate(boilerplate, context);
3285 return *result;
3286}
3287
3288
3289static Object* Runtime_NewObject(Arguments args) {
3290 NoHandleAllocation ha;
3291 ASSERT(args.length() == 1);
3292
3293 Object* constructor = args[0];
3294 if (constructor->IsJSFunction()) {
3295 JSFunction* function = JSFunction::cast(constructor);
3296
3297 // Handle steping into constructors.
3298 if (Debug::StepInActive()) {
3299 StackFrameIterator it;
3300 it.Advance();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003301 ASSERT(it.frame()->is_construct());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003302 it.Advance();
3303 if (it.frame()->fp() == Debug::step_in_fp()) {
3304 HandleScope scope;
3305 Debug::FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
3306 }
3307 }
3308
3309 if (function->has_initial_map() &&
3310 function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
3311 // The 'Function' function ignores the receiver object when
3312 // called using 'new' and creates a new JSFunction object that
3313 // is returned. The receiver object is only used for error
3314 // reporting if an error occurs when constructing the new
3315 // JSFunction. AllocateJSObject should not be used to allocate
3316 // JSFunctions since it does not properly initialize the shared
3317 // part of the function. Since the receiver is ignored anyway,
3318 // we use the global object as the receiver instead of a new
3319 // JSFunction object. This way, errors are reported the same
3320 // way whether or not 'Function' is called using 'new'.
3321 return Top::context()->global();
3322 }
3323 return Heap::AllocateJSObject(function);
3324 }
3325
3326 HandleScope scope;
3327 Handle<Object> cons(constructor);
3328 // The constructor is not a function; throw a type error.
3329 Handle<Object> type_error =
3330 Factory::NewTypeError("not_constructor", HandleVector(&cons, 1));
3331 return Top::Throw(*type_error);
3332}
3333
3334
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003335static Object* Runtime_LazyCompile(Arguments args) {
3336 HandleScope scope;
3337 ASSERT(args.length() == 1);
3338
3339 Handle<JSFunction> function = args.at<JSFunction>(0);
3340#ifdef DEBUG
3341 if (FLAG_trace_lazy) {
3342 PrintF("[lazy: ");
3343 function->shared()->name()->Print();
3344 PrintF("]\n");
3345 }
3346#endif
3347
3348 // Compile the target function.
3349 ASSERT(!function->is_compiled());
3350 if (!CompileLazy(function, KEEP_EXCEPTION)) {
3351 return Failure::Exception();
3352 }
3353
3354 return function->code();
3355}
3356
3357
3358static Object* Runtime_GetCalledFunction(Arguments args) {
3359 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00003360 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003361 StackFrameIterator it;
3362 // Get past the JS-to-C exit frame.
3363 ASSERT(it.frame()->is_exit());
3364 it.Advance();
3365 // Get past the CALL_NON_FUNCTION activation frame.
3366 ASSERT(it.frame()->is_java_script());
3367 it.Advance();
3368 // Argument adaptor frames do not copy the function; we have to skip
3369 // past them to get to the real calling frame.
3370 if (it.frame()->is_arguments_adaptor()) it.Advance();
3371 // Get the function from the top of the expression stack of the
3372 // calling frame.
3373 StandardFrame* frame = StandardFrame::cast(it.frame());
3374 int index = frame->ComputeExpressionsCount() - 1;
3375 Object* result = frame->GetExpression(index);
3376 return result;
3377}
3378
3379
3380static Object* Runtime_GetFunctionDelegate(Arguments args) {
3381 HandleScope scope;
3382 ASSERT(args.length() == 1);
3383 RUNTIME_ASSERT(!args[0]->IsJSFunction());
3384 return *Execution::GetFunctionDelegate(args.at<Object>(0));
3385}
3386
3387
3388static Object* Runtime_NewContext(Arguments args) {
3389 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00003390 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003391
kasper.lund7276f142008-07-30 08:49:36 +00003392 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003393 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
3394 Object* result = Heap::AllocateFunctionContext(length, function);
3395 if (result->IsFailure()) return result;
3396
3397 Top::set_context(Context::cast(result));
3398
kasper.lund7276f142008-07-30 08:49:36 +00003399 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003400}
3401
3402
3403static Object* Runtime_PushContext(Arguments args) {
3404 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00003405 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003406
3407 // Convert the object to a proper JavaScript object.
kasper.lund7276f142008-07-30 08:49:36 +00003408 Object* object = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003409 if (!object->IsJSObject()) {
3410 object = object->ToObject();
3411 if (object->IsFailure()) {
3412 if (!Failure::cast(object)->IsInternalError()) return object;
3413 HandleScope scope;
kasper.lund7276f142008-07-30 08:49:36 +00003414 Handle<Object> handle(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003415 Handle<Object> result =
3416 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
3417 return Top::Throw(*result);
3418 }
3419 }
3420
3421 Object* result =
3422 Heap::AllocateWithContext(Top::context(), JSObject::cast(object));
3423 if (result->IsFailure()) return result;
3424
3425 Top::set_context(Context::cast(result));
3426
kasper.lund7276f142008-07-30 08:49:36 +00003427 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003428}
3429
3430
3431static Object* Runtime_LookupContext(Arguments args) {
3432 HandleScope scope;
3433 ASSERT(args.length() == 2);
3434
3435 CONVERT_ARG_CHECKED(Context, context, 0);
3436 CONVERT_ARG_CHECKED(String, name, 1);
3437
3438 int index;
3439 PropertyAttributes attributes;
3440 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003441 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003442 context->Lookup(name, flags, &index, &attributes);
3443
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003444 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003445 ASSERT(holder->IsJSObject());
3446 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003447 }
3448
3449 // No intermediate context found. Use global object by default.
3450 return Top::context()->global();
3451}
3452
3453
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003454// A mechanism to return pairs of Object*'s. This is somewhat
3455// compiler-dependent as it assumes that a 64-bit value (a long long)
3456// is returned via two registers (edx:eax on ia32). Both the ia32 and
3457// arm platform support this; it is mostly an issue of "coaxing" the
3458// compiler to do the right thing.
3459//
3460// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003461typedef uint64_t ObjectPair;
3462static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003463 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003464 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003465}
3466
3467
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003468static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003469 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
3470 USE(attributes);
3471 return x->IsTheHole() ? Heap::undefined_value() : x;
3472}
3473
3474
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003475static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
3476 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003477 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003478 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003479 JSFunction* context_extension_function =
3480 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003481 // If the holder isn't a context extension object, we just return it
3482 // as the receiver. This allows arguments objects to be used as
3483 // receivers, but only if they are put in the context scope chain
3484 // explicitly via a with-statement.
3485 Object* constructor = holder->map()->constructor();
3486 if (constructor != context_extension_function) return holder;
3487 // Fall back to using the global object as the receiver if the
3488 // property turns out to be a local variable allocated in a context
3489 // extension object - introduced via eval.
3490 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003491}
3492
3493
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003494static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003495 HandleScope scope;
3496 ASSERT(args.length() == 2);
3497
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003498 if (!args[0]->IsContext() || !args[1]->IsString()) {
3499 return MakePair(IllegalOperation(), NULL);
3500 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003501 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003502 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003503
3504 int index;
3505 PropertyAttributes attributes;
3506 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003507 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003508 context->Lookup(name, flags, &index, &attributes);
3509
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003510 // If the index is non-negative, the slot has been found in a local
3511 // variable or a parameter. Read it from the context object or the
3512 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003513 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003514 // If the "property" we were looking for is a local variable or an
3515 // argument in a context, the receiver is the global object; see
3516 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
3517 JSObject* receiver = Top::context()->global()->global_receiver();
3518 Object* value = (holder->IsContext())
3519 ? Context::cast(*holder)->get(index)
3520 : JSObject::cast(*holder)->GetElement(index);
3521 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003522 }
3523
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003524 // If the holder is found, we read the property from it.
3525 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00003526 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003527 JSObject* object = JSObject::cast(*holder);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003528 JSObject* receiver = (object->IsGlobalObject())
3529 ? GlobalObject::cast(object)->global_receiver()
3530 : ComputeReceiverForNonGlobal(object);
3531 // No need to unhole the value here. This is taken care of by the
3532 // GetProperty function.
3533 Object* value = object->GetProperty(*name);
3534 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003535 }
3536
3537 if (throw_error) {
3538 // The property doesn't exist - throw exception.
3539 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003540 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003541 return MakePair(Top::Throw(*reference_error), NULL);
3542 } else {
3543 // The property doesn't exist - return undefined
3544 return MakePair(Heap::undefined_value(), Heap::undefined_value());
3545 }
3546}
3547
3548
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003549static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003550 return LoadContextSlotHelper(args, true);
3551}
3552
3553
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003554static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003555 return LoadContextSlotHelper(args, false);
3556}
3557
3558
3559static Object* Runtime_StoreContextSlot(Arguments args) {
3560 HandleScope scope;
3561 ASSERT(args.length() == 3);
3562
3563 Handle<Object> value(args[0]);
3564 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003565 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003566
3567 int index;
3568 PropertyAttributes attributes;
3569 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003570 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003571 context->Lookup(name, flags, &index, &attributes);
3572
3573 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003574 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003575 // Ignore if read_only variable.
3576 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003577 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003578 }
3579 } else {
3580 ASSERT((attributes & READ_ONLY) == 0);
3581 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003582 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003583 USE(result);
3584 ASSERT(!result->IsFailure());
3585 }
3586 return *value;
3587 }
3588
3589 // Slow case: The property is not in a FixedArray context.
3590 // It is either in an JSObject extension context or it was not found.
3591 Handle<JSObject> context_ext;
3592
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003593 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003594 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003595 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003596 } else {
3597 // The property was not found. It needs to be stored in the global context.
3598 ASSERT(attributes == ABSENT);
3599 attributes = NONE;
3600 context_ext = Handle<JSObject>(Top::context()->global());
3601 }
3602
3603 // Set the property, but ignore if read_only variable.
3604 if ((attributes & READ_ONLY) == 0) {
3605 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
3606 if (set.is_null()) {
3607 // Failure::Exception is converted to a null handle in the
3608 // handle-based methods such as SetProperty. We therefore need
3609 // to convert null handles back to exceptions.
3610 ASSERT(Top::has_pending_exception());
3611 return Failure::Exception();
3612 }
3613 }
3614 return *value;
3615}
3616
3617
3618static Object* Runtime_Throw(Arguments args) {
3619 HandleScope scope;
3620 ASSERT(args.length() == 1);
3621
3622 return Top::Throw(args[0]);
3623}
3624
3625
3626static Object* Runtime_ReThrow(Arguments args) {
3627 HandleScope scope;
3628 ASSERT(args.length() == 1);
3629
3630 return Top::ReThrow(args[0]);
3631}
3632
3633
3634static Object* Runtime_ThrowReferenceError(Arguments args) {
3635 HandleScope scope;
3636 ASSERT(args.length() == 1);
3637
3638 Handle<Object> name(args[0]);
3639 Handle<Object> reference_error =
3640 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
3641 return Top::Throw(*reference_error);
3642}
3643
3644
3645static Object* Runtime_StackOverflow(Arguments args) {
3646 NoHandleAllocation na;
3647 return Top::StackOverflow();
3648}
3649
3650
3651static Object* RuntimePreempt(Arguments args) {
3652 // Clear the preempt request flag.
3653 StackGuard::Continue(PREEMPT);
3654
3655 ContextSwitcher::PreemptionReceived();
3656
3657 {
3658 v8::Unlocker unlocker;
3659 Thread::YieldCPU();
3660 }
3661
3662 return Heap::undefined_value();
3663}
3664
3665
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003666static Object* DebugBreakHelper() {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003667 // Just continue if breaks are disabled.
3668 if (Debug::disable_break()) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003669 return Heap::undefined_value();
kasper.lundbd3ec4e2008-07-09 11:06:54 +00003670 }
3671
kasper.lund7276f142008-07-30 08:49:36 +00003672 // Don't break in system functions. If the current function is
3673 // either in the builtins object of some context or is in the debug
3674 // context just return with the debug break stack guard active.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003675 JavaScriptFrameIterator it;
3676 JavaScriptFrame* frame = it.frame();
3677 Object* fun = frame->function();
3678 if (fun->IsJSFunction()) {
3679 GlobalObject* global = JSFunction::cast(fun)->context()->global();
3680 if (global->IsJSBuiltinsObject() || Debug::IsDebugGlobal(global)) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003681 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003682 }
3683 }
3684
3685 // Clear the debug request flag.
3686 StackGuard::Continue(DEBUGBREAK);
3687
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003688 HandleScope scope;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003689 // Enter the debugger. Just continue if we fail to enter the debugger.
3690 EnterDebugger debugger;
3691 if (debugger.FailedToEnter()) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003692 return Heap::undefined_value();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003693 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003694
kasper.lund7276f142008-07-30 08:49:36 +00003695 // Notify the debug event listeners.
3696 Debugger::OnDebugBreak(Factory::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003697
3698 // Return to continue execution.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003699 return Heap::undefined_value();
3700}
3701
3702
3703static Object* Runtime_DebugBreak(Arguments args) {
3704 ASSERT(args.length() == 0);
3705 return DebugBreakHelper();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003706}
3707
3708
3709static Object* Runtime_StackGuard(Arguments args) {
3710 ASSERT(args.length() == 1);
3711
3712 // First check if this is a real stack overflow.
3713 if (StackGuard::IsStackOverflow()) return Runtime_StackOverflow(args);
3714
3715 // If not real stack overflow the stack guard was used to interrupt
3716 // execution for another purpose.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003717 if (StackGuard::IsDebugBreak()) DebugBreakHelper();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003718 if (StackGuard::IsPreempted()) RuntimePreempt(args);
3719 if (StackGuard::IsInterrupted()) {
3720 // interrupt
3721 StackGuard::Continue(INTERRUPT);
3722 return Top::StackOverflow();
3723 }
3724 return Heap::undefined_value();
3725}
3726
3727
3728// NOTE: These PrintXXX functions are defined for all builds (not just
3729// DEBUG builds) because we may want to be able to trace function
3730// calls in all modes.
3731static void PrintString(String* str) {
3732 // not uncommon to have empty strings
3733 if (str->length() > 0) {
3734 SmartPointer<char> s =
3735 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
3736 PrintF("%s", *s);
3737 }
3738}
3739
3740
3741static void PrintObject(Object* obj) {
3742 if (obj->IsSmi()) {
3743 PrintF("%d", Smi::cast(obj)->value());
3744 } else if (obj->IsString() || obj->IsSymbol()) {
3745 PrintString(String::cast(obj));
3746 } else if (obj->IsNumber()) {
3747 PrintF("%g", obj->Number());
3748 } else if (obj->IsFailure()) {
3749 PrintF("<failure>");
3750 } else if (obj->IsUndefined()) {
3751 PrintF("<undefined>");
3752 } else if (obj->IsNull()) {
3753 PrintF("<null>");
3754 } else if (obj->IsTrue()) {
3755 PrintF("<true>");
3756 } else if (obj->IsFalse()) {
3757 PrintF("<false>");
3758 } else {
3759 PrintF("%p", obj);
3760 }
3761}
3762
3763
3764static int StackSize() {
3765 int n = 0;
3766 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
3767 return n;
3768}
3769
3770
3771static void PrintTransition(Object* result) {
3772 // indentation
3773 { const int nmax = 80;
3774 int n = StackSize();
3775 if (n <= nmax)
3776 PrintF("%4d:%*s", n, n, "");
3777 else
3778 PrintF("%4d:%*s", n, nmax, "...");
3779 }
3780
3781 if (result == NULL) {
3782 // constructor calls
3783 JavaScriptFrameIterator it;
3784 JavaScriptFrame* frame = it.frame();
3785 if (frame->IsConstructor()) PrintF("new ");
3786 // function name
3787 Object* fun = frame->function();
3788 if (fun->IsJSFunction()) {
3789 PrintObject(JSFunction::cast(fun)->shared()->name());
3790 } else {
3791 PrintObject(fun);
3792 }
3793 // function arguments
3794 // (we are intentionally only printing the actually
3795 // supplied parameters, not all parameters required)
3796 PrintF("(this=");
3797 PrintObject(frame->receiver());
3798 const int length = frame->GetProvidedParametersCount();
3799 for (int i = 0; i < length; i++) {
3800 PrintF(", ");
3801 PrintObject(frame->GetParameter(i));
3802 }
3803 PrintF(") {\n");
3804
3805 } else {
3806 // function result
3807 PrintF("} -> ");
3808 PrintObject(result);
3809 PrintF("\n");
3810 }
3811}
3812
3813
3814static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003815 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003816 NoHandleAllocation ha;
3817 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003818 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003819}
3820
3821
3822static Object* Runtime_TraceExit(Arguments args) {
3823 NoHandleAllocation ha;
3824 PrintTransition(args[0]);
3825 return args[0]; // return TOS
3826}
3827
3828
3829static Object* Runtime_DebugPrint(Arguments args) {
3830 NoHandleAllocation ha;
3831 ASSERT(args.length() == 1);
3832
3833#ifdef DEBUG
3834 if (args[0]->IsString()) {
3835 // If we have a string, assume it's a code "marker"
3836 // and print some interesting cpu debugging info.
3837 JavaScriptFrameIterator it;
3838 JavaScriptFrame* frame = it.frame();
3839 PrintF("fp = %p, sp = %p, pp = %p: ",
3840 frame->fp(), frame->sp(), frame->pp());
3841 } else {
3842 PrintF("DebugPrint: ");
3843 }
3844 args[0]->Print();
3845#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003846 // ShortPrint is available in release mode. Print is not.
3847 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003848#endif
3849 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00003850 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003851
3852 return args[0]; // return TOS
3853}
3854
3855
3856static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003857 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003858 NoHandleAllocation ha;
3859 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003860 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003861}
3862
3863
mads.s.ager31e71382008-08-13 09:32:07 +00003864static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003865 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003866 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003867
3868 // According to ECMA-262, section 15.9.1, page 117, the precision of
3869 // the number in a Date object representing a particular instant in
3870 // time is milliseconds. Therefore, we floor the result of getting
3871 // the OS time.
3872 double millis = floor(OS::TimeCurrentMillis());
3873 return Heap::NumberFromDouble(millis);
3874}
3875
3876
3877static Object* Runtime_DateParseString(Arguments args) {
3878 HandleScope scope;
3879 ASSERT(args.length() == 1);
3880
3881 CONVERT_CHECKED(String, string_object, args[0]);
3882
3883 Handle<String> str(string_object);
3884 Handle<FixedArray> output = Factory::NewFixedArray(DateParser::OUTPUT_SIZE);
3885 if (DateParser::Parse(*str, *output)) {
3886 return *Factory::NewJSArrayWithElements(output);
3887 } else {
3888 return *Factory::null_value();
3889 }
3890}
3891
3892
3893static Object* Runtime_DateLocalTimezone(Arguments args) {
3894 NoHandleAllocation ha;
3895 ASSERT(args.length() == 1);
3896
3897 CONVERT_DOUBLE_CHECKED(x, args[0]);
3898 char* zone = OS::LocalTimezone(x);
3899 return Heap::AllocateStringFromUtf8(CStrVector(zone));
3900}
3901
3902
3903static Object* Runtime_DateLocalTimeOffset(Arguments args) {
3904 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003905 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003906
3907 return Heap::NumberFromDouble(OS::LocalTimeOffset());
3908}
3909
3910
3911static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
3912 NoHandleAllocation ha;
3913 ASSERT(args.length() == 1);
3914
3915 CONVERT_DOUBLE_CHECKED(x, args[0]);
3916 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
3917}
3918
3919
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003920static Object* Runtime_NumberIsFinite(Arguments args) {
3921 NoHandleAllocation ha;
3922 ASSERT(args.length() == 1);
3923
3924 CONVERT_DOUBLE_CHECKED(value, args[0]);
3925 Object* result;
3926 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
3927 result = Heap::false_value();
3928 } else {
3929 result = Heap::true_value();
3930 }
3931 return result;
3932}
3933
3934
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003935static Object* EvalContext() {
3936 // The topmost JS frame belongs to the eval function which called
3937 // the CompileString runtime function. We need to unwind one level
3938 // to get to the caller of eval.
3939 StackFrameLocator locator;
3940 JavaScriptFrame* frame = locator.FindJavaScriptFrame(1);
3941
kasper.lund44510672008-07-25 07:37:58 +00003942 // TODO(900055): Right now we check if the caller of eval() supports
3943 // eval to determine if it's an aliased eval or not. This may not be
3944 // entirely correct in the unlikely case where a function uses both
3945 // aliased and direct eval calls.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003946 HandleScope scope;
3947 if (!ScopeInfo<>::SupportsEval(frame->FindCode())) {
kasper.lund44510672008-07-25 07:37:58 +00003948 // Aliased eval: Evaluate in the global context of the eval
3949 // function to support aliased, cross environment evals.
3950 return *Top::global_context();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003951 }
3952
3953 // Fetch the caller context from the frame.
3954 Handle<Context> caller(Context::cast(frame->context()));
3955
3956 // Check for eval() invocations that cross environments. Use the
3957 // context from the stack if evaluating in current environment.
3958 Handle<Context> target = Top::global_context();
3959 if (caller->global_context() == *target) return *caller;
3960
3961 // Compute a function closure that captures the calling context. We
3962 // need a function that has trivial scope info, since it is only
3963 // used to hold the context chain together.
3964 Handle<JSFunction> closure = Factory::NewFunction(Factory::empty_symbol(),
3965 Factory::undefined_value());
3966 closure->set_context(*caller);
3967
3968 // Create a new adaptor context that has the target environment as
3969 // the extension object. This enables the evaluated code to see both
3970 // the current context with locals and everything and to see global
3971 // variables declared in the target global object. Furthermore, any
3972 // properties introduced with 'var' will be added to the target
3973 // global object because it is the extension object.
3974 Handle<Context> adaptor =
3975 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, closure);
3976 adaptor->set_extension(target->global());
3977 return *adaptor;
3978}
3979
3980
3981static Object* Runtime_EvalReceiver(Arguments args) {
3982 StackFrameLocator locator;
3983 return locator.FindJavaScriptFrame(1)->receiver();
3984}
3985
3986
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003987static Object* Runtime_GlobalReceiver(Arguments args) {
3988 ASSERT(args.length() == 1);
3989 Object* global = args[0];
3990 if (!global->IsJSGlobalObject()) return Heap::null_value();
3991 return JSGlobalObject::cast(global)->global_receiver();
3992}
3993
3994
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003995static Object* Runtime_CompileString(Arguments args) {
3996 HandleScope scope;
ager@chromium.org236ad962008-09-25 09:45:57 +00003997 ASSERT(args.length() == 3);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003998 CONVERT_ARG_CHECKED(String, source, 0);
ager@chromium.org236ad962008-09-25 09:45:57 +00003999 CONVERT_ARG_CHECKED(Smi, line_offset, 1);
4000 bool contextual = args[2]->IsTrue();
4001 RUNTIME_ASSERT(contextual || args[2]->IsFalse());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004002
4003 // Compute the eval context.
4004 Handle<Context> context;
4005 if (contextual) {
4006 // Get eval context. May not be available if we are calling eval
4007 // through an alias, and the corresponding frame doesn't have a
4008 // proper eval context set up.
4009 Object* eval_context = EvalContext();
4010 if (eval_context->IsFailure()) return eval_context;
4011 context = Handle<Context>(Context::cast(eval_context));
4012 } else {
4013 context = Handle<Context>(Top::context()->global_context());
4014 }
4015
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004016
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004017 // Compile source string.
4018 bool is_global = context->IsGlobalContext();
4019 Handle<JSFunction> boilerplate =
ager@chromium.org236ad962008-09-25 09:45:57 +00004020 Compiler::CompileEval(source, line_offset->value(), is_global);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004021 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004022 Handle<JSFunction> fun =
4023 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4024 return *fun;
4025}
4026
4027
4028static Object* Runtime_CompileScript(Arguments args) {
4029 HandleScope scope;
4030 ASSERT(args.length() == 4);
4031
4032 CONVERT_ARG_CHECKED(String, source, 0);
4033 CONVERT_ARG_CHECKED(String, script, 1);
4034 CONVERT_CHECKED(Smi, line_attrs, args[2]);
4035 int line = line_attrs->value();
4036 CONVERT_CHECKED(Smi, col_attrs, args[3]);
4037 int col = col_attrs->value();
4038 Handle<JSFunction> boilerplate =
4039 Compiler::Compile(source, script, line, col, NULL, NULL);
4040 if (boilerplate.is_null()) return Failure::Exception();
4041 Handle<JSFunction> fun =
4042 Factory::NewFunctionFromBoilerplate(boilerplate,
4043 Handle<Context>(Top::context()));
4044 return *fun;
4045}
4046
4047
4048static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
4049 // This utility adjusts the property attributes for newly created Function
4050 // object ("new Function(...)") by changing the map.
4051 // All it does is changing the prototype property to enumerable
4052 // as specified in ECMA262, 15.3.5.2.
4053 HandleScope scope;
4054 ASSERT(args.length() == 1);
4055 CONVERT_ARG_CHECKED(JSFunction, func, 0);
4056 ASSERT(func->map()->instance_type() ==
4057 Top::function_instance_map()->instance_type());
4058 ASSERT(func->map()->instance_size() ==
4059 Top::function_instance_map()->instance_size());
4060 func->set_map(*Top::function_instance_map());
4061 return *func;
4062}
4063
4064
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004065// Push an array unto an array of arrays if it is not already in the
4066// array. Returns true if the element was pushed on the stack and
4067// false otherwise.
4068static Object* Runtime_PushIfAbsent(Arguments args) {
4069 ASSERT(args.length() == 2);
4070 CONVERT_CHECKED(JSArray, array, args[0]);
4071 CONVERT_CHECKED(JSArray, element, args[1]);
4072 RUNTIME_ASSERT(array->HasFastElements());
4073 int length = Smi::cast(array->length())->value();
4074 FixedArray* elements = FixedArray::cast(array->elements());
4075 for (int i = 0; i < length; i++) {
4076 if (elements->get(i) == element) return Heap::false_value();
4077 }
4078 Object* obj = array->SetFastElement(length, element);
4079 if (obj->IsFailure()) return obj;
4080 return Heap::true_value();
4081}
4082
4083
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004084/**
4085 * A simple visitor visits every element of Array's.
4086 * The backend storage can be a fixed array for fast elements case,
4087 * or a dictionary for sparse array. Since Dictionary is a subtype
4088 * of FixedArray, the class can be used by both fast and slow cases.
4089 * The second parameter of the constructor, fast_elements, specifies
4090 * whether the storage is a FixedArray or Dictionary.
4091 *
4092 * An index limit is used to deal with the situation that a result array
4093 * length overflows 32-bit non-negative integer.
4094 */
4095class ArrayConcatVisitor {
4096 public:
4097 ArrayConcatVisitor(Handle<FixedArray> storage,
4098 uint32_t index_limit,
4099 bool fast_elements) :
4100 storage_(storage), index_limit_(index_limit),
4101 fast_elements_(fast_elements), index_offset_(0) { }
4102
4103 void visit(uint32_t i, Handle<Object> elm) {
4104 uint32_t index = i + index_offset_;
4105 if (index >= index_limit_) return;
4106
4107 if (fast_elements_) {
4108 ASSERT(index < static_cast<uint32_t>(storage_->length()));
4109 storage_->set(index, *elm);
4110
4111 } else {
4112 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
4113 Handle<Dictionary> result =
4114 Factory::DictionaryAtNumberPut(dict, index, elm);
4115 if (!result.is_identical_to(dict))
4116 storage_ = result;
4117 }
4118 }
4119
4120 void increase_index_offset(uint32_t delta) {
4121 index_offset_ += delta;
4122 }
4123
4124 private:
4125 Handle<FixedArray> storage_;
4126 uint32_t index_limit_;
4127 bool fast_elements_;
4128 uint32_t index_offset_;
4129};
4130
4131
4132/**
4133 * A helper function that visits elements of a JSObject. Only elements
4134 * whose index between 0 and range (exclusive) are visited.
4135 *
4136 * If the third parameter, visitor, is not NULL, the visitor is called
4137 * with parameters, 'visitor_index_offset + element index' and the element.
4138 *
4139 * It returns the number of visisted elements.
4140 */
4141static uint32_t IterateElements(Handle<JSObject> receiver,
4142 uint32_t range,
4143 ArrayConcatVisitor* visitor) {
4144 uint32_t num_of_elements = 0;
4145
4146 if (receiver->HasFastElements()) {
4147 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
4148 uint32_t len = elements->length();
4149 if (range < len) len = range;
4150
4151 for (uint32_t j = 0; j < len; j++) {
4152 Handle<Object> e(elements->get(j));
4153 if (!e->IsTheHole()) {
4154 num_of_elements++;
4155 if (visitor)
4156 visitor->visit(j, e);
4157 }
4158 }
4159
4160 } else {
4161 Handle<Dictionary> dict(receiver->element_dictionary());
4162 uint32_t capacity = dict->Capacity();
4163 for (uint32_t j = 0; j < capacity; j++) {
4164 Handle<Object> k(dict->KeyAt(j));
4165 if (dict->IsKey(*k)) {
4166 ASSERT(k->IsNumber());
4167 uint32_t index = static_cast<uint32_t>(k->Number());
4168 if (index < range) {
4169 num_of_elements++;
4170 if (visitor) {
4171 visitor->visit(index,
4172 Handle<Object>(dict->ValueAt(j)));
4173 }
4174 }
4175 }
4176 }
4177 }
4178
4179 return num_of_elements;
4180}
4181
4182
4183/**
4184 * A helper function that visits elements of an Array object, and elements
4185 * on its prototypes.
4186 *
4187 * Elements on prototypes are visited first, and only elements whose indices
4188 * less than Array length are visited.
4189 *
4190 * If a ArrayConcatVisitor object is given, the visitor is called with
4191 * parameters, element's index + visitor_index_offset and the element.
4192 */
4193static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
4194 ArrayConcatVisitor* visitor) {
4195 uint32_t range = static_cast<uint32_t>(array->length()->Number());
4196 Handle<Object> obj = array;
4197
4198 static const int kEstimatedPrototypes = 3;
4199 List< Handle<JSObject> > objects(kEstimatedPrototypes);
4200
4201 // Visit prototype first. If an element on the prototype is shadowed by
4202 // the inheritor using the same index, the ArrayConcatVisitor visits
4203 // the prototype element before the shadowing element.
4204 // The visitor can simply overwrite the old value by new value using
4205 // the same index. This follows Array::concat semantics.
4206 while (!obj->IsNull()) {
4207 objects.Add(Handle<JSObject>::cast(obj));
4208 obj = Handle<Object>(obj->GetPrototype());
4209 }
4210
4211 uint32_t nof_elements = 0;
4212 for (int i = objects.length() - 1; i >= 0; i--) {
4213 Handle<JSObject> obj = objects[i];
4214 nof_elements +=
4215 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
4216 }
4217
4218 return nof_elements;
4219}
4220
4221
4222/**
4223 * A helper function of Runtime_ArrayConcat.
4224 *
4225 * The first argument is an Array of arrays and objects. It is the
4226 * same as the arguments array of Array::concat JS function.
4227 *
4228 * If an argument is an Array object, the function visits array
4229 * elements. If an argument is not an Array object, the function
4230 * visits the object as if it is an one-element array.
4231 *
4232 * If the result array index overflows 32-bit integer, the rounded
4233 * non-negative number is used as new length. For example, if one
4234 * array length is 2^32 - 1, second array length is 1, the
4235 * concatenated array length is 0.
4236 */
4237static uint32_t IterateArguments(Handle<JSArray> arguments,
4238 ArrayConcatVisitor* visitor) {
4239 uint32_t visited_elements = 0;
4240 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
4241
4242 for (uint32_t i = 0; i < num_of_args; i++) {
4243 Handle<Object> obj(arguments->GetElement(i));
4244 if (obj->IsJSArray()) {
4245 Handle<JSArray> array = Handle<JSArray>::cast(obj);
4246 uint32_t len = static_cast<uint32_t>(array->length()->Number());
4247 uint32_t nof_elements =
4248 IterateArrayAndPrototypeElements(array, visitor);
4249 // Total elements of array and its prototype chain can be more than
4250 // the array length, but ArrayConcat can only concatenate at most
4251 // the array length number of elements.
4252 visited_elements += (nof_elements > len) ? len : nof_elements;
4253 if (visitor) visitor->increase_index_offset(len);
4254
4255 } else {
4256 if (visitor) {
4257 visitor->visit(0, obj);
4258 visitor->increase_index_offset(1);
4259 }
4260 visited_elements++;
4261 }
4262 }
4263 return visited_elements;
4264}
4265
4266
4267/**
4268 * Array::concat implementation.
4269 * See ECMAScript 262, 15.4.4.4.
4270 */
4271static Object* Runtime_ArrayConcat(Arguments args) {
4272 ASSERT(args.length() == 1);
4273 HandleScope handle_scope;
4274
4275 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
4276 Handle<JSArray> arguments(arg_arrays);
4277
4278 // Pass 1: estimate the number of elements of the result
4279 // (it could be more than real numbers if prototype has elements).
4280 uint32_t result_length = 0;
4281 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
4282
4283 { AssertNoAllocation nogc;
4284 for (uint32_t i = 0; i < num_of_args; i++) {
4285 Object* obj = arguments->GetElement(i);
4286 if (obj->IsJSArray()) {
4287 result_length +=
4288 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
4289 } else {
4290 result_length++;
4291 }
4292 }
4293 }
4294
4295 // Allocate an empty array, will set length and content later.
4296 Handle<JSArray> result = Factory::NewJSArray(0);
4297
4298 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
4299 // If estimated number of elements is more than half of length, a
4300 // fixed array (fast case) is more time and space-efficient than a
4301 // dictionary.
4302 bool fast_case = (estimate_nof_elements * 2) >= result_length;
4303
4304 Handle<FixedArray> storage;
4305 if (fast_case) {
4306 // The backing storage array must have non-existing elements to
4307 // preserve holes across concat operations.
4308 storage = Factory::NewFixedArrayWithHoles(result_length);
4309
4310 } else {
4311 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
4312 uint32_t at_least_space_for = estimate_nof_elements +
4313 (estimate_nof_elements >> 2);
4314 storage = Handle<FixedArray>::cast(
4315 Factory::NewDictionary(at_least_space_for));
4316 }
4317
4318 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
4319
4320 ArrayConcatVisitor visitor(storage, result_length, fast_case);
4321
4322 IterateArguments(arguments, &visitor);
4323
4324 result->set_length(*len);
4325 result->set_elements(*storage);
4326
4327 return *result;
4328}
4329
4330
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004331// This will not allocate (flatten the string), but it may run
4332// very slowly for very deeply nested ConsStrings. For debugging use only.
4333static Object* Runtime_GlobalPrint(Arguments args) {
4334 NoHandleAllocation ha;
4335 ASSERT(args.length() == 1);
4336
4337 CONVERT_CHECKED(String, string, args[0]);
4338 StringInputBuffer buffer(string);
4339 while (buffer.has_more()) {
4340 uint16_t character = buffer.GetNext();
4341 PrintF("%c", character);
4342 }
4343 return string;
4344}
4345
4346
4347static Object* Runtime_RemoveArrayHoles(Arguments args) {
4348 ASSERT(args.length() == 1);
4349 // Ignore the case if this is not a JSArray.
4350 if (!args[0]->IsJSArray()) return args[0];
4351 return JSArray::cast(args[0])->RemoveHoles();
4352}
4353
4354
4355// Move contents of argument 0 (an array) to argument 1 (an array)
4356static Object* Runtime_MoveArrayContents(Arguments args) {
4357 ASSERT(args.length() == 2);
4358 CONVERT_CHECKED(JSArray, from, args[0]);
4359 CONVERT_CHECKED(JSArray, to, args[1]);
4360 to->SetContent(FixedArray::cast(from->elements()));
4361 to->set_length(from->length());
4362 from->SetContent(Heap::empty_fixed_array());
4363 from->set_length(0);
4364 return to;
4365}
4366
4367
4368// How many elements does this array have?
4369static Object* Runtime_EstimateNumberOfElements(Arguments args) {
4370 ASSERT(args.length() == 1);
4371 CONVERT_CHECKED(JSArray, array, args[0]);
4372 HeapObject* elements = array->elements();
4373 if (elements->IsDictionary()) {
4374 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
4375 } else {
4376 return array->length();
4377 }
4378}
4379
4380
4381// Returns an array that tells you where in the [0, length) interval an array
4382// might have elements. Can either return keys or intervals. Keys can have
4383// gaps in (undefined). Intervals can also span over some undefined keys.
4384static Object* Runtime_GetArrayKeys(Arguments args) {
4385 ASSERT(args.length() == 2);
4386 HandleScope scope;
4387 CONVERT_CHECKED(JSArray, raw_array, args[0]);
4388 Handle<JSArray> array(raw_array);
4389 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004390 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004391 // Create an array and get all the keys into it, then remove all the
4392 // keys that are not integers in the range 0 to length-1.
4393 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
4394 int keys_length = keys->length();
4395 for (int i = 0; i < keys_length; i++) {
4396 Object* key = keys->get(i);
4397 uint32_t index;
4398 if (!Array::IndexFromObject(key, &index) || index >= length) {
4399 // Zap invalid keys.
4400 keys->set_undefined(i);
4401 }
4402 }
4403 return *Factory::NewJSArrayWithElements(keys);
4404 } else {
4405 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
4406 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004407 single_interval->set(0,
4408 Smi::FromInt(-1),
4409 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004410 Handle<Object> length_object =
4411 Factory::NewNumber(static_cast<double>(length));
4412 single_interval->set(1, *length_object);
4413 return *Factory::NewJSArrayWithElements(single_interval);
4414 }
4415}
4416
4417
4418// DefineAccessor takes an optional final argument which is the
4419// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
4420// to the way accessors are implemented, it is set for both the getter
4421// and setter on the first call to DefineAccessor and ignored on
4422// subsequent calls.
4423static Object* Runtime_DefineAccessor(Arguments args) {
4424 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
4425 // Compute attributes.
4426 PropertyAttributes attributes = NONE;
4427 if (args.length() == 5) {
4428 CONVERT_CHECKED(Smi, attrs, args[4]);
4429 int value = attrs->value();
4430 // Only attribute bits should be set.
4431 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4432 attributes = static_cast<PropertyAttributes>(value);
4433 }
4434
4435 CONVERT_CHECKED(JSObject, obj, args[0]);
4436 CONVERT_CHECKED(String, name, args[1]);
4437 CONVERT_CHECKED(Smi, flag, args[2]);
4438 CONVERT_CHECKED(JSFunction, fun, args[3]);
4439 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
4440}
4441
4442
4443static Object* Runtime_LookupAccessor(Arguments args) {
4444 ASSERT(args.length() == 3);
4445 CONVERT_CHECKED(JSObject, obj, args[0]);
4446 CONVERT_CHECKED(String, name, args[1]);
4447 CONVERT_CHECKED(Smi, flag, args[2]);
4448 return obj->LookupAccessor(name, flag->value() == 0);
4449}
4450
4451
4452// Helper functions for wrapping and unwrapping stack frame ids.
4453static Smi* WrapFrameId(StackFrame::Id id) {
4454 ASSERT(IsAligned(OffsetFrom(id), 4));
4455 return Smi::FromInt(id >> 2);
4456}
4457
4458
4459static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
4460 return static_cast<StackFrame::Id>(wrapped->value() << 2);
4461}
4462
4463
4464// Adds a JavaScript function as a debug event listener.
4465// args[0]: debug event listener function
4466// args[1]: object supplied during callback
4467static Object* Runtime_AddDebugEventListener(Arguments args) {
4468 ASSERT(args.length() == 2);
4469 // Convert the parameters to API objects to call the API function for adding
4470 // a JavaScript function as debug event listener.
4471 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
4472 v8::Handle<v8::Function> fun(ToApi<v8::Function>(raw_fun));
4473 v8::Handle<v8::Value> data(ToApi<v8::Value>(args.at<Object>(0)));
4474 v8::Debug::AddDebugEventListener(fun, data);
4475
4476 return Heap::undefined_value();
4477}
4478
4479
4480// Removes a JavaScript function debug event listener.
4481// args[0]: debug event listener function
4482static Object* Runtime_RemoveDebugEventListener(Arguments args) {
4483 ASSERT(args.length() == 1);
4484 // Convert the parameter to an API object to call the API function for
4485 // removing a JavaScript function debug event listener.
4486 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
4487 v8::Handle<v8::Function> fun(ToApi<v8::Function>(raw_fun));
4488 v8::Debug::RemoveDebugEventListener(fun);
4489
4490 return Heap::undefined_value();
4491}
4492
4493
4494static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00004495 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004496 StackGuard::DebugBreak();
4497 return Heap::undefined_value();
4498}
4499
4500
4501static Object* DebugLookupResultValue(LookupResult* result) {
4502 Object* value;
4503 switch (result->type()) {
4504 case NORMAL: {
4505 Dictionary* dict =
4506 JSObject::cast(result->holder())->property_dictionary();
4507 value = dict->ValueAt(result->GetDictionaryEntry());
4508 if (value->IsTheHole()) {
4509 return Heap::undefined_value();
4510 }
4511 return value;
4512 }
4513 case FIELD:
4514 value =
4515 JSObject::cast(
ager@chromium.org7c537e22008-10-16 08:43:32 +00004516 result->holder())->FastPropertyAt(result->GetFieldIndex());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004517 if (value->IsTheHole()) {
4518 return Heap::undefined_value();
4519 }
4520 return value;
4521 case CONSTANT_FUNCTION:
4522 return result->GetConstantFunction();
4523 case CALLBACKS:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004524 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004525 case MAP_TRANSITION:
4526 case CONSTANT_TRANSITION:
4527 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004528 return Heap::undefined_value();
4529 default:
4530 UNREACHABLE();
4531 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004532 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004533 return Heap::undefined_value();
4534}
4535
4536
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004537static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004538 HandleScope scope;
4539
4540 ASSERT(args.length() == 2);
4541
4542 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4543 CONVERT_ARG_CHECKED(String, name, 1);
4544
4545 // Check if the name is trivially convertible to an index and get the element
4546 // if so.
4547 uint32_t index;
4548 if (name->AsArrayIndex(&index)) {
4549 Handle<FixedArray> details = Factory::NewFixedArray(2);
4550 details->set(0, Runtime::GetElementOrCharAt(obj, index));
4551 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
4552 return *Factory::NewJSArrayWithElements(details);
4553 }
4554
4555 // Perform standard local lookup on the object.
4556 LookupResult result;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004557 obj->Lookup(*name, &result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004558 if (result.IsProperty()) {
4559 Handle<Object> value(DebugLookupResultValue(&result));
4560 Handle<FixedArray> details = Factory::NewFixedArray(2);
4561 details->set(0, *value);
4562 details->set(1, result.GetPropertyDetails().AsSmi());
4563 return *Factory::NewJSArrayWithElements(details);
4564 }
4565 return Heap::undefined_value();
4566}
4567
4568
4569static Object* Runtime_DebugGetProperty(Arguments args) {
4570 HandleScope scope;
4571
4572 ASSERT(args.length() == 2);
4573
4574 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4575 CONVERT_ARG_CHECKED(String, name, 1);
4576
4577 LookupResult result;
4578 obj->Lookup(*name, &result);
4579 if (result.IsProperty()) {
4580 return DebugLookupResultValue(&result);
4581 }
4582 return Heap::undefined_value();
4583}
4584
4585
4586// Return the names of the local named properties.
4587// args[0]: object
4588static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
4589 HandleScope scope;
4590 ASSERT(args.length() == 1);
4591 if (!args[0]->IsJSObject()) {
4592 return Heap::undefined_value();
4593 }
4594 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4595
4596 int n = obj->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4597 Handle<FixedArray> names = Factory::NewFixedArray(n);
4598 obj->GetLocalPropertyNames(*names);
4599 return *Factory::NewJSArrayWithElements(names);
4600}
4601
4602
4603// Return the names of the local indexed properties.
4604// args[0]: object
4605static Object* Runtime_DebugLocalElementNames(Arguments args) {
4606 HandleScope scope;
4607 ASSERT(args.length() == 1);
4608 if (!args[0]->IsJSObject()) {
4609 return Heap::undefined_value();
4610 }
4611 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4612
4613 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4614 Handle<FixedArray> names = Factory::NewFixedArray(n);
4615 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4616 return *Factory::NewJSArrayWithElements(names);
4617}
4618
4619
4620// Return the property type calculated from the property details.
4621// args[0]: smi with property details.
4622static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
4623 ASSERT(args.length() == 1);
4624 CONVERT_CHECKED(Smi, details, args[0]);
4625 PropertyType type = PropertyDetails(details).type();
4626 return Smi::FromInt(static_cast<int>(type));
4627}
4628
4629
4630// Return the property attribute calculated from the property details.
4631// args[0]: smi with property details.
4632static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
4633 ASSERT(args.length() == 1);
4634 CONVERT_CHECKED(Smi, details, args[0]);
4635 PropertyAttributes attributes = PropertyDetails(details).attributes();
4636 return Smi::FromInt(static_cast<int>(attributes));
4637}
4638
4639
4640// Return the property insertion index calculated from the property details.
4641// args[0]: smi with property details.
4642static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
4643 ASSERT(args.length() == 1);
4644 CONVERT_CHECKED(Smi, details, args[0]);
4645 int index = PropertyDetails(details).index();
4646 return Smi::FromInt(index);
4647}
4648
4649
4650// Return information on whether an object has a named or indexed interceptor.
4651// args[0]: object
4652static Object* Runtime_DebugInterceptorInfo(Arguments args) {
4653 HandleScope scope;
4654 ASSERT(args.length() == 1);
4655 if (!args[0]->IsJSObject()) {
4656 return Smi::FromInt(0);
4657 }
4658 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4659
4660 int result = 0;
4661 if (obj->HasNamedInterceptor()) result |= 2;
4662 if (obj->HasIndexedInterceptor()) result |= 1;
4663
4664 return Smi::FromInt(result);
4665}
4666
4667
4668// Return property names from named interceptor.
4669// args[0]: object
4670static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
4671 HandleScope scope;
4672 ASSERT(args.length() == 1);
4673 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4674 RUNTIME_ASSERT(obj->HasNamedInterceptor());
4675
4676 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4677 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4678 return Heap::undefined_value();
4679}
4680
4681
4682// Return element names from indexed interceptor.
4683// args[0]: object
4684static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
4685 HandleScope scope;
4686 ASSERT(args.length() == 1);
4687 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4688 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
4689
4690 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4691 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4692 return Heap::undefined_value();
4693}
4694
4695
4696// Return property value from named interceptor.
4697// args[0]: object
4698// args[1]: property name
4699static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
4700 HandleScope scope;
4701 ASSERT(args.length() == 2);
4702 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4703 RUNTIME_ASSERT(obj->HasNamedInterceptor());
4704 CONVERT_ARG_CHECKED(String, name, 1);
4705
4706 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004707 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004708}
4709
4710
4711// Return element value from indexed interceptor.
4712// args[0]: object
4713// args[1]: index
4714static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
4715 HandleScope scope;
4716 ASSERT(args.length() == 2);
4717 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4718 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
4719 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
4720
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004721 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004722}
4723
4724
4725static Object* Runtime_CheckExecutionState(Arguments args) {
4726 ASSERT(args.length() >= 1);
4727 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
4728 // Check that the break id is valid and that there is a valid frame
4729 // where execution is broken.
4730 if (break_id != Top::break_id() ||
4731 Top::break_frame_id() == StackFrame::NO_ID) {
4732 return Top::Throw(Heap::illegal_execution_state_symbol());
4733 }
4734
4735 return Heap::true_value();
4736}
4737
4738
4739static Object* Runtime_GetFrameCount(Arguments args) {
4740 HandleScope scope;
4741 ASSERT(args.length() == 1);
4742
4743 // Check arguments.
4744 Object* result = Runtime_CheckExecutionState(args);
4745 if (result->IsFailure()) return result;
4746
4747 // Count all frames which are relevant to debugging stack trace.
4748 int n = 0;
4749 StackFrame::Id id = Top::break_frame_id();
4750 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
4751 return Smi::FromInt(n);
4752}
4753
4754
4755static const int kFrameDetailsFrameIdIndex = 0;
4756static const int kFrameDetailsReceiverIndex = 1;
4757static const int kFrameDetailsFunctionIndex = 2;
4758static const int kFrameDetailsArgumentCountIndex = 3;
4759static const int kFrameDetailsLocalCountIndex = 4;
4760static const int kFrameDetailsSourcePositionIndex = 5;
4761static const int kFrameDetailsConstructCallIndex = 6;
4762static const int kFrameDetailsDebuggerFrameIndex = 7;
4763static const int kFrameDetailsFirstDynamicIndex = 8;
4764
4765// Return an array with frame details
4766// args[0]: number: break id
4767// args[1]: number: frame index
4768//
4769// The array returned contains the following information:
4770// 0: Frame id
4771// 1: Receiver
4772// 2: Function
4773// 3: Argument count
4774// 4: Local count
4775// 5: Source position
4776// 6: Constructor call
4777// 7: Debugger frame
4778// Arguments name, value
4779// Locals name, value
4780static Object* Runtime_GetFrameDetails(Arguments args) {
4781 HandleScope scope;
4782 ASSERT(args.length() == 2);
4783
4784 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004785 Object* check = Runtime_CheckExecutionState(args);
4786 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004787 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
4788
4789 // Find the relevant frame with the requested index.
4790 StackFrame::Id id = Top::break_frame_id();
4791 int count = 0;
4792 JavaScriptFrameIterator it(id);
4793 for (; !it.done(); it.Advance()) {
4794 if (count == index) break;
4795 count++;
4796 }
4797 if (it.done()) return Heap::undefined_value();
4798
4799 // Traverse the saved contexts chain to find the active context for the
4800 // selected frame.
4801 SaveContext* save = Top::save_context();
4802 while (save != NULL && reinterpret_cast<Address>(save) < it.frame()->sp()) {
4803 save = save->prev();
4804 }
4805
4806 // Get the frame id.
4807 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
4808
4809 // Find source position.
4810 int position = it.frame()->FindCode()->SourcePosition(it.frame()->pc());
4811
4812 // Check for constructor frame.
4813 bool constructor = it.frame()->IsConstructor();
4814
4815 // Get code and read scope info from it for local variable information.
4816 Handle<Code> code(it.frame()->FindCode());
4817 ScopeInfo<> info(*code);
4818
4819 // Get the context.
4820 Handle<Context> context(Context::cast(it.frame()->context()));
4821
4822 // Get the locals names and values into a temporary array.
4823 //
4824 // TODO(1240907): Hide compiler-introduced stack variables
4825 // (e.g. .result)? For users of the debugger, they will probably be
4826 // confusing.
4827 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
4828 for (int i = 0; i < info.NumberOfLocals(); i++) {
4829 // Name of the local.
4830 locals->set(i * 2, *info.LocalName(i));
4831
4832 // Fetch the value of the local - either from the stack or from a
4833 // heap-allocated context.
4834 if (i < info.number_of_stack_slots()) {
4835 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
4836 } else {
4837 Handle<String> name = info.LocalName(i);
4838 // Traverse the context chain to the function context as all local
4839 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004840 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004841 context = Handle<Context>(context->previous());
4842 }
4843 ASSERT(context->is_function_context());
4844 locals->set(i * 2 + 1,
4845 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
4846 NULL)));
4847 }
4848 }
4849
4850 // Now advance to the arguments adapter frame (if any). If contains all
4851 // the provided parameters and
4852
4853 // Now advance to the arguments adapter frame (if any). It contains all
4854 // the provided parameters whereas the function frame always have the number
4855 // of arguments matching the functions parameters. The rest of the
4856 // information (except for what is collected above) is the same.
4857 it.AdvanceToArgumentsFrame();
4858
4859 // Find the number of arguments to fill. At least fill the number of
4860 // parameters for the function and fill more if more parameters are provided.
4861 int argument_count = info.number_of_parameters();
4862 if (argument_count < it.frame()->GetProvidedParametersCount()) {
4863 argument_count = it.frame()->GetProvidedParametersCount();
4864 }
4865
4866 // Calculate the size of the result.
4867 int details_size = kFrameDetailsFirstDynamicIndex +
4868 2 * (argument_count + info.NumberOfLocals());
4869 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
4870
4871 // Add the frame id.
4872 details->set(kFrameDetailsFrameIdIndex, *frame_id);
4873
4874 // Add the function (same as in function frame).
4875 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
4876
4877 // Add the arguments count.
4878 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
4879
4880 // Add the locals count
4881 details->set(kFrameDetailsLocalCountIndex,
4882 Smi::FromInt(info.NumberOfLocals()));
4883
4884 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00004885 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004886 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
4887 } else {
4888 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
4889 }
4890
4891 // Add the constructor information.
4892 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
4893
4894 // Add information on whether this frame is invoked in the debugger context.
4895 details->set(kFrameDetailsDebuggerFrameIndex,
4896 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
4897
4898 // Fill the dynamic part.
4899 int details_index = kFrameDetailsFirstDynamicIndex;
4900
4901 // Add arguments name and value.
4902 for (int i = 0; i < argument_count; i++) {
4903 // Name of the argument.
4904 if (i < info.number_of_parameters()) {
4905 details->set(details_index++, *info.parameter_name(i));
4906 } else {
4907 details->set(details_index++, Heap::undefined_value());
4908 }
4909
4910 // Parameter value.
4911 if (i < it.frame()->GetProvidedParametersCount()) {
4912 details->set(details_index++, it.frame()->GetParameter(i));
4913 } else {
4914 details->set(details_index++, Heap::undefined_value());
4915 }
4916 }
4917
4918 // Add locals name and value from the temporary copy from the function frame.
4919 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
4920 details->set(details_index++, locals->get(i));
4921 }
4922
4923 // Add the receiver (same as in function frame).
4924 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
4925 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
4926 Handle<Object> receiver(it.frame()->receiver());
4927 if (!receiver->IsJSObject()) {
4928 // If the receiver is NOT a JSObject we have hit an optimization
4929 // where a value object is not converted into a wrapped JS objects.
4930 // To hide this optimization from the debugger, we wrap the receiver
4931 // by creating correct wrapper object based on the calling frame's
4932 // global context.
4933 it.Advance();
4934 Handle<Context> calling_frames_global_context(
4935 Context::cast(Context::cast(it.frame()->context())->global_context()));
4936 receiver = Factory::ToObject(receiver, calling_frames_global_context);
4937 }
4938 details->set(kFrameDetailsReceiverIndex, *receiver);
4939
4940 ASSERT_EQ(details_size, details_index);
4941 return *Factory::NewJSArrayWithElements(details);
4942}
4943
4944
4945static Object* Runtime_GetCFrames(Arguments args) {
4946 HandleScope scope;
4947 ASSERT(args.length() == 1);
4948 Object* result = Runtime_CheckExecutionState(args);
4949 if (result->IsFailure()) return result;
4950
4951 static const int kMaxCFramesSize = 200;
4952 OS::StackFrame frames[kMaxCFramesSize];
4953 int frames_count = OS::StackWalk(frames, kMaxCFramesSize);
4954 if (frames_count == OS::kStackWalkError) {
4955 return Heap::undefined_value();
4956 }
4957
4958 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
4959 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
4960 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
4961 for (int i = 0; i < frames_count; i++) {
4962 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
4963 frame_value->SetProperty(
4964 *address_str,
4965 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
4966 NONE);
4967
4968 // Get the stack walk text for this frame.
4969 Handle<String> frame_text;
4970 if (strlen(frames[i].text) > 0) {
4971 Vector<const char> str(frames[i].text, strlen(frames[i].text));
4972 frame_text = Factory::NewStringFromAscii(str);
4973 }
4974
4975 if (!frame_text.is_null()) {
4976 frame_value->SetProperty(*text_str, *frame_text, NONE);
4977 }
4978
4979 frames_array->set(i, *frame_value);
4980 }
4981 return *Factory::NewJSArrayWithElements(frames_array);
4982}
4983
4984
4985static Object* Runtime_GetBreakLocations(Arguments args) {
4986 HandleScope scope;
4987 ASSERT(args.length() == 1);
4988
4989 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
4990 Handle<SharedFunctionInfo> shared(raw_fun->shared());
4991 // Find the number of break points
4992 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
4993 if (break_locations->IsUndefined()) return Heap::undefined_value();
4994 // Return array as JS array
4995 return *Factory::NewJSArrayWithElements(
4996 Handle<FixedArray>::cast(break_locations));
4997}
4998
4999
5000// Set a break point in a function
5001// args[0]: function
5002// args[1]: number: break source position (within the function source)
5003// args[2]: number: break point object
5004static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
5005 HandleScope scope;
5006 ASSERT(args.length() == 3);
5007 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
5008 Handle<SharedFunctionInfo> shared(raw_fun->shared());
5009 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
5010 RUNTIME_ASSERT(source_position >= 0);
5011 Handle<Object> break_point_object_arg = args.at<Object>(2);
5012
5013 // Set break point.
5014 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
5015
5016 return Heap::undefined_value();
5017}
5018
5019
5020static Object* FindSharedFunctionInfoInScript(Handle<Script> script,
5021 int position) {
5022 // Iterate the heap looking for SharedFunctionInfo generated from the
5023 // script. The inner most SharedFunctionInfo containing the source position
5024 // for the requested break point is found.
5025 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
5026 // which is found is not compiled it is compiled and the heap is iterated
5027 // again as the compilation might create inner functions from the newly
5028 // compiled function and the actual requested break point might be in one of
5029 // these functions.
5030 bool done = false;
5031 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00005032 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005033 Handle<SharedFunctionInfo> target;
5034 // The current candidate for the last function in script:
5035 Handle<SharedFunctionInfo> last;
5036 while (!done) {
5037 HeapIterator iterator;
5038 while (iterator.has_next()) {
5039 HeapObject* obj = iterator.next();
5040 ASSERT(obj != NULL);
5041 if (obj->IsSharedFunctionInfo()) {
5042 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
5043 if (shared->script() == *script) {
5044 // If the SharedFunctionInfo found has the requested script data and
5045 // contains the source position it is a candidate.
5046 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00005047 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005048 start_position = shared->start_position();
5049 }
5050 if (start_position <= position &&
5051 position <= shared->end_position()) {
5052 // If there is no candidate or this function is within the currrent
5053 // candidate this is the new candidate.
5054 if (target.is_null()) {
5055 target_start_position = start_position;
5056 target = shared;
5057 } else {
5058 if (target_start_position < start_position &&
5059 shared->end_position() < target->end_position()) {
5060 target_start_position = start_position;
5061 target = shared;
5062 }
5063 }
5064 }
5065
5066 // Keep track of the last function in the script.
5067 if (last.is_null() ||
5068 shared->end_position() > last->start_position()) {
5069 last = shared;
5070 }
5071 }
5072 }
5073 }
5074
5075 // Make sure some candidate is selected.
5076 if (target.is_null()) {
5077 if (!last.is_null()) {
5078 // Position after the last function - use last.
5079 target = last;
5080 } else {
5081 // Unable to find function - possibly script without any function.
5082 return Heap::undefined_value();
5083 }
5084 }
5085
5086 // If the candidate found is compiled we are done. NOTE: when lazy
5087 // compilation of inner functions is introduced some additional checking
5088 // needs to be done here to compile inner functions.
5089 done = target->is_compiled();
5090 if (!done) {
5091 // If the candidate is not compiled compile it to reveal any inner
5092 // functions which might contain the requested source position.
5093 CompileLazyShared(target, KEEP_EXCEPTION);
5094 }
5095 }
5096
5097 return *target;
5098}
5099
5100
5101// Change the state of a break point in a script. NOTE: Regarding performance
5102// see the NOTE for GetScriptFromScriptData.
5103// args[0]: script to set break point in
5104// args[1]: number: break source position (within the script source)
5105// args[2]: number: break point object
5106static Object* Runtime_SetScriptBreakPoint(Arguments args) {
5107 HandleScope scope;
5108 ASSERT(args.length() == 3);
5109 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
5110 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
5111 RUNTIME_ASSERT(source_position >= 0);
5112 Handle<Object> break_point_object_arg = args.at<Object>(2);
5113
5114 // Get the script from the script wrapper.
5115 RUNTIME_ASSERT(wrapper->value()->IsScript());
5116 Handle<Script> script(Script::cast(wrapper->value()));
5117
5118 Object* result = FindSharedFunctionInfoInScript(script, source_position);
5119 if (!result->IsUndefined()) {
5120 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
5121 // Find position within function. The script position might be before the
5122 // source position of the first function.
5123 int position;
5124 if (shared->start_position() > source_position) {
5125 position = 0;
5126 } else {
5127 position = source_position - shared->start_position();
5128 }
5129 Debug::SetBreakPoint(shared, position, break_point_object_arg);
5130 }
5131 return Heap::undefined_value();
5132}
5133
5134
5135// Clear a break point
5136// args[0]: number: break point object
5137static Object* Runtime_ClearBreakPoint(Arguments args) {
5138 HandleScope scope;
5139 ASSERT(args.length() == 1);
5140 Handle<Object> break_point_object_arg = args.at<Object>(0);
5141
5142 // Clear break point.
5143 Debug::ClearBreakPoint(break_point_object_arg);
5144
5145 return Heap::undefined_value();
5146}
5147
5148
5149// Change the state of break on exceptions
5150// args[0]: boolean indicating uncaught exceptions
5151// args[1]: boolean indicating on/off
5152static Object* Runtime_ChangeBreakOnException(Arguments args) {
5153 HandleScope scope;
5154 ASSERT(args.length() == 2);
5155 ASSERT(args[0]->IsNumber());
5156 ASSERT(args[1]->IsBoolean());
5157
5158 // Update break point state
5159 ExceptionBreakType type =
5160 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
5161 bool enable = args[1]->ToBoolean()->IsTrue();
5162 Debug::ChangeBreakOnException(type, enable);
5163 return Heap::undefined_value();
5164}
5165
5166
5167// Prepare for stepping
5168// args[0]: break id for checking execution state
5169// args[1]: step action from the enumeration StepAction
5170// args[2]: number of times to perform the step
5171static Object* Runtime_PrepareStep(Arguments args) {
5172 HandleScope scope;
5173 ASSERT(args.length() == 3);
5174 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005175 Object* check = Runtime_CheckExecutionState(args);
5176 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005177 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
5178 return Top::Throw(Heap::illegal_argument_symbol());
5179 }
5180
5181 // Get the step action and check validity.
5182 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
5183 if (step_action != StepIn &&
5184 step_action != StepNext &&
5185 step_action != StepOut &&
5186 step_action != StepInMin &&
5187 step_action != StepMin) {
5188 return Top::Throw(Heap::illegal_argument_symbol());
5189 }
5190
5191 // Get the number of steps.
5192 int step_count = NumberToInt32(args[2]);
5193 if (step_count < 1) {
5194 return Top::Throw(Heap::illegal_argument_symbol());
5195 }
5196
5197 // Prepare step.
5198 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
5199 return Heap::undefined_value();
5200}
5201
5202
5203// Clear all stepping set by PrepareStep.
5204static Object* Runtime_ClearStepping(Arguments args) {
5205 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00005206 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005207 Debug::ClearStepping();
5208 return Heap::undefined_value();
5209}
5210
5211
5212// Creates a copy of the with context chain. The copy of the context chain is
5213// is linked to the function context supplied.
5214static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
5215 Handle<Context> function_context) {
5216 // At the bottom of the chain. Return the function context to link to.
5217 if (context_chain->is_function_context()) {
5218 return function_context;
5219 }
5220
5221 // Recursively copy the with contexts.
5222 Handle<Context> previous(context_chain->previous());
5223 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
5224 return Factory::NewWithContext(
5225 CopyWithContextChain(function_context, previous), extension);
5226}
5227
5228
5229// Helper function to find or create the arguments object for
5230// Runtime_DebugEvaluate.
5231static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
5232 Handle<JSFunction> function,
5233 Handle<Code> code,
5234 const ScopeInfo<>* sinfo,
5235 Handle<Context> function_context) {
5236 // Try to find the value of 'arguments' to pass as parameter. If it is not
5237 // found (that is the debugged function does not reference 'arguments' and
5238 // does not support eval) then create an 'arguments' object.
5239 int index;
5240 if (sinfo->number_of_stack_slots() > 0) {
5241 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
5242 if (index != -1) {
5243 return Handle<Object>(frame->GetExpression(index));
5244 }
5245 }
5246
5247 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
5248 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
5249 NULL);
5250 if (index != -1) {
5251 return Handle<Object>(function_context->get(index));
5252 }
5253 }
5254
5255 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005256 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
5257 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005258 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005259 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005260 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005261 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005262 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005263 return arguments;
5264}
5265
5266
5267// Evaluate a piece of JavaScript in the context of a stack frame for
5268// debugging. This is acomplished by creating a new context which in its
5269// extension part has all the parameters and locals of the function on the
5270// stack frame. A function which calls eval with the code to evaluate is then
5271// compiled in this context and called in this context. As this context
5272// replaces the context of the function on the stack frame a new (empty)
5273// function is created as well to be used as the closure for the context.
5274// This function and the context acts as replacements for the function on the
5275// stack frame presenting the same view of the values of parameters and
5276// local variables as if the piece of JavaScript was evaluated at the point
5277// where the function on the stack frame is currently stopped.
5278static Object* Runtime_DebugEvaluate(Arguments args) {
5279 HandleScope scope;
5280
5281 // Check the execution state and decode arguments frame and source to be
5282 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005283 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005284 Object* check_result = Runtime_CheckExecutionState(args);
5285 if (check_result->IsFailure()) return check_result;
5286 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
5287 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005288 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
5289
5290 // Handle the processing of break.
5291 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005292
5293 // Get the frame where the debugging is performed.
5294 StackFrame::Id id = UnwrapFrameId(wrapped_id);
5295 JavaScriptFrameIterator it(id);
5296 JavaScriptFrame* frame = it.frame();
5297 Handle<JSFunction> function(JSFunction::cast(frame->function()));
5298 Handle<Code> code(function->code());
5299 ScopeInfo<> sinfo(*code);
5300
5301 // Traverse the saved contexts chain to find the active context for the
5302 // selected frame.
5303 SaveContext* save = Top::save_context();
5304 while (save != NULL && reinterpret_cast<Address>(save) < frame->sp()) {
5305 save = save->prev();
5306 }
5307 ASSERT(save != NULL);
5308 SaveContext savex;
5309 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005310
5311 // Create the (empty) function replacing the function on the stack frame for
5312 // the purpose of evaluating in the context created below. It is important
5313 // that this function does not describe any parameters and local variables
5314 // in the context. If it does then this will cause problems with the lookup
5315 // in Context::Lookup, where context slots for parameters and local variables
5316 // are looked at before the extension object.
5317 Handle<JSFunction> go_between =
5318 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
5319 go_between->set_context(function->context());
5320#ifdef DEBUG
5321 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
5322 ASSERT(go_between_sinfo.number_of_parameters() == 0);
5323 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
5324#endif
5325
5326 // Allocate and initialize a context extension object with all the
5327 // arguments, stack locals heap locals and extension properties of the
5328 // debugged function.
5329 Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function());
5330 // First fill all parameters to the context extension.
5331 for (int i = 0; i < sinfo.number_of_parameters(); ++i) {
5332 SetProperty(context_ext,
5333 sinfo.parameter_name(i),
5334 Handle<Object>(frame->GetParameter(i)), NONE);
5335 }
5336 // Second fill all stack locals to the context extension.
5337 for (int i = 0; i < sinfo.number_of_stack_slots(); i++) {
5338 SetProperty(context_ext,
5339 sinfo.stack_slot_name(i),
5340 Handle<Object>(frame->GetExpression(i)), NONE);
5341 }
5342 // Third fill all context locals to the context extension.
5343 Handle<Context> frame_context(Context::cast(frame->context()));
5344 Handle<Context> function_context(frame_context->fcontext());
5345 for (int i = Context::MIN_CONTEXT_SLOTS;
5346 i < sinfo.number_of_context_slots();
5347 ++i) {
5348 int context_index =
5349 ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL);
5350 SetProperty(context_ext,
5351 sinfo.context_slot_name(i),
5352 Handle<Object>(function_context->get(context_index)), NONE);
5353 }
5354 // Finally copy any properties from the function context extension. This will
5355 // be variables introduced by eval.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005356 if (function_context->has_extension() &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005357 !function_context->IsGlobalContext()) {
5358 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
5359 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
5360 for (int i = 0; i < keys->length(); i++) {
5361 // Names of variables introduced by eval are strings.
5362 ASSERT(keys->get(i)->IsString());
5363 Handle<String> key(String::cast(keys->get(i)));
5364 SetProperty(context_ext, key, GetProperty(ext, key), NONE);
5365 }
5366 }
5367
5368 // Allocate a new context for the debug evaluation and set the extension
5369 // object build.
5370 Handle<Context> context =
5371 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
5372 context->set_extension(*context_ext);
5373 // Copy any with contexts present and chain them in front of this context.
5374 context = CopyWithContextChain(frame_context, context);
5375
5376 // Wrap the evaluation statement in a new function compiled in the newly
5377 // created context. The function has one parameter which has to be called
5378 // 'arguments'. This it to have access to what would have been 'arguments' in
5379 // the function beeing debugged.
5380 // function(arguments,__source__) {return eval(__source__);}
5381 static const char* source_str =
5382 "function(arguments,__source__){return eval(__source__);}";
5383 static const int source_str_length = strlen(source_str);
5384 Handle<String> function_source =
5385 Factory::NewStringFromAscii(Vector<const char>(source_str,
5386 source_str_length));
5387 Handle<JSFunction> boilerplate =
ager@chromium.org236ad962008-09-25 09:45:57 +00005388 Compiler::CompileEval(function_source, 0, context->IsGlobalContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005389 if (boilerplate.is_null()) return Failure::Exception();
5390 Handle<JSFunction> compiled_function =
5391 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5392
5393 // Invoke the result of the compilation to get the evaluation function.
5394 bool has_pending_exception;
5395 Handle<Object> receiver(frame->receiver());
5396 Handle<Object> evaluation_function =
5397 Execution::Call(compiled_function, receiver, 0, NULL,
5398 &has_pending_exception);
5399
5400 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
5401 function_context);
5402
5403 // Invoke the evaluation function and return the result.
5404 const int argc = 2;
5405 Object** argv[argc] = { arguments.location(),
5406 Handle<Object>::cast(source).location() };
5407 Handle<Object> result =
5408 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
5409 argc, argv, &has_pending_exception);
5410 return *result;
5411}
5412
5413
5414static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
5415 HandleScope scope;
5416
5417 // Check the execution state and decode arguments frame and source to be
5418 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005419 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005420 Object* check_result = Runtime_CheckExecutionState(args);
5421 if (check_result->IsFailure()) return check_result;
5422 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005423 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
5424
5425 // Handle the processing of break.
5426 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005427
5428 // Enter the top context from before the debugger was invoked.
5429 SaveContext save;
5430 SaveContext* top = &save;
5431 while (top != NULL && *top->context() == *Debug::debug_context()) {
5432 top = top->prev();
5433 }
5434 if (top != NULL) {
5435 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005436 }
5437
5438 // Get the global context now set to the top context from before the
5439 // debugger was invoked.
5440 Handle<Context> context = Top::global_context();
5441
5442 // Compile the source to be evaluated.
ager@chromium.org236ad962008-09-25 09:45:57 +00005443 Handle<JSFunction> boilerplate(Compiler::CompileEval(source, 0, true));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005444 if (boilerplate.is_null()) return Failure::Exception();
5445 Handle<JSFunction> compiled_function =
5446 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
5447 context));
5448
5449 // Invoke the result of the compilation to get the evaluation function.
5450 bool has_pending_exception;
5451 Handle<Object> receiver = Top::global();
5452 Handle<Object> result =
5453 Execution::Call(compiled_function, receiver, 0, NULL,
5454 &has_pending_exception);
5455 return *result;
5456}
5457
5458
5459// Helper function used by Runtime_DebugGetLoadedScripts below.
5460static int DebugGetLoadedScripts(FixedArray* instances, int instances_size) {
5461 NoHandleAllocation ha;
5462 AssertNoAllocation no_alloc;
5463
5464 // Get hold of the current empty script.
5465 Context* context = Top::context()->global_context();
5466 Script* empty = context->empty_script();
5467
5468 // Scan heap for Script objects.
5469 int count = 0;
5470 HeapIterator iterator;
5471 while (iterator.has_next()) {
5472 HeapObject* obj = iterator.next();
5473 ASSERT(obj != NULL);
5474 if (obj->IsScript() && obj != empty) {
5475 if (instances != NULL && count < instances_size) {
5476 instances->set(count, obj);
5477 }
5478 count++;
5479 }
5480 }
5481
5482 return count;
5483}
5484
5485
5486static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
5487 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00005488 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005489
5490 // Perform two GCs to get rid of all unreferenced scripts. The first GC gets
5491 // rid of all the cached script wrappes and the second gets rid of the
5492 // scripts which is no longer referenced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005493 Heap::CollectAllGarbage();
5494 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005495
5496 // Get the number of scripts.
5497 int count;
5498 count = DebugGetLoadedScripts(NULL, 0);
5499
5500 // Allocate an array to hold the result.
5501 Handle<FixedArray> instances = Factory::NewFixedArray(count);
5502
5503 // Fill the script objects.
5504 count = DebugGetLoadedScripts(*instances, count);
5505
5506 // Convert the script objects to proper JS objects.
5507 for (int i = 0; i < count; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005508 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
5509 // Get the script wrapper in a local handle before calling GetScriptWrapper,
5510 // because using
5511 // instances->set(i, *GetScriptWrapper(script))
5512 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
5513 // already have deferenced the instances handle.
5514 Handle<JSValue> wrapper = GetScriptWrapper(script);
5515 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005516 }
5517
5518 // Return result as a JS array.
5519 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
5520 Handle<JSArray>::cast(result)->SetContent(*instances);
5521 return *result;
5522}
5523
5524
5525// Helper function used by Runtime_DebugReferencedBy below.
5526static int DebugReferencedBy(JSObject* target,
5527 Object* instance_filter, int max_references,
5528 FixedArray* instances, int instances_size,
5529 JSFunction* context_extension_function,
5530 JSFunction* arguments_function) {
5531 NoHandleAllocation ha;
5532 AssertNoAllocation no_alloc;
5533
5534 // Iterate the heap.
5535 int count = 0;
5536 JSObject* last = NULL;
5537 HeapIterator iterator;
5538 while (iterator.has_next() &&
5539 (max_references == 0 || count < max_references)) {
5540 // Only look at all JSObjects.
5541 HeapObject* heap_obj = iterator.next();
5542 if (heap_obj->IsJSObject()) {
5543 // Skip context extension objects and argument arrays as these are
5544 // checked in the context of functions using them.
5545 JSObject* obj = JSObject::cast(heap_obj);
5546 if (obj->map()->constructor() == context_extension_function ||
5547 obj->map()->constructor() == arguments_function) {
5548 continue;
5549 }
5550
5551 // Check if the JS object has a reference to the object looked for.
5552 if (obj->ReferencesObject(target)) {
5553 // Check instance filter if supplied. This is normally used to avoid
5554 // references from mirror objects (see Runtime_IsInPrototypeChain).
5555 if (!instance_filter->IsUndefined()) {
5556 Object* V = obj;
5557 while (true) {
5558 Object* prototype = V->GetPrototype();
5559 if (prototype->IsNull()) {
5560 break;
5561 }
5562 if (instance_filter == prototype) {
5563 obj = NULL; // Don't add this object.
5564 break;
5565 }
5566 V = prototype;
5567 }
5568 }
5569
5570 if (obj != NULL) {
5571 // Valid reference found add to instance array if supplied an update
5572 // count.
5573 if (instances != NULL && count < instances_size) {
5574 instances->set(count, obj);
5575 }
5576 last = obj;
5577 count++;
5578 }
5579 }
5580 }
5581 }
5582
5583 // Check for circular reference only. This can happen when the object is only
5584 // referenced from mirrors and has a circular reference in which case the
5585 // object is not really alive and would have been garbage collected if not
5586 // referenced from the mirror.
5587 if (count == 1 && last == target) {
5588 count = 0;
5589 }
5590
5591 // Return the number of referencing objects found.
5592 return count;
5593}
5594
5595
5596// Scan the heap for objects with direct references to an object
5597// args[0]: the object to find references to
5598// args[1]: constructor function for instances to exclude (Mirror)
5599// args[2]: the the maximum number of objects to return
5600static Object* Runtime_DebugReferencedBy(Arguments args) {
5601 ASSERT(args.length() == 3);
5602
5603 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005604 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005605
5606 // Check parameters.
5607 CONVERT_CHECKED(JSObject, target, args[0]);
5608 Object* instance_filter = args[1];
5609 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
5610 instance_filter->IsJSObject());
5611 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
5612 RUNTIME_ASSERT(max_references >= 0);
5613
5614 // Get the constructor function for context extension and arguments array.
5615 JSFunction* context_extension_function =
5616 Top::context()->global_context()->context_extension_function();
5617 JSObject* arguments_boilerplate =
5618 Top::context()->global_context()->arguments_boilerplate();
5619 JSFunction* arguments_function =
5620 JSFunction::cast(arguments_boilerplate->map()->constructor());
5621
5622 // Get the number of referencing objects.
5623 int count;
5624 count = DebugReferencedBy(target, instance_filter, max_references,
5625 NULL, 0,
5626 context_extension_function, arguments_function);
5627
5628 // Allocate an array to hold the result.
5629 Object* object = Heap::AllocateFixedArray(count);
5630 if (object->IsFailure()) return object;
5631 FixedArray* instances = FixedArray::cast(object);
5632
5633 // Fill the referencing objects.
5634 count = DebugReferencedBy(target, instance_filter, max_references,
5635 instances, count,
5636 context_extension_function, arguments_function);
5637
5638 // Return result as JS array.
5639 Object* result =
5640 Heap::AllocateJSObject(
5641 Top::context()->global_context()->array_function());
5642 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
5643 return result;
5644}
5645
5646
5647// Helper function used by Runtime_DebugConstructedBy below.
5648static int DebugConstructedBy(JSFunction* constructor, int max_references,
5649 FixedArray* instances, int instances_size) {
5650 AssertNoAllocation no_alloc;
5651
5652 // Iterate the heap.
5653 int count = 0;
5654 HeapIterator iterator;
5655 while (iterator.has_next() &&
5656 (max_references == 0 || count < max_references)) {
5657 // Only look at all JSObjects.
5658 HeapObject* heap_obj = iterator.next();
5659 if (heap_obj->IsJSObject()) {
5660 JSObject* obj = JSObject::cast(heap_obj);
5661 if (obj->map()->constructor() == constructor) {
5662 // Valid reference found add to instance array if supplied an update
5663 // count.
5664 if (instances != NULL && count < instances_size) {
5665 instances->set(count, obj);
5666 }
5667 count++;
5668 }
5669 }
5670 }
5671
5672 // Return the number of referencing objects found.
5673 return count;
5674}
5675
5676
5677// Scan the heap for objects constructed by a specific function.
5678// args[0]: the constructor to find instances of
5679// args[1]: the the maximum number of objects to return
5680static Object* Runtime_DebugConstructedBy(Arguments args) {
5681 ASSERT(args.length() == 2);
5682
5683 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005684 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005685
5686 // Check parameters.
5687 CONVERT_CHECKED(JSFunction, constructor, args[0]);
5688 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
5689 RUNTIME_ASSERT(max_references >= 0);
5690
5691 // Get the number of referencing objects.
5692 int count;
5693 count = DebugConstructedBy(constructor, max_references, NULL, 0);
5694
5695 // Allocate an array to hold the result.
5696 Object* object = Heap::AllocateFixedArray(count);
5697 if (object->IsFailure()) return object;
5698 FixedArray* instances = FixedArray::cast(object);
5699
5700 // Fill the referencing objects.
5701 count = DebugConstructedBy(constructor, max_references, instances, count);
5702
5703 // Return result as JS array.
5704 Object* result =
5705 Heap::AllocateJSObject(
5706 Top::context()->global_context()->array_function());
5707 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
5708 return result;
5709}
5710
5711
5712static Object* Runtime_GetPrototype(Arguments args) {
5713 ASSERT(args.length() == 1);
5714
5715 CONVERT_CHECKED(JSObject, obj, args[0]);
5716
5717 return obj->GetPrototype();
5718}
5719
5720
5721static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005722 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005723 CPU::DebugBreak();
5724 return Heap::undefined_value();
5725}
5726
5727
5728// Finds the script object from the script data. NOTE: This operation uses
5729// heap traversal to find the function generated for the source position
5730// for the requested break point. For lazily compiled functions several heap
5731// traversals might be required rendering this operation as a rather slow
5732// operation. However for setting break points which is normally done through
5733// some kind of user interaction the performance is not crucial.
5734static Handle<Object> Runtime_GetScriptFromScriptName(
5735 Handle<String> script_name) {
5736 // Scan the heap for Script objects to find the script with the requested
5737 // script data.
5738 Handle<Script> script;
5739 HeapIterator iterator;
5740 while (script.is_null() && iterator.has_next()) {
5741 HeapObject* obj = iterator.next();
5742 // If a script is found check if it has the script data requested.
5743 if (obj->IsScript()) {
5744 if (Script::cast(obj)->name()->IsString()) {
5745 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
5746 script = Handle<Script>(Script::cast(obj));
5747 }
5748 }
5749 }
5750 }
5751
5752 // If no script with the requested script data is found return undefined.
5753 if (script.is_null()) return Factory::undefined_value();
5754
5755 // Return the script found.
5756 return GetScriptWrapper(script);
5757}
5758
5759
5760// Get the script object from script data. NOTE: Regarding performance
5761// see the NOTE for GetScriptFromScriptData.
5762// args[0]: script data for the script to find the source for
5763static Object* Runtime_GetScript(Arguments args) {
5764 HandleScope scope;
5765
5766 ASSERT(args.length() == 1);
5767
5768 CONVERT_CHECKED(String, script_name, args[0]);
5769
5770 // Find the requested script.
5771 Handle<Object> result =
5772 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
5773 return *result;
5774}
5775
5776
5777static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
5778#ifdef DEBUG
5779 HandleScope scope;
5780 ASSERT(args.length() == 1);
5781 // Get the function and make sure it is compiled.
5782 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5783 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
5784 return Failure::Exception();
5785 }
5786 func->code()->PrintLn();
5787#endif // DEBUG
5788 return Heap::undefined_value();
5789}
5790
5791
5792static Object* Runtime_Abort(Arguments args) {
5793 ASSERT(args.length() == 2);
5794 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
5795 Smi::cast(args[1])->value());
5796 Top::PrintStack();
5797 OS::Abort();
5798 UNREACHABLE();
5799 return NULL;
5800}
5801
5802
kasper.lund44510672008-07-25 07:37:58 +00005803#ifdef DEBUG
5804// ListNatives is ONLY used by the fuzz-natives.js in debug mode
5805// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005806static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005807 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005808 HandleScope scope;
5809 Handle<JSArray> result = Factory::NewJSArray(0);
5810 int index = 0;
5811#define ADD_ENTRY(Name, argc) \
5812 { \
5813 HandleScope inner; \
5814 Handle<String> name = \
5815 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
5816 Handle<JSArray> pair = Factory::NewJSArray(0); \
5817 SetElement(pair, 0, name); \
5818 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
5819 SetElement(result, index++, pair); \
5820 }
5821 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
5822#undef ADD_ENTRY
5823 return *result;
5824}
kasper.lund44510672008-07-25 07:37:58 +00005825#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005826
5827
5828static Object* Runtime_IS_VAR(Arguments args) {
5829 UNREACHABLE(); // implemented as macro in the parser
5830 return NULL;
5831}
5832
5833
5834// ----------------------------------------------------------------------------
5835// Implementation of Runtime
5836
5837#define F(name, nargs) \
5838 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
5839 static_cast<int>(Runtime::k##name) },
5840
5841static Runtime::Function Runtime_functions[] = {
5842 RUNTIME_FUNCTION_LIST(F)
5843 { NULL, NULL, NULL, 0, -1 }
5844};
5845
5846#undef F
5847
5848
5849Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
5850 ASSERT(0 <= fid && fid < kNofFunctions);
5851 return &Runtime_functions[fid];
5852}
5853
5854
5855Runtime::Function* Runtime::FunctionForName(const char* name) {
5856 for (Function* f = Runtime_functions; f->name != NULL; f++) {
5857 if (strcmp(f->name, name) == 0) {
5858 return f;
5859 }
5860 }
5861 return NULL;
5862}
5863
5864
5865void Runtime::PerformGC(Object* result) {
5866 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005867 if (failure->IsRetryAfterGC()) {
5868 // Try to do a garbage collection; ignore it if it fails. The C
5869 // entry stub will throw an out-of-memory exception in that case.
5870 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
5871 } else {
5872 // Handle last resort GC and make sure to allow future allocations
5873 // to grow the heap without causing GCs (if possible).
5874 Counters::gc_last_resort_from_js.Increment();
5875 Heap::CollectAllGarbage();
5876 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005877}
5878
5879
5880} } // namespace v8::internal