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