blob: 6fe7cc68b19a9bb1b627f399f4fb8a86d9923e16 [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.orgc3e50d82008-11-05 11:53:10 +0000972 subject->TryFlatten(StringShape(subject));
ager@chromium.org870a0b62008-11-04 11:43:05 +0000973 StringShape shape(subject);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +0000974 if (i >= static_cast<uint32_t>(subject->length(shape))) {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000975 return Heap::nan_value();
976 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +0000977 return Smi::FromInt(subject->Get(shape, 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);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001358 ASSERT(start_index <= sub->length(sub_shape));
ager@chromium.org7c537e22008-10-16 08:43:32 +00001359
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001360 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001361 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001362
ager@chromium.org870a0b62008-11-04 11:43:05 +00001363 int subject_length = sub->length(sub_shape);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001364 if (start_index + pattern_length > subject_length) return -1;
1365
ager@chromium.org870a0b62008-11-04 11:43:05 +00001366 if (!sub->IsFlat(sub_shape)) {
1367 FlattenString(sub);
1368 sub_shape = StringShape(*sub);
1369 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001370 StringShape pat_shape(*pat);
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);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001389 sub_shape = StringShape(*sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001390 }
ager@chromium.org236ad962008-09-25 09:45:57 +00001391
ager@chromium.org7c537e22008-10-16 08:43:32 +00001392 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
1393 // dispatch on type of strings
ager@chromium.org870a0b62008-11-04 11:43:05 +00001394 if (pat_shape.IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001395 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org870a0b62008-11-04 11:43:05 +00001396 if (sub_shape.IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001397 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00001398 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001399 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00001400 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001401 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org870a0b62008-11-04 11:43:05 +00001402 if (sub_shape.IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001403 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001404 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001405 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001406}
1407
1408
1409static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001410 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001411 ASSERT(args.length() == 3);
1412
ager@chromium.org7c537e22008-10-16 08:43:32 +00001413 CONVERT_ARG_CHECKED(String, sub, 0);
1414 CONVERT_ARG_CHECKED(String, pat, 1);
1415
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001416 Object* index = args[2];
1417 uint32_t start_index;
1418 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
1419
ager@chromium.org870a0b62008-11-04 11:43:05 +00001420 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00001421 int position = Runtime::StringMatch(sub, pat, start_index);
1422 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001423}
1424
1425
1426static Object* Runtime_StringLastIndexOf(Arguments args) {
1427 NoHandleAllocation ha;
1428 ASSERT(args.length() == 3);
1429
1430 CONVERT_CHECKED(String, sub, args[0]);
1431 CONVERT_CHECKED(String, pat, args[1]);
1432 Object* index = args[2];
1433
ager@chromium.org870a0b62008-11-04 11:43:05 +00001434 sub->TryFlatten(StringShape(sub));
1435 pat->TryFlatten(StringShape(pat));
1436
1437 StringShape sub_shape(sub);
1438 StringShape pat_shape(pat);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001439
1440 uint32_t start_index;
1441 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
1442
ager@chromium.org870a0b62008-11-04 11:43:05 +00001443 uint32_t pattern_length = pat->length(pat_shape);
1444 uint32_t sub_length = sub->length(sub_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001445
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001446 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001447 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001448 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001449
1450 for (int i = start_index; i >= 0; i--) {
1451 bool found = true;
1452 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001453 if (sub->Get(sub_shape, i + j) != pat->Get(pat_shape, j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001454 found = false;
1455 break;
1456 }
1457 }
1458 if (found) return Smi::FromInt(i);
1459 }
1460
1461 return Smi::FromInt(-1);
1462}
1463
1464
1465static Object* Runtime_StringLocaleCompare(Arguments args) {
1466 NoHandleAllocation ha;
1467 ASSERT(args.length() == 2);
1468
1469 CONVERT_CHECKED(String, str1, args[0]);
1470 CONVERT_CHECKED(String, str2, args[1]);
1471
1472 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.org870a0b62008-11-04 11:43:05 +00001473 StringShape shape1(str1);
1474 StringShape shape2(str2);
1475 int str1_length = str1->length(shape1);
1476 int str2_length = str2->length(shape2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001477
1478 // Decide trivial cases without flattening.
1479 if (str1_length == 0) {
1480 if (str2_length == 0) return Smi::FromInt(0); // Equal.
1481 return Smi::FromInt(-str2_length);
1482 } else {
1483 if (str2_length == 0) return Smi::FromInt(str1_length);
1484 }
1485
1486 int end = str1_length < str2_length ? str1_length : str2_length;
1487
1488 // No need to flatten if we are going to find the answer on the first
1489 // character. At this point we know there is at least one character
1490 // in each string, due to the trivial case handling above.
ager@chromium.org870a0b62008-11-04 11:43:05 +00001491 int d = str1->Get(shape1, 0) - str2->Get(shape2, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001492 if (d != 0) return Smi::FromInt(d);
1493
ager@chromium.org870a0b62008-11-04 11:43:05 +00001494 str1->TryFlatten(shape1); // Shapes are no longer valid now!
1495 str2->TryFlatten(shape2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001496
1497 static StringInputBuffer buf1;
1498 static StringInputBuffer buf2;
1499
1500 buf1.Reset(str1);
1501 buf2.Reset(str2);
1502
1503 for (int i = 0; i < end; i++) {
1504 uint16_t char1 = buf1.GetNext();
1505 uint16_t char2 = buf2.GetNext();
1506 if (char1 != char2) return Smi::FromInt(char1 - char2);
1507 }
1508
1509 return Smi::FromInt(str1_length - str2_length);
1510}
1511
1512
1513static Object* Runtime_StringSlice(Arguments args) {
1514 NoHandleAllocation ha;
1515 ASSERT(args.length() == 3);
1516
1517 CONVERT_CHECKED(String, value, args[0]);
1518 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
1519 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
1520
1521 int start = FastD2I(from_number);
1522 int end = FastD2I(to_number);
1523
1524 RUNTIME_ASSERT(end >= start);
1525 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001526 RUNTIME_ASSERT(end <= value->length());
1527 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001528}
1529
1530
1531static Object* Runtime_NumberToRadixString(Arguments args) {
1532 NoHandleAllocation ha;
1533 ASSERT(args.length() == 2);
1534
1535 CONVERT_DOUBLE_CHECKED(value, args[0]);
1536 if (isnan(value)) {
1537 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1538 }
1539 if (isinf(value)) {
1540 if (value < 0) {
1541 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1542 }
1543 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1544 }
1545 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
1546 int radix = FastD2I(radix_number);
1547 RUNTIME_ASSERT(2 <= radix && radix <= 36);
1548 char* str = DoubleToRadixCString(value, radix);
1549 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
1550 DeleteArray(str);
1551 return result;
1552}
1553
1554
1555static Object* Runtime_NumberToFixed(Arguments args) {
1556 NoHandleAllocation ha;
1557 ASSERT(args.length() == 2);
1558
1559 CONVERT_DOUBLE_CHECKED(value, args[0]);
1560 if (isnan(value)) {
1561 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1562 }
1563 if (isinf(value)) {
1564 if (value < 0) {
1565 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1566 }
1567 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1568 }
1569 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
1570 int f = FastD2I(f_number);
1571 RUNTIME_ASSERT(f >= 0);
1572 char* str = DoubleToFixedCString(value, f);
1573 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
1574 DeleteArray(str);
1575 return res;
1576}
1577
1578
1579static Object* Runtime_NumberToExponential(Arguments args) {
1580 NoHandleAllocation ha;
1581 ASSERT(args.length() == 2);
1582
1583 CONVERT_DOUBLE_CHECKED(value, args[0]);
1584 if (isnan(value)) {
1585 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1586 }
1587 if (isinf(value)) {
1588 if (value < 0) {
1589 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1590 }
1591 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1592 }
1593 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
1594 int f = FastD2I(f_number);
1595 RUNTIME_ASSERT(f >= -1 && f <= 20);
1596 char* str = DoubleToExponentialCString(value, f);
1597 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
1598 DeleteArray(str);
1599 return res;
1600}
1601
1602
1603static Object* Runtime_NumberToPrecision(Arguments args) {
1604 NoHandleAllocation ha;
1605 ASSERT(args.length() == 2);
1606
1607 CONVERT_DOUBLE_CHECKED(value, args[0]);
1608 if (isnan(value)) {
1609 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1610 }
1611 if (isinf(value)) {
1612 if (value < 0) {
1613 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1614 }
1615 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1616 }
1617 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
1618 int f = FastD2I(f_number);
1619 RUNTIME_ASSERT(f >= 1 && f <= 21);
1620 char* str = DoubleToPrecisionCString(value, f);
1621 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
1622 DeleteArray(str);
1623 return res;
1624}
1625
1626
1627// Returns a single character string where first character equals
1628// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001629static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001630 StringShape shape(*string);
1631 if (index < static_cast<uint32_t>(string->length(shape))) {
1632 string->TryFlatten(shape); // Invalidates shape!
1633 return LookupSingleCharacterStringFromCode(
1634 string->Get(StringShape(*string), index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001635 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001636 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001637}
1638
1639
1640Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
1641 // Handle [] indexing on Strings
1642 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001643 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
1644 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001645 }
1646
1647 // Handle [] indexing on String objects
1648 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001649 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
1650 Handle<Object> result =
1651 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
1652 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001653 }
1654
1655 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001656 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001657 return prototype->GetElement(index);
1658 }
1659
1660 return object->GetElement(index);
1661}
1662
1663
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001664Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
1665 HandleScope scope;
1666
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001667 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001668 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001669 Handle<Object> error =
1670 Factory::NewTypeError("non_object_property_load",
1671 HandleVector(args, 2));
1672 return Top::Throw(*error);
1673 }
1674
1675 // Check if the given key is an array index.
1676 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001677 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001678 return GetElementOrCharAt(object, index);
1679 }
1680
1681 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001682 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001683 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001684 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001685 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001686 bool has_pending_exception = false;
1687 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001688 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001689 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001690 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001691 }
1692
1693 // Check if the name is trivially convertable to an index and get
1694 // the element if so.
1695 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001696 return GetElementOrCharAt(object, index);
1697 } else {
1698 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001699 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001700 }
1701}
1702
1703
1704static Object* Runtime_GetProperty(Arguments args) {
1705 NoHandleAllocation ha;
1706 ASSERT(args.length() == 2);
1707
1708 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001709 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001710
1711 return Runtime::GetObjectProperty(object, key);
1712}
1713
1714
ager@chromium.org7c537e22008-10-16 08:43:32 +00001715
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001716// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001717static Object* Runtime_KeyedGetProperty(Arguments args) {
1718 NoHandleAllocation ha;
1719 ASSERT(args.length() == 2);
1720
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001721 // Fast cases for getting named properties of the receiver JSObject
1722 // itself. The global proxy objects has to be excluded since
1723 // LocalLookup on the global proxy object can return a valid result
1724 // eventhough the global proxy object never has properties. This is
1725 // the case because the global proxy object forwards everything to
1726 // its hidden prototype including local lookups.
1727 if (args[0]->IsJSObject() &&
1728 !args[0]->IsJSGlobalProxy() &&
1729 args[1]->IsString()) {
1730 JSObject* receiver = JSObject::cast(args[0]);
1731 String* key = String::cast(args[1]);
1732 if (receiver->HasFastProperties()) {
1733 // Attempt to use lookup cache.
1734 Object* obj = Heap::GetKeyedLookupCache();
1735 if (obj->IsFailure()) return obj;
1736 LookupCache* cache = LookupCache::cast(obj);
1737 Map* receiver_map = receiver->map();
1738 int offset = cache->Lookup(receiver_map, key);
1739 if (offset != LookupCache::kNotFound) {
1740 Object* value = receiver->FastPropertyAt(offset);
1741 return value->IsTheHole() ? Heap::undefined_value() : value;
1742 }
1743 // Lookup cache miss. Perform lookup and update the cache if
1744 // appropriate.
1745 LookupResult result;
1746 receiver->LocalLookup(key, &result);
1747 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
1748 int offset = result.GetFieldIndex();
1749 Object* obj = cache->Put(receiver_map, key, offset);
1750 if (obj->IsFailure()) return obj;
1751 Heap::SetKeyedLookupCache(LookupCache::cast(obj));
1752 Object* value = receiver->FastPropertyAt(offset);
1753 return value->IsTheHole() ? Heap::undefined_value() : value;
1754 }
1755 } else {
1756 // Attempt dictionary lookup.
1757 Dictionary* dictionary = receiver->property_dictionary();
1758 int entry = dictionary->FindStringEntry(key);
1759 if ((entry != DescriptorArray::kNotFound) &&
1760 (dictionary->DetailsAt(entry).type() == NORMAL)) {
1761 return dictionary->ValueAt(entry);
1762 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001763 }
1764 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001765
1766 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001767 return Runtime::GetObjectProperty(args.at<Object>(0),
1768 args.at<Object>(1));
1769}
1770
1771
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001772Object* Runtime::SetObjectProperty(Handle<Object> object,
1773 Handle<Object> key,
1774 Handle<Object> value,
1775 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001776 HandleScope scope;
1777
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001778 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001779 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001780 Handle<Object> error =
1781 Factory::NewTypeError("non_object_property_store",
1782 HandleVector(args, 2));
1783 return Top::Throw(*error);
1784 }
1785
1786 // If the object isn't a JavaScript object, we ignore the store.
1787 if (!object->IsJSObject()) return *value;
1788
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001789 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
1790
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001791 // Check if the given key is an array index.
1792 uint32_t index;
1793 if (Array::IndexFromObject(*key, &index)) {
1794 ASSERT(attr == NONE);
1795
1796 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
1797 // of a string using [] notation. We need to support this too in
1798 // JavaScript.
1799 // In the case of a String object we just need to redirect the assignment to
1800 // the underlying string if the index is in range. Since the underlying
1801 // string does nothing with the assignment then we can ignore such
1802 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001803 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001804 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001805 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001806
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001807 Handle<Object> result = SetElement(js_object, index, value);
1808 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001809 return *value;
1810 }
1811
1812 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001813 Handle<Object> result;
1814 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001815 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001816 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001817 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001818 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001819 key_string->TryFlatten(StringShape(*key_string));
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001820 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001821 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001822 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001823 return *value;
1824 }
1825
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001826 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001827 bool has_pending_exception = false;
1828 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
1829 if (has_pending_exception) return Failure::Exception();
1830 Handle<String> name = Handle<String>::cast(converted);
1831
1832 if (name->AsArrayIndex(&index)) {
1833 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001834 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001835 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001836 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001837 }
1838}
1839
1840
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001841static Object* Runtime_SetProperty(Arguments args) {
1842 NoHandleAllocation ha;
1843 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
1844
1845 Handle<Object> object = args.at<Object>(0);
1846 Handle<Object> key = args.at<Object>(1);
1847 Handle<Object> value = args.at<Object>(2);
1848
1849 // Compute attributes.
1850 PropertyAttributes attributes = NONE;
1851 if (args.length() == 4) {
1852 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001853 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001854 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001855 RUNTIME_ASSERT(
1856 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
1857 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001858 }
1859 return Runtime::SetObjectProperty(object, key, value, attributes);
1860}
1861
1862
1863// Set a local property, even if it is READ_ONLY. If the property does not
1864// exist, it will be added with attributes NONE.
1865static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
1866 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001867 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001868 CONVERT_CHECKED(JSObject, object, args[0]);
1869 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001870 // Compute attributes.
1871 PropertyAttributes attributes = NONE;
1872 if (args.length() == 4) {
1873 CONVERT_CHECKED(Smi, value_obj, args[3]);
1874 int unchecked_value = value_obj->value();
1875 // Only attribute bits should be set.
1876 RUNTIME_ASSERT(
1877 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
1878 attributes = static_cast<PropertyAttributes>(unchecked_value);
1879 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001880
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001881 return object->
1882 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001883}
1884
1885
1886static Object* Runtime_DeleteProperty(Arguments args) {
1887 NoHandleAllocation ha;
1888 ASSERT(args.length() == 2);
1889
1890 CONVERT_CHECKED(JSObject, object, args[0]);
1891 CONVERT_CHECKED(String, key, args[1]);
1892 return object->DeleteProperty(key);
1893}
1894
1895
1896static Object* Runtime_HasLocalProperty(Arguments args) {
1897 NoHandleAllocation ha;
1898 ASSERT(args.length() == 2);
1899 CONVERT_CHECKED(String, key, args[1]);
1900
1901 // Only JS objects can have properties.
1902 if (args[0]->IsJSObject()) {
1903 JSObject* object = JSObject::cast(args[0]);
1904 if (object->HasLocalProperty(key)) return Heap::true_value();
1905 } else if (args[0]->IsString()) {
1906 // Well, there is one exception: Handle [] on strings.
1907 uint32_t index;
1908 if (key->AsArrayIndex(&index)) {
1909 String* string = String::cast(args[0]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001910 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001911 return Heap::true_value();
1912 }
1913 }
1914 return Heap::false_value();
1915}
1916
1917
1918static Object* Runtime_HasProperty(Arguments args) {
1919 NoHandleAllocation na;
1920 ASSERT(args.length() == 2);
1921
1922 // Only JS objects can have properties.
1923 if (args[0]->IsJSObject()) {
1924 JSObject* object = JSObject::cast(args[0]);
1925 CONVERT_CHECKED(String, key, args[1]);
1926 if (object->HasProperty(key)) return Heap::true_value();
1927 }
1928 return Heap::false_value();
1929}
1930
1931
1932static Object* Runtime_HasElement(Arguments args) {
1933 NoHandleAllocation na;
1934 ASSERT(args.length() == 2);
1935
1936 // Only JS objects can have elements.
1937 if (args[0]->IsJSObject()) {
1938 JSObject* object = JSObject::cast(args[0]);
1939 CONVERT_CHECKED(Smi, index_obj, args[1]);
1940 uint32_t index = index_obj->value();
1941 if (object->HasElement(index)) return Heap::true_value();
1942 }
1943 return Heap::false_value();
1944}
1945
1946
1947static Object* Runtime_IsPropertyEnumerable(Arguments args) {
1948 NoHandleAllocation ha;
1949 ASSERT(args.length() == 2);
1950
1951 CONVERT_CHECKED(JSObject, object, args[0]);
1952 CONVERT_CHECKED(String, key, args[1]);
1953
1954 uint32_t index;
1955 if (key->AsArrayIndex(&index)) {
1956 return Heap::ToBoolean(object->HasElement(index));
1957 }
1958
ager@chromium.org870a0b62008-11-04 11:43:05 +00001959 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
1960 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001961}
1962
1963
1964static Object* Runtime_GetPropertyNames(Arguments args) {
1965 HandleScope scope;
1966 ASSERT(args.length() == 1);
1967
1968 CONVERT_CHECKED(JSObject, raw_object, args[0]);
1969 Handle<JSObject> object(raw_object);
1970 return *GetKeysFor(object);
1971}
1972
1973
1974// Returns either a FixedArray as Runtime_GetPropertyNames,
1975// or, if the given object has an enum cache that contains
1976// all enumerable properties of the object and its prototypes
1977// have none, the map of the object. This is used to speed up
1978// the check for deletions during a for-in.
1979static Object* Runtime_GetPropertyNamesFast(Arguments args) {
1980 ASSERT(args.length() == 1);
1981
1982 CONVERT_CHECKED(JSObject, raw_object, args[0]);
1983
1984 if (raw_object->IsSimpleEnum()) return raw_object->map();
1985
1986 HandleScope scope;
1987 Handle<JSObject> object(raw_object);
1988 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
1989
1990 // Test again, since cache may have been built by preceding call.
1991 if (object->IsSimpleEnum()) return object->map();
1992
1993 return *content;
1994}
1995
1996
1997static Object* Runtime_GetArgumentsProperty(Arguments args) {
1998 NoHandleAllocation ha;
1999 ASSERT(args.length() == 1);
2000
2001 // Compute the frame holding the arguments.
2002 JavaScriptFrameIterator it;
2003 it.AdvanceToArgumentsFrame();
2004 JavaScriptFrame* frame = it.frame();
2005
2006 // Get the actual number of provided arguments.
2007 const uint32_t n = frame->GetProvidedParametersCount();
2008
2009 // Try to convert the key to an index. If successful and within
2010 // index return the the argument from the frame.
2011 uint32_t index;
2012 if (Array::IndexFromObject(args[0], &index) && index < n) {
2013 return frame->GetParameter(index);
2014 }
2015
2016 // Convert the key to a string.
2017 HandleScope scope;
2018 bool exception = false;
2019 Handle<Object> converted =
2020 Execution::ToString(args.at<Object>(0), &exception);
2021 if (exception) return Failure::Exception();
2022 Handle<String> key = Handle<String>::cast(converted);
2023
2024 // Try to convert the string key into an array index.
2025 if (key->AsArrayIndex(&index)) {
2026 if (index < n) {
2027 return frame->GetParameter(index);
2028 } else {
2029 return Top::initial_object_prototype()->GetElement(index);
2030 }
2031 }
2032
2033 // Handle special arguments properties.
2034 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
2035 if (key->Equals(Heap::callee_symbol())) return frame->function();
2036
2037 // Lookup in the initial Object.prototype object.
2038 return Top::initial_object_prototype()->GetProperty(*key);
2039}
2040
2041
2042static Object* Runtime_ToBool(Arguments args) {
2043 NoHandleAllocation ha;
2044 ASSERT(args.length() == 1);
2045
2046 return args[0]->ToBoolean();
2047}
2048
2049
2050// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
2051// Possible optimizations: put the type string into the oddballs.
2052static Object* Runtime_Typeof(Arguments args) {
2053 NoHandleAllocation ha;
2054
2055 Object* obj = args[0];
2056 if (obj->IsNumber()) return Heap::number_symbol();
2057 HeapObject* heap_obj = HeapObject::cast(obj);
2058
2059 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002060 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002061
2062 InstanceType instance_type = heap_obj->map()->instance_type();
2063 if (instance_type < FIRST_NONSTRING_TYPE) {
2064 return Heap::string_symbol();
2065 }
2066
2067 switch (instance_type) {
2068 case ODDBALL_TYPE:
2069 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
2070 return Heap::boolean_symbol();
2071 }
2072 if (heap_obj->IsNull()) {
2073 return Heap::object_symbol();
2074 }
2075 ASSERT(heap_obj->IsUndefined());
2076 return Heap::undefined_symbol();
2077 case JS_FUNCTION_TYPE:
2078 return Heap::function_symbol();
2079 default:
2080 // For any kind of object not handled above, the spec rule for
2081 // host objects gives that it is okay to return "object"
2082 return Heap::object_symbol();
2083 }
2084}
2085
2086
2087static Object* Runtime_StringToNumber(Arguments args) {
2088 NoHandleAllocation ha;
2089 ASSERT(args.length() == 1);
2090 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002091 subject->TryFlatten(StringShape(subject));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002092 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
2093}
2094
2095
2096static Object* Runtime_StringFromCharCodeArray(Arguments args) {
2097 NoHandleAllocation ha;
2098 ASSERT(args.length() == 1);
2099
2100 CONVERT_CHECKED(JSArray, codes, args[0]);
2101 int length = Smi::cast(codes->length())->value();
2102
2103 // Check if the string can be ASCII.
2104 int i;
2105 for (i = 0; i < length; i++) {
2106 Object* element = codes->GetElement(i);
2107 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
2108 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
2109 break;
2110 }
2111
2112 Object* object = NULL;
2113 if (i == length) { // The string is ASCII.
2114 object = Heap::AllocateRawAsciiString(length);
2115 } else { // The string is not ASCII.
2116 object = Heap::AllocateRawTwoByteString(length);
2117 }
2118
2119 if (object->IsFailure()) return object;
2120 String* result = String::cast(object);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002121 StringShape result_shape(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002122 for (int i = 0; i < length; i++) {
2123 Object* element = codes->GetElement(i);
2124 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002125 result->Set(result_shape, i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002126 }
2127 return result;
2128}
2129
2130
2131// kNotEscaped is generated by the following:
2132//
2133// #!/bin/perl
2134// for (my $i = 0; $i < 256; $i++) {
2135// print "\n" if $i % 16 == 0;
2136// my $c = chr($i);
2137// my $escaped = 1;
2138// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
2139// print $escaped ? "0, " : "1, ";
2140// }
2141
2142
2143static bool IsNotEscaped(uint16_t character) {
2144 // Only for 8 bit characters, the rest are always escaped (in a different way)
2145 ASSERT(character < 256);
2146 static const char kNotEscaped[256] = {
2147 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2148 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2149 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
2150 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
2151 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2152 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
2153 0, 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, 0,
2155 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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 };
2164 return kNotEscaped[character] != 0;
2165}
2166
2167
2168static Object* Runtime_URIEscape(Arguments args) {
2169 const char hex_chars[] = "0123456789ABCDEF";
2170 NoHandleAllocation ha;
2171 ASSERT(args.length() == 1);
2172 CONVERT_CHECKED(String, source, args[0]);
2173
ager@chromium.org870a0b62008-11-04 11:43:05 +00002174 source->TryFlatten(StringShape(source));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002175
2176 int escaped_length = 0;
2177 int length = source->length();
2178 {
2179 Access<StringInputBuffer> buffer(&string_input_buffer);
2180 buffer->Reset(source);
2181 while (buffer->has_more()) {
2182 uint16_t character = buffer->GetNext();
2183 if (character >= 256) {
2184 escaped_length += 6;
2185 } else if (IsNotEscaped(character)) {
2186 escaped_length++;
2187 } else {
2188 escaped_length += 3;
2189 }
2190 // We don't allow strings that are longer than Smi range.
2191 if (!Smi::IsValid(escaped_length)) {
2192 Top::context()->mark_out_of_memory();
2193 return Failure::OutOfMemoryException();
2194 }
2195 }
2196 }
2197 // No length change implies no change. Return original string if no change.
2198 if (escaped_length == length) {
2199 return source;
2200 }
2201 Object* o = Heap::AllocateRawAsciiString(escaped_length);
2202 if (o->IsFailure()) return o;
2203 String* destination = String::cast(o);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002204 StringShape dshape(destination);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002205 int dest_position = 0;
2206
2207 Access<StringInputBuffer> buffer(&string_input_buffer);
2208 buffer->Rewind();
2209 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002210 uint16_t chr = buffer->GetNext();
2211 if (chr >= 256) {
2212 destination->Set(dshape, dest_position, '%');
2213 destination->Set(dshape, dest_position+1, 'u');
2214 destination->Set(dshape, dest_position+2, hex_chars[chr >> 12]);
2215 destination->Set(dshape, dest_position+3, hex_chars[(chr >> 8) & 0xf]);
2216 destination->Set(dshape, dest_position+4, hex_chars[(chr >> 4) & 0xf]);
2217 destination->Set(dshape, dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002218 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002219 } else if (IsNotEscaped(chr)) {
2220 destination->Set(dshape, dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002221 dest_position++;
2222 } else {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002223 destination->Set(dshape, dest_position, '%');
2224 destination->Set(dshape, dest_position+1, hex_chars[chr >> 4]);
2225 destination->Set(dshape, dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002226 dest_position += 3;
2227 }
2228 }
2229 return destination;
2230}
2231
2232
2233static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
2234 static const signed char kHexValue['g'] = {
2235 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2236 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2237 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2238 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
2239 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2240 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2241 -1, 10, 11, 12, 13, 14, 15 };
2242
2243 if (character1 > 'f') return -1;
2244 int hi = kHexValue[character1];
2245 if (hi == -1) return -1;
2246 if (character2 > 'f') return -1;
2247 int lo = kHexValue[character2];
2248 if (lo == -1) return -1;
2249 return (hi << 4) + lo;
2250}
2251
2252
ager@chromium.org870a0b62008-11-04 11:43:05 +00002253static inline int Unescape(String* source,
2254 StringShape shape,
2255 int i,
2256 int length,
2257 int* step) {
2258 uint16_t character = source->Get(shape, i);
2259 int32_t hi = 0;
2260 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002261 if (character == '%' &&
2262 i <= length - 6 &&
ager@chromium.org870a0b62008-11-04 11:43:05 +00002263 source->Get(shape, i + 1) == 'u' &&
2264 (hi = TwoDigitHex(source->Get(shape, i + 2),
2265 source->Get(shape, i + 3))) != -1 &&
2266 (lo = TwoDigitHex(source->Get(shape, i + 4),
2267 source->Get(shape, i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002268 *step = 6;
2269 return (hi << 8) + lo;
2270 } else if (character == '%' &&
2271 i <= length - 3 &&
ager@chromium.org870a0b62008-11-04 11:43:05 +00002272 (lo = TwoDigitHex(source->Get(shape, i + 1),
2273 source->Get(shape, i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002274 *step = 3;
2275 return lo;
2276 } else {
2277 *step = 1;
2278 return character;
2279 }
2280}
2281
2282
2283static Object* Runtime_URIUnescape(Arguments args) {
2284 NoHandleAllocation ha;
2285 ASSERT(args.length() == 1);
2286 CONVERT_CHECKED(String, source, args[0]);
2287
ager@chromium.org870a0b62008-11-04 11:43:05 +00002288 source->TryFlatten(StringShape(source));
2289 StringShape source_shape(source);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002290
2291 bool ascii = true;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002292 int length = source->length(source_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002293
2294 int unescaped_length = 0;
2295 for (int i = 0; i < length; unescaped_length++) {
2296 int step;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002297 if (Unescape(source,
2298 source_shape,
2299 i,
2300 length,
2301 &step) >
2302 String::kMaxAsciiCharCode)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002303 ascii = false;
2304 i += step;
2305 }
2306
2307 // No length change implies no change. Return original string if no change.
2308 if (unescaped_length == length)
2309 return source;
2310
2311 Object* o = ascii ?
2312 Heap::AllocateRawAsciiString(unescaped_length) :
2313 Heap::AllocateRawTwoByteString(unescaped_length);
2314 if (o->IsFailure()) return o;
2315 String* destination = String::cast(o);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002316 StringShape destination_shape(destination);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002317
2318 int dest_position = 0;
2319 for (int i = 0; i < length; dest_position++) {
2320 int step;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002321 destination->Set(destination_shape,
2322 dest_position,
2323 Unescape(source, source_shape, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002324 i += step;
2325 }
2326 return destination;
2327}
2328
2329
2330static Object* Runtime_StringParseInt(Arguments args) {
2331 NoHandleAllocation ha;
2332
2333 CONVERT_CHECKED(String, s, args[0]);
2334 CONVERT_DOUBLE_CHECKED(n, args[1]);
2335 int radix = FastD2I(n);
2336
ager@chromium.org870a0b62008-11-04 11:43:05 +00002337 s->TryFlatten(StringShape(s));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002338
ager@chromium.org870a0b62008-11-04 11:43:05 +00002339 StringShape shape(s);
2340
2341 int len = s->length(shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002342 int i;
2343
2344 // Skip leading white space.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002345 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(shape, i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002346 if (i == len) return Heap::nan_value();
2347
2348 // Compute the sign (default to +).
2349 int sign = 1;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002350 if (s->Get(shape, i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002351 sign = -1;
2352 i++;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002353 } else if (s->Get(shape, i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002354 i++;
2355 }
2356
2357 // Compute the radix if 0.
2358 if (radix == 0) {
2359 radix = 10;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002360 if (i < len && s->Get(shape, i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002361 radix = 8;
2362 if (i + 1 < len) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002363 int c = s->Get(shape, i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002364 if (c == 'x' || c == 'X') {
2365 radix = 16;
2366 i += 2;
2367 }
2368 }
2369 }
2370 } else if (radix == 16) {
2371 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002372 if (i + 1 < len && s->Get(shape, i) == '0') {
2373 int c = s->Get(shape, i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002374 if (c == 'x' || c == 'X') i += 2;
2375 }
2376 }
2377
2378 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2379 double value;
2380 int end_index = StringToInt(s, i, radix, &value);
2381 if (end_index != i) {
2382 return Heap::NumberFromDouble(sign * value);
2383 }
2384 return Heap::nan_value();
2385}
2386
2387
2388static Object* Runtime_StringParseFloat(Arguments args) {
2389 NoHandleAllocation ha;
2390 CONVERT_CHECKED(String, str, args[0]);
2391
2392 // ECMA-262 section 15.1.2.3, empty string is NaN
2393 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
2394
2395 // Create a number object from the value.
2396 return Heap::NumberFromDouble(value);
2397}
2398
2399
2400static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
2401static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
2402
2403
2404template <class Converter>
2405static Object* ConvertCase(Arguments args,
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002406 unibrow::Mapping<Converter, 128>* mapping) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002407 NoHandleAllocation ha;
2408
2409 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002410 s->TryFlatten(StringShape(s));
2411 StringShape shape(s);
2412
2413 int raw_string_length = s->length(shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002414 // Assume that the string is not empty; we need this assumption later
2415 if (raw_string_length == 0) return s;
2416 int length = raw_string_length;
2417
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002418
2419 // We try this twice, once with the assumption that the result is
2420 // no longer than the input and, if that assumption breaks, again
2421 // with the exact length. This is implemented using a goto back
2422 // to this label if we discover that the assumption doesn't hold.
2423 // I apologize sincerely for this and will give a vaffel-is to
mads.s.ager31e71382008-08-13 09:32:07 +00002424 // the first person who can implement it in a nicer way.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002425 try_convert:
2426
2427 // Allocate the resulting string.
2428 //
2429 // NOTE: This assumes that the upper/lower case of an ascii
2430 // character is also ascii. This is currently the case, but it
2431 // might break in the future if we implement more context and locale
2432 // dependent upper/lower conversions.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002433 Object* o = shape.IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002434 ? Heap::AllocateRawAsciiString(length)
2435 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002436 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002437 String* result = String::cast(o);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002438 StringShape result_shape(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002439 bool has_changed_character = false;
2440
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002441 // Convert all characters to upper case, assuming that they will fit
2442 // in the buffer
2443 Access<StringInputBuffer> buffer(&string_input_buffer);
2444 buffer->Reset(s);
2445 unibrow::uchar chars[unibrow::kMaxCaseConvertedSize];
2446 int i = 0;
2447 // We can assume that the string is not empty
2448 uc32 current = buffer->GetNext();
2449 while (i < length) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002450 bool has_next = buffer->has_more();
2451 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002452 int char_length = mapping->get(current, next, chars);
2453 if (char_length == 0) {
2454 // The case conversion of this character is the character itself.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002455 result->Set(result_shape, i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002456 i++;
2457 } else if (char_length == 1) {
2458 // Common case: converting the letter resulted in one character.
2459 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002460 result->Set(result_shape, i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002461 has_changed_character = true;
2462 i++;
2463 } else if (length == raw_string_length) {
2464 // We've assumed that the result would be as long as the
2465 // input but here is a character that converts to several
2466 // characters. No matter, we calculate the exact length
2467 // of the result and try the whole thing again.
2468 //
2469 // Note that this leaves room for optimization. We could just
2470 // memcpy what we already have to the result string. Also,
2471 // the result string is the last object allocated we could
2472 // "realloc" it and probably, in the vast majority of cases,
2473 // extend the existing string to be able to hold the full
2474 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002475 int next_length = 0;
2476 if (has_next) {
2477 next_length = mapping->get(next, 0, chars);
2478 if (next_length == 0) next_length = 1;
2479 }
2480 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002481 while (buffer->has_more()) {
2482 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002483 // NOTE: we use 0 as the next character here because, while
2484 // the next character may affect what a character converts to,
2485 // it does not in any case affect the length of what it convert
2486 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002487 int char_length = mapping->get(current, 0, chars);
2488 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002489 current_length += char_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002490 }
2491 length = current_length;
2492 goto try_convert;
2493 } else {
2494 for (int j = 0; j < char_length; j++) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002495 result->Set(result_shape, i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002496 i++;
2497 }
2498 has_changed_character = true;
2499 }
2500 current = next;
2501 }
2502 if (has_changed_character) {
2503 return result;
2504 } else {
2505 // If we didn't actually change anything in doing the conversion
2506 // we simple return the result and let the converted string
2507 // become garbage; there is no reason to keep two identical strings
2508 // alive.
2509 return s;
2510 }
2511}
2512
2513
2514static Object* Runtime_StringToLowerCase(Arguments args) {
2515 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
2516}
2517
2518
2519static Object* Runtime_StringToUpperCase(Arguments args) {
2520 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
2521}
2522
2523
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002524static Object* Runtime_NumberToString(Arguments args) {
2525 NoHandleAllocation ha;
2526 ASSERT(args.length() == 1);
2527
2528 Object* number = args[0];
2529 RUNTIME_ASSERT(number->IsNumber());
2530
2531 Object* cached = Heap::GetNumberStringCache(number);
2532 if (cached != Heap::undefined_value()) {
2533 return cached;
2534 }
2535
2536 char arr[100];
2537 Vector<char> buffer(arr, ARRAY_SIZE(arr));
2538 const char* str;
2539 if (number->IsSmi()) {
2540 int num = Smi::cast(number)->value();
2541 str = IntToCString(num, buffer);
2542 } else {
2543 double num = HeapNumber::cast(number)->value();
2544 str = DoubleToCString(num, buffer);
2545 }
2546 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2547
2548 if (!result->IsFailure()) {
2549 Heap::SetNumberStringCache(number, String::cast(result));
2550 }
2551 return result;
2552}
2553
2554
2555static Object* Runtime_NumberToInteger(Arguments args) {
2556 NoHandleAllocation ha;
2557 ASSERT(args.length() == 1);
2558
2559 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002560 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002561 CONVERT_DOUBLE_CHECKED(number, obj);
2562 return Heap::NumberFromDouble(DoubleToInteger(number));
2563}
2564
2565
2566static Object* Runtime_NumberToJSUint32(Arguments args) {
2567 NoHandleAllocation ha;
2568 ASSERT(args.length() == 1);
2569
2570 Object* obj = args[0];
2571 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
2572 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
2573 return Heap::NumberFromUint32(number);
2574}
2575
2576
2577static Object* Runtime_NumberToJSInt32(Arguments args) {
2578 NoHandleAllocation ha;
2579 ASSERT(args.length() == 1);
2580
2581 Object* obj = args[0];
2582 if (obj->IsSmi()) return obj;
2583 CONVERT_DOUBLE_CHECKED(number, obj);
2584 return Heap::NumberFromInt32(DoubleToInt32(number));
2585}
2586
2587
ager@chromium.org870a0b62008-11-04 11:43:05 +00002588// Converts a Number to a Smi, if possible. Returns NaN if the number is not
2589// a small integer.
2590static Object* Runtime_NumberToSmi(Arguments args) {
2591 NoHandleAllocation ha;
2592 ASSERT(args.length() == 1);
2593
2594 Object* obj = args[0];
2595 if (obj->IsSmi()) {
2596 return obj;
2597 }
2598 if (obj->IsHeapNumber()) {
2599 double value = HeapNumber::cast(obj)->value();
2600 int int_value = FastD2I(value);
2601 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
2602 return Smi::FromInt(int_value);
2603 }
2604 }
2605 return Heap::nan_value();
2606}
2607
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002608static Object* Runtime_NumberAdd(Arguments args) {
2609 NoHandleAllocation ha;
2610 ASSERT(args.length() == 2);
2611
2612 CONVERT_DOUBLE_CHECKED(x, args[0]);
2613 CONVERT_DOUBLE_CHECKED(y, args[1]);
2614 return Heap::AllocateHeapNumber(x + y);
2615}
2616
2617
2618static Object* Runtime_NumberSub(Arguments args) {
2619 NoHandleAllocation ha;
2620 ASSERT(args.length() == 2);
2621
2622 CONVERT_DOUBLE_CHECKED(x, args[0]);
2623 CONVERT_DOUBLE_CHECKED(y, args[1]);
2624 return Heap::AllocateHeapNumber(x - y);
2625}
2626
2627
2628static Object* Runtime_NumberMul(Arguments args) {
2629 NoHandleAllocation ha;
2630 ASSERT(args.length() == 2);
2631
2632 CONVERT_DOUBLE_CHECKED(x, args[0]);
2633 CONVERT_DOUBLE_CHECKED(y, args[1]);
2634 return Heap::AllocateHeapNumber(x * y);
2635}
2636
2637
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002638static Object* Runtime_NumberUnaryMinus(Arguments args) {
2639 NoHandleAllocation ha;
2640 ASSERT(args.length() == 1);
2641
2642 CONVERT_DOUBLE_CHECKED(x, args[0]);
2643 return Heap::AllocateHeapNumber(-x);
2644}
2645
2646
2647static Object* Runtime_NumberDiv(Arguments args) {
2648 NoHandleAllocation ha;
2649 ASSERT(args.length() == 2);
2650
2651 CONVERT_DOUBLE_CHECKED(x, args[0]);
2652 CONVERT_DOUBLE_CHECKED(y, args[1]);
2653 return Heap::NewNumberFromDouble(x / y);
2654}
2655
2656
2657static Object* Runtime_NumberMod(Arguments args) {
2658 NoHandleAllocation ha;
2659 ASSERT(args.length() == 2);
2660
2661 CONVERT_DOUBLE_CHECKED(x, args[0]);
2662 CONVERT_DOUBLE_CHECKED(y, args[1]);
2663
2664#ifdef WIN32
2665 // Workaround MS fmod bugs. ECMA-262 says:
2666 // dividend is finite and divisor is an infinity => result equals dividend
2667 // dividend is a zero and divisor is nonzero finite => result equals dividend
2668 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
2669 !(x == 0 && (y != 0 && isfinite(y))))
2670#endif
2671 x = fmod(x, y);
2672 // NewNumberFromDouble may return a Smi instead of a Number object
2673 return Heap::NewNumberFromDouble(x);
2674}
2675
2676
2677static Object* Runtime_StringAdd(Arguments args) {
2678 NoHandleAllocation ha;
2679 ASSERT(args.length() == 2);
2680
2681 CONVERT_CHECKED(String, str1, args[0]);
2682 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002683 int len1 = str1->length();
2684 int len2 = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002685 if (len1 == 0) return str2;
2686 if (len2 == 0) return str1;
2687 int length_sum = len1 + len2;
2688 // Make sure that an out of memory exception is thrown if the length
2689 // of the new cons string is too large to fit in a Smi.
2690 if (length_sum > Smi::kMaxValue || length_sum < 0) {
2691 Top::context()->mark_out_of_memory();
2692 return Failure::OutOfMemoryException();
2693 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002694 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002695}
2696
2697
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002698template<typename sinkchar>
2699static inline void StringBuilderConcatHelper(String* special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00002700 StringShape special_shape,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002701 sinkchar* sink,
2702 FixedArray* fixed_array,
2703 int array_length) {
2704 int position = 0;
2705 for (int i = 0; i < array_length; i++) {
2706 Object* element = fixed_array->get(i);
2707 if (element->IsSmi()) {
2708 int len = Smi::cast(element)->value();
2709 int pos = len >> 11;
2710 len &= 0x7ff;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002711 String::WriteToFlat(special,
2712 special_shape,
2713 sink + position,
2714 pos,
2715 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002716 position += len;
2717 } else {
2718 String* string = String::cast(element);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002719 StringShape shape(string);
2720 int element_length = string->length(shape);
2721 String::WriteToFlat(string, shape, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002722 position += element_length;
2723 }
2724 }
2725}
2726
2727
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002728static Object* Runtime_StringBuilderConcat(Arguments args) {
2729 NoHandleAllocation ha;
2730 ASSERT(args.length() == 2);
2731 CONVERT_CHECKED(JSArray, array, args[0]);
2732 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002733 StringShape special_shape(special);
2734 int special_length = special->length(special_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002735 Object* smi_array_length = array->length();
2736 if (!smi_array_length->IsSmi()) {
2737 Top::context()->mark_out_of_memory();
2738 return Failure::OutOfMemoryException();
2739 }
2740 int array_length = Smi::cast(smi_array_length)->value();
2741 if (!array->HasFastElements()) {
2742 return Top::Throw(Heap::illegal_argument_symbol());
2743 }
2744 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002745 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002746 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002747 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002748
2749 if (array_length == 0) {
2750 return Heap::empty_string();
2751 } else if (array_length == 1) {
2752 Object* first = fixed_array->get(0);
2753 if (first->IsString()) return first;
2754 }
2755
ager@chromium.org870a0b62008-11-04 11:43:05 +00002756 bool ascii = special_shape.IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002757 int position = 0;
2758 for (int i = 0; i < array_length; i++) {
2759 Object* elt = fixed_array->get(i);
2760 if (elt->IsSmi()) {
2761 int len = Smi::cast(elt)->value();
2762 int pos = len >> 11;
2763 len &= 0x7ff;
2764 if (pos + len > special_length) {
2765 return Top::Throw(Heap::illegal_argument_symbol());
2766 }
2767 position += len;
2768 } else if (elt->IsString()) {
2769 String* element = String::cast(elt);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002770 StringShape element_shape(element);
2771 int element_length = element->length(element_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002772 if (!Smi::IsValid(element_length + position)) {
2773 Top::context()->mark_out_of_memory();
2774 return Failure::OutOfMemoryException();
2775 }
2776 position += element_length;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002777 if (ascii && !element_shape.IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002778 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002779 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002780 } else {
2781 return Top::Throw(Heap::illegal_argument_symbol());
2782 }
2783 }
2784
2785 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002786 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002787
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002788 if (ascii) {
2789 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002790 if (object->IsFailure()) return object;
2791 SeqAsciiString* answer = SeqAsciiString::cast(object);
2792 StringBuilderConcatHelper(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00002793 special_shape,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002794 answer->GetChars(),
2795 fixed_array,
2796 array_length);
2797 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002798 } else {
2799 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002800 if (object->IsFailure()) return object;
2801 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
2802 StringBuilderConcatHelper(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00002803 special_shape,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002804 answer->GetChars(),
2805 fixed_array,
2806 array_length);
2807 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002808 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002809}
2810
2811
2812static Object* Runtime_NumberOr(Arguments args) {
2813 NoHandleAllocation ha;
2814 ASSERT(args.length() == 2);
2815
2816 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2817 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2818 return Heap::NumberFromInt32(x | y);
2819}
2820
2821
2822static Object* Runtime_NumberAnd(Arguments args) {
2823 NoHandleAllocation ha;
2824 ASSERT(args.length() == 2);
2825
2826 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2827 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2828 return Heap::NumberFromInt32(x & y);
2829}
2830
2831
2832static Object* Runtime_NumberXor(Arguments args) {
2833 NoHandleAllocation ha;
2834 ASSERT(args.length() == 2);
2835
2836 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2837 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2838 return Heap::NumberFromInt32(x ^ y);
2839}
2840
2841
2842static Object* Runtime_NumberNot(Arguments args) {
2843 NoHandleAllocation ha;
2844 ASSERT(args.length() == 1);
2845
2846 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2847 return Heap::NumberFromInt32(~x);
2848}
2849
2850
2851static Object* Runtime_NumberShl(Arguments args) {
2852 NoHandleAllocation ha;
2853 ASSERT(args.length() == 2);
2854
2855 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2856 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2857 return Heap::NumberFromInt32(x << (y & 0x1f));
2858}
2859
2860
2861static Object* Runtime_NumberShr(Arguments args) {
2862 NoHandleAllocation ha;
2863 ASSERT(args.length() == 2);
2864
2865 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
2866 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2867 return Heap::NumberFromUint32(x >> (y & 0x1f));
2868}
2869
2870
2871static Object* Runtime_NumberSar(Arguments args) {
2872 NoHandleAllocation ha;
2873 ASSERT(args.length() == 2);
2874
2875 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2876 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2877 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
2878}
2879
2880
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002881static Object* Runtime_NumberEquals(Arguments args) {
2882 NoHandleAllocation ha;
2883 ASSERT(args.length() == 2);
2884
2885 CONVERT_DOUBLE_CHECKED(x, args[0]);
2886 CONVERT_DOUBLE_CHECKED(y, args[1]);
2887 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
2888 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
2889 if (x == y) return Smi::FromInt(EQUAL);
2890 Object* result;
2891 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
2892 result = Smi::FromInt(EQUAL);
2893 } else {
2894 result = Smi::FromInt(NOT_EQUAL);
2895 }
2896 return result;
2897}
2898
2899
2900static Object* Runtime_StringEquals(Arguments args) {
2901 NoHandleAllocation ha;
2902 ASSERT(args.length() == 2);
2903
2904 CONVERT_CHECKED(String, x, args[0]);
2905 CONVERT_CHECKED(String, y, args[1]);
2906
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002907 bool not_equal = !x->Equals(y);
2908 // This is slightly convoluted because the value that signifies
2909 // equality is 0 and inequality is 1 so we have to negate the result
2910 // from String::Equals.
2911 ASSERT(not_equal == 0 || not_equal == 1);
2912 STATIC_CHECK(EQUAL == 0);
2913 STATIC_CHECK(NOT_EQUAL == 1);
2914 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002915}
2916
2917
2918static Object* Runtime_NumberCompare(Arguments args) {
2919 NoHandleAllocation ha;
2920 ASSERT(args.length() == 3);
2921
2922 CONVERT_DOUBLE_CHECKED(x, args[0]);
2923 CONVERT_DOUBLE_CHECKED(y, args[1]);
2924 if (isnan(x) || isnan(y)) return args[2];
2925 if (x == y) return Smi::FromInt(EQUAL);
2926 if (isless(x, y)) return Smi::FromInt(LESS);
2927 return Smi::FromInt(GREATER);
2928}
2929
2930
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002931// Compare two Smis as if they were converted to strings and then
2932// compared lexicographically.
2933static Object* Runtime_SmiLexicographicCompare(Arguments args) {
2934 NoHandleAllocation ha;
2935 ASSERT(args.length() == 2);
2936
2937 // Arrays for the individual characters of the two Smis. Smis are
2938 // 31 bit integers and 10 decimal digits are therefore enough.
2939 static int x_elms[10];
2940 static int y_elms[10];
2941
2942 // Extract the integer values from the Smis.
2943 CONVERT_CHECKED(Smi, x, args[0]);
2944 CONVERT_CHECKED(Smi, y, args[1]);
2945 int x_value = x->value();
2946 int y_value = y->value();
2947
2948 // If the integers are equal so are the string representations.
2949 if (x_value == y_value) return Smi::FromInt(EQUAL);
2950
2951 // If one of the integers are zero the normal integer order is the
2952 // same as the lexicographic order of the string representations.
2953 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
2954
2955 // If only one of the intergers is negative the negative number is
2956 // smallest because the char code of '-' is less than the char code
2957 // of any digit. Otherwise, we make both values positive.
2958 if (x_value < 0 || y_value < 0) {
2959 if (y_value >= 0) return Smi::FromInt(LESS);
2960 if (x_value >= 0) return Smi::FromInt(GREATER);
2961 x_value = -x_value;
2962 y_value = -y_value;
2963 }
2964
2965 // Convert the integers to arrays of their decimal digits.
2966 int x_index = 0;
2967 int y_index = 0;
2968 while (x_value > 0) {
2969 x_elms[x_index++] = x_value % 10;
2970 x_value /= 10;
2971 }
2972 while (y_value > 0) {
2973 y_elms[y_index++] = y_value % 10;
2974 y_value /= 10;
2975 }
2976
2977 // Loop through the arrays of decimal digits finding the first place
2978 // where they differ.
2979 while (--x_index >= 0 && --y_index >= 0) {
2980 int diff = x_elms[x_index] - y_elms[y_index];
2981 if (diff != 0) return Smi::FromInt(diff);
2982 }
2983
2984 // If one array is a suffix of the other array, the longest array is
2985 // the representation of the largest of the Smis in the
2986 // lexicographic ordering.
2987 return Smi::FromInt(x_index - y_index);
2988}
2989
2990
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002991static Object* Runtime_StringCompare(Arguments args) {
2992 NoHandleAllocation ha;
2993 ASSERT(args.length() == 2);
2994
2995 CONVERT_CHECKED(String, x, args[0]);
2996 CONVERT_CHECKED(String, y, args[1]);
2997
ager@chromium.org870a0b62008-11-04 11:43:05 +00002998 StringShape x_shape(x);
2999 StringShape y_shape(y);
3000
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003001 // A few fast case tests before we flatten.
3002 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003003 if (y->length(y_shape) == 0) {
3004 if (x->length(x_shape) == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003005 return Smi::FromInt(GREATER);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003006 } else if (x->length(x_shape) == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003007 return Smi::FromInt(LESS);
3008 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003009
ager@chromium.org870a0b62008-11-04 11:43:05 +00003010 int d = x->Get(x_shape, 0) - y->Get(y_shape, 0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003011 if (d < 0) return Smi::FromInt(LESS);
3012 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003013
ager@chromium.org870a0b62008-11-04 11:43:05 +00003014 x->TryFlatten(x_shape); // Shapes are no longer valid!
3015 y->TryFlatten(y_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003016
3017 static StringInputBuffer bufx;
3018 static StringInputBuffer bufy;
3019 bufx.Reset(x);
3020 bufy.Reset(y);
3021 while (bufx.has_more() && bufy.has_more()) {
3022 int d = bufx.GetNext() - bufy.GetNext();
3023 if (d < 0) return Smi::FromInt(LESS);
3024 else if (d > 0) return Smi::FromInt(GREATER);
3025 }
3026
3027 // x is (non-trivial) prefix of y:
3028 if (bufy.has_more()) return Smi::FromInt(LESS);
3029 // y is prefix of x:
3030 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
3031}
3032
3033
3034static Object* Runtime_Math_abs(Arguments args) {
3035 NoHandleAllocation ha;
3036 ASSERT(args.length() == 1);
3037
3038 CONVERT_DOUBLE_CHECKED(x, args[0]);
3039 return Heap::AllocateHeapNumber(fabs(x));
3040}
3041
3042
3043static Object* Runtime_Math_acos(Arguments args) {
3044 NoHandleAllocation ha;
3045 ASSERT(args.length() == 1);
3046
3047 CONVERT_DOUBLE_CHECKED(x, args[0]);
3048 return Heap::AllocateHeapNumber(acos(x));
3049}
3050
3051
3052static Object* Runtime_Math_asin(Arguments args) {
3053 NoHandleAllocation ha;
3054 ASSERT(args.length() == 1);
3055
3056 CONVERT_DOUBLE_CHECKED(x, args[0]);
3057 return Heap::AllocateHeapNumber(asin(x));
3058}
3059
3060
3061static Object* Runtime_Math_atan(Arguments args) {
3062 NoHandleAllocation ha;
3063 ASSERT(args.length() == 1);
3064
3065 CONVERT_DOUBLE_CHECKED(x, args[0]);
3066 return Heap::AllocateHeapNumber(atan(x));
3067}
3068
3069
3070static Object* Runtime_Math_atan2(Arguments args) {
3071 NoHandleAllocation ha;
3072 ASSERT(args.length() == 2);
3073
3074 CONVERT_DOUBLE_CHECKED(x, args[0]);
3075 CONVERT_DOUBLE_CHECKED(y, args[1]);
3076 double result;
3077 if (isinf(x) && isinf(y)) {
3078 // Make sure that the result in case of two infinite arguments
3079 // is a multiple of Pi / 4. The sign of the result is determined
3080 // by the first argument (x) and the sign of the second argument
3081 // determines the multiplier: one or three.
3082 static double kPiDividedBy4 = 0.78539816339744830962;
3083 int multiplier = (x < 0) ? -1 : 1;
3084 if (y < 0) multiplier *= 3;
3085 result = multiplier * kPiDividedBy4;
3086 } else {
3087 result = atan2(x, y);
3088 }
3089 return Heap::AllocateHeapNumber(result);
3090}
3091
3092
3093static Object* Runtime_Math_ceil(Arguments args) {
3094 NoHandleAllocation ha;
3095 ASSERT(args.length() == 1);
3096
3097 CONVERT_DOUBLE_CHECKED(x, args[0]);
3098 return Heap::NumberFromDouble(ceiling(x));
3099}
3100
3101
3102static Object* Runtime_Math_cos(Arguments args) {
3103 NoHandleAllocation ha;
3104 ASSERT(args.length() == 1);
3105
3106 CONVERT_DOUBLE_CHECKED(x, args[0]);
3107 return Heap::AllocateHeapNumber(cos(x));
3108}
3109
3110
3111static Object* Runtime_Math_exp(Arguments args) {
3112 NoHandleAllocation ha;
3113 ASSERT(args.length() == 1);
3114
3115 CONVERT_DOUBLE_CHECKED(x, args[0]);
3116 return Heap::AllocateHeapNumber(exp(x));
3117}
3118
3119
3120static Object* Runtime_Math_floor(Arguments args) {
3121 NoHandleAllocation ha;
3122 ASSERT(args.length() == 1);
3123
3124 CONVERT_DOUBLE_CHECKED(x, args[0]);
3125 return Heap::NumberFromDouble(floor(x));
3126}
3127
3128
3129static Object* Runtime_Math_log(Arguments args) {
3130 NoHandleAllocation ha;
3131 ASSERT(args.length() == 1);
3132
3133 CONVERT_DOUBLE_CHECKED(x, args[0]);
3134 return Heap::AllocateHeapNumber(log(x));
3135}
3136
3137
3138static Object* Runtime_Math_pow(Arguments args) {
3139 NoHandleAllocation ha;
3140 ASSERT(args.length() == 2);
3141
3142 CONVERT_DOUBLE_CHECKED(x, args[0]);
3143 CONVERT_DOUBLE_CHECKED(y, args[1]);
3144 if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
3145 return Heap::nan_value();
3146 } else if (y == 0) {
3147 return Smi::FromInt(1);
3148 } else {
3149 return Heap::AllocateHeapNumber(pow(x, y));
3150 }
3151}
3152
3153// Returns a number value with positive sign, greater than or equal to
3154// 0 but less than 1, chosen randomly.
mads.s.ager31e71382008-08-13 09:32:07 +00003155static Object* Runtime_Math_random(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003156 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003157 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003158
3159 // To get much better precision, we combine the results of two
3160 // invocations of random(). The result is computed by normalizing a
3161 // double in the range [0, RAND_MAX + 1) obtained by adding the
3162 // high-order bits in the range [0, RAND_MAX] with the low-order
3163 // bits in the range [0, 1).
3164 double lo = static_cast<double>(random()) / (RAND_MAX + 1.0);
3165 double hi = static_cast<double>(random());
3166 double result = (hi + lo) / (RAND_MAX + 1.0);
3167 ASSERT(result >= 0 && result < 1);
3168 return Heap::AllocateHeapNumber(result);
3169}
3170
3171
3172static Object* Runtime_Math_round(Arguments args) {
3173 NoHandleAllocation ha;
3174 ASSERT(args.length() == 1);
3175
3176 CONVERT_DOUBLE_CHECKED(x, args[0]);
3177 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
3178 return Heap::NumberFromDouble(floor(x + 0.5));
3179}
3180
3181
3182static Object* Runtime_Math_sin(Arguments args) {
3183 NoHandleAllocation ha;
3184 ASSERT(args.length() == 1);
3185
3186 CONVERT_DOUBLE_CHECKED(x, args[0]);
3187 return Heap::AllocateHeapNumber(sin(x));
3188}
3189
3190
3191static Object* Runtime_Math_sqrt(Arguments args) {
3192 NoHandleAllocation ha;
3193 ASSERT(args.length() == 1);
3194
3195 CONVERT_DOUBLE_CHECKED(x, args[0]);
3196 return Heap::AllocateHeapNumber(sqrt(x));
3197}
3198
3199
3200static Object* Runtime_Math_tan(Arguments args) {
3201 NoHandleAllocation ha;
3202 ASSERT(args.length() == 1);
3203
3204 CONVERT_DOUBLE_CHECKED(x, args[0]);
3205 return Heap::AllocateHeapNumber(tan(x));
3206}
3207
3208
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003209// The NewArguments function is only used when constructing the
3210// arguments array when calling non-functions from JavaScript in
3211// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003212static Object* Runtime_NewArguments(Arguments args) {
3213 NoHandleAllocation ha;
3214 ASSERT(args.length() == 1);
3215
3216 // ECMA-262, 3rd., 10.1.8, p.39
3217 CONVERT_CHECKED(JSFunction, callee, args[0]);
3218
3219 // Compute the frame holding the arguments.
3220 JavaScriptFrameIterator it;
3221 it.AdvanceToArgumentsFrame();
3222 JavaScriptFrame* frame = it.frame();
3223
3224 const int length = frame->GetProvidedParametersCount();
3225 Object* result = Heap::AllocateArgumentsObject(callee, length);
3226 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003227 if (length > 0) {
3228 Object* obj = Heap::AllocateFixedArray(length);
3229 if (obj->IsFailure()) return obj;
3230 FixedArray* array = FixedArray::cast(obj);
3231 ASSERT(array->length() == length);
3232 WriteBarrierMode mode = array->GetWriteBarrierMode();
3233 for (int i = 0; i < length; i++) {
3234 array->set(i, frame->GetParameter(i), mode);
3235 }
3236 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003237 }
3238 return result;
3239}
3240
3241
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003242static Object* Runtime_NewArgumentsFast(Arguments args) {
3243 NoHandleAllocation ha;
3244 ASSERT(args.length() == 3);
3245
3246 JSFunction* callee = JSFunction::cast(args[0]);
3247 Object** parameters = reinterpret_cast<Object**>(args[1]);
3248 const int length = Smi::cast(args[2])->value();
3249
3250 Object* result = Heap::AllocateArgumentsObject(callee, length);
3251 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003252 ASSERT(Heap::InNewSpace(result));
3253
3254 // Allocate the elements if needed.
3255 if (length > 0) {
3256 // Allocate the fixed array.
3257 Object* obj = Heap::AllocateRawFixedArray(length);
3258 if (obj->IsFailure()) return obj;
3259 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
3260 FixedArray* array = FixedArray::cast(obj);
3261 array->set_length(length);
3262 WriteBarrierMode mode = array->GetWriteBarrierMode();
3263 for (int i = 0; i < length; i++) {
3264 array->set(i, *--parameters, mode);
3265 }
3266 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
3267 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003268 }
3269 return result;
3270}
3271
3272
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003273static Object* Runtime_NewClosure(Arguments args) {
3274 HandleScope scope;
3275 ASSERT(args.length() == 2);
3276 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
3277 CONVERT_ARG_CHECKED(Context, context, 1);
3278
3279 Handle<JSFunction> result =
3280 Factory::NewFunctionFromBoilerplate(boilerplate, context);
3281 return *result;
3282}
3283
3284
3285static Object* Runtime_NewObject(Arguments args) {
3286 NoHandleAllocation ha;
3287 ASSERT(args.length() == 1);
3288
3289 Object* constructor = args[0];
3290 if (constructor->IsJSFunction()) {
3291 JSFunction* function = JSFunction::cast(constructor);
3292
3293 // Handle steping into constructors.
3294 if (Debug::StepInActive()) {
3295 StackFrameIterator it;
3296 it.Advance();
ager@chromium.org7c537e22008-10-16 08:43:32 +00003297 ASSERT(it.frame()->is_construct());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003298 it.Advance();
3299 if (it.frame()->fp() == Debug::step_in_fp()) {
3300 HandleScope scope;
3301 Debug::FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
3302 }
3303 }
3304
3305 if (function->has_initial_map() &&
3306 function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
3307 // The 'Function' function ignores the receiver object when
3308 // called using 'new' and creates a new JSFunction object that
3309 // is returned. The receiver object is only used for error
3310 // reporting if an error occurs when constructing the new
3311 // JSFunction. AllocateJSObject should not be used to allocate
3312 // JSFunctions since it does not properly initialize the shared
3313 // part of the function. Since the receiver is ignored anyway,
3314 // we use the global object as the receiver instead of a new
3315 // JSFunction object. This way, errors are reported the same
3316 // way whether or not 'Function' is called using 'new'.
3317 return Top::context()->global();
3318 }
3319 return Heap::AllocateJSObject(function);
3320 }
3321
3322 HandleScope scope;
3323 Handle<Object> cons(constructor);
3324 // The constructor is not a function; throw a type error.
3325 Handle<Object> type_error =
3326 Factory::NewTypeError("not_constructor", HandleVector(&cons, 1));
3327 return Top::Throw(*type_error);
3328}
3329
3330
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003331static Object* Runtime_LazyCompile(Arguments args) {
3332 HandleScope scope;
3333 ASSERT(args.length() == 1);
3334
3335 Handle<JSFunction> function = args.at<JSFunction>(0);
3336#ifdef DEBUG
3337 if (FLAG_trace_lazy) {
3338 PrintF("[lazy: ");
3339 function->shared()->name()->Print();
3340 PrintF("]\n");
3341 }
3342#endif
3343
3344 // Compile the target function.
3345 ASSERT(!function->is_compiled());
3346 if (!CompileLazy(function, KEEP_EXCEPTION)) {
3347 return Failure::Exception();
3348 }
3349
3350 return function->code();
3351}
3352
3353
3354static Object* Runtime_GetCalledFunction(Arguments args) {
3355 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00003356 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003357 StackFrameIterator it;
3358 // Get past the JS-to-C exit frame.
3359 ASSERT(it.frame()->is_exit());
3360 it.Advance();
3361 // Get past the CALL_NON_FUNCTION activation frame.
3362 ASSERT(it.frame()->is_java_script());
3363 it.Advance();
3364 // Argument adaptor frames do not copy the function; we have to skip
3365 // past them to get to the real calling frame.
3366 if (it.frame()->is_arguments_adaptor()) it.Advance();
3367 // Get the function from the top of the expression stack of the
3368 // calling frame.
3369 StandardFrame* frame = StandardFrame::cast(it.frame());
3370 int index = frame->ComputeExpressionsCount() - 1;
3371 Object* result = frame->GetExpression(index);
3372 return result;
3373}
3374
3375
3376static Object* Runtime_GetFunctionDelegate(Arguments args) {
3377 HandleScope scope;
3378 ASSERT(args.length() == 1);
3379 RUNTIME_ASSERT(!args[0]->IsJSFunction());
3380 return *Execution::GetFunctionDelegate(args.at<Object>(0));
3381}
3382
3383
3384static Object* Runtime_NewContext(Arguments args) {
3385 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00003386 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003387
kasper.lund7276f142008-07-30 08:49:36 +00003388 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003389 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
3390 Object* result = Heap::AllocateFunctionContext(length, function);
3391 if (result->IsFailure()) return result;
3392
3393 Top::set_context(Context::cast(result));
3394
kasper.lund7276f142008-07-30 08:49:36 +00003395 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003396}
3397
3398
3399static Object* Runtime_PushContext(Arguments args) {
3400 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00003401 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003402
3403 // Convert the object to a proper JavaScript object.
kasper.lund7276f142008-07-30 08:49:36 +00003404 Object* object = args[0];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003405 if (!object->IsJSObject()) {
3406 object = object->ToObject();
3407 if (object->IsFailure()) {
3408 if (!Failure::cast(object)->IsInternalError()) return object;
3409 HandleScope scope;
kasper.lund7276f142008-07-30 08:49:36 +00003410 Handle<Object> handle(args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003411 Handle<Object> result =
3412 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
3413 return Top::Throw(*result);
3414 }
3415 }
3416
3417 Object* result =
3418 Heap::AllocateWithContext(Top::context(), JSObject::cast(object));
3419 if (result->IsFailure()) return result;
3420
3421 Top::set_context(Context::cast(result));
3422
kasper.lund7276f142008-07-30 08:49:36 +00003423 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003424}
3425
3426
3427static Object* Runtime_LookupContext(Arguments args) {
3428 HandleScope scope;
3429 ASSERT(args.length() == 2);
3430
3431 CONVERT_ARG_CHECKED(Context, context, 0);
3432 CONVERT_ARG_CHECKED(String, name, 1);
3433
3434 int index;
3435 PropertyAttributes attributes;
3436 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003437 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003438 context->Lookup(name, flags, &index, &attributes);
3439
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003440 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003441 ASSERT(holder->IsJSObject());
3442 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003443 }
3444
3445 // No intermediate context found. Use global object by default.
3446 return Top::context()->global();
3447}
3448
3449
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003450// A mechanism to return pairs of Object*'s. This is somewhat
3451// compiler-dependent as it assumes that a 64-bit value (a long long)
3452// is returned via two registers (edx:eax on ia32). Both the ia32 and
3453// arm platform support this; it is mostly an issue of "coaxing" the
3454// compiler to do the right thing.
3455//
3456// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003457typedef uint64_t ObjectPair;
3458static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003459 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003460 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003461}
3462
3463
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003464static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003465 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
3466 USE(attributes);
3467 return x->IsTheHole() ? Heap::undefined_value() : x;
3468}
3469
3470
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003471static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
3472 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003473 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003474 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003475 JSFunction* context_extension_function =
3476 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003477 // If the holder isn't a context extension object, we just return it
3478 // as the receiver. This allows arguments objects to be used as
3479 // receivers, but only if they are put in the context scope chain
3480 // explicitly via a with-statement.
3481 Object* constructor = holder->map()->constructor();
3482 if (constructor != context_extension_function) return holder;
3483 // Fall back to using the global object as the receiver if the
3484 // property turns out to be a local variable allocated in a context
3485 // extension object - introduced via eval.
3486 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003487}
3488
3489
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003490static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003491 HandleScope scope;
3492 ASSERT(args.length() == 2);
3493
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003494 if (!args[0]->IsContext() || !args[1]->IsString()) {
3495 return MakePair(IllegalOperation(), NULL);
3496 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003497 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003498 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003499
3500 int index;
3501 PropertyAttributes attributes;
3502 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003503 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003504 context->Lookup(name, flags, &index, &attributes);
3505
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003506 // If the index is non-negative, the slot has been found in a local
3507 // variable or a parameter. Read it from the context object or the
3508 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003509 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003510 // If the "property" we were looking for is a local variable or an
3511 // argument in a context, the receiver is the global object; see
3512 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
3513 JSObject* receiver = Top::context()->global()->global_receiver();
3514 Object* value = (holder->IsContext())
3515 ? Context::cast(*holder)->get(index)
3516 : JSObject::cast(*holder)->GetElement(index);
3517 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003518 }
3519
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003520 // If the holder is found, we read the property from it.
3521 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00003522 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003523 JSObject* object = JSObject::cast(*holder);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003524 JSObject* receiver = (object->IsGlobalObject())
3525 ? GlobalObject::cast(object)->global_receiver()
3526 : ComputeReceiverForNonGlobal(object);
3527 // No need to unhole the value here. This is taken care of by the
3528 // GetProperty function.
3529 Object* value = object->GetProperty(*name);
3530 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003531 }
3532
3533 if (throw_error) {
3534 // The property doesn't exist - throw exception.
3535 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003536 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003537 return MakePair(Top::Throw(*reference_error), NULL);
3538 } else {
3539 // The property doesn't exist - return undefined
3540 return MakePair(Heap::undefined_value(), Heap::undefined_value());
3541 }
3542}
3543
3544
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003545static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003546 return LoadContextSlotHelper(args, true);
3547}
3548
3549
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003550static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003551 return LoadContextSlotHelper(args, false);
3552}
3553
3554
3555static Object* Runtime_StoreContextSlot(Arguments args) {
3556 HandleScope scope;
3557 ASSERT(args.length() == 3);
3558
3559 Handle<Object> value(args[0]);
3560 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003561 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003562
3563 int index;
3564 PropertyAttributes attributes;
3565 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003566 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003567 context->Lookup(name, flags, &index, &attributes);
3568
3569 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003570 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003571 // Ignore if read_only variable.
3572 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003573 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003574 }
3575 } else {
3576 ASSERT((attributes & READ_ONLY) == 0);
3577 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003578 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003579 USE(result);
3580 ASSERT(!result->IsFailure());
3581 }
3582 return *value;
3583 }
3584
3585 // Slow case: The property is not in a FixedArray context.
3586 // It is either in an JSObject extension context or it was not found.
3587 Handle<JSObject> context_ext;
3588
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003589 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003590 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003591 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003592 } else {
3593 // The property was not found. It needs to be stored in the global context.
3594 ASSERT(attributes == ABSENT);
3595 attributes = NONE;
3596 context_ext = Handle<JSObject>(Top::context()->global());
3597 }
3598
3599 // Set the property, but ignore if read_only variable.
3600 if ((attributes & READ_ONLY) == 0) {
3601 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
3602 if (set.is_null()) {
3603 // Failure::Exception is converted to a null handle in the
3604 // handle-based methods such as SetProperty. We therefore need
3605 // to convert null handles back to exceptions.
3606 ASSERT(Top::has_pending_exception());
3607 return Failure::Exception();
3608 }
3609 }
3610 return *value;
3611}
3612
3613
3614static Object* Runtime_Throw(Arguments args) {
3615 HandleScope scope;
3616 ASSERT(args.length() == 1);
3617
3618 return Top::Throw(args[0]);
3619}
3620
3621
3622static Object* Runtime_ReThrow(Arguments args) {
3623 HandleScope scope;
3624 ASSERT(args.length() == 1);
3625
3626 return Top::ReThrow(args[0]);
3627}
3628
3629
3630static Object* Runtime_ThrowReferenceError(Arguments args) {
3631 HandleScope scope;
3632 ASSERT(args.length() == 1);
3633
3634 Handle<Object> name(args[0]);
3635 Handle<Object> reference_error =
3636 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
3637 return Top::Throw(*reference_error);
3638}
3639
3640
3641static Object* Runtime_StackOverflow(Arguments args) {
3642 NoHandleAllocation na;
3643 return Top::StackOverflow();
3644}
3645
3646
3647static Object* RuntimePreempt(Arguments args) {
3648 // Clear the preempt request flag.
3649 StackGuard::Continue(PREEMPT);
3650
3651 ContextSwitcher::PreemptionReceived();
3652
3653 {
3654 v8::Unlocker unlocker;
3655 Thread::YieldCPU();
3656 }
3657
3658 return Heap::undefined_value();
3659}
3660
3661
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003662static Object* DebugBreakHelper() {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003663 // Just continue if breaks are disabled.
3664 if (Debug::disable_break()) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003665 return Heap::undefined_value();
kasper.lundbd3ec4e2008-07-09 11:06:54 +00003666 }
3667
kasper.lund7276f142008-07-30 08:49:36 +00003668 // Don't break in system functions. If the current function is
3669 // either in the builtins object of some context or is in the debug
3670 // context just return with the debug break stack guard active.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003671 JavaScriptFrameIterator it;
3672 JavaScriptFrame* frame = it.frame();
3673 Object* fun = frame->function();
3674 if (fun->IsJSFunction()) {
3675 GlobalObject* global = JSFunction::cast(fun)->context()->global();
3676 if (global->IsJSBuiltinsObject() || Debug::IsDebugGlobal(global)) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003677 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003678 }
3679 }
3680
3681 // Clear the debug request flag.
3682 StackGuard::Continue(DEBUGBREAK);
3683
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003684 HandleScope scope;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003685 // Enter the debugger. Just continue if we fail to enter the debugger.
3686 EnterDebugger debugger;
3687 if (debugger.FailedToEnter()) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003688 return Heap::undefined_value();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003689 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003690
kasper.lund7276f142008-07-30 08:49:36 +00003691 // Notify the debug event listeners.
3692 Debugger::OnDebugBreak(Factory::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003693
3694 // Return to continue execution.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003695 return Heap::undefined_value();
3696}
3697
3698
3699static Object* Runtime_DebugBreak(Arguments args) {
3700 ASSERT(args.length() == 0);
3701 return DebugBreakHelper();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003702}
3703
3704
3705static Object* Runtime_StackGuard(Arguments args) {
3706 ASSERT(args.length() == 1);
3707
3708 // First check if this is a real stack overflow.
3709 if (StackGuard::IsStackOverflow()) return Runtime_StackOverflow(args);
3710
3711 // If not real stack overflow the stack guard was used to interrupt
3712 // execution for another purpose.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003713 if (StackGuard::IsDebugBreak()) DebugBreakHelper();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003714 if (StackGuard::IsPreempted()) RuntimePreempt(args);
3715 if (StackGuard::IsInterrupted()) {
3716 // interrupt
3717 StackGuard::Continue(INTERRUPT);
3718 return Top::StackOverflow();
3719 }
3720 return Heap::undefined_value();
3721}
3722
3723
3724// NOTE: These PrintXXX functions are defined for all builds (not just
3725// DEBUG builds) because we may want to be able to trace function
3726// calls in all modes.
3727static void PrintString(String* str) {
3728 // not uncommon to have empty strings
3729 if (str->length() > 0) {
3730 SmartPointer<char> s =
3731 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
3732 PrintF("%s", *s);
3733 }
3734}
3735
3736
3737static void PrintObject(Object* obj) {
3738 if (obj->IsSmi()) {
3739 PrintF("%d", Smi::cast(obj)->value());
3740 } else if (obj->IsString() || obj->IsSymbol()) {
3741 PrintString(String::cast(obj));
3742 } else if (obj->IsNumber()) {
3743 PrintF("%g", obj->Number());
3744 } else if (obj->IsFailure()) {
3745 PrintF("<failure>");
3746 } else if (obj->IsUndefined()) {
3747 PrintF("<undefined>");
3748 } else if (obj->IsNull()) {
3749 PrintF("<null>");
3750 } else if (obj->IsTrue()) {
3751 PrintF("<true>");
3752 } else if (obj->IsFalse()) {
3753 PrintF("<false>");
3754 } else {
3755 PrintF("%p", obj);
3756 }
3757}
3758
3759
3760static int StackSize() {
3761 int n = 0;
3762 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
3763 return n;
3764}
3765
3766
3767static void PrintTransition(Object* result) {
3768 // indentation
3769 { const int nmax = 80;
3770 int n = StackSize();
3771 if (n <= nmax)
3772 PrintF("%4d:%*s", n, n, "");
3773 else
3774 PrintF("%4d:%*s", n, nmax, "...");
3775 }
3776
3777 if (result == NULL) {
3778 // constructor calls
3779 JavaScriptFrameIterator it;
3780 JavaScriptFrame* frame = it.frame();
3781 if (frame->IsConstructor()) PrintF("new ");
3782 // function name
3783 Object* fun = frame->function();
3784 if (fun->IsJSFunction()) {
3785 PrintObject(JSFunction::cast(fun)->shared()->name());
3786 } else {
3787 PrintObject(fun);
3788 }
3789 // function arguments
3790 // (we are intentionally only printing the actually
3791 // supplied parameters, not all parameters required)
3792 PrintF("(this=");
3793 PrintObject(frame->receiver());
3794 const int length = frame->GetProvidedParametersCount();
3795 for (int i = 0; i < length; i++) {
3796 PrintF(", ");
3797 PrintObject(frame->GetParameter(i));
3798 }
3799 PrintF(") {\n");
3800
3801 } else {
3802 // function result
3803 PrintF("} -> ");
3804 PrintObject(result);
3805 PrintF("\n");
3806 }
3807}
3808
3809
3810static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003811 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003812 NoHandleAllocation ha;
3813 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003814 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003815}
3816
3817
3818static Object* Runtime_TraceExit(Arguments args) {
3819 NoHandleAllocation ha;
3820 PrintTransition(args[0]);
3821 return args[0]; // return TOS
3822}
3823
3824
3825static Object* Runtime_DebugPrint(Arguments args) {
3826 NoHandleAllocation ha;
3827 ASSERT(args.length() == 1);
3828
3829#ifdef DEBUG
3830 if (args[0]->IsString()) {
3831 // If we have a string, assume it's a code "marker"
3832 // and print some interesting cpu debugging info.
3833 JavaScriptFrameIterator it;
3834 JavaScriptFrame* frame = it.frame();
3835 PrintF("fp = %p, sp = %p, pp = %p: ",
3836 frame->fp(), frame->sp(), frame->pp());
3837 } else {
3838 PrintF("DebugPrint: ");
3839 }
3840 args[0]->Print();
3841#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003842 // ShortPrint is available in release mode. Print is not.
3843 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003844#endif
3845 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00003846 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003847
3848 return args[0]; // return TOS
3849}
3850
3851
3852static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003853 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003854 NoHandleAllocation ha;
3855 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003856 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003857}
3858
3859
mads.s.ager31e71382008-08-13 09:32:07 +00003860static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003861 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003862 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003863
3864 // According to ECMA-262, section 15.9.1, page 117, the precision of
3865 // the number in a Date object representing a particular instant in
3866 // time is milliseconds. Therefore, we floor the result of getting
3867 // the OS time.
3868 double millis = floor(OS::TimeCurrentMillis());
3869 return Heap::NumberFromDouble(millis);
3870}
3871
3872
3873static Object* Runtime_DateParseString(Arguments args) {
3874 HandleScope scope;
3875 ASSERT(args.length() == 1);
3876
3877 CONVERT_CHECKED(String, string_object, args[0]);
3878
3879 Handle<String> str(string_object);
3880 Handle<FixedArray> output = Factory::NewFixedArray(DateParser::OUTPUT_SIZE);
3881 if (DateParser::Parse(*str, *output)) {
3882 return *Factory::NewJSArrayWithElements(output);
3883 } else {
3884 return *Factory::null_value();
3885 }
3886}
3887
3888
3889static Object* Runtime_DateLocalTimezone(Arguments args) {
3890 NoHandleAllocation ha;
3891 ASSERT(args.length() == 1);
3892
3893 CONVERT_DOUBLE_CHECKED(x, args[0]);
3894 char* zone = OS::LocalTimezone(x);
3895 return Heap::AllocateStringFromUtf8(CStrVector(zone));
3896}
3897
3898
3899static Object* Runtime_DateLocalTimeOffset(Arguments args) {
3900 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003901 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003902
3903 return Heap::NumberFromDouble(OS::LocalTimeOffset());
3904}
3905
3906
3907static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
3908 NoHandleAllocation ha;
3909 ASSERT(args.length() == 1);
3910
3911 CONVERT_DOUBLE_CHECKED(x, args[0]);
3912 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
3913}
3914
3915
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003916static Object* Runtime_NumberIsFinite(Arguments args) {
3917 NoHandleAllocation ha;
3918 ASSERT(args.length() == 1);
3919
3920 CONVERT_DOUBLE_CHECKED(value, args[0]);
3921 Object* result;
3922 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
3923 result = Heap::false_value();
3924 } else {
3925 result = Heap::true_value();
3926 }
3927 return result;
3928}
3929
3930
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003931static Object* EvalContext() {
3932 // The topmost JS frame belongs to the eval function which called
3933 // the CompileString runtime function. We need to unwind one level
3934 // to get to the caller of eval.
3935 StackFrameLocator locator;
3936 JavaScriptFrame* frame = locator.FindJavaScriptFrame(1);
3937
kasper.lund44510672008-07-25 07:37:58 +00003938 // TODO(900055): Right now we check if the caller of eval() supports
3939 // eval to determine if it's an aliased eval or not. This may not be
3940 // entirely correct in the unlikely case where a function uses both
3941 // aliased and direct eval calls.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003942 HandleScope scope;
3943 if (!ScopeInfo<>::SupportsEval(frame->FindCode())) {
kasper.lund44510672008-07-25 07:37:58 +00003944 // Aliased eval: Evaluate in the global context of the eval
3945 // function to support aliased, cross environment evals.
3946 return *Top::global_context();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003947 }
3948
3949 // Fetch the caller context from the frame.
3950 Handle<Context> caller(Context::cast(frame->context()));
3951
3952 // Check for eval() invocations that cross environments. Use the
3953 // context from the stack if evaluating in current environment.
3954 Handle<Context> target = Top::global_context();
3955 if (caller->global_context() == *target) return *caller;
3956
3957 // Compute a function closure that captures the calling context. We
3958 // need a function that has trivial scope info, since it is only
3959 // used to hold the context chain together.
3960 Handle<JSFunction> closure = Factory::NewFunction(Factory::empty_symbol(),
3961 Factory::undefined_value());
3962 closure->set_context(*caller);
3963
3964 // Create a new adaptor context that has the target environment as
3965 // the extension object. This enables the evaluated code to see both
3966 // the current context with locals and everything and to see global
3967 // variables declared in the target global object. Furthermore, any
3968 // properties introduced with 'var' will be added to the target
3969 // global object because it is the extension object.
3970 Handle<Context> adaptor =
3971 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, closure);
3972 adaptor->set_extension(target->global());
3973 return *adaptor;
3974}
3975
3976
3977static Object* Runtime_EvalReceiver(Arguments args) {
3978 StackFrameLocator locator;
3979 return locator.FindJavaScriptFrame(1)->receiver();
3980}
3981
3982
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003983static Object* Runtime_GlobalReceiver(Arguments args) {
3984 ASSERT(args.length() == 1);
3985 Object* global = args[0];
3986 if (!global->IsJSGlobalObject()) return Heap::null_value();
3987 return JSGlobalObject::cast(global)->global_receiver();
3988}
3989
3990
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003991static Object* Runtime_CompileString(Arguments args) {
3992 HandleScope scope;
ager@chromium.org236ad962008-09-25 09:45:57 +00003993 ASSERT(args.length() == 3);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003994 CONVERT_ARG_CHECKED(String, source, 0);
ager@chromium.org236ad962008-09-25 09:45:57 +00003995 CONVERT_ARG_CHECKED(Smi, line_offset, 1);
3996 bool contextual = args[2]->IsTrue();
3997 RUNTIME_ASSERT(contextual || args[2]->IsFalse());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003998
3999 // Compute the eval context.
4000 Handle<Context> context;
4001 if (contextual) {
4002 // Get eval context. May not be available if we are calling eval
4003 // through an alias, and the corresponding frame doesn't have a
4004 // proper eval context set up.
4005 Object* eval_context = EvalContext();
4006 if (eval_context->IsFailure()) return eval_context;
4007 context = Handle<Context>(Context::cast(eval_context));
4008 } else {
4009 context = Handle<Context>(Top::context()->global_context());
4010 }
4011
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004012
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004013 // Compile source string.
4014 bool is_global = context->IsGlobalContext();
4015 Handle<JSFunction> boilerplate =
ager@chromium.org236ad962008-09-25 09:45:57 +00004016 Compiler::CompileEval(source, line_offset->value(), is_global);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00004017 if (boilerplate.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004018 Handle<JSFunction> fun =
4019 Factory::NewFunctionFromBoilerplate(boilerplate, context);
4020 return *fun;
4021}
4022
4023
4024static Object* Runtime_CompileScript(Arguments args) {
4025 HandleScope scope;
4026 ASSERT(args.length() == 4);
4027
4028 CONVERT_ARG_CHECKED(String, source, 0);
4029 CONVERT_ARG_CHECKED(String, script, 1);
4030 CONVERT_CHECKED(Smi, line_attrs, args[2]);
4031 int line = line_attrs->value();
4032 CONVERT_CHECKED(Smi, col_attrs, args[3]);
4033 int col = col_attrs->value();
4034 Handle<JSFunction> boilerplate =
4035 Compiler::Compile(source, script, line, col, NULL, NULL);
4036 if (boilerplate.is_null()) return Failure::Exception();
4037 Handle<JSFunction> fun =
4038 Factory::NewFunctionFromBoilerplate(boilerplate,
4039 Handle<Context>(Top::context()));
4040 return *fun;
4041}
4042
4043
4044static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
4045 // This utility adjusts the property attributes for newly created Function
4046 // object ("new Function(...)") by changing the map.
4047 // All it does is changing the prototype property to enumerable
4048 // as specified in ECMA262, 15.3.5.2.
4049 HandleScope scope;
4050 ASSERT(args.length() == 1);
4051 CONVERT_ARG_CHECKED(JSFunction, func, 0);
4052 ASSERT(func->map()->instance_type() ==
4053 Top::function_instance_map()->instance_type());
4054 ASSERT(func->map()->instance_size() ==
4055 Top::function_instance_map()->instance_size());
4056 func->set_map(*Top::function_instance_map());
4057 return *func;
4058}
4059
4060
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004061// Push an array unto an array of arrays if it is not already in the
4062// array. Returns true if the element was pushed on the stack and
4063// false otherwise.
4064static Object* Runtime_PushIfAbsent(Arguments args) {
4065 ASSERT(args.length() == 2);
4066 CONVERT_CHECKED(JSArray, array, args[0]);
4067 CONVERT_CHECKED(JSArray, element, args[1]);
4068 RUNTIME_ASSERT(array->HasFastElements());
4069 int length = Smi::cast(array->length())->value();
4070 FixedArray* elements = FixedArray::cast(array->elements());
4071 for (int i = 0; i < length; i++) {
4072 if (elements->get(i) == element) return Heap::false_value();
4073 }
4074 Object* obj = array->SetFastElement(length, element);
4075 if (obj->IsFailure()) return obj;
4076 return Heap::true_value();
4077}
4078
4079
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004080/**
4081 * A simple visitor visits every element of Array's.
4082 * The backend storage can be a fixed array for fast elements case,
4083 * or a dictionary for sparse array. Since Dictionary is a subtype
4084 * of FixedArray, the class can be used by both fast and slow cases.
4085 * The second parameter of the constructor, fast_elements, specifies
4086 * whether the storage is a FixedArray or Dictionary.
4087 *
4088 * An index limit is used to deal with the situation that a result array
4089 * length overflows 32-bit non-negative integer.
4090 */
4091class ArrayConcatVisitor {
4092 public:
4093 ArrayConcatVisitor(Handle<FixedArray> storage,
4094 uint32_t index_limit,
4095 bool fast_elements) :
4096 storage_(storage), index_limit_(index_limit),
4097 fast_elements_(fast_elements), index_offset_(0) { }
4098
4099 void visit(uint32_t i, Handle<Object> elm) {
4100 uint32_t index = i + index_offset_;
4101 if (index >= index_limit_) return;
4102
4103 if (fast_elements_) {
4104 ASSERT(index < static_cast<uint32_t>(storage_->length()));
4105 storage_->set(index, *elm);
4106
4107 } else {
4108 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
4109 Handle<Dictionary> result =
4110 Factory::DictionaryAtNumberPut(dict, index, elm);
4111 if (!result.is_identical_to(dict))
4112 storage_ = result;
4113 }
4114 }
4115
4116 void increase_index_offset(uint32_t delta) {
4117 index_offset_ += delta;
4118 }
4119
4120 private:
4121 Handle<FixedArray> storage_;
4122 uint32_t index_limit_;
4123 bool fast_elements_;
4124 uint32_t index_offset_;
4125};
4126
4127
4128/**
4129 * A helper function that visits elements of a JSObject. Only elements
4130 * whose index between 0 and range (exclusive) are visited.
4131 *
4132 * If the third parameter, visitor, is not NULL, the visitor is called
4133 * with parameters, 'visitor_index_offset + element index' and the element.
4134 *
4135 * It returns the number of visisted elements.
4136 */
4137static uint32_t IterateElements(Handle<JSObject> receiver,
4138 uint32_t range,
4139 ArrayConcatVisitor* visitor) {
4140 uint32_t num_of_elements = 0;
4141
4142 if (receiver->HasFastElements()) {
4143 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
4144 uint32_t len = elements->length();
4145 if (range < len) len = range;
4146
4147 for (uint32_t j = 0; j < len; j++) {
4148 Handle<Object> e(elements->get(j));
4149 if (!e->IsTheHole()) {
4150 num_of_elements++;
4151 if (visitor)
4152 visitor->visit(j, e);
4153 }
4154 }
4155
4156 } else {
4157 Handle<Dictionary> dict(receiver->element_dictionary());
4158 uint32_t capacity = dict->Capacity();
4159 for (uint32_t j = 0; j < capacity; j++) {
4160 Handle<Object> k(dict->KeyAt(j));
4161 if (dict->IsKey(*k)) {
4162 ASSERT(k->IsNumber());
4163 uint32_t index = static_cast<uint32_t>(k->Number());
4164 if (index < range) {
4165 num_of_elements++;
4166 if (visitor) {
4167 visitor->visit(index,
4168 Handle<Object>(dict->ValueAt(j)));
4169 }
4170 }
4171 }
4172 }
4173 }
4174
4175 return num_of_elements;
4176}
4177
4178
4179/**
4180 * A helper function that visits elements of an Array object, and elements
4181 * on its prototypes.
4182 *
4183 * Elements on prototypes are visited first, and only elements whose indices
4184 * less than Array length are visited.
4185 *
4186 * If a ArrayConcatVisitor object is given, the visitor is called with
4187 * parameters, element's index + visitor_index_offset and the element.
4188 */
4189static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
4190 ArrayConcatVisitor* visitor) {
4191 uint32_t range = static_cast<uint32_t>(array->length()->Number());
4192 Handle<Object> obj = array;
4193
4194 static const int kEstimatedPrototypes = 3;
4195 List< Handle<JSObject> > objects(kEstimatedPrototypes);
4196
4197 // Visit prototype first. If an element on the prototype is shadowed by
4198 // the inheritor using the same index, the ArrayConcatVisitor visits
4199 // the prototype element before the shadowing element.
4200 // The visitor can simply overwrite the old value by new value using
4201 // the same index. This follows Array::concat semantics.
4202 while (!obj->IsNull()) {
4203 objects.Add(Handle<JSObject>::cast(obj));
4204 obj = Handle<Object>(obj->GetPrototype());
4205 }
4206
4207 uint32_t nof_elements = 0;
4208 for (int i = objects.length() - 1; i >= 0; i--) {
4209 Handle<JSObject> obj = objects[i];
4210 nof_elements +=
4211 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
4212 }
4213
4214 return nof_elements;
4215}
4216
4217
4218/**
4219 * A helper function of Runtime_ArrayConcat.
4220 *
4221 * The first argument is an Array of arrays and objects. It is the
4222 * same as the arguments array of Array::concat JS function.
4223 *
4224 * If an argument is an Array object, the function visits array
4225 * elements. If an argument is not an Array object, the function
4226 * visits the object as if it is an one-element array.
4227 *
4228 * If the result array index overflows 32-bit integer, the rounded
4229 * non-negative number is used as new length. For example, if one
4230 * array length is 2^32 - 1, second array length is 1, the
4231 * concatenated array length is 0.
4232 */
4233static uint32_t IterateArguments(Handle<JSArray> arguments,
4234 ArrayConcatVisitor* visitor) {
4235 uint32_t visited_elements = 0;
4236 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
4237
4238 for (uint32_t i = 0; i < num_of_args; i++) {
4239 Handle<Object> obj(arguments->GetElement(i));
4240 if (obj->IsJSArray()) {
4241 Handle<JSArray> array = Handle<JSArray>::cast(obj);
4242 uint32_t len = static_cast<uint32_t>(array->length()->Number());
4243 uint32_t nof_elements =
4244 IterateArrayAndPrototypeElements(array, visitor);
4245 // Total elements of array and its prototype chain can be more than
4246 // the array length, but ArrayConcat can only concatenate at most
4247 // the array length number of elements.
4248 visited_elements += (nof_elements > len) ? len : nof_elements;
4249 if (visitor) visitor->increase_index_offset(len);
4250
4251 } else {
4252 if (visitor) {
4253 visitor->visit(0, obj);
4254 visitor->increase_index_offset(1);
4255 }
4256 visited_elements++;
4257 }
4258 }
4259 return visited_elements;
4260}
4261
4262
4263/**
4264 * Array::concat implementation.
4265 * See ECMAScript 262, 15.4.4.4.
4266 */
4267static Object* Runtime_ArrayConcat(Arguments args) {
4268 ASSERT(args.length() == 1);
4269 HandleScope handle_scope;
4270
4271 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
4272 Handle<JSArray> arguments(arg_arrays);
4273
4274 // Pass 1: estimate the number of elements of the result
4275 // (it could be more than real numbers if prototype has elements).
4276 uint32_t result_length = 0;
4277 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
4278
4279 { AssertNoAllocation nogc;
4280 for (uint32_t i = 0; i < num_of_args; i++) {
4281 Object* obj = arguments->GetElement(i);
4282 if (obj->IsJSArray()) {
4283 result_length +=
4284 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
4285 } else {
4286 result_length++;
4287 }
4288 }
4289 }
4290
4291 // Allocate an empty array, will set length and content later.
4292 Handle<JSArray> result = Factory::NewJSArray(0);
4293
4294 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
4295 // If estimated number of elements is more than half of length, a
4296 // fixed array (fast case) is more time and space-efficient than a
4297 // dictionary.
4298 bool fast_case = (estimate_nof_elements * 2) >= result_length;
4299
4300 Handle<FixedArray> storage;
4301 if (fast_case) {
4302 // The backing storage array must have non-existing elements to
4303 // preserve holes across concat operations.
4304 storage = Factory::NewFixedArrayWithHoles(result_length);
4305
4306 } else {
4307 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
4308 uint32_t at_least_space_for = estimate_nof_elements +
4309 (estimate_nof_elements >> 2);
4310 storage = Handle<FixedArray>::cast(
4311 Factory::NewDictionary(at_least_space_for));
4312 }
4313
4314 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
4315
4316 ArrayConcatVisitor visitor(storage, result_length, fast_case);
4317
4318 IterateArguments(arguments, &visitor);
4319
4320 result->set_length(*len);
4321 result->set_elements(*storage);
4322
4323 return *result;
4324}
4325
4326
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004327// This will not allocate (flatten the string), but it may run
4328// very slowly for very deeply nested ConsStrings. For debugging use only.
4329static Object* Runtime_GlobalPrint(Arguments args) {
4330 NoHandleAllocation ha;
4331 ASSERT(args.length() == 1);
4332
4333 CONVERT_CHECKED(String, string, args[0]);
4334 StringInputBuffer buffer(string);
4335 while (buffer.has_more()) {
4336 uint16_t character = buffer.GetNext();
4337 PrintF("%c", character);
4338 }
4339 return string;
4340}
4341
4342
4343static Object* Runtime_RemoveArrayHoles(Arguments args) {
4344 ASSERT(args.length() == 1);
4345 // Ignore the case if this is not a JSArray.
4346 if (!args[0]->IsJSArray()) return args[0];
4347 return JSArray::cast(args[0])->RemoveHoles();
4348}
4349
4350
4351// Move contents of argument 0 (an array) to argument 1 (an array)
4352static Object* Runtime_MoveArrayContents(Arguments args) {
4353 ASSERT(args.length() == 2);
4354 CONVERT_CHECKED(JSArray, from, args[0]);
4355 CONVERT_CHECKED(JSArray, to, args[1]);
4356 to->SetContent(FixedArray::cast(from->elements()));
4357 to->set_length(from->length());
4358 from->SetContent(Heap::empty_fixed_array());
4359 from->set_length(0);
4360 return to;
4361}
4362
4363
4364// How many elements does this array have?
4365static Object* Runtime_EstimateNumberOfElements(Arguments args) {
4366 ASSERT(args.length() == 1);
4367 CONVERT_CHECKED(JSArray, array, args[0]);
4368 HeapObject* elements = array->elements();
4369 if (elements->IsDictionary()) {
4370 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
4371 } else {
4372 return array->length();
4373 }
4374}
4375
4376
4377// Returns an array that tells you where in the [0, length) interval an array
4378// might have elements. Can either return keys or intervals. Keys can have
4379// gaps in (undefined). Intervals can also span over some undefined keys.
4380static Object* Runtime_GetArrayKeys(Arguments args) {
4381 ASSERT(args.length() == 2);
4382 HandleScope scope;
4383 CONVERT_CHECKED(JSArray, raw_array, args[0]);
4384 Handle<JSArray> array(raw_array);
4385 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004386 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004387 // Create an array and get all the keys into it, then remove all the
4388 // keys that are not integers in the range 0 to length-1.
4389 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
4390 int keys_length = keys->length();
4391 for (int i = 0; i < keys_length; i++) {
4392 Object* key = keys->get(i);
4393 uint32_t index;
4394 if (!Array::IndexFromObject(key, &index) || index >= length) {
4395 // Zap invalid keys.
4396 keys->set_undefined(i);
4397 }
4398 }
4399 return *Factory::NewJSArrayWithElements(keys);
4400 } else {
4401 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
4402 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004403 single_interval->set(0,
4404 Smi::FromInt(-1),
4405 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004406 Handle<Object> length_object =
4407 Factory::NewNumber(static_cast<double>(length));
4408 single_interval->set(1, *length_object);
4409 return *Factory::NewJSArrayWithElements(single_interval);
4410 }
4411}
4412
4413
4414// DefineAccessor takes an optional final argument which is the
4415// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
4416// to the way accessors are implemented, it is set for both the getter
4417// and setter on the first call to DefineAccessor and ignored on
4418// subsequent calls.
4419static Object* Runtime_DefineAccessor(Arguments args) {
4420 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
4421 // Compute attributes.
4422 PropertyAttributes attributes = NONE;
4423 if (args.length() == 5) {
4424 CONVERT_CHECKED(Smi, attrs, args[4]);
4425 int value = attrs->value();
4426 // Only attribute bits should be set.
4427 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4428 attributes = static_cast<PropertyAttributes>(value);
4429 }
4430
4431 CONVERT_CHECKED(JSObject, obj, args[0]);
4432 CONVERT_CHECKED(String, name, args[1]);
4433 CONVERT_CHECKED(Smi, flag, args[2]);
4434 CONVERT_CHECKED(JSFunction, fun, args[3]);
4435 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
4436}
4437
4438
4439static Object* Runtime_LookupAccessor(Arguments args) {
4440 ASSERT(args.length() == 3);
4441 CONVERT_CHECKED(JSObject, obj, args[0]);
4442 CONVERT_CHECKED(String, name, args[1]);
4443 CONVERT_CHECKED(Smi, flag, args[2]);
4444 return obj->LookupAccessor(name, flag->value() == 0);
4445}
4446
4447
4448// Helper functions for wrapping and unwrapping stack frame ids.
4449static Smi* WrapFrameId(StackFrame::Id id) {
4450 ASSERT(IsAligned(OffsetFrom(id), 4));
4451 return Smi::FromInt(id >> 2);
4452}
4453
4454
4455static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
4456 return static_cast<StackFrame::Id>(wrapped->value() << 2);
4457}
4458
4459
4460// Adds a JavaScript function as a debug event listener.
4461// args[0]: debug event listener function
4462// args[1]: object supplied during callback
4463static Object* Runtime_AddDebugEventListener(Arguments args) {
4464 ASSERT(args.length() == 2);
4465 // Convert the parameters to API objects to call the API function for adding
4466 // a JavaScript function as debug event listener.
4467 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
4468 v8::Handle<v8::Function> fun(ToApi<v8::Function>(raw_fun));
4469 v8::Handle<v8::Value> data(ToApi<v8::Value>(args.at<Object>(0)));
4470 v8::Debug::AddDebugEventListener(fun, data);
4471
4472 return Heap::undefined_value();
4473}
4474
4475
4476// Removes a JavaScript function debug event listener.
4477// args[0]: debug event listener function
4478static Object* Runtime_RemoveDebugEventListener(Arguments args) {
4479 ASSERT(args.length() == 1);
4480 // Convert the parameter to an API object to call the API function for
4481 // removing a JavaScript function debug event listener.
4482 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
4483 v8::Handle<v8::Function> fun(ToApi<v8::Function>(raw_fun));
4484 v8::Debug::RemoveDebugEventListener(fun);
4485
4486 return Heap::undefined_value();
4487}
4488
4489
4490static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00004491 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004492 StackGuard::DebugBreak();
4493 return Heap::undefined_value();
4494}
4495
4496
4497static Object* DebugLookupResultValue(LookupResult* result) {
4498 Object* value;
4499 switch (result->type()) {
4500 case NORMAL: {
4501 Dictionary* dict =
4502 JSObject::cast(result->holder())->property_dictionary();
4503 value = dict->ValueAt(result->GetDictionaryEntry());
4504 if (value->IsTheHole()) {
4505 return Heap::undefined_value();
4506 }
4507 return value;
4508 }
4509 case FIELD:
4510 value =
4511 JSObject::cast(
ager@chromium.org7c537e22008-10-16 08:43:32 +00004512 result->holder())->FastPropertyAt(result->GetFieldIndex());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004513 if (value->IsTheHole()) {
4514 return Heap::undefined_value();
4515 }
4516 return value;
4517 case CONSTANT_FUNCTION:
4518 return result->GetConstantFunction();
4519 case CALLBACKS:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004520 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004521 case MAP_TRANSITION:
4522 case CONSTANT_TRANSITION:
4523 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004524 return Heap::undefined_value();
4525 default:
4526 UNREACHABLE();
4527 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004528 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004529 return Heap::undefined_value();
4530}
4531
4532
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004533static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004534 HandleScope scope;
4535
4536 ASSERT(args.length() == 2);
4537
4538 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4539 CONVERT_ARG_CHECKED(String, name, 1);
4540
4541 // Check if the name is trivially convertible to an index and get the element
4542 // if so.
4543 uint32_t index;
4544 if (name->AsArrayIndex(&index)) {
4545 Handle<FixedArray> details = Factory::NewFixedArray(2);
4546 details->set(0, Runtime::GetElementOrCharAt(obj, index));
4547 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
4548 return *Factory::NewJSArrayWithElements(details);
4549 }
4550
4551 // Perform standard local lookup on the object.
4552 LookupResult result;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004553 obj->Lookup(*name, &result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004554 if (result.IsProperty()) {
4555 Handle<Object> value(DebugLookupResultValue(&result));
4556 Handle<FixedArray> details = Factory::NewFixedArray(2);
4557 details->set(0, *value);
4558 details->set(1, result.GetPropertyDetails().AsSmi());
4559 return *Factory::NewJSArrayWithElements(details);
4560 }
4561 return Heap::undefined_value();
4562}
4563
4564
4565static Object* Runtime_DebugGetProperty(Arguments args) {
4566 HandleScope scope;
4567
4568 ASSERT(args.length() == 2);
4569
4570 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4571 CONVERT_ARG_CHECKED(String, name, 1);
4572
4573 LookupResult result;
4574 obj->Lookup(*name, &result);
4575 if (result.IsProperty()) {
4576 return DebugLookupResultValue(&result);
4577 }
4578 return Heap::undefined_value();
4579}
4580
4581
4582// Return the names of the local named properties.
4583// args[0]: object
4584static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
4585 HandleScope scope;
4586 ASSERT(args.length() == 1);
4587 if (!args[0]->IsJSObject()) {
4588 return Heap::undefined_value();
4589 }
4590 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4591
4592 int n = obj->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4593 Handle<FixedArray> names = Factory::NewFixedArray(n);
4594 obj->GetLocalPropertyNames(*names);
4595 return *Factory::NewJSArrayWithElements(names);
4596}
4597
4598
4599// Return the names of the local indexed properties.
4600// args[0]: object
4601static Object* Runtime_DebugLocalElementNames(Arguments args) {
4602 HandleScope scope;
4603 ASSERT(args.length() == 1);
4604 if (!args[0]->IsJSObject()) {
4605 return Heap::undefined_value();
4606 }
4607 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4608
4609 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4610 Handle<FixedArray> names = Factory::NewFixedArray(n);
4611 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4612 return *Factory::NewJSArrayWithElements(names);
4613}
4614
4615
4616// Return the property type calculated from the property details.
4617// args[0]: smi with property details.
4618static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
4619 ASSERT(args.length() == 1);
4620 CONVERT_CHECKED(Smi, details, args[0]);
4621 PropertyType type = PropertyDetails(details).type();
4622 return Smi::FromInt(static_cast<int>(type));
4623}
4624
4625
4626// Return the property attribute calculated from the property details.
4627// args[0]: smi with property details.
4628static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
4629 ASSERT(args.length() == 1);
4630 CONVERT_CHECKED(Smi, details, args[0]);
4631 PropertyAttributes attributes = PropertyDetails(details).attributes();
4632 return Smi::FromInt(static_cast<int>(attributes));
4633}
4634
4635
4636// Return the property insertion index calculated from the property details.
4637// args[0]: smi with property details.
4638static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
4639 ASSERT(args.length() == 1);
4640 CONVERT_CHECKED(Smi, details, args[0]);
4641 int index = PropertyDetails(details).index();
4642 return Smi::FromInt(index);
4643}
4644
4645
4646// Return information on whether an object has a named or indexed interceptor.
4647// args[0]: object
4648static Object* Runtime_DebugInterceptorInfo(Arguments args) {
4649 HandleScope scope;
4650 ASSERT(args.length() == 1);
4651 if (!args[0]->IsJSObject()) {
4652 return Smi::FromInt(0);
4653 }
4654 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4655
4656 int result = 0;
4657 if (obj->HasNamedInterceptor()) result |= 2;
4658 if (obj->HasIndexedInterceptor()) result |= 1;
4659
4660 return Smi::FromInt(result);
4661}
4662
4663
4664// Return property names from named interceptor.
4665// args[0]: object
4666static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
4667 HandleScope scope;
4668 ASSERT(args.length() == 1);
4669 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4670 RUNTIME_ASSERT(obj->HasNamedInterceptor());
4671
4672 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4673 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4674 return Heap::undefined_value();
4675}
4676
4677
4678// Return element names from indexed interceptor.
4679// args[0]: object
4680static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
4681 HandleScope scope;
4682 ASSERT(args.length() == 1);
4683 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4684 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
4685
4686 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4687 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4688 return Heap::undefined_value();
4689}
4690
4691
4692// Return property value from named interceptor.
4693// args[0]: object
4694// args[1]: property name
4695static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
4696 HandleScope scope;
4697 ASSERT(args.length() == 2);
4698 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4699 RUNTIME_ASSERT(obj->HasNamedInterceptor());
4700 CONVERT_ARG_CHECKED(String, name, 1);
4701
4702 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004703 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004704}
4705
4706
4707// Return element value from indexed interceptor.
4708// args[0]: object
4709// args[1]: index
4710static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
4711 HandleScope scope;
4712 ASSERT(args.length() == 2);
4713 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4714 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
4715 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
4716
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004717 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004718}
4719
4720
4721static Object* Runtime_CheckExecutionState(Arguments args) {
4722 ASSERT(args.length() >= 1);
4723 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
4724 // Check that the break id is valid and that there is a valid frame
4725 // where execution is broken.
4726 if (break_id != Top::break_id() ||
4727 Top::break_frame_id() == StackFrame::NO_ID) {
4728 return Top::Throw(Heap::illegal_execution_state_symbol());
4729 }
4730
4731 return Heap::true_value();
4732}
4733
4734
4735static Object* Runtime_GetFrameCount(Arguments args) {
4736 HandleScope scope;
4737 ASSERT(args.length() == 1);
4738
4739 // Check arguments.
4740 Object* result = Runtime_CheckExecutionState(args);
4741 if (result->IsFailure()) return result;
4742
4743 // Count all frames which are relevant to debugging stack trace.
4744 int n = 0;
4745 StackFrame::Id id = Top::break_frame_id();
4746 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
4747 return Smi::FromInt(n);
4748}
4749
4750
4751static const int kFrameDetailsFrameIdIndex = 0;
4752static const int kFrameDetailsReceiverIndex = 1;
4753static const int kFrameDetailsFunctionIndex = 2;
4754static const int kFrameDetailsArgumentCountIndex = 3;
4755static const int kFrameDetailsLocalCountIndex = 4;
4756static const int kFrameDetailsSourcePositionIndex = 5;
4757static const int kFrameDetailsConstructCallIndex = 6;
4758static const int kFrameDetailsDebuggerFrameIndex = 7;
4759static const int kFrameDetailsFirstDynamicIndex = 8;
4760
4761// Return an array with frame details
4762// args[0]: number: break id
4763// args[1]: number: frame index
4764//
4765// The array returned contains the following information:
4766// 0: Frame id
4767// 1: Receiver
4768// 2: Function
4769// 3: Argument count
4770// 4: Local count
4771// 5: Source position
4772// 6: Constructor call
4773// 7: Debugger frame
4774// Arguments name, value
4775// Locals name, value
4776static Object* Runtime_GetFrameDetails(Arguments args) {
4777 HandleScope scope;
4778 ASSERT(args.length() == 2);
4779
4780 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004781 Object* check = Runtime_CheckExecutionState(args);
4782 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004783 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
4784
4785 // Find the relevant frame with the requested index.
4786 StackFrame::Id id = Top::break_frame_id();
4787 int count = 0;
4788 JavaScriptFrameIterator it(id);
4789 for (; !it.done(); it.Advance()) {
4790 if (count == index) break;
4791 count++;
4792 }
4793 if (it.done()) return Heap::undefined_value();
4794
4795 // Traverse the saved contexts chain to find the active context for the
4796 // selected frame.
4797 SaveContext* save = Top::save_context();
4798 while (save != NULL && reinterpret_cast<Address>(save) < it.frame()->sp()) {
4799 save = save->prev();
4800 }
4801
4802 // Get the frame id.
4803 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
4804
4805 // Find source position.
4806 int position = it.frame()->FindCode()->SourcePosition(it.frame()->pc());
4807
4808 // Check for constructor frame.
4809 bool constructor = it.frame()->IsConstructor();
4810
4811 // Get code and read scope info from it for local variable information.
4812 Handle<Code> code(it.frame()->FindCode());
4813 ScopeInfo<> info(*code);
4814
4815 // Get the context.
4816 Handle<Context> context(Context::cast(it.frame()->context()));
4817
4818 // Get the locals names and values into a temporary array.
4819 //
4820 // TODO(1240907): Hide compiler-introduced stack variables
4821 // (e.g. .result)? For users of the debugger, they will probably be
4822 // confusing.
4823 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
4824 for (int i = 0; i < info.NumberOfLocals(); i++) {
4825 // Name of the local.
4826 locals->set(i * 2, *info.LocalName(i));
4827
4828 // Fetch the value of the local - either from the stack or from a
4829 // heap-allocated context.
4830 if (i < info.number_of_stack_slots()) {
4831 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
4832 } else {
4833 Handle<String> name = info.LocalName(i);
4834 // Traverse the context chain to the function context as all local
4835 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004836 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004837 context = Handle<Context>(context->previous());
4838 }
4839 ASSERT(context->is_function_context());
4840 locals->set(i * 2 + 1,
4841 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
4842 NULL)));
4843 }
4844 }
4845
4846 // Now advance to the arguments adapter frame (if any). If contains all
4847 // the provided parameters and
4848
4849 // Now advance to the arguments adapter frame (if any). It contains all
4850 // the provided parameters whereas the function frame always have the number
4851 // of arguments matching the functions parameters. The rest of the
4852 // information (except for what is collected above) is the same.
4853 it.AdvanceToArgumentsFrame();
4854
4855 // Find the number of arguments to fill. At least fill the number of
4856 // parameters for the function and fill more if more parameters are provided.
4857 int argument_count = info.number_of_parameters();
4858 if (argument_count < it.frame()->GetProvidedParametersCount()) {
4859 argument_count = it.frame()->GetProvidedParametersCount();
4860 }
4861
4862 // Calculate the size of the result.
4863 int details_size = kFrameDetailsFirstDynamicIndex +
4864 2 * (argument_count + info.NumberOfLocals());
4865 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
4866
4867 // Add the frame id.
4868 details->set(kFrameDetailsFrameIdIndex, *frame_id);
4869
4870 // Add the function (same as in function frame).
4871 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
4872
4873 // Add the arguments count.
4874 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
4875
4876 // Add the locals count
4877 details->set(kFrameDetailsLocalCountIndex,
4878 Smi::FromInt(info.NumberOfLocals()));
4879
4880 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00004881 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004882 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
4883 } else {
4884 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
4885 }
4886
4887 // Add the constructor information.
4888 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
4889
4890 // Add information on whether this frame is invoked in the debugger context.
4891 details->set(kFrameDetailsDebuggerFrameIndex,
4892 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
4893
4894 // Fill the dynamic part.
4895 int details_index = kFrameDetailsFirstDynamicIndex;
4896
4897 // Add arguments name and value.
4898 for (int i = 0; i < argument_count; i++) {
4899 // Name of the argument.
4900 if (i < info.number_of_parameters()) {
4901 details->set(details_index++, *info.parameter_name(i));
4902 } else {
4903 details->set(details_index++, Heap::undefined_value());
4904 }
4905
4906 // Parameter value.
4907 if (i < it.frame()->GetProvidedParametersCount()) {
4908 details->set(details_index++, it.frame()->GetParameter(i));
4909 } else {
4910 details->set(details_index++, Heap::undefined_value());
4911 }
4912 }
4913
4914 // Add locals name and value from the temporary copy from the function frame.
4915 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
4916 details->set(details_index++, locals->get(i));
4917 }
4918
4919 // Add the receiver (same as in function frame).
4920 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
4921 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
4922 Handle<Object> receiver(it.frame()->receiver());
4923 if (!receiver->IsJSObject()) {
4924 // If the receiver is NOT a JSObject we have hit an optimization
4925 // where a value object is not converted into a wrapped JS objects.
4926 // To hide this optimization from the debugger, we wrap the receiver
4927 // by creating correct wrapper object based on the calling frame's
4928 // global context.
4929 it.Advance();
4930 Handle<Context> calling_frames_global_context(
4931 Context::cast(Context::cast(it.frame()->context())->global_context()));
4932 receiver = Factory::ToObject(receiver, calling_frames_global_context);
4933 }
4934 details->set(kFrameDetailsReceiverIndex, *receiver);
4935
4936 ASSERT_EQ(details_size, details_index);
4937 return *Factory::NewJSArrayWithElements(details);
4938}
4939
4940
4941static Object* Runtime_GetCFrames(Arguments args) {
4942 HandleScope scope;
4943 ASSERT(args.length() == 1);
4944 Object* result = Runtime_CheckExecutionState(args);
4945 if (result->IsFailure()) return result;
4946
4947 static const int kMaxCFramesSize = 200;
4948 OS::StackFrame frames[kMaxCFramesSize];
4949 int frames_count = OS::StackWalk(frames, kMaxCFramesSize);
4950 if (frames_count == OS::kStackWalkError) {
4951 return Heap::undefined_value();
4952 }
4953
4954 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
4955 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
4956 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
4957 for (int i = 0; i < frames_count; i++) {
4958 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
4959 frame_value->SetProperty(
4960 *address_str,
4961 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
4962 NONE);
4963
4964 // Get the stack walk text for this frame.
4965 Handle<String> frame_text;
4966 if (strlen(frames[i].text) > 0) {
4967 Vector<const char> str(frames[i].text, strlen(frames[i].text));
4968 frame_text = Factory::NewStringFromAscii(str);
4969 }
4970
4971 if (!frame_text.is_null()) {
4972 frame_value->SetProperty(*text_str, *frame_text, NONE);
4973 }
4974
4975 frames_array->set(i, *frame_value);
4976 }
4977 return *Factory::NewJSArrayWithElements(frames_array);
4978}
4979
4980
4981static Object* Runtime_GetBreakLocations(Arguments args) {
4982 HandleScope scope;
4983 ASSERT(args.length() == 1);
4984
4985 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
4986 Handle<SharedFunctionInfo> shared(raw_fun->shared());
4987 // Find the number of break points
4988 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
4989 if (break_locations->IsUndefined()) return Heap::undefined_value();
4990 // Return array as JS array
4991 return *Factory::NewJSArrayWithElements(
4992 Handle<FixedArray>::cast(break_locations));
4993}
4994
4995
4996// Set a break point in a function
4997// args[0]: function
4998// args[1]: number: break source position (within the function source)
4999// args[2]: number: break point object
5000static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
5001 HandleScope scope;
5002 ASSERT(args.length() == 3);
5003 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
5004 Handle<SharedFunctionInfo> shared(raw_fun->shared());
5005 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
5006 RUNTIME_ASSERT(source_position >= 0);
5007 Handle<Object> break_point_object_arg = args.at<Object>(2);
5008
5009 // Set break point.
5010 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
5011
5012 return Heap::undefined_value();
5013}
5014
5015
5016static Object* FindSharedFunctionInfoInScript(Handle<Script> script,
5017 int position) {
5018 // Iterate the heap looking for SharedFunctionInfo generated from the
5019 // script. The inner most SharedFunctionInfo containing the source position
5020 // for the requested break point is found.
5021 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
5022 // which is found is not compiled it is compiled and the heap is iterated
5023 // again as the compilation might create inner functions from the newly
5024 // compiled function and the actual requested break point might be in one of
5025 // these functions.
5026 bool done = false;
5027 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00005028 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005029 Handle<SharedFunctionInfo> target;
5030 // The current candidate for the last function in script:
5031 Handle<SharedFunctionInfo> last;
5032 while (!done) {
5033 HeapIterator iterator;
5034 while (iterator.has_next()) {
5035 HeapObject* obj = iterator.next();
5036 ASSERT(obj != NULL);
5037 if (obj->IsSharedFunctionInfo()) {
5038 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
5039 if (shared->script() == *script) {
5040 // If the SharedFunctionInfo found has the requested script data and
5041 // contains the source position it is a candidate.
5042 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00005043 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005044 start_position = shared->start_position();
5045 }
5046 if (start_position <= position &&
5047 position <= shared->end_position()) {
5048 // If there is no candidate or this function is within the currrent
5049 // candidate this is the new candidate.
5050 if (target.is_null()) {
5051 target_start_position = start_position;
5052 target = shared;
5053 } else {
5054 if (target_start_position < start_position &&
5055 shared->end_position() < target->end_position()) {
5056 target_start_position = start_position;
5057 target = shared;
5058 }
5059 }
5060 }
5061
5062 // Keep track of the last function in the script.
5063 if (last.is_null() ||
5064 shared->end_position() > last->start_position()) {
5065 last = shared;
5066 }
5067 }
5068 }
5069 }
5070
5071 // Make sure some candidate is selected.
5072 if (target.is_null()) {
5073 if (!last.is_null()) {
5074 // Position after the last function - use last.
5075 target = last;
5076 } else {
5077 // Unable to find function - possibly script without any function.
5078 return Heap::undefined_value();
5079 }
5080 }
5081
5082 // If the candidate found is compiled we are done. NOTE: when lazy
5083 // compilation of inner functions is introduced some additional checking
5084 // needs to be done here to compile inner functions.
5085 done = target->is_compiled();
5086 if (!done) {
5087 // If the candidate is not compiled compile it to reveal any inner
5088 // functions which might contain the requested source position.
5089 CompileLazyShared(target, KEEP_EXCEPTION);
5090 }
5091 }
5092
5093 return *target;
5094}
5095
5096
5097// Change the state of a break point in a script. NOTE: Regarding performance
5098// see the NOTE for GetScriptFromScriptData.
5099// args[0]: script to set break point in
5100// args[1]: number: break source position (within the script source)
5101// args[2]: number: break point object
5102static Object* Runtime_SetScriptBreakPoint(Arguments args) {
5103 HandleScope scope;
5104 ASSERT(args.length() == 3);
5105 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
5106 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
5107 RUNTIME_ASSERT(source_position >= 0);
5108 Handle<Object> break_point_object_arg = args.at<Object>(2);
5109
5110 // Get the script from the script wrapper.
5111 RUNTIME_ASSERT(wrapper->value()->IsScript());
5112 Handle<Script> script(Script::cast(wrapper->value()));
5113
5114 Object* result = FindSharedFunctionInfoInScript(script, source_position);
5115 if (!result->IsUndefined()) {
5116 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
5117 // Find position within function. The script position might be before the
5118 // source position of the first function.
5119 int position;
5120 if (shared->start_position() > source_position) {
5121 position = 0;
5122 } else {
5123 position = source_position - shared->start_position();
5124 }
5125 Debug::SetBreakPoint(shared, position, break_point_object_arg);
5126 }
5127 return Heap::undefined_value();
5128}
5129
5130
5131// Clear a break point
5132// args[0]: number: break point object
5133static Object* Runtime_ClearBreakPoint(Arguments args) {
5134 HandleScope scope;
5135 ASSERT(args.length() == 1);
5136 Handle<Object> break_point_object_arg = args.at<Object>(0);
5137
5138 // Clear break point.
5139 Debug::ClearBreakPoint(break_point_object_arg);
5140
5141 return Heap::undefined_value();
5142}
5143
5144
5145// Change the state of break on exceptions
5146// args[0]: boolean indicating uncaught exceptions
5147// args[1]: boolean indicating on/off
5148static Object* Runtime_ChangeBreakOnException(Arguments args) {
5149 HandleScope scope;
5150 ASSERT(args.length() == 2);
5151 ASSERT(args[0]->IsNumber());
5152 ASSERT(args[1]->IsBoolean());
5153
5154 // Update break point state
5155 ExceptionBreakType type =
5156 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
5157 bool enable = args[1]->ToBoolean()->IsTrue();
5158 Debug::ChangeBreakOnException(type, enable);
5159 return Heap::undefined_value();
5160}
5161
5162
5163// Prepare for stepping
5164// args[0]: break id for checking execution state
5165// args[1]: step action from the enumeration StepAction
5166// args[2]: number of times to perform the step
5167static Object* Runtime_PrepareStep(Arguments args) {
5168 HandleScope scope;
5169 ASSERT(args.length() == 3);
5170 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005171 Object* check = Runtime_CheckExecutionState(args);
5172 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005173 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
5174 return Top::Throw(Heap::illegal_argument_symbol());
5175 }
5176
5177 // Get the step action and check validity.
5178 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
5179 if (step_action != StepIn &&
5180 step_action != StepNext &&
5181 step_action != StepOut &&
5182 step_action != StepInMin &&
5183 step_action != StepMin) {
5184 return Top::Throw(Heap::illegal_argument_symbol());
5185 }
5186
5187 // Get the number of steps.
5188 int step_count = NumberToInt32(args[2]);
5189 if (step_count < 1) {
5190 return Top::Throw(Heap::illegal_argument_symbol());
5191 }
5192
5193 // Prepare step.
5194 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
5195 return Heap::undefined_value();
5196}
5197
5198
5199// Clear all stepping set by PrepareStep.
5200static Object* Runtime_ClearStepping(Arguments args) {
5201 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00005202 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005203 Debug::ClearStepping();
5204 return Heap::undefined_value();
5205}
5206
5207
5208// Creates a copy of the with context chain. The copy of the context chain is
5209// is linked to the function context supplied.
5210static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
5211 Handle<Context> function_context) {
5212 // At the bottom of the chain. Return the function context to link to.
5213 if (context_chain->is_function_context()) {
5214 return function_context;
5215 }
5216
5217 // Recursively copy the with contexts.
5218 Handle<Context> previous(context_chain->previous());
5219 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
5220 return Factory::NewWithContext(
5221 CopyWithContextChain(function_context, previous), extension);
5222}
5223
5224
5225// Helper function to find or create the arguments object for
5226// Runtime_DebugEvaluate.
5227static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
5228 Handle<JSFunction> function,
5229 Handle<Code> code,
5230 const ScopeInfo<>* sinfo,
5231 Handle<Context> function_context) {
5232 // Try to find the value of 'arguments' to pass as parameter. If it is not
5233 // found (that is the debugged function does not reference 'arguments' and
5234 // does not support eval) then create an 'arguments' object.
5235 int index;
5236 if (sinfo->number_of_stack_slots() > 0) {
5237 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
5238 if (index != -1) {
5239 return Handle<Object>(frame->GetExpression(index));
5240 }
5241 }
5242
5243 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
5244 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
5245 NULL);
5246 if (index != -1) {
5247 return Handle<Object>(function_context->get(index));
5248 }
5249 }
5250
5251 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005252 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
5253 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005254 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005255 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005256 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005257 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005258 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005259 return arguments;
5260}
5261
5262
5263// Evaluate a piece of JavaScript in the context of a stack frame for
5264// debugging. This is acomplished by creating a new context which in its
5265// extension part has all the parameters and locals of the function on the
5266// stack frame. A function which calls eval with the code to evaluate is then
5267// compiled in this context and called in this context. As this context
5268// replaces the context of the function on the stack frame a new (empty)
5269// function is created as well to be used as the closure for the context.
5270// This function and the context acts as replacements for the function on the
5271// stack frame presenting the same view of the values of parameters and
5272// local variables as if the piece of JavaScript was evaluated at the point
5273// where the function on the stack frame is currently stopped.
5274static Object* Runtime_DebugEvaluate(Arguments args) {
5275 HandleScope scope;
5276
5277 // Check the execution state and decode arguments frame and source to be
5278 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005279 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005280 Object* check_result = Runtime_CheckExecutionState(args);
5281 if (check_result->IsFailure()) return check_result;
5282 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
5283 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005284 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
5285
5286 // Handle the processing of break.
5287 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005288
5289 // Get the frame where the debugging is performed.
5290 StackFrame::Id id = UnwrapFrameId(wrapped_id);
5291 JavaScriptFrameIterator it(id);
5292 JavaScriptFrame* frame = it.frame();
5293 Handle<JSFunction> function(JSFunction::cast(frame->function()));
5294 Handle<Code> code(function->code());
5295 ScopeInfo<> sinfo(*code);
5296
5297 // Traverse the saved contexts chain to find the active context for the
5298 // selected frame.
5299 SaveContext* save = Top::save_context();
5300 while (save != NULL && reinterpret_cast<Address>(save) < frame->sp()) {
5301 save = save->prev();
5302 }
5303 ASSERT(save != NULL);
5304 SaveContext savex;
5305 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005306
5307 // Create the (empty) function replacing the function on the stack frame for
5308 // the purpose of evaluating in the context created below. It is important
5309 // that this function does not describe any parameters and local variables
5310 // in the context. If it does then this will cause problems with the lookup
5311 // in Context::Lookup, where context slots for parameters and local variables
5312 // are looked at before the extension object.
5313 Handle<JSFunction> go_between =
5314 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
5315 go_between->set_context(function->context());
5316#ifdef DEBUG
5317 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
5318 ASSERT(go_between_sinfo.number_of_parameters() == 0);
5319 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
5320#endif
5321
5322 // Allocate and initialize a context extension object with all the
5323 // arguments, stack locals heap locals and extension properties of the
5324 // debugged function.
5325 Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function());
5326 // First fill all parameters to the context extension.
5327 for (int i = 0; i < sinfo.number_of_parameters(); ++i) {
5328 SetProperty(context_ext,
5329 sinfo.parameter_name(i),
5330 Handle<Object>(frame->GetParameter(i)), NONE);
5331 }
5332 // Second fill all stack locals to the context extension.
5333 for (int i = 0; i < sinfo.number_of_stack_slots(); i++) {
5334 SetProperty(context_ext,
5335 sinfo.stack_slot_name(i),
5336 Handle<Object>(frame->GetExpression(i)), NONE);
5337 }
5338 // Third fill all context locals to the context extension.
5339 Handle<Context> frame_context(Context::cast(frame->context()));
5340 Handle<Context> function_context(frame_context->fcontext());
5341 for (int i = Context::MIN_CONTEXT_SLOTS;
5342 i < sinfo.number_of_context_slots();
5343 ++i) {
5344 int context_index =
5345 ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL);
5346 SetProperty(context_ext,
5347 sinfo.context_slot_name(i),
5348 Handle<Object>(function_context->get(context_index)), NONE);
5349 }
5350 // Finally copy any properties from the function context extension. This will
5351 // be variables introduced by eval.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005352 if (function_context->has_extension() &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005353 !function_context->IsGlobalContext()) {
5354 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
5355 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
5356 for (int i = 0; i < keys->length(); i++) {
5357 // Names of variables introduced by eval are strings.
5358 ASSERT(keys->get(i)->IsString());
5359 Handle<String> key(String::cast(keys->get(i)));
5360 SetProperty(context_ext, key, GetProperty(ext, key), NONE);
5361 }
5362 }
5363
5364 // Allocate a new context for the debug evaluation and set the extension
5365 // object build.
5366 Handle<Context> context =
5367 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
5368 context->set_extension(*context_ext);
5369 // Copy any with contexts present and chain them in front of this context.
5370 context = CopyWithContextChain(frame_context, context);
5371
5372 // Wrap the evaluation statement in a new function compiled in the newly
5373 // created context. The function has one parameter which has to be called
5374 // 'arguments'. This it to have access to what would have been 'arguments' in
5375 // the function beeing debugged.
5376 // function(arguments,__source__) {return eval(__source__);}
5377 static const char* source_str =
5378 "function(arguments,__source__){return eval(__source__);}";
5379 static const int source_str_length = strlen(source_str);
5380 Handle<String> function_source =
5381 Factory::NewStringFromAscii(Vector<const char>(source_str,
5382 source_str_length));
5383 Handle<JSFunction> boilerplate =
ager@chromium.org236ad962008-09-25 09:45:57 +00005384 Compiler::CompileEval(function_source, 0, context->IsGlobalContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005385 if (boilerplate.is_null()) return Failure::Exception();
5386 Handle<JSFunction> compiled_function =
5387 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5388
5389 // Invoke the result of the compilation to get the evaluation function.
5390 bool has_pending_exception;
5391 Handle<Object> receiver(frame->receiver());
5392 Handle<Object> evaluation_function =
5393 Execution::Call(compiled_function, receiver, 0, NULL,
5394 &has_pending_exception);
5395
5396 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
5397 function_context);
5398
5399 // Invoke the evaluation function and return the result.
5400 const int argc = 2;
5401 Object** argv[argc] = { arguments.location(),
5402 Handle<Object>::cast(source).location() };
5403 Handle<Object> result =
5404 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
5405 argc, argv, &has_pending_exception);
5406 return *result;
5407}
5408
5409
5410static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
5411 HandleScope scope;
5412
5413 // Check the execution state and decode arguments frame and source to be
5414 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005415 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005416 Object* check_result = Runtime_CheckExecutionState(args);
5417 if (check_result->IsFailure()) return check_result;
5418 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005419 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
5420
5421 // Handle the processing of break.
5422 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005423
5424 // Enter the top context from before the debugger was invoked.
5425 SaveContext save;
5426 SaveContext* top = &save;
5427 while (top != NULL && *top->context() == *Debug::debug_context()) {
5428 top = top->prev();
5429 }
5430 if (top != NULL) {
5431 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005432 }
5433
5434 // Get the global context now set to the top context from before the
5435 // debugger was invoked.
5436 Handle<Context> context = Top::global_context();
5437
5438 // Compile the source to be evaluated.
ager@chromium.org236ad962008-09-25 09:45:57 +00005439 Handle<JSFunction> boilerplate(Compiler::CompileEval(source, 0, true));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005440 if (boilerplate.is_null()) return Failure::Exception();
5441 Handle<JSFunction> compiled_function =
5442 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
5443 context));
5444
5445 // Invoke the result of the compilation to get the evaluation function.
5446 bool has_pending_exception;
5447 Handle<Object> receiver = Top::global();
5448 Handle<Object> result =
5449 Execution::Call(compiled_function, receiver, 0, NULL,
5450 &has_pending_exception);
5451 return *result;
5452}
5453
5454
5455// Helper function used by Runtime_DebugGetLoadedScripts below.
5456static int DebugGetLoadedScripts(FixedArray* instances, int instances_size) {
5457 NoHandleAllocation ha;
5458 AssertNoAllocation no_alloc;
5459
5460 // Get hold of the current empty script.
5461 Context* context = Top::context()->global_context();
5462 Script* empty = context->empty_script();
5463
5464 // Scan heap for Script objects.
5465 int count = 0;
5466 HeapIterator iterator;
5467 while (iterator.has_next()) {
5468 HeapObject* obj = iterator.next();
5469 ASSERT(obj != NULL);
5470 if (obj->IsScript() && obj != empty) {
5471 if (instances != NULL && count < instances_size) {
5472 instances->set(count, obj);
5473 }
5474 count++;
5475 }
5476 }
5477
5478 return count;
5479}
5480
5481
5482static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
5483 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00005484 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005485
5486 // Perform two GCs to get rid of all unreferenced scripts. The first GC gets
5487 // rid of all the cached script wrappes and the second gets rid of the
5488 // scripts which is no longer referenced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005489 Heap::CollectAllGarbage();
5490 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005491
5492 // Get the number of scripts.
5493 int count;
5494 count = DebugGetLoadedScripts(NULL, 0);
5495
5496 // Allocate an array to hold the result.
5497 Handle<FixedArray> instances = Factory::NewFixedArray(count);
5498
5499 // Fill the script objects.
5500 count = DebugGetLoadedScripts(*instances, count);
5501
5502 // Convert the script objects to proper JS objects.
5503 for (int i = 0; i < count; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005504 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
5505 // Get the script wrapper in a local handle before calling GetScriptWrapper,
5506 // because using
5507 // instances->set(i, *GetScriptWrapper(script))
5508 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
5509 // already have deferenced the instances handle.
5510 Handle<JSValue> wrapper = GetScriptWrapper(script);
5511 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005512 }
5513
5514 // Return result as a JS array.
5515 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
5516 Handle<JSArray>::cast(result)->SetContent(*instances);
5517 return *result;
5518}
5519
5520
5521// Helper function used by Runtime_DebugReferencedBy below.
5522static int DebugReferencedBy(JSObject* target,
5523 Object* instance_filter, int max_references,
5524 FixedArray* instances, int instances_size,
5525 JSFunction* context_extension_function,
5526 JSFunction* arguments_function) {
5527 NoHandleAllocation ha;
5528 AssertNoAllocation no_alloc;
5529
5530 // Iterate the heap.
5531 int count = 0;
5532 JSObject* last = NULL;
5533 HeapIterator iterator;
5534 while (iterator.has_next() &&
5535 (max_references == 0 || count < max_references)) {
5536 // Only look at all JSObjects.
5537 HeapObject* heap_obj = iterator.next();
5538 if (heap_obj->IsJSObject()) {
5539 // Skip context extension objects and argument arrays as these are
5540 // checked in the context of functions using them.
5541 JSObject* obj = JSObject::cast(heap_obj);
5542 if (obj->map()->constructor() == context_extension_function ||
5543 obj->map()->constructor() == arguments_function) {
5544 continue;
5545 }
5546
5547 // Check if the JS object has a reference to the object looked for.
5548 if (obj->ReferencesObject(target)) {
5549 // Check instance filter if supplied. This is normally used to avoid
5550 // references from mirror objects (see Runtime_IsInPrototypeChain).
5551 if (!instance_filter->IsUndefined()) {
5552 Object* V = obj;
5553 while (true) {
5554 Object* prototype = V->GetPrototype();
5555 if (prototype->IsNull()) {
5556 break;
5557 }
5558 if (instance_filter == prototype) {
5559 obj = NULL; // Don't add this object.
5560 break;
5561 }
5562 V = prototype;
5563 }
5564 }
5565
5566 if (obj != NULL) {
5567 // Valid reference found add to instance array if supplied an update
5568 // count.
5569 if (instances != NULL && count < instances_size) {
5570 instances->set(count, obj);
5571 }
5572 last = obj;
5573 count++;
5574 }
5575 }
5576 }
5577 }
5578
5579 // Check for circular reference only. This can happen when the object is only
5580 // referenced from mirrors and has a circular reference in which case the
5581 // object is not really alive and would have been garbage collected if not
5582 // referenced from the mirror.
5583 if (count == 1 && last == target) {
5584 count = 0;
5585 }
5586
5587 // Return the number of referencing objects found.
5588 return count;
5589}
5590
5591
5592// Scan the heap for objects with direct references to an object
5593// args[0]: the object to find references to
5594// args[1]: constructor function for instances to exclude (Mirror)
5595// args[2]: the the maximum number of objects to return
5596static Object* Runtime_DebugReferencedBy(Arguments args) {
5597 ASSERT(args.length() == 3);
5598
5599 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005600 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005601
5602 // Check parameters.
5603 CONVERT_CHECKED(JSObject, target, args[0]);
5604 Object* instance_filter = args[1];
5605 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
5606 instance_filter->IsJSObject());
5607 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
5608 RUNTIME_ASSERT(max_references >= 0);
5609
5610 // Get the constructor function for context extension and arguments array.
5611 JSFunction* context_extension_function =
5612 Top::context()->global_context()->context_extension_function();
5613 JSObject* arguments_boilerplate =
5614 Top::context()->global_context()->arguments_boilerplate();
5615 JSFunction* arguments_function =
5616 JSFunction::cast(arguments_boilerplate->map()->constructor());
5617
5618 // Get the number of referencing objects.
5619 int count;
5620 count = DebugReferencedBy(target, instance_filter, max_references,
5621 NULL, 0,
5622 context_extension_function, arguments_function);
5623
5624 // Allocate an array to hold the result.
5625 Object* object = Heap::AllocateFixedArray(count);
5626 if (object->IsFailure()) return object;
5627 FixedArray* instances = FixedArray::cast(object);
5628
5629 // Fill the referencing objects.
5630 count = DebugReferencedBy(target, instance_filter, max_references,
5631 instances, count,
5632 context_extension_function, arguments_function);
5633
5634 // Return result as JS array.
5635 Object* result =
5636 Heap::AllocateJSObject(
5637 Top::context()->global_context()->array_function());
5638 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
5639 return result;
5640}
5641
5642
5643// Helper function used by Runtime_DebugConstructedBy below.
5644static int DebugConstructedBy(JSFunction* constructor, int max_references,
5645 FixedArray* instances, int instances_size) {
5646 AssertNoAllocation no_alloc;
5647
5648 // Iterate the heap.
5649 int count = 0;
5650 HeapIterator iterator;
5651 while (iterator.has_next() &&
5652 (max_references == 0 || count < max_references)) {
5653 // Only look at all JSObjects.
5654 HeapObject* heap_obj = iterator.next();
5655 if (heap_obj->IsJSObject()) {
5656 JSObject* obj = JSObject::cast(heap_obj);
5657 if (obj->map()->constructor() == constructor) {
5658 // Valid reference found add to instance array if supplied an update
5659 // count.
5660 if (instances != NULL && count < instances_size) {
5661 instances->set(count, obj);
5662 }
5663 count++;
5664 }
5665 }
5666 }
5667
5668 // Return the number of referencing objects found.
5669 return count;
5670}
5671
5672
5673// Scan the heap for objects constructed by a specific function.
5674// args[0]: the constructor to find instances of
5675// args[1]: the the maximum number of objects to return
5676static Object* Runtime_DebugConstructedBy(Arguments args) {
5677 ASSERT(args.length() == 2);
5678
5679 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005680 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005681
5682 // Check parameters.
5683 CONVERT_CHECKED(JSFunction, constructor, args[0]);
5684 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
5685 RUNTIME_ASSERT(max_references >= 0);
5686
5687 // Get the number of referencing objects.
5688 int count;
5689 count = DebugConstructedBy(constructor, max_references, NULL, 0);
5690
5691 // Allocate an array to hold the result.
5692 Object* object = Heap::AllocateFixedArray(count);
5693 if (object->IsFailure()) return object;
5694 FixedArray* instances = FixedArray::cast(object);
5695
5696 // Fill the referencing objects.
5697 count = DebugConstructedBy(constructor, max_references, instances, count);
5698
5699 // Return result as JS array.
5700 Object* result =
5701 Heap::AllocateJSObject(
5702 Top::context()->global_context()->array_function());
5703 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
5704 return result;
5705}
5706
5707
5708static Object* Runtime_GetPrototype(Arguments args) {
5709 ASSERT(args.length() == 1);
5710
5711 CONVERT_CHECKED(JSObject, obj, args[0]);
5712
5713 return obj->GetPrototype();
5714}
5715
5716
5717static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005718 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005719 CPU::DebugBreak();
5720 return Heap::undefined_value();
5721}
5722
5723
5724// Finds the script object from the script data. NOTE: This operation uses
5725// heap traversal to find the function generated for the source position
5726// for the requested break point. For lazily compiled functions several heap
5727// traversals might be required rendering this operation as a rather slow
5728// operation. However for setting break points which is normally done through
5729// some kind of user interaction the performance is not crucial.
5730static Handle<Object> Runtime_GetScriptFromScriptName(
5731 Handle<String> script_name) {
5732 // Scan the heap for Script objects to find the script with the requested
5733 // script data.
5734 Handle<Script> script;
5735 HeapIterator iterator;
5736 while (script.is_null() && iterator.has_next()) {
5737 HeapObject* obj = iterator.next();
5738 // If a script is found check if it has the script data requested.
5739 if (obj->IsScript()) {
5740 if (Script::cast(obj)->name()->IsString()) {
5741 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
5742 script = Handle<Script>(Script::cast(obj));
5743 }
5744 }
5745 }
5746 }
5747
5748 // If no script with the requested script data is found return undefined.
5749 if (script.is_null()) return Factory::undefined_value();
5750
5751 // Return the script found.
5752 return GetScriptWrapper(script);
5753}
5754
5755
5756// Get the script object from script data. NOTE: Regarding performance
5757// see the NOTE for GetScriptFromScriptData.
5758// args[0]: script data for the script to find the source for
5759static Object* Runtime_GetScript(Arguments args) {
5760 HandleScope scope;
5761
5762 ASSERT(args.length() == 1);
5763
5764 CONVERT_CHECKED(String, script_name, args[0]);
5765
5766 // Find the requested script.
5767 Handle<Object> result =
5768 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
5769 return *result;
5770}
5771
5772
5773static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
5774#ifdef DEBUG
5775 HandleScope scope;
5776 ASSERT(args.length() == 1);
5777 // Get the function and make sure it is compiled.
5778 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5779 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
5780 return Failure::Exception();
5781 }
5782 func->code()->PrintLn();
5783#endif // DEBUG
5784 return Heap::undefined_value();
5785}
5786
5787
5788static Object* Runtime_Abort(Arguments args) {
5789 ASSERT(args.length() == 2);
5790 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
5791 Smi::cast(args[1])->value());
5792 Top::PrintStack();
5793 OS::Abort();
5794 UNREACHABLE();
5795 return NULL;
5796}
5797
5798
kasper.lund44510672008-07-25 07:37:58 +00005799#ifdef DEBUG
5800// ListNatives is ONLY used by the fuzz-natives.js in debug mode
5801// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005802static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005803 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005804 HandleScope scope;
5805 Handle<JSArray> result = Factory::NewJSArray(0);
5806 int index = 0;
5807#define ADD_ENTRY(Name, argc) \
5808 { \
5809 HandleScope inner; \
5810 Handle<String> name = \
5811 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
5812 Handle<JSArray> pair = Factory::NewJSArray(0); \
5813 SetElement(pair, 0, name); \
5814 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
5815 SetElement(result, index++, pair); \
5816 }
5817 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
5818#undef ADD_ENTRY
5819 return *result;
5820}
kasper.lund44510672008-07-25 07:37:58 +00005821#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005822
5823
5824static Object* Runtime_IS_VAR(Arguments args) {
5825 UNREACHABLE(); // implemented as macro in the parser
5826 return NULL;
5827}
5828
5829
5830// ----------------------------------------------------------------------------
5831// Implementation of Runtime
5832
5833#define F(name, nargs) \
5834 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
5835 static_cast<int>(Runtime::k##name) },
5836
5837static Runtime::Function Runtime_functions[] = {
5838 RUNTIME_FUNCTION_LIST(F)
5839 { NULL, NULL, NULL, 0, -1 }
5840};
5841
5842#undef F
5843
5844
5845Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
5846 ASSERT(0 <= fid && fid < kNofFunctions);
5847 return &Runtime_functions[fid];
5848}
5849
5850
5851Runtime::Function* Runtime::FunctionForName(const char* name) {
5852 for (Function* f = Runtime_functions; f->name != NULL; f++) {
5853 if (strcmp(f->name, name) == 0) {
5854 return f;
5855 }
5856 }
5857 return NULL;
5858}
5859
5860
5861void Runtime::PerformGC(Object* result) {
5862 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005863 if (failure->IsRetryAfterGC()) {
5864 // Try to do a garbage collection; ignore it if it fails. The C
5865 // entry stub will throw an out-of-memory exception in that case.
5866 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
5867 } else {
5868 // Handle last resort GC and make sure to allow future allocations
5869 // to grow the heap without causing GCs (if possible).
5870 Counters::gc_last_resort_from_js.Increment();
5871 Heap::CollectAllGarbage();
5872 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005873}
5874
5875
5876} } // namespace v8::internal