blob: 931c2a03e5a960384ce733727a599236d8422cc8 [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) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000291 HandleScope scope;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000292 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());
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000789 Handle<Object> result = RegExpImpl::Exec(regexp, subject, index);
790 if (result.is_null()) return Failure::Exception();
791 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000792}
793
794
795static Object* Runtime_RegExpExecGlobal(Arguments args) {
796 HandleScope scope;
797 ASSERT(args.length() == 2);
ager@chromium.org236ad962008-09-25 09:45:57 +0000798 CONVERT_CHECKED(JSRegExp, raw_regexp, args[0]);
799 Handle<JSRegExp> regexp(raw_regexp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000800 CONVERT_CHECKED(String, raw_subject, args[1]);
801 Handle<String> subject(raw_subject);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000802 Handle<Object> result = RegExpImpl::ExecGlobal(regexp, subject);
803 if (result.is_null()) return Failure::Exception();
804 return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000805}
806
807
808static Object* Runtime_MaterializeRegExpLiteral(Arguments args) {
809 HandleScope scope;
810 ASSERT(args.length() == 4);
811 CONVERT_ARG_CHECKED(FixedArray, literals, 0);
812 int index = Smi::cast(args[1])->value();
813 Handle<String> pattern = args.at<String>(2);
814 Handle<String> flags = args.at<String>(3);
815
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000816 // Get the RegExp function from the context in the literals array.
817 // This is the RegExp function from the context in which the
818 // function was created. We do not use the RegExp function from the
819 // current global context because this might be the RegExp function
820 // from another context which we should not have access to.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000821 Handle<JSFunction> constructor =
ager@chromium.org236ad962008-09-25 09:45:57 +0000822 Handle<JSFunction>(
823 JSFunction::GlobalContextFromLiterals(*literals)->regexp_function());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000824 // Compute the regular expression literal.
825 bool has_pending_exception;
826 Handle<Object> regexp =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000827 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags,
828 &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000829 if (has_pending_exception) {
830 ASSERT(Top::has_pending_exception());
831 return Failure::Exception();
832 }
833 literals->set(index, *regexp);
834 return *regexp;
835}
836
837
838static Object* Runtime_FunctionGetName(Arguments args) {
839 NoHandleAllocation ha;
840 ASSERT(args.length() == 1);
841
842 CONVERT_CHECKED(JSFunction, f, args[0]);
843 return f->shared()->name();
844}
845
846
ager@chromium.org236ad962008-09-25 09:45:57 +0000847static Object* Runtime_FunctionSetName(Arguments args) {
848 NoHandleAllocation ha;
849 ASSERT(args.length() == 2);
850
851 CONVERT_CHECKED(JSFunction, f, args[0]);
852 CONVERT_CHECKED(String, name, args[1]);
853 f->shared()->set_name(name);
854 return Heap::undefined_value();
855}
856
857
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000858static Object* Runtime_FunctionGetScript(Arguments args) {
859 HandleScope scope;
860 ASSERT(args.length() == 1);
861
862 CONVERT_CHECKED(JSFunction, fun, args[0]);
863 Handle<Object> script = Handle<Object>(fun->shared()->script());
864 if (!script->IsScript()) return Heap::undefined_value();
865
866 return *GetScriptWrapper(Handle<Script>::cast(script));
867}
868
869
870static Object* Runtime_FunctionGetSourceCode(Arguments args) {
871 NoHandleAllocation ha;
872 ASSERT(args.length() == 1);
873
874 CONVERT_CHECKED(JSFunction, f, args[0]);
875 return f->shared()->GetSourceCode();
876}
877
878
879static Object* Runtime_FunctionGetScriptSourcePosition(Arguments args) {
880 NoHandleAllocation ha;
881 ASSERT(args.length() == 1);
882
883 CONVERT_CHECKED(JSFunction, fun, args[0]);
884 int pos = fun->shared()->start_position();
885 return Smi::FromInt(pos);
886}
887
888
889static Object* Runtime_FunctionSetInstanceClassName(Arguments args) {
890 NoHandleAllocation ha;
891 ASSERT(args.length() == 2);
892
893 CONVERT_CHECKED(JSFunction, fun, args[0]);
894 CONVERT_CHECKED(String, name, args[1]);
895 fun->SetInstanceClassName(name);
896 return Heap::undefined_value();
897}
898
899
900static Object* Runtime_FunctionSetLength(Arguments args) {
901 NoHandleAllocation ha;
902 ASSERT(args.length() == 2);
903
904 CONVERT_CHECKED(JSFunction, fun, args[0]);
905 CONVERT_CHECKED(Smi, length, args[1]);
906 fun->shared()->set_length(length->value());
907 return length;
908}
909
910
911static Object* Runtime_FunctionSetPrototype(Arguments args) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000912 NoHandleAllocation ha;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000913 ASSERT(args.length() == 2);
914
915 CONVERT_CHECKED(JSFunction, fun, args[0]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000916 Object* obj = Accessors::FunctionSetPrototype(fun, args[1], NULL);
917 if (obj->IsFailure()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000918 return args[0]; // return TOS
919}
920
921
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000922static Object* Runtime_FunctionIsAPIFunction(Arguments args) {
923 NoHandleAllocation ha;
924 ASSERT(args.length() == 1);
925
926 CONVERT_CHECKED(JSFunction, f, args[0]);
927 // The function_data field of the shared function info is used exclusively by
928 // the API.
929 return !f->shared()->function_data()->IsUndefined() ? Heap::true_value()
930 : Heap::false_value();
931}
932
933
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000934static Object* Runtime_SetCode(Arguments args) {
935 HandleScope scope;
936 ASSERT(args.length() == 2);
937
938 CONVERT_CHECKED(JSFunction, raw_target, args[0]);
939 Handle<JSFunction> target(raw_target);
940 Handle<Object> code = args.at<Object>(1);
941
942 Handle<Context> context(target->context());
943
944 if (!code->IsNull()) {
945 RUNTIME_ASSERT(code->IsJSFunction());
946 Handle<JSFunction> fun = Handle<JSFunction>::cast(code);
947 SetExpectedNofProperties(target, fun->shared()->expected_nof_properties());
948 if (!fun->is_compiled() && !CompileLazy(fun, KEEP_EXCEPTION)) {
949 return Failure::Exception();
950 }
951 // Set the code, formal parameter count, and the length of the target
952 // function.
953 target->set_code(fun->code());
954 target->shared()->set_length(fun->shared()->length());
955 target->shared()->set_formal_parameter_count(
kasperl@chromium.org41044eb2008-10-06 08:24:46 +0000956 fun->shared()->formal_parameter_count());
ager@chromium.org7c537e22008-10-16 08:43:32 +0000957 // Set the source code of the target function to undefined.
958 // SetCode is only used for built-in constructors like String,
959 // Array, and Object, and some web code
960 // doesn't like seeing source code for constructors.
961 target->shared()->set_script(Heap::undefined_value());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000962 context = Handle<Context>(fun->context());
963
964 // Make sure we get a fresh copy of the literal vector to avoid
965 // cross context contamination.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000966 int number_of_literals = fun->NumberOfLiterals();
967 Handle<FixedArray> literals =
968 Factory::NewFixedArray(number_of_literals, TENURED);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000969 if (number_of_literals > 0) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000970 // Insert the object, regexp and array functions in the literals
971 // array prefix. These are the functions that will be used when
972 // creating object, regexp and array literals.
ager@chromium.org236ad962008-09-25 09:45:57 +0000973 literals->set(JSFunction::kLiteralGlobalContextIndex,
974 context->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000975 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000976 target->set_literals(*literals, SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000977 }
978
979 target->set_context(*context);
980 return *target;
981}
982
983
984static Object* CharCodeAt(String* subject, Object* index) {
985 uint32_t i = 0;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000986 if (!Array::IndexFromObject(index, &i)) return Heap::nan_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000987 // Flatten the string. If someone wants to get a char at an index
988 // in a cons string, it is likely that more indices will be
989 // accessed.
ager@chromium.orgc3e50d82008-11-05 11:53:10 +0000990 subject->TryFlatten(StringShape(subject));
ager@chromium.org870a0b62008-11-04 11:43:05 +0000991 StringShape shape(subject);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +0000992 if (i >= static_cast<uint32_t>(subject->length(shape))) {
ager@chromium.org870a0b62008-11-04 11:43:05 +0000993 return Heap::nan_value();
994 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +0000995 return Smi::FromInt(subject->Get(shape, i));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000996}
997
998
999static Object* Runtime_StringCharCodeAt(Arguments args) {
1000 NoHandleAllocation ha;
1001 ASSERT(args.length() == 2);
1002
1003 CONVERT_CHECKED(String, subject, args[0]);
1004 Object* index = args[1];
1005 return CharCodeAt(subject, index);
1006}
1007
1008
1009static Object* Runtime_CharFromCode(Arguments args) {
1010 NoHandleAllocation ha;
1011 ASSERT(args.length() == 1);
1012 uint32_t code;
1013 if (Array::IndexFromObject(args[0], &code)) {
1014 if (code <= 0xffff) {
1015 return Heap::LookupSingleCharacterStringFromCode(code);
1016 }
1017 }
1018 return Heap::empty_string();
1019}
1020
1021
ager@chromium.org7c537e22008-10-16 08:43:32 +00001022// Cap on the maximal shift in the Boyer-Moore implementation. By setting a
1023// limit, we can fix the size of tables.
1024static const int kBMMaxShift = 0xff;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001025// Reduce alphabet to this size.
1026static const int kBMAlphabetSize = 0x100;
1027// For patterns below this length, the skip length of Boyer-Moore is too short
1028// to compensate for the algorithmic overhead compared to simple brute force.
1029static const int kBMMinPatternLength = 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001030
ager@chromium.org7c537e22008-10-16 08:43:32 +00001031// Holds the two buffers used by Boyer-Moore string search's Good Suffix
1032// shift. Only allows the last kBMMaxShift characters of the needle
1033// to be indexed.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001034class BMGoodSuffixBuffers {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001035 public:
1036 BMGoodSuffixBuffers() {}
1037 inline void init(int needle_length) {
1038 ASSERT(needle_length > 1);
1039 int start = needle_length < kBMMaxShift ? 0 : needle_length - kBMMaxShift;
1040 int len = needle_length - start;
1041 biased_suffixes_ = suffixes_ - start;
1042 biased_good_suffix_shift_ = good_suffix_shift_ - start;
1043 for (int i = 0; i <= len; i++) {
1044 good_suffix_shift_[i] = len;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001045 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001046 }
1047 inline int& suffix(int index) {
1048 ASSERT(biased_suffixes_ + index >= suffixes_);
1049 return biased_suffixes_[index];
1050 }
1051 inline int& shift(int index) {
1052 ASSERT(biased_good_suffix_shift_ + index >= good_suffix_shift_);
1053 return biased_good_suffix_shift_[index];
1054 }
1055 private:
1056 int suffixes_[kBMMaxShift + 1];
1057 int good_suffix_shift_[kBMMaxShift + 1];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001058 int* biased_suffixes_;
1059 int* biased_good_suffix_shift_;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001060 DISALLOW_COPY_AND_ASSIGN(BMGoodSuffixBuffers);
1061};
1062
1063// buffers reused by BoyerMoore
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001064static int bad_char_occurrence[kBMAlphabetSize];
ager@chromium.org7c537e22008-10-16 08:43:32 +00001065static BMGoodSuffixBuffers bmgs_buffers;
1066
1067// Compute the bad-char table for Boyer-Moore in the static buffer.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001068template <typename pchar>
1069static void BoyerMoorePopulateBadCharTable(Vector<const pchar> pattern,
1070 int start) {
1071 // Run forwards to populate bad_char_table, so that *last* instance
1072 // of character equivalence class is the one registered.
1073 // Notice: Doesn't include the last character.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001074 int table_size = (sizeof(pchar) == 1) ? String::kMaxAsciiCharCode + 1
1075 : kBMAlphabetSize;
1076 if (start == 0) { // All patterns less than kBMMaxShift in length.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001077 memset(bad_char_occurrence, -1, table_size * sizeof(*bad_char_occurrence));
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001078 } else {
1079 for (int i = 0; i < table_size; i++) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001080 bad_char_occurrence[i] = start - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001081 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001082 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001083 for (int i = start; i < pattern.length() - 1; i++) {
1084 pchar c = pattern[i];
1085 int bucket = (sizeof(pchar) ==1) ? c : c % kBMAlphabetSize;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001086 bad_char_occurrence[bucket] = i;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001087 }
1088}
1089
1090template <typename pchar>
1091static void BoyerMoorePopulateGoodSuffixTable(Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001092 int start) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001093 int m = pattern.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001094 int len = m - start;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001095 // Compute Good Suffix tables.
1096 bmgs_buffers.init(m);
1097
1098 bmgs_buffers.shift(m-1) = 1;
1099 bmgs_buffers.suffix(m) = m + 1;
1100 pchar last_char = pattern[m - 1];
1101 int suffix = m + 1;
1102 for (int i = m; i > start;) {
1103 for (pchar c = pattern[i - 1]; suffix <= m && c != pattern[suffix - 1];) {
1104 if (bmgs_buffers.shift(suffix) == len) {
1105 bmgs_buffers.shift(suffix) = suffix - i;
1106 }
1107 suffix = bmgs_buffers.suffix(suffix);
1108 }
1109 i--;
1110 suffix--;
1111 bmgs_buffers.suffix(i) = suffix;
1112 if (suffix == m) {
1113 // No suffix to extend, so we check against last_char only.
1114 while (i > start && pattern[i - 1] != last_char) {
1115 if (bmgs_buffers.shift(m) == len) {
1116 bmgs_buffers.shift(m) = m - i;
1117 }
1118 i--;
1119 bmgs_buffers.suffix(i) = m;
1120 }
1121 if (i > start) {
1122 i--;
1123 suffix--;
1124 bmgs_buffers.suffix(i) = suffix;
1125 }
1126 }
1127 }
1128 if (suffix < m) {
1129 for (int i = start; i <= m; i++) {
1130 if (bmgs_buffers.shift(i) == len) {
1131 bmgs_buffers.shift(i) = suffix - start;
1132 }
1133 if (i == suffix) {
1134 suffix = bmgs_buffers.suffix(suffix);
1135 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001136 }
1137 }
1138}
1139
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001140template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001141static inline int CharOccurrence(int char_code) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001142 if (sizeof(schar) == 1) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001143 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001144 }
1145 if (sizeof(pchar) == 1) {
1146 if (char_code > String::kMaxAsciiCharCode) {
1147 return -1;
1148 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001149 return bad_char_occurrence[char_code];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001150 }
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001151 return bad_char_occurrence[char_code % kBMAlphabetSize];
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001152}
1153
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001154// Restricted simplified Boyer-Moore string matching.
1155// Uses only the bad-shift table of Boyer-Moore and only uses it
1156// for the character compared to the last character of the needle.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001157template <typename schar, typename pchar>
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001158static int BoyerMooreHorsepool(Vector<const schar> subject,
1159 Vector<const pchar> pattern,
1160 int start_index,
1161 bool* complete) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001162 int n = subject.length();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001163 int m = pattern.length();
ager@chromium.org7c537e22008-10-16 08:43:32 +00001164 // Only preprocess at most kBMMaxShift last characters of pattern.
1165 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001166
ager@chromium.org7c537e22008-10-16 08:43:32 +00001167 BoyerMoorePopulateBadCharTable(pattern, start);
1168
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001169 int badness = -m; // How bad we are doing without a good-suffix table.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001170 int idx; // No matches found prior to this index.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001171 pchar last_char = pattern[m - 1];
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001172 int last_char_shift = m - 1 - CharOccurrence<schar, pchar>(last_char);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001173 // Perform search
1174 for (idx = start_index; idx <= n - m;) {
1175 int j = m - 1;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001176 int c;
1177 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001178 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001179 int shift = j - bc_occ;
1180 idx += shift;
1181 badness += 1 - shift; // at most zero, so badness cannot increase.
1182 if (idx > n - m) {
1183 *complete = true;
1184 return -1;
1185 }
1186 }
1187 j--;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001188 while (j >= 0 && pattern[j] == (subject[idx + j])) j--;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001189 if (j < 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001190 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001191 return idx;
1192 } else {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001193 idx += last_char_shift;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001194 // Badness increases by the number of characters we have
1195 // checked, and decreases by the number of characters we
1196 // can skip by shifting. It's a measure of how we are doing
1197 // compared to reading each character exactly once.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001198 badness += (m - j) - last_char_shift;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001199 if (badness > 0) {
1200 *complete = false;
1201 return idx;
1202 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001203 }
1204 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001205 *complete = true;
1206 return -1;
1207}
ager@chromium.org7c537e22008-10-16 08:43:32 +00001208
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001209
1210template <typename schar, typename pchar>
1211static int BoyerMooreIndexOf(Vector<const schar> subject,
1212 Vector<const pchar> pattern,
1213 int idx) {
1214 int n = subject.length();
1215 int m = pattern.length();
1216 // Only preprocess at most kBMMaxShift last characters of pattern.
1217 int start = m < kBMMaxShift ? 0 : m - kBMMaxShift;
1218
1219 // Build the Good Suffix table and continue searching.
1220 BoyerMoorePopulateGoodSuffixTable(pattern, start);
1221 pchar last_char = pattern[m - 1];
1222 // Continue search from i.
1223 do {
1224 int j = m - 1;
1225 schar c;
1226 while (last_char != (c = subject[idx + j])) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001227 int shift = j - CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001228 idx += shift;
1229 if (idx > n - m) {
1230 return -1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001231 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001232 }
1233 while (j >= 0 && pattern[j] == (c = subject[idx + j])) j--;
1234 if (j < 0) {
1235 return idx;
1236 } else if (j < start) {
1237 // we have matched more than our tables allow us to be smart about.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001238 // Fall back on BMH shift.
1239 idx += m - 1 - CharOccurrence<schar, pchar>(last_char);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001240 } else {
1241 int gs_shift = bmgs_buffers.shift(j + 1); // Good suffix shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001242 int bc_occ = CharOccurrence<schar, pchar>(c);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001243 int shift = j - bc_occ; // Bad-char shift.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001244 if (gs_shift > shift) {
1245 shift = gs_shift;
1246 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001247 idx += shift;
1248 }
1249 } while (idx <= n - m);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001250
1251 return -1;
1252}
1253
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001254
1255template <typename schar>
ager@chromium.org7c537e22008-10-16 08:43:32 +00001256static int SingleCharIndexOf(Vector<const schar> string,
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001257 schar pattern_char,
ager@chromium.org7c537e22008-10-16 08:43:32 +00001258 int start_index) {
1259 for (int i = start_index, n = string.length(); i < n; i++) {
1260 if (pattern_char == string[i]) {
1261 return i;
1262 }
1263 }
1264 return -1;
1265}
1266
1267// Trivial string search for shorter strings.
1268// On return, if "complete" is set to true, the return value is the
1269// final result of searching for the patter in the subject.
1270// If "complete" is set to false, the return value is the index where
1271// further checking should start, i.e., it's guaranteed that the pattern
1272// does not occur at a position prior to the returned index.
1273template <typename pchar, typename schar>
1274static int SimpleIndexOf(Vector<const schar> subject,
1275 Vector<const pchar> pattern,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001276 int idx,
1277 bool* complete) {
1278 // Badness is a count of how much work we have done. When we have
1279 // done enough work we decide it's probably worth switching to a better
1280 // algorithm.
1281 int badness = -10 - (pattern.length() << 2);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001282 // We know our pattern is at least 2 characters, we cache the first so
1283 // the common case of the first character not matching is faster.
1284 pchar pattern_first_char = pattern[0];
1285
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001286 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
1287 badness++;
1288 if (badness > 0) {
1289 *complete = false;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001290 return i;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001291 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001292 if (subject[i] != pattern_first_char) continue;
1293 int j = 1;
1294 do {
1295 if (pattern[j] != subject[i+j]) {
1296 break;
1297 }
1298 j++;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001299 } while (j < pattern.length());
1300 if (j == pattern.length()) {
1301 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001302 return i;
1303 }
1304 badness += j;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001305 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001306 *complete = true;
ager@chromium.org7c537e22008-10-16 08:43:32 +00001307 return -1;
1308}
1309
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001310// Simple indexOf that never bails out. For short patterns only.
1311template <typename pchar, typename schar>
1312static int SimpleIndexOf(Vector<const schar> subject,
1313 Vector<const pchar> pattern,
1314 int idx) {
1315 pchar pattern_first_char = pattern[0];
1316 for (int i = idx, n = subject.length() - pattern.length(); i <= n; i++) {
1317 if (subject[i] != pattern_first_char) continue;
1318 int j = 1;
1319 do {
1320 if (pattern[j] != subject[i+j]) {
1321 break;
1322 }
1323 j++;
1324 } while (j < pattern.length());
1325 if (j == pattern.length()) {
1326 return i;
1327 }
1328 }
1329 return -1;
1330}
1331
1332
1333// Dispatch to different algorithms.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001334template <typename schar, typename pchar>
1335static int StringMatchStrategy(Vector<const schar> sub,
1336 Vector<const pchar> pat,
1337 int start_index) {
1338 ASSERT(pat.length() > 1);
1339
1340 // We have an ASCII haystack and a non-ASCII needle. Check if there
1341 // really is a non-ASCII character in the needle and bail out if there
1342 // is.
1343 if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
1344 for (int i = 0; i < pat.length(); i++) {
1345 uc16 c = pat[i];
1346 if (c > String::kMaxAsciiCharCode) {
1347 return -1;
1348 }
1349 }
1350 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001351 if (pat.length() < kBMMinPatternLength) {
1352 // We don't believe fancy searching can ever be more efficient.
1353 // The max shift of Boyer-Moore on a pattern of this length does
1354 // not compensate for the overhead.
1355 return SimpleIndexOf(sub, pat, start_index);
1356 }
1357 // Try algorithms in order of increasing setup cost and expected performance.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001358 bool complete;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001359 int idx = SimpleIndexOf(sub, pat, start_index, &complete);
1360 if (complete) return idx;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001361 idx = BoyerMooreHorsepool(sub, pat, idx, &complete);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001362 if (complete) return idx;
1363 return BoyerMooreIndexOf(sub, pat, idx);
1364}
1365
1366// Perform string match of pattern on subject, starting at start index.
1367// Caller must ensure that 0 <= start_index <= sub->length(),
1368// and should check that pat->length() + start_index <= sub->length()
1369int Runtime::StringMatch(Handle<String> sub,
1370 Handle<String> pat,
1371 int start_index) {
1372 ASSERT(0 <= start_index);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001373 StringShape sub_shape(*sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001374 ASSERT(start_index <= sub->length(sub_shape));
ager@chromium.org7c537e22008-10-16 08:43:32 +00001375
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001376 int pattern_length = pat->length();
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001377 if (pattern_length == 0) return start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001378
ager@chromium.org870a0b62008-11-04 11:43:05 +00001379 int subject_length = sub->length(sub_shape);
ager@chromium.org7c537e22008-10-16 08:43:32 +00001380 if (start_index + pattern_length > subject_length) return -1;
1381
ager@chromium.org870a0b62008-11-04 11:43:05 +00001382 if (!sub->IsFlat(sub_shape)) {
1383 FlattenString(sub);
1384 sub_shape = StringShape(*sub);
1385 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001386 StringShape pat_shape(*pat);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001387 // Searching for one specific character is common. For one
ager@chromium.org7c537e22008-10-16 08:43:32 +00001388 // character patterns linear search is necessary, so any smart
1389 // algorithm is unnecessary overhead.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001390 if (pattern_length == 1) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001391 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
ager@chromium.org870a0b62008-11-04 11:43:05 +00001392 if (sub_shape.IsAsciiRepresentation()) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001393 uc16 pchar = pat->Get(pat_shape, 0);
1394 if (pchar > String::kMaxAsciiCharCode) {
1395 return -1;
1396 }
1397 Vector<const char> ascii_vector =
1398 sub->ToAsciiVector().SubVector(start_index, subject_length);
1399 const void* pos = memchr(ascii_vector.start(),
1400 static_cast<const char>(pchar),
1401 static_cast<size_t>(ascii_vector.length()));
1402 if (pos == NULL) {
1403 return -1;
1404 }
1405 return reinterpret_cast<const char*>(pos) - ascii_vector.start()
1406 + start_index;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001407 }
ager@chromium.org870a0b62008-11-04 11:43:05 +00001408 return SingleCharIndexOf(sub->ToUC16Vector(),
1409 pat->Get(pat_shape, 0),
1410 start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001411 }
1412
ager@chromium.org870a0b62008-11-04 11:43:05 +00001413 if (!pat->IsFlat(pat_shape)) {
1414 FlattenString(pat);
1415 pat_shape = StringShape(*pat);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001416 sub_shape = StringShape(*sub);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001417 }
ager@chromium.org236ad962008-09-25 09:45:57 +00001418
ager@chromium.org7c537e22008-10-16 08:43:32 +00001419 AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
1420 // dispatch on type of strings
ager@chromium.org870a0b62008-11-04 11:43:05 +00001421 if (pat_shape.IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001422 Vector<const char> pat_vector = pat->ToAsciiVector();
ager@chromium.org870a0b62008-11-04 11:43:05 +00001423 if (sub_shape.IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001424 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00001425 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001426 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
ager@chromium.org236ad962008-09-25 09:45:57 +00001427 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001428 Vector<const uc16> pat_vector = pat->ToUC16Vector();
ager@chromium.org870a0b62008-11-04 11:43:05 +00001429 if (sub_shape.IsAsciiRepresentation()) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001430 return StringMatchStrategy(sub->ToAsciiVector(), pat_vector, start_index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001431 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001432 return StringMatchStrategy(sub->ToUC16Vector(), pat_vector, start_index);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001433}
1434
1435
1436static Object* Runtime_StringIndexOf(Arguments args) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00001437 HandleScope scope; // create a new handle scope
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001438 ASSERT(args.length() == 3);
1439
ager@chromium.org7c537e22008-10-16 08:43:32 +00001440 CONVERT_ARG_CHECKED(String, sub, 0);
1441 CONVERT_ARG_CHECKED(String, pat, 1);
1442
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001443 Object* index = args[2];
1444 uint32_t start_index;
1445 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
1446
ager@chromium.org870a0b62008-11-04 11:43:05 +00001447 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length()));
ager@chromium.org7c537e22008-10-16 08:43:32 +00001448 int position = Runtime::StringMatch(sub, pat, start_index);
1449 return Smi::FromInt(position);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001450}
1451
1452
1453static Object* Runtime_StringLastIndexOf(Arguments args) {
1454 NoHandleAllocation ha;
1455 ASSERT(args.length() == 3);
1456
1457 CONVERT_CHECKED(String, sub, args[0]);
1458 CONVERT_CHECKED(String, pat, args[1]);
1459 Object* index = args[2];
1460
ager@chromium.org870a0b62008-11-04 11:43:05 +00001461 sub->TryFlatten(StringShape(sub));
1462 pat->TryFlatten(StringShape(pat));
1463
1464 StringShape sub_shape(sub);
1465 StringShape pat_shape(pat);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001466
1467 uint32_t start_index;
1468 if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
1469
ager@chromium.org870a0b62008-11-04 11:43:05 +00001470 uint32_t pattern_length = pat->length(pat_shape);
1471 uint32_t sub_length = sub->length(sub_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001472
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001473 if (start_index + pattern_length > sub_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001474 start_index = sub_length - pattern_length;
kasper.lundbd3ec4e2008-07-09 11:06:54 +00001475 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001476
1477 for (int i = start_index; i >= 0; i--) {
1478 bool found = true;
1479 for (uint32_t j = 0; j < pattern_length; j++) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001480 if (sub->Get(sub_shape, i + j) != pat->Get(pat_shape, j)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001481 found = false;
1482 break;
1483 }
1484 }
1485 if (found) return Smi::FromInt(i);
1486 }
1487
1488 return Smi::FromInt(-1);
1489}
1490
1491
1492static Object* Runtime_StringLocaleCompare(Arguments args) {
1493 NoHandleAllocation ha;
1494 ASSERT(args.length() == 2);
1495
1496 CONVERT_CHECKED(String, str1, args[0]);
1497 CONVERT_CHECKED(String, str2, args[1]);
1498
1499 if (str1 == str2) return Smi::FromInt(0); // Equal.
ager@chromium.org870a0b62008-11-04 11:43:05 +00001500 StringShape shape1(str1);
1501 StringShape shape2(str2);
1502 int str1_length = str1->length(shape1);
1503 int str2_length = str2->length(shape2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001504
1505 // Decide trivial cases without flattening.
1506 if (str1_length == 0) {
1507 if (str2_length == 0) return Smi::FromInt(0); // Equal.
1508 return Smi::FromInt(-str2_length);
1509 } else {
1510 if (str2_length == 0) return Smi::FromInt(str1_length);
1511 }
1512
1513 int end = str1_length < str2_length ? str1_length : str2_length;
1514
1515 // No need to flatten if we are going to find the answer on the first
1516 // character. At this point we know there is at least one character
1517 // in each string, due to the trivial case handling above.
ager@chromium.org870a0b62008-11-04 11:43:05 +00001518 int d = str1->Get(shape1, 0) - str2->Get(shape2, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001519 if (d != 0) return Smi::FromInt(d);
1520
ager@chromium.org870a0b62008-11-04 11:43:05 +00001521 str1->TryFlatten(shape1); // Shapes are no longer valid now!
1522 str2->TryFlatten(shape2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001523
1524 static StringInputBuffer buf1;
1525 static StringInputBuffer buf2;
1526
1527 buf1.Reset(str1);
1528 buf2.Reset(str2);
1529
1530 for (int i = 0; i < end; i++) {
1531 uint16_t char1 = buf1.GetNext();
1532 uint16_t char2 = buf2.GetNext();
1533 if (char1 != char2) return Smi::FromInt(char1 - char2);
1534 }
1535
1536 return Smi::FromInt(str1_length - str2_length);
1537}
1538
1539
1540static Object* Runtime_StringSlice(Arguments args) {
1541 NoHandleAllocation ha;
1542 ASSERT(args.length() == 3);
1543
1544 CONVERT_CHECKED(String, value, args[0]);
1545 CONVERT_DOUBLE_CHECKED(from_number, args[1]);
1546 CONVERT_DOUBLE_CHECKED(to_number, args[2]);
1547
1548 int start = FastD2I(from_number);
1549 int end = FastD2I(to_number);
1550
1551 RUNTIME_ASSERT(end >= start);
1552 RUNTIME_ASSERT(start >= 0);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001553 RUNTIME_ASSERT(end <= value->length());
1554 return value->Slice(start, end);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001555}
1556
1557
1558static Object* Runtime_NumberToRadixString(Arguments args) {
1559 NoHandleAllocation ha;
1560 ASSERT(args.length() == 2);
1561
1562 CONVERT_DOUBLE_CHECKED(value, args[0]);
1563 if (isnan(value)) {
1564 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1565 }
1566 if (isinf(value)) {
1567 if (value < 0) {
1568 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1569 }
1570 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1571 }
1572 CONVERT_DOUBLE_CHECKED(radix_number, args[1]);
1573 int radix = FastD2I(radix_number);
1574 RUNTIME_ASSERT(2 <= radix && radix <= 36);
1575 char* str = DoubleToRadixCString(value, radix);
1576 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
1577 DeleteArray(str);
1578 return result;
1579}
1580
1581
1582static Object* Runtime_NumberToFixed(Arguments args) {
1583 NoHandleAllocation ha;
1584 ASSERT(args.length() == 2);
1585
1586 CONVERT_DOUBLE_CHECKED(value, args[0]);
1587 if (isnan(value)) {
1588 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1589 }
1590 if (isinf(value)) {
1591 if (value < 0) {
1592 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1593 }
1594 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1595 }
1596 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
1597 int f = FastD2I(f_number);
1598 RUNTIME_ASSERT(f >= 0);
1599 char* str = DoubleToFixedCString(value, f);
1600 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
1601 DeleteArray(str);
1602 return res;
1603}
1604
1605
1606static Object* Runtime_NumberToExponential(Arguments args) {
1607 NoHandleAllocation ha;
1608 ASSERT(args.length() == 2);
1609
1610 CONVERT_DOUBLE_CHECKED(value, args[0]);
1611 if (isnan(value)) {
1612 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1613 }
1614 if (isinf(value)) {
1615 if (value < 0) {
1616 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1617 }
1618 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1619 }
1620 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
1621 int f = FastD2I(f_number);
1622 RUNTIME_ASSERT(f >= -1 && f <= 20);
1623 char* str = DoubleToExponentialCString(value, f);
1624 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
1625 DeleteArray(str);
1626 return res;
1627}
1628
1629
1630static Object* Runtime_NumberToPrecision(Arguments args) {
1631 NoHandleAllocation ha;
1632 ASSERT(args.length() == 2);
1633
1634 CONVERT_DOUBLE_CHECKED(value, args[0]);
1635 if (isnan(value)) {
1636 return Heap::AllocateStringFromAscii(CStrVector("NaN"));
1637 }
1638 if (isinf(value)) {
1639 if (value < 0) {
1640 return Heap::AllocateStringFromAscii(CStrVector("-Infinity"));
1641 }
1642 return Heap::AllocateStringFromAscii(CStrVector("Infinity"));
1643 }
1644 CONVERT_DOUBLE_CHECKED(f_number, args[1]);
1645 int f = FastD2I(f_number);
1646 RUNTIME_ASSERT(f >= 1 && f <= 21);
1647 char* str = DoubleToPrecisionCString(value, f);
1648 Object* res = Heap::AllocateStringFromAscii(CStrVector(str));
1649 DeleteArray(str);
1650 return res;
1651}
1652
1653
1654// Returns a single character string where first character equals
1655// string->Get(index).
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001656static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00001657 StringShape shape(*string);
1658 if (index < static_cast<uint32_t>(string->length(shape))) {
1659 string->TryFlatten(shape); // Invalidates shape!
1660 return LookupSingleCharacterStringFromCode(
1661 string->Get(StringShape(*string), index));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001662 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001663 return Execution::CharAt(string, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001664}
1665
1666
1667Object* Runtime::GetElementOrCharAt(Handle<Object> object, uint32_t index) {
1668 // Handle [] indexing on Strings
1669 if (object->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001670 Handle<Object> result = GetCharAt(Handle<String>::cast(object), index);
1671 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001672 }
1673
1674 // Handle [] indexing on String objects
1675 if (object->IsStringObjectWithCharacterAt(index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001676 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
1677 Handle<Object> result =
1678 GetCharAt(Handle<String>(String::cast(js_value->value())), index);
1679 if (!result->IsUndefined()) return *result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001680 }
1681
1682 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001683 Handle<Object> prototype = GetPrototype(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001684 return prototype->GetElement(index);
1685 }
1686
1687 return object->GetElement(index);
1688}
1689
1690
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001691Object* Runtime::GetObjectProperty(Handle<Object> object, Handle<Object> key) {
1692 HandleScope scope;
1693
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001694 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001695 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001696 Handle<Object> error =
1697 Factory::NewTypeError("non_object_property_load",
1698 HandleVector(args, 2));
1699 return Top::Throw(*error);
1700 }
1701
1702 // Check if the given key is an array index.
1703 uint32_t index;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001704 if (Array::IndexFromObject(*key, &index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001705 return GetElementOrCharAt(object, index);
1706 }
1707
1708 // Convert the key to a string - possibly by calling back into JavaScript.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001709 Handle<String> name;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001710 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001711 name = Handle<String>::cast(key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001712 } else {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001713 bool has_pending_exception = false;
1714 Handle<Object> converted =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001715 Execution::ToString(key, &has_pending_exception);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001716 if (has_pending_exception) return Failure::Exception();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001717 name = Handle<String>::cast(converted);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001718 }
1719
1720 // Check if the name is trivially convertable to an index and get
1721 // the element if so.
1722 if (name->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001723 return GetElementOrCharAt(object, index);
1724 } else {
1725 PropertyAttributes attr;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001726 return object->GetProperty(*name, &attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001727 }
1728}
1729
1730
1731static Object* Runtime_GetProperty(Arguments args) {
1732 NoHandleAllocation ha;
1733 ASSERT(args.length() == 2);
1734
1735 Handle<Object> object = args.at<Object>(0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001736 Handle<Object> key = args.at<Object>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001737
1738 return Runtime::GetObjectProperty(object, key);
1739}
1740
1741
ager@chromium.org7c537e22008-10-16 08:43:32 +00001742
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001743// KeyedStringGetProperty is called from KeyedLoadIC::GenerateGeneric.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001744static Object* Runtime_KeyedGetProperty(Arguments args) {
1745 NoHandleAllocation ha;
1746 ASSERT(args.length() == 2);
1747
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001748 // Fast cases for getting named properties of the receiver JSObject
ager@chromium.org8bb60582008-12-11 12:02:20 +00001749 // itself.
1750 //
1751 // The global proxy objects has to be excluded since LocalLookup on
1752 // the global proxy object can return a valid result eventhough the
1753 // global proxy object never has properties. This is the case
1754 // because the global proxy object forwards everything to its hidden
1755 // prototype including local lookups.
1756 //
1757 // Additionally, we need to make sure that we do not cache results
1758 // for objects that require access checks.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001759 if (args[0]->IsJSObject() &&
1760 !args[0]->IsJSGlobalProxy() &&
ager@chromium.org8bb60582008-12-11 12:02:20 +00001761 !args[0]->IsAccessCheckNeeded() &&
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001762 args[1]->IsString()) {
1763 JSObject* receiver = JSObject::cast(args[0]);
1764 String* key = String::cast(args[1]);
1765 if (receiver->HasFastProperties()) {
1766 // Attempt to use lookup cache.
1767 Object* obj = Heap::GetKeyedLookupCache();
1768 if (obj->IsFailure()) return obj;
1769 LookupCache* cache = LookupCache::cast(obj);
1770 Map* receiver_map = receiver->map();
1771 int offset = cache->Lookup(receiver_map, key);
1772 if (offset != LookupCache::kNotFound) {
1773 Object* value = receiver->FastPropertyAt(offset);
1774 return value->IsTheHole() ? Heap::undefined_value() : value;
1775 }
1776 // Lookup cache miss. Perform lookup and update the cache if
1777 // appropriate.
1778 LookupResult result;
1779 receiver->LocalLookup(key, &result);
1780 if (result.IsProperty() && result.IsLoaded() && result.type() == FIELD) {
1781 int offset = result.GetFieldIndex();
1782 Object* obj = cache->Put(receiver_map, key, offset);
1783 if (obj->IsFailure()) return obj;
1784 Heap::SetKeyedLookupCache(LookupCache::cast(obj));
1785 Object* value = receiver->FastPropertyAt(offset);
1786 return value->IsTheHole() ? Heap::undefined_value() : value;
1787 }
1788 } else {
1789 // Attempt dictionary lookup.
1790 Dictionary* dictionary = receiver->property_dictionary();
1791 int entry = dictionary->FindStringEntry(key);
1792 if ((entry != DescriptorArray::kNotFound) &&
1793 (dictionary->DetailsAt(entry).type() == NORMAL)) {
1794 return dictionary->ValueAt(entry);
1795 }
ager@chromium.org7c537e22008-10-16 08:43:32 +00001796 }
1797 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001798
1799 // Fall back to GetObjectProperty.
ager@chromium.org7c537e22008-10-16 08:43:32 +00001800 return Runtime::GetObjectProperty(args.at<Object>(0),
1801 args.at<Object>(1));
1802}
1803
1804
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001805Object* Runtime::SetObjectProperty(Handle<Object> object,
1806 Handle<Object> key,
1807 Handle<Object> value,
1808 PropertyAttributes attr) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001809 HandleScope scope;
1810
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001811 if (object->IsUndefined() || object->IsNull()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001812 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001813 Handle<Object> error =
1814 Factory::NewTypeError("non_object_property_store",
1815 HandleVector(args, 2));
1816 return Top::Throw(*error);
1817 }
1818
1819 // If the object isn't a JavaScript object, we ignore the store.
1820 if (!object->IsJSObject()) return *value;
1821
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001822 Handle<JSObject> js_object = Handle<JSObject>::cast(object);
1823
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001824 // Check if the given key is an array index.
1825 uint32_t index;
1826 if (Array::IndexFromObject(*key, &index)) {
1827 ASSERT(attr == NONE);
1828
1829 // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
1830 // of a string using [] notation. We need to support this too in
1831 // JavaScript.
1832 // In the case of a String object we just need to redirect the assignment to
1833 // the underlying string if the index is in range. Since the underlying
1834 // string does nothing with the assignment then we can ignore such
1835 // assignments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001836 if (js_object->IsStringObjectWithCharacterAt(index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001837 return *value;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001838 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001839
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001840 Handle<Object> result = SetElement(js_object, index, value);
1841 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001842 return *value;
1843 }
1844
1845 if (key->IsString()) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001846 Handle<Object> result;
1847 if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001848 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001849 result = SetElement(js_object, index, value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001850 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001851 Handle<String> key_string = Handle<String>::cast(key);
ager@chromium.org870a0b62008-11-04 11:43:05 +00001852 key_string->TryFlatten(StringShape(*key_string));
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001853 result = SetProperty(js_object, key_string, value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001854 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001855 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001856 return *value;
1857 }
1858
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001859 // Call-back into JavaScript to convert the key to a string.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001860 bool has_pending_exception = false;
1861 Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
1862 if (has_pending_exception) return Failure::Exception();
1863 Handle<String> name = Handle<String>::cast(converted);
1864
1865 if (name->AsArrayIndex(&index)) {
1866 ASSERT(attr == NONE);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001867 return js_object->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001868 } else {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001869 return js_object->SetProperty(*name, *value, attr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001870 }
1871}
1872
1873
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001874static Object* Runtime_SetProperty(Arguments args) {
1875 NoHandleAllocation ha;
1876 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
1877
1878 Handle<Object> object = args.at<Object>(0);
1879 Handle<Object> key = args.at<Object>(1);
1880 Handle<Object> value = args.at<Object>(2);
1881
1882 // Compute attributes.
1883 PropertyAttributes attributes = NONE;
1884 if (args.length() == 4) {
1885 CONVERT_CHECKED(Smi, value_obj, args[3]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001886 int unchecked_value = value_obj->value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001887 // Only attribute bits should be set.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001888 RUNTIME_ASSERT(
1889 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
1890 attributes = static_cast<PropertyAttributes>(unchecked_value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001891 }
1892 return Runtime::SetObjectProperty(object, key, value, attributes);
1893}
1894
1895
1896// Set a local property, even if it is READ_ONLY. If the property does not
1897// exist, it will be added with attributes NONE.
1898static Object* Runtime_IgnoreAttributesAndSetProperty(Arguments args) {
1899 NoHandleAllocation ha;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001900 RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001901 CONVERT_CHECKED(JSObject, object, args[0]);
1902 CONVERT_CHECKED(String, name, args[1]);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001903 // Compute attributes.
1904 PropertyAttributes attributes = NONE;
1905 if (args.length() == 4) {
1906 CONVERT_CHECKED(Smi, value_obj, args[3]);
1907 int unchecked_value = value_obj->value();
1908 // Only attribute bits should be set.
1909 RUNTIME_ASSERT(
1910 (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
1911 attributes = static_cast<PropertyAttributes>(unchecked_value);
1912 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001913
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001914 return object->
1915 IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001916}
1917
1918
1919static Object* Runtime_DeleteProperty(Arguments args) {
1920 NoHandleAllocation ha;
1921 ASSERT(args.length() == 2);
1922
1923 CONVERT_CHECKED(JSObject, object, args[0]);
1924 CONVERT_CHECKED(String, key, args[1]);
1925 return object->DeleteProperty(key);
1926}
1927
1928
1929static Object* Runtime_HasLocalProperty(Arguments args) {
1930 NoHandleAllocation ha;
1931 ASSERT(args.length() == 2);
1932 CONVERT_CHECKED(String, key, args[1]);
1933
1934 // Only JS objects can have properties.
1935 if (args[0]->IsJSObject()) {
1936 JSObject* object = JSObject::cast(args[0]);
1937 if (object->HasLocalProperty(key)) return Heap::true_value();
1938 } else if (args[0]->IsString()) {
1939 // Well, there is one exception: Handle [] on strings.
1940 uint32_t index;
1941 if (key->AsArrayIndex(&index)) {
1942 String* string = String::cast(args[0]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00001943 if (index < static_cast<uint32_t>(string->length()))
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001944 return Heap::true_value();
1945 }
1946 }
1947 return Heap::false_value();
1948}
1949
1950
1951static Object* Runtime_HasProperty(Arguments args) {
1952 NoHandleAllocation na;
1953 ASSERT(args.length() == 2);
1954
1955 // Only JS objects can have properties.
1956 if (args[0]->IsJSObject()) {
1957 JSObject* object = JSObject::cast(args[0]);
1958 CONVERT_CHECKED(String, key, args[1]);
1959 if (object->HasProperty(key)) return Heap::true_value();
1960 }
1961 return Heap::false_value();
1962}
1963
1964
1965static Object* Runtime_HasElement(Arguments args) {
1966 NoHandleAllocation na;
1967 ASSERT(args.length() == 2);
1968
1969 // Only JS objects can have elements.
1970 if (args[0]->IsJSObject()) {
1971 JSObject* object = JSObject::cast(args[0]);
1972 CONVERT_CHECKED(Smi, index_obj, args[1]);
1973 uint32_t index = index_obj->value();
1974 if (object->HasElement(index)) return Heap::true_value();
1975 }
1976 return Heap::false_value();
1977}
1978
1979
1980static Object* Runtime_IsPropertyEnumerable(Arguments args) {
1981 NoHandleAllocation ha;
1982 ASSERT(args.length() == 2);
1983
1984 CONVERT_CHECKED(JSObject, object, args[0]);
1985 CONVERT_CHECKED(String, key, args[1]);
1986
1987 uint32_t index;
1988 if (key->AsArrayIndex(&index)) {
1989 return Heap::ToBoolean(object->HasElement(index));
1990 }
1991
ager@chromium.org870a0b62008-11-04 11:43:05 +00001992 PropertyAttributes att = object->GetLocalPropertyAttribute(key);
1993 return Heap::ToBoolean(att != ABSENT && (att & DONT_ENUM) == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001994}
1995
1996
1997static Object* Runtime_GetPropertyNames(Arguments args) {
1998 HandleScope scope;
1999 ASSERT(args.length() == 1);
2000
2001 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2002 Handle<JSObject> object(raw_object);
2003 return *GetKeysFor(object);
2004}
2005
2006
2007// Returns either a FixedArray as Runtime_GetPropertyNames,
2008// or, if the given object has an enum cache that contains
2009// all enumerable properties of the object and its prototypes
2010// have none, the map of the object. This is used to speed up
2011// the check for deletions during a for-in.
2012static Object* Runtime_GetPropertyNamesFast(Arguments args) {
2013 ASSERT(args.length() == 1);
2014
2015 CONVERT_CHECKED(JSObject, raw_object, args[0]);
2016
2017 if (raw_object->IsSimpleEnum()) return raw_object->map();
2018
2019 HandleScope scope;
2020 Handle<JSObject> object(raw_object);
2021 Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
2022
2023 // Test again, since cache may have been built by preceding call.
2024 if (object->IsSimpleEnum()) return object->map();
2025
2026 return *content;
2027}
2028
2029
2030static Object* Runtime_GetArgumentsProperty(Arguments args) {
2031 NoHandleAllocation ha;
2032 ASSERT(args.length() == 1);
2033
2034 // Compute the frame holding the arguments.
2035 JavaScriptFrameIterator it;
2036 it.AdvanceToArgumentsFrame();
2037 JavaScriptFrame* frame = it.frame();
2038
2039 // Get the actual number of provided arguments.
2040 const uint32_t n = frame->GetProvidedParametersCount();
2041
2042 // Try to convert the key to an index. If successful and within
2043 // index return the the argument from the frame.
2044 uint32_t index;
2045 if (Array::IndexFromObject(args[0], &index) && index < n) {
2046 return frame->GetParameter(index);
2047 }
2048
2049 // Convert the key to a string.
2050 HandleScope scope;
2051 bool exception = false;
2052 Handle<Object> converted =
2053 Execution::ToString(args.at<Object>(0), &exception);
2054 if (exception) return Failure::Exception();
2055 Handle<String> key = Handle<String>::cast(converted);
2056
2057 // Try to convert the string key into an array index.
2058 if (key->AsArrayIndex(&index)) {
2059 if (index < n) {
2060 return frame->GetParameter(index);
2061 } else {
2062 return Top::initial_object_prototype()->GetElement(index);
2063 }
2064 }
2065
2066 // Handle special arguments properties.
2067 if (key->Equals(Heap::length_symbol())) return Smi::FromInt(n);
2068 if (key->Equals(Heap::callee_symbol())) return frame->function();
2069
2070 // Lookup in the initial Object.prototype object.
2071 return Top::initial_object_prototype()->GetProperty(*key);
2072}
2073
2074
2075static Object* Runtime_ToBool(Arguments args) {
2076 NoHandleAllocation ha;
2077 ASSERT(args.length() == 1);
2078
2079 return args[0]->ToBoolean();
2080}
2081
2082
2083// Returns the type string of a value; see ECMA-262, 11.4.3 (p 47).
2084// Possible optimizations: put the type string into the oddballs.
2085static Object* Runtime_Typeof(Arguments args) {
2086 NoHandleAllocation ha;
2087
2088 Object* obj = args[0];
2089 if (obj->IsNumber()) return Heap::number_symbol();
2090 HeapObject* heap_obj = HeapObject::cast(obj);
2091
2092 // typeof an undetectable object is 'undefined'
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002093 if (heap_obj->map()->is_undetectable()) return Heap::undefined_symbol();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002094
2095 InstanceType instance_type = heap_obj->map()->instance_type();
2096 if (instance_type < FIRST_NONSTRING_TYPE) {
2097 return Heap::string_symbol();
2098 }
2099
2100 switch (instance_type) {
2101 case ODDBALL_TYPE:
2102 if (heap_obj->IsTrue() || heap_obj->IsFalse()) {
2103 return Heap::boolean_symbol();
2104 }
2105 if (heap_obj->IsNull()) {
2106 return Heap::object_symbol();
2107 }
2108 ASSERT(heap_obj->IsUndefined());
2109 return Heap::undefined_symbol();
2110 case JS_FUNCTION_TYPE:
2111 return Heap::function_symbol();
2112 default:
2113 // For any kind of object not handled above, the spec rule for
2114 // host objects gives that it is okay to return "object"
2115 return Heap::object_symbol();
2116 }
2117}
2118
2119
2120static Object* Runtime_StringToNumber(Arguments args) {
2121 NoHandleAllocation ha;
2122 ASSERT(args.length() == 1);
2123 CONVERT_CHECKED(String, subject, args[0]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002124 subject->TryFlatten(StringShape(subject));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002125 return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
2126}
2127
2128
2129static Object* Runtime_StringFromCharCodeArray(Arguments args) {
2130 NoHandleAllocation ha;
2131 ASSERT(args.length() == 1);
2132
2133 CONVERT_CHECKED(JSArray, codes, args[0]);
2134 int length = Smi::cast(codes->length())->value();
2135
2136 // Check if the string can be ASCII.
2137 int i;
2138 for (i = 0; i < length; i++) {
2139 Object* element = codes->GetElement(i);
2140 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
2141 if ((chr & 0xffff) > String::kMaxAsciiCharCode)
2142 break;
2143 }
2144
2145 Object* object = NULL;
2146 if (i == length) { // The string is ASCII.
2147 object = Heap::AllocateRawAsciiString(length);
2148 } else { // The string is not ASCII.
2149 object = Heap::AllocateRawTwoByteString(length);
2150 }
2151
2152 if (object->IsFailure()) return object;
2153 String* result = String::cast(object);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002154 StringShape result_shape(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002155 for (int i = 0; i < length; i++) {
2156 Object* element = codes->GetElement(i);
2157 CONVERT_NUMBER_CHECKED(int, chr, Int32, element);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002158 result->Set(result_shape, i, chr & 0xffff);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002159 }
2160 return result;
2161}
2162
2163
2164// kNotEscaped is generated by the following:
2165//
2166// #!/bin/perl
2167// for (my $i = 0; $i < 256; $i++) {
2168// print "\n" if $i % 16 == 0;
2169// my $c = chr($i);
2170// my $escaped = 1;
2171// $escaped = 0 if $c =~ m#[A-Za-z0-9@*_+./-]#;
2172// print $escaped ? "0, " : "1, ";
2173// }
2174
2175
2176static bool IsNotEscaped(uint16_t character) {
2177 // Only for 8 bit characters, the rest are always escaped (in a different way)
2178 ASSERT(character < 256);
2179 static const char kNotEscaped[256] = {
2180 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2181 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2182 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
2183 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
2184 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2185 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
2186 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2187 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
2188 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2189 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2190 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2191 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2192 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2193 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2194 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2195 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2196 };
2197 return kNotEscaped[character] != 0;
2198}
2199
2200
2201static Object* Runtime_URIEscape(Arguments args) {
2202 const char hex_chars[] = "0123456789ABCDEF";
2203 NoHandleAllocation ha;
2204 ASSERT(args.length() == 1);
2205 CONVERT_CHECKED(String, source, args[0]);
2206
ager@chromium.org870a0b62008-11-04 11:43:05 +00002207 source->TryFlatten(StringShape(source));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002208
2209 int escaped_length = 0;
2210 int length = source->length();
2211 {
2212 Access<StringInputBuffer> buffer(&string_input_buffer);
2213 buffer->Reset(source);
2214 while (buffer->has_more()) {
2215 uint16_t character = buffer->GetNext();
2216 if (character >= 256) {
2217 escaped_length += 6;
2218 } else if (IsNotEscaped(character)) {
2219 escaped_length++;
2220 } else {
2221 escaped_length += 3;
2222 }
2223 // We don't allow strings that are longer than Smi range.
2224 if (!Smi::IsValid(escaped_length)) {
2225 Top::context()->mark_out_of_memory();
2226 return Failure::OutOfMemoryException();
2227 }
2228 }
2229 }
2230 // No length change implies no change. Return original string if no change.
2231 if (escaped_length == length) {
2232 return source;
2233 }
2234 Object* o = Heap::AllocateRawAsciiString(escaped_length);
2235 if (o->IsFailure()) return o;
2236 String* destination = String::cast(o);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002237 StringShape dshape(destination);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002238 int dest_position = 0;
2239
2240 Access<StringInputBuffer> buffer(&string_input_buffer);
2241 buffer->Rewind();
2242 while (buffer->has_more()) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002243 uint16_t chr = buffer->GetNext();
2244 if (chr >= 256) {
2245 destination->Set(dshape, dest_position, '%');
2246 destination->Set(dshape, dest_position+1, 'u');
2247 destination->Set(dshape, dest_position+2, hex_chars[chr >> 12]);
2248 destination->Set(dshape, dest_position+3, hex_chars[(chr >> 8) & 0xf]);
2249 destination->Set(dshape, dest_position+4, hex_chars[(chr >> 4) & 0xf]);
2250 destination->Set(dshape, dest_position+5, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002251 dest_position += 6;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002252 } else if (IsNotEscaped(chr)) {
2253 destination->Set(dshape, dest_position, chr);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002254 dest_position++;
2255 } else {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002256 destination->Set(dshape, dest_position, '%');
2257 destination->Set(dshape, dest_position+1, hex_chars[chr >> 4]);
2258 destination->Set(dshape, dest_position+2, hex_chars[chr & 0xf]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002259 dest_position += 3;
2260 }
2261 }
2262 return destination;
2263}
2264
2265
2266static inline int TwoDigitHex(uint16_t character1, uint16_t character2) {
2267 static const signed char kHexValue['g'] = {
2268 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2269 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2270 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2271 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
2272 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2273 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2274 -1, 10, 11, 12, 13, 14, 15 };
2275
2276 if (character1 > 'f') return -1;
2277 int hi = kHexValue[character1];
2278 if (hi == -1) return -1;
2279 if (character2 > 'f') return -1;
2280 int lo = kHexValue[character2];
2281 if (lo == -1) return -1;
2282 return (hi << 4) + lo;
2283}
2284
2285
ager@chromium.org870a0b62008-11-04 11:43:05 +00002286static inline int Unescape(String* source,
2287 StringShape shape,
2288 int i,
2289 int length,
2290 int* step) {
2291 uint16_t character = source->Get(shape, i);
2292 int32_t hi = 0;
2293 int32_t lo = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002294 if (character == '%' &&
2295 i <= length - 6 &&
ager@chromium.org870a0b62008-11-04 11:43:05 +00002296 source->Get(shape, i + 1) == 'u' &&
2297 (hi = TwoDigitHex(source->Get(shape, i + 2),
2298 source->Get(shape, i + 3))) != -1 &&
2299 (lo = TwoDigitHex(source->Get(shape, i + 4),
2300 source->Get(shape, i + 5))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002301 *step = 6;
2302 return (hi << 8) + lo;
2303 } else if (character == '%' &&
2304 i <= length - 3 &&
ager@chromium.org870a0b62008-11-04 11:43:05 +00002305 (lo = TwoDigitHex(source->Get(shape, i + 1),
2306 source->Get(shape, i + 2))) != -1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002307 *step = 3;
2308 return lo;
2309 } else {
2310 *step = 1;
2311 return character;
2312 }
2313}
2314
2315
2316static Object* Runtime_URIUnescape(Arguments args) {
2317 NoHandleAllocation ha;
2318 ASSERT(args.length() == 1);
2319 CONVERT_CHECKED(String, source, args[0]);
2320
ager@chromium.org870a0b62008-11-04 11:43:05 +00002321 source->TryFlatten(StringShape(source));
2322 StringShape source_shape(source);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002323
2324 bool ascii = true;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002325 int length = source->length(source_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002326
2327 int unescaped_length = 0;
2328 for (int i = 0; i < length; unescaped_length++) {
2329 int step;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002330 if (Unescape(source,
2331 source_shape,
2332 i,
2333 length,
2334 &step) >
2335 String::kMaxAsciiCharCode)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002336 ascii = false;
2337 i += step;
2338 }
2339
2340 // No length change implies no change. Return original string if no change.
2341 if (unescaped_length == length)
2342 return source;
2343
2344 Object* o = ascii ?
2345 Heap::AllocateRawAsciiString(unescaped_length) :
2346 Heap::AllocateRawTwoByteString(unescaped_length);
2347 if (o->IsFailure()) return o;
2348 String* destination = String::cast(o);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002349 StringShape destination_shape(destination);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002350
2351 int dest_position = 0;
2352 for (int i = 0; i < length; dest_position++) {
2353 int step;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002354 destination->Set(destination_shape,
2355 dest_position,
2356 Unescape(source, source_shape, i, length, &step));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002357 i += step;
2358 }
2359 return destination;
2360}
2361
2362
2363static Object* Runtime_StringParseInt(Arguments args) {
2364 NoHandleAllocation ha;
2365
2366 CONVERT_CHECKED(String, s, args[0]);
2367 CONVERT_DOUBLE_CHECKED(n, args[1]);
2368 int radix = FastD2I(n);
2369
ager@chromium.org870a0b62008-11-04 11:43:05 +00002370 s->TryFlatten(StringShape(s));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002371
ager@chromium.org870a0b62008-11-04 11:43:05 +00002372 StringShape shape(s);
2373
2374 int len = s->length(shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002375 int i;
2376
2377 // Skip leading white space.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002378 for (i = 0; i < len && Scanner::kIsWhiteSpace.get(s->Get(shape, i)); i++) ;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002379 if (i == len) return Heap::nan_value();
2380
2381 // Compute the sign (default to +).
2382 int sign = 1;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002383 if (s->Get(shape, i) == '-') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002384 sign = -1;
2385 i++;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002386 } else if (s->Get(shape, i) == '+') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002387 i++;
2388 }
2389
2390 // Compute the radix if 0.
2391 if (radix == 0) {
2392 radix = 10;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002393 if (i < len && s->Get(shape, i) == '0') {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002394 radix = 8;
2395 if (i + 1 < len) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002396 int c = s->Get(shape, i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002397 if (c == 'x' || c == 'X') {
2398 radix = 16;
2399 i += 2;
2400 }
2401 }
2402 }
2403 } else if (radix == 16) {
2404 // Allow 0x or 0X prefix if radix is 16.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002405 if (i + 1 < len && s->Get(shape, i) == '0') {
2406 int c = s->Get(shape, i + 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002407 if (c == 'x' || c == 'X') i += 2;
2408 }
2409 }
2410
2411 RUNTIME_ASSERT(2 <= radix && radix <= 36);
2412 double value;
2413 int end_index = StringToInt(s, i, radix, &value);
2414 if (end_index != i) {
2415 return Heap::NumberFromDouble(sign * value);
2416 }
2417 return Heap::nan_value();
2418}
2419
2420
2421static Object* Runtime_StringParseFloat(Arguments args) {
2422 NoHandleAllocation ha;
2423 CONVERT_CHECKED(String, str, args[0]);
2424
2425 // ECMA-262 section 15.1.2.3, empty string is NaN
2426 double value = StringToDouble(str, ALLOW_TRAILING_JUNK, OS::nan_value());
2427
2428 // Create a number object from the value.
2429 return Heap::NumberFromDouble(value);
2430}
2431
2432
2433static unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping;
2434static unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping;
2435
2436
2437template <class Converter>
2438static Object* ConvertCase(Arguments args,
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002439 unibrow::Mapping<Converter, 128>* mapping) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002440 NoHandleAllocation ha;
2441
2442 CONVERT_CHECKED(String, s, args[0]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002443 s->TryFlatten(StringShape(s));
2444 StringShape shape(s);
2445
2446 int raw_string_length = s->length(shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002447 // Assume that the string is not empty; we need this assumption later
2448 if (raw_string_length == 0) return s;
2449 int length = raw_string_length;
2450
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002451
2452 // We try this twice, once with the assumption that the result is
2453 // no longer than the input and, if that assumption breaks, again
2454 // with the exact length. This is implemented using a goto back
2455 // to this label if we discover that the assumption doesn't hold.
2456 // I apologize sincerely for this and will give a vaffel-is to
mads.s.ager31e71382008-08-13 09:32:07 +00002457 // the first person who can implement it in a nicer way.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002458 try_convert:
2459
2460 // Allocate the resulting string.
2461 //
2462 // NOTE: This assumes that the upper/lower case of an ascii
2463 // character is also ascii. This is currently the case, but it
2464 // might break in the future if we implement more context and locale
2465 // dependent upper/lower conversions.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002466 Object* o = shape.IsAsciiRepresentation()
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002467 ? Heap::AllocateRawAsciiString(length)
2468 : Heap::AllocateRawTwoByteString(length);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002469 if (o->IsFailure()) return o;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002470 String* result = String::cast(o);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002471 StringShape result_shape(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002472 bool has_changed_character = false;
2473
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002474 // Convert all characters to upper case, assuming that they will fit
2475 // in the buffer
2476 Access<StringInputBuffer> buffer(&string_input_buffer);
2477 buffer->Reset(s);
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002478 unibrow::uchar chars[Converter::kMaxWidth];
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002479 int i = 0;
2480 // We can assume that the string is not empty
2481 uc32 current = buffer->GetNext();
2482 while (i < length) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00002483 bool has_next = buffer->has_more();
2484 uc32 next = has_next ? buffer->GetNext() : 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002485 int char_length = mapping->get(current, next, chars);
2486 if (char_length == 0) {
2487 // The case conversion of this character is the character itself.
ager@chromium.org870a0b62008-11-04 11:43:05 +00002488 result->Set(result_shape, i, current);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002489 i++;
2490 } else if (char_length == 1) {
2491 // Common case: converting the letter resulted in one character.
2492 ASSERT(static_cast<uc32>(chars[0]) != current);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002493 result->Set(result_shape, i, chars[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002494 has_changed_character = true;
2495 i++;
2496 } else if (length == raw_string_length) {
2497 // We've assumed that the result would be as long as the
2498 // input but here is a character that converts to several
2499 // characters. No matter, we calculate the exact length
2500 // of the result and try the whole thing again.
2501 //
2502 // Note that this leaves room for optimization. We could just
2503 // memcpy what we already have to the result string. Also,
2504 // the result string is the last object allocated we could
2505 // "realloc" it and probably, in the vast majority of cases,
2506 // extend the existing string to be able to hold the full
2507 // result.
ager@chromium.org7c537e22008-10-16 08:43:32 +00002508 int next_length = 0;
2509 if (has_next) {
2510 next_length = mapping->get(next, 0, chars);
2511 if (next_length == 0) next_length = 1;
2512 }
2513 int current_length = i + char_length + next_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002514 while (buffer->has_more()) {
2515 current = buffer->GetNext();
ager@chromium.org7c537e22008-10-16 08:43:32 +00002516 // NOTE: we use 0 as the next character here because, while
2517 // the next character may affect what a character converts to,
2518 // it does not in any case affect the length of what it convert
2519 // to.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002520 int char_length = mapping->get(current, 0, chars);
2521 if (char_length == 0) char_length = 1;
ager@chromium.org7c537e22008-10-16 08:43:32 +00002522 current_length += char_length;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002523 }
2524 length = current_length;
2525 goto try_convert;
2526 } else {
2527 for (int j = 0; j < char_length; j++) {
ager@chromium.org870a0b62008-11-04 11:43:05 +00002528 result->Set(result_shape, i, chars[j]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002529 i++;
2530 }
2531 has_changed_character = true;
2532 }
2533 current = next;
2534 }
2535 if (has_changed_character) {
2536 return result;
2537 } else {
2538 // If we didn't actually change anything in doing the conversion
2539 // we simple return the result and let the converted string
2540 // become garbage; there is no reason to keep two identical strings
2541 // alive.
2542 return s;
2543 }
2544}
2545
2546
2547static Object* Runtime_StringToLowerCase(Arguments args) {
2548 return ConvertCase<unibrow::ToLowercase>(args, &to_lower_mapping);
2549}
2550
2551
2552static Object* Runtime_StringToUpperCase(Arguments args) {
2553 return ConvertCase<unibrow::ToUppercase>(args, &to_upper_mapping);
2554}
2555
2556
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002557static Object* Runtime_NumberToString(Arguments args) {
2558 NoHandleAllocation ha;
2559 ASSERT(args.length() == 1);
2560
2561 Object* number = args[0];
2562 RUNTIME_ASSERT(number->IsNumber());
2563
2564 Object* cached = Heap::GetNumberStringCache(number);
2565 if (cached != Heap::undefined_value()) {
2566 return cached;
2567 }
2568
2569 char arr[100];
2570 Vector<char> buffer(arr, ARRAY_SIZE(arr));
2571 const char* str;
2572 if (number->IsSmi()) {
2573 int num = Smi::cast(number)->value();
2574 str = IntToCString(num, buffer);
2575 } else {
2576 double num = HeapNumber::cast(number)->value();
2577 str = DoubleToCString(num, buffer);
2578 }
2579 Object* result = Heap::AllocateStringFromAscii(CStrVector(str));
2580
2581 if (!result->IsFailure()) {
2582 Heap::SetNumberStringCache(number, String::cast(result));
2583 }
2584 return result;
2585}
2586
2587
2588static Object* Runtime_NumberToInteger(Arguments args) {
2589 NoHandleAllocation ha;
2590 ASSERT(args.length() == 1);
2591
2592 Object* obj = args[0];
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002593 if (obj->IsSmi()) return obj;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002594 CONVERT_DOUBLE_CHECKED(number, obj);
2595 return Heap::NumberFromDouble(DoubleToInteger(number));
2596}
2597
2598
2599static Object* Runtime_NumberToJSUint32(Arguments args) {
2600 NoHandleAllocation ha;
2601 ASSERT(args.length() == 1);
2602
2603 Object* obj = args[0];
2604 if (obj->IsSmi() && Smi::cast(obj)->value() >= 0) return obj;
2605 CONVERT_NUMBER_CHECKED(int32_t, number, Uint32, obj);
2606 return Heap::NumberFromUint32(number);
2607}
2608
2609
2610static Object* Runtime_NumberToJSInt32(Arguments args) {
2611 NoHandleAllocation ha;
2612 ASSERT(args.length() == 1);
2613
2614 Object* obj = args[0];
2615 if (obj->IsSmi()) return obj;
2616 CONVERT_DOUBLE_CHECKED(number, obj);
2617 return Heap::NumberFromInt32(DoubleToInt32(number));
2618}
2619
2620
ager@chromium.org870a0b62008-11-04 11:43:05 +00002621// Converts a Number to a Smi, if possible. Returns NaN if the number is not
2622// a small integer.
2623static Object* Runtime_NumberToSmi(Arguments args) {
2624 NoHandleAllocation ha;
2625 ASSERT(args.length() == 1);
2626
2627 Object* obj = args[0];
2628 if (obj->IsSmi()) {
2629 return obj;
2630 }
2631 if (obj->IsHeapNumber()) {
2632 double value = HeapNumber::cast(obj)->value();
2633 int int_value = FastD2I(value);
2634 if (value == FastI2D(int_value) && Smi::IsValid(int_value)) {
2635 return Smi::FromInt(int_value);
2636 }
2637 }
2638 return Heap::nan_value();
2639}
2640
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002641static Object* Runtime_NumberAdd(Arguments args) {
2642 NoHandleAllocation ha;
2643 ASSERT(args.length() == 2);
2644
2645 CONVERT_DOUBLE_CHECKED(x, args[0]);
2646 CONVERT_DOUBLE_CHECKED(y, args[1]);
2647 return Heap::AllocateHeapNumber(x + y);
2648}
2649
2650
2651static Object* Runtime_NumberSub(Arguments args) {
2652 NoHandleAllocation ha;
2653 ASSERT(args.length() == 2);
2654
2655 CONVERT_DOUBLE_CHECKED(x, args[0]);
2656 CONVERT_DOUBLE_CHECKED(y, args[1]);
2657 return Heap::AllocateHeapNumber(x - y);
2658}
2659
2660
2661static Object* Runtime_NumberMul(Arguments args) {
2662 NoHandleAllocation ha;
2663 ASSERT(args.length() == 2);
2664
2665 CONVERT_DOUBLE_CHECKED(x, args[0]);
2666 CONVERT_DOUBLE_CHECKED(y, args[1]);
2667 return Heap::AllocateHeapNumber(x * y);
2668}
2669
2670
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002671static Object* Runtime_NumberUnaryMinus(Arguments args) {
2672 NoHandleAllocation ha;
2673 ASSERT(args.length() == 1);
2674
2675 CONVERT_DOUBLE_CHECKED(x, args[0]);
2676 return Heap::AllocateHeapNumber(-x);
2677}
2678
2679
2680static Object* Runtime_NumberDiv(Arguments args) {
2681 NoHandleAllocation ha;
2682 ASSERT(args.length() == 2);
2683
2684 CONVERT_DOUBLE_CHECKED(x, args[0]);
2685 CONVERT_DOUBLE_CHECKED(y, args[1]);
2686 return Heap::NewNumberFromDouble(x / y);
2687}
2688
2689
2690static Object* Runtime_NumberMod(Arguments args) {
2691 NoHandleAllocation ha;
2692 ASSERT(args.length() == 2);
2693
2694 CONVERT_DOUBLE_CHECKED(x, args[0]);
2695 CONVERT_DOUBLE_CHECKED(y, args[1]);
2696
2697#ifdef WIN32
2698 // Workaround MS fmod bugs. ECMA-262 says:
2699 // dividend is finite and divisor is an infinity => result equals dividend
2700 // dividend is a zero and divisor is nonzero finite => result equals dividend
2701 if (!(isfinite(x) && (!isfinite(y) && !isnan(y))) &&
2702 !(x == 0 && (y != 0 && isfinite(y))))
2703#endif
2704 x = fmod(x, y);
2705 // NewNumberFromDouble may return a Smi instead of a Number object
2706 return Heap::NewNumberFromDouble(x);
2707}
2708
2709
2710static Object* Runtime_StringAdd(Arguments args) {
2711 NoHandleAllocation ha;
2712 ASSERT(args.length() == 2);
2713
2714 CONVERT_CHECKED(String, str1, args[0]);
2715 CONVERT_CHECKED(String, str2, args[1]);
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002716 int len1 = str1->length();
2717 int len2 = str2->length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002718 if (len1 == 0) return str2;
2719 if (len2 == 0) return str1;
2720 int length_sum = len1 + len2;
2721 // Make sure that an out of memory exception is thrown if the length
2722 // of the new cons string is too large to fit in a Smi.
2723 if (length_sum > Smi::kMaxValue || length_sum < 0) {
2724 Top::context()->mark_out_of_memory();
2725 return Failure::OutOfMemoryException();
2726 }
ager@chromium.orgc3e50d82008-11-05 11:53:10 +00002727 return Heap::AllocateConsString(str1, str2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002728}
2729
2730
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002731template<typename sinkchar>
2732static inline void StringBuilderConcatHelper(String* special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00002733 StringShape special_shape,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002734 sinkchar* sink,
2735 FixedArray* fixed_array,
2736 int array_length) {
2737 int position = 0;
2738 for (int i = 0; i < array_length; i++) {
2739 Object* element = fixed_array->get(i);
2740 if (element->IsSmi()) {
2741 int len = Smi::cast(element)->value();
2742 int pos = len >> 11;
2743 len &= 0x7ff;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002744 String::WriteToFlat(special,
2745 special_shape,
2746 sink + position,
2747 pos,
2748 pos + len);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002749 position += len;
2750 } else {
2751 String* string = String::cast(element);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002752 StringShape shape(string);
2753 int element_length = string->length(shape);
2754 String::WriteToFlat(string, shape, sink + position, 0, element_length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002755 position += element_length;
2756 }
2757 }
2758}
2759
2760
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002761static Object* Runtime_StringBuilderConcat(Arguments args) {
2762 NoHandleAllocation ha;
2763 ASSERT(args.length() == 2);
2764 CONVERT_CHECKED(JSArray, array, args[0]);
2765 CONVERT_CHECKED(String, special, args[1]);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002766 StringShape special_shape(special);
2767 int special_length = special->length(special_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002768 Object* smi_array_length = array->length();
2769 if (!smi_array_length->IsSmi()) {
2770 Top::context()->mark_out_of_memory();
2771 return Failure::OutOfMemoryException();
2772 }
2773 int array_length = Smi::cast(smi_array_length)->value();
2774 if (!array->HasFastElements()) {
2775 return Top::Throw(Heap::illegal_argument_symbol());
2776 }
2777 FixedArray* fixed_array = FixedArray::cast(array->elements());
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002778 if (fixed_array->length() < array_length) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002779 array_length = fixed_array->length();
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002780 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002781
2782 if (array_length == 0) {
2783 return Heap::empty_string();
2784 } else if (array_length == 1) {
2785 Object* first = fixed_array->get(0);
2786 if (first->IsString()) return first;
2787 }
2788
ager@chromium.org870a0b62008-11-04 11:43:05 +00002789 bool ascii = special_shape.IsAsciiRepresentation();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002790 int position = 0;
2791 for (int i = 0; i < array_length; i++) {
2792 Object* elt = fixed_array->get(i);
2793 if (elt->IsSmi()) {
2794 int len = Smi::cast(elt)->value();
2795 int pos = len >> 11;
2796 len &= 0x7ff;
2797 if (pos + len > special_length) {
2798 return Top::Throw(Heap::illegal_argument_symbol());
2799 }
2800 position += len;
2801 } else if (elt->IsString()) {
2802 String* element = String::cast(elt);
ager@chromium.org870a0b62008-11-04 11:43:05 +00002803 StringShape element_shape(element);
2804 int element_length = element->length(element_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002805 if (!Smi::IsValid(element_length + position)) {
2806 Top::context()->mark_out_of_memory();
2807 return Failure::OutOfMemoryException();
2808 }
2809 position += element_length;
ager@chromium.org870a0b62008-11-04 11:43:05 +00002810 if (ascii && !element_shape.IsAsciiRepresentation()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002811 ascii = false;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002812 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002813 } else {
2814 return Top::Throw(Heap::illegal_argument_symbol());
2815 }
2816 }
2817
2818 int length = position;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002819 Object* object;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002820
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002821 if (ascii) {
2822 object = Heap::AllocateRawAsciiString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002823 if (object->IsFailure()) return object;
2824 SeqAsciiString* answer = SeqAsciiString::cast(object);
2825 StringBuilderConcatHelper(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00002826 special_shape,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002827 answer->GetChars(),
2828 fixed_array,
2829 array_length);
2830 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002831 } else {
2832 object = Heap::AllocateRawTwoByteString(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002833 if (object->IsFailure()) return object;
2834 SeqTwoByteString* answer = SeqTwoByteString::cast(object);
2835 StringBuilderConcatHelper(special,
ager@chromium.org870a0b62008-11-04 11:43:05 +00002836 special_shape,
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002837 answer->GetChars(),
2838 fixed_array,
2839 array_length);
2840 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002841 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002842}
2843
2844
2845static Object* Runtime_NumberOr(Arguments args) {
2846 NoHandleAllocation ha;
2847 ASSERT(args.length() == 2);
2848
2849 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2850 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2851 return Heap::NumberFromInt32(x | y);
2852}
2853
2854
2855static Object* Runtime_NumberAnd(Arguments args) {
2856 NoHandleAllocation ha;
2857 ASSERT(args.length() == 2);
2858
2859 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2860 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2861 return Heap::NumberFromInt32(x & y);
2862}
2863
2864
2865static Object* Runtime_NumberXor(Arguments args) {
2866 NoHandleAllocation ha;
2867 ASSERT(args.length() == 2);
2868
2869 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2870 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2871 return Heap::NumberFromInt32(x ^ y);
2872}
2873
2874
2875static Object* Runtime_NumberNot(Arguments args) {
2876 NoHandleAllocation ha;
2877 ASSERT(args.length() == 1);
2878
2879 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2880 return Heap::NumberFromInt32(~x);
2881}
2882
2883
2884static Object* Runtime_NumberShl(Arguments args) {
2885 NoHandleAllocation ha;
2886 ASSERT(args.length() == 2);
2887
2888 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2889 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2890 return Heap::NumberFromInt32(x << (y & 0x1f));
2891}
2892
2893
2894static Object* Runtime_NumberShr(Arguments args) {
2895 NoHandleAllocation ha;
2896 ASSERT(args.length() == 2);
2897
2898 CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
2899 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2900 return Heap::NumberFromUint32(x >> (y & 0x1f));
2901}
2902
2903
2904static Object* Runtime_NumberSar(Arguments args) {
2905 NoHandleAllocation ha;
2906 ASSERT(args.length() == 2);
2907
2908 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]);
2909 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]);
2910 return Heap::NumberFromInt32(ArithmeticShiftRight(x, y & 0x1f));
2911}
2912
2913
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002914static Object* Runtime_NumberEquals(Arguments args) {
2915 NoHandleAllocation ha;
2916 ASSERT(args.length() == 2);
2917
2918 CONVERT_DOUBLE_CHECKED(x, args[0]);
2919 CONVERT_DOUBLE_CHECKED(y, args[1]);
2920 if (isnan(x)) return Smi::FromInt(NOT_EQUAL);
2921 if (isnan(y)) return Smi::FromInt(NOT_EQUAL);
2922 if (x == y) return Smi::FromInt(EQUAL);
2923 Object* result;
2924 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) {
2925 result = Smi::FromInt(EQUAL);
2926 } else {
2927 result = Smi::FromInt(NOT_EQUAL);
2928 }
2929 return result;
2930}
2931
2932
2933static Object* Runtime_StringEquals(Arguments args) {
2934 NoHandleAllocation ha;
2935 ASSERT(args.length() == 2);
2936
2937 CONVERT_CHECKED(String, x, args[0]);
2938 CONVERT_CHECKED(String, y, args[1]);
2939
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00002940 bool not_equal = !x->Equals(y);
2941 // This is slightly convoluted because the value that signifies
2942 // equality is 0 and inequality is 1 so we have to negate the result
2943 // from String::Equals.
2944 ASSERT(not_equal == 0 || not_equal == 1);
2945 STATIC_CHECK(EQUAL == 0);
2946 STATIC_CHECK(NOT_EQUAL == 1);
2947 return Smi::FromInt(not_equal);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002948}
2949
2950
2951static Object* Runtime_NumberCompare(Arguments args) {
2952 NoHandleAllocation ha;
2953 ASSERT(args.length() == 3);
2954
2955 CONVERT_DOUBLE_CHECKED(x, args[0]);
2956 CONVERT_DOUBLE_CHECKED(y, args[1]);
2957 if (isnan(x) || isnan(y)) return args[2];
2958 if (x == y) return Smi::FromInt(EQUAL);
2959 if (isless(x, y)) return Smi::FromInt(LESS);
2960 return Smi::FromInt(GREATER);
2961}
2962
2963
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002964// Compare two Smis as if they were converted to strings and then
2965// compared lexicographically.
2966static Object* Runtime_SmiLexicographicCompare(Arguments args) {
2967 NoHandleAllocation ha;
2968 ASSERT(args.length() == 2);
2969
2970 // Arrays for the individual characters of the two Smis. Smis are
2971 // 31 bit integers and 10 decimal digits are therefore enough.
2972 static int x_elms[10];
2973 static int y_elms[10];
2974
2975 // Extract the integer values from the Smis.
2976 CONVERT_CHECKED(Smi, x, args[0]);
2977 CONVERT_CHECKED(Smi, y, args[1]);
2978 int x_value = x->value();
2979 int y_value = y->value();
2980
2981 // If the integers are equal so are the string representations.
2982 if (x_value == y_value) return Smi::FromInt(EQUAL);
2983
2984 // If one of the integers are zero the normal integer order is the
2985 // same as the lexicographic order of the string representations.
2986 if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
2987
2988 // If only one of the intergers is negative the negative number is
2989 // smallest because the char code of '-' is less than the char code
2990 // of any digit. Otherwise, we make both values positive.
2991 if (x_value < 0 || y_value < 0) {
2992 if (y_value >= 0) return Smi::FromInt(LESS);
2993 if (x_value >= 0) return Smi::FromInt(GREATER);
2994 x_value = -x_value;
2995 y_value = -y_value;
2996 }
2997
2998 // Convert the integers to arrays of their decimal digits.
2999 int x_index = 0;
3000 int y_index = 0;
3001 while (x_value > 0) {
3002 x_elms[x_index++] = x_value % 10;
3003 x_value /= 10;
3004 }
3005 while (y_value > 0) {
3006 y_elms[y_index++] = y_value % 10;
3007 y_value /= 10;
3008 }
3009
3010 // Loop through the arrays of decimal digits finding the first place
3011 // where they differ.
3012 while (--x_index >= 0 && --y_index >= 0) {
3013 int diff = x_elms[x_index] - y_elms[y_index];
3014 if (diff != 0) return Smi::FromInt(diff);
3015 }
3016
3017 // If one array is a suffix of the other array, the longest array is
3018 // the representation of the largest of the Smis in the
3019 // lexicographic ordering.
3020 return Smi::FromInt(x_index - y_index);
3021}
3022
3023
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003024static Object* Runtime_StringCompare(Arguments args) {
3025 NoHandleAllocation ha;
3026 ASSERT(args.length() == 2);
3027
3028 CONVERT_CHECKED(String, x, args[0]);
3029 CONVERT_CHECKED(String, y, args[1]);
3030
ager@chromium.org870a0b62008-11-04 11:43:05 +00003031 StringShape x_shape(x);
3032 StringShape y_shape(y);
3033
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003034 // A few fast case tests before we flatten.
3035 if (x == y) return Smi::FromInt(EQUAL);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003036 if (y->length(y_shape) == 0) {
3037 if (x->length(x_shape) == 0) return Smi::FromInt(EQUAL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003038 return Smi::FromInt(GREATER);
ager@chromium.org870a0b62008-11-04 11:43:05 +00003039 } else if (x->length(x_shape) == 0) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003040 return Smi::FromInt(LESS);
3041 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003042
ager@chromium.org870a0b62008-11-04 11:43:05 +00003043 int d = x->Get(x_shape, 0) - y->Get(y_shape, 0);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003044 if (d < 0) return Smi::FromInt(LESS);
3045 else if (d > 0) return Smi::FromInt(GREATER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003046
ager@chromium.org870a0b62008-11-04 11:43:05 +00003047 x->TryFlatten(x_shape); // Shapes are no longer valid!
3048 y->TryFlatten(y_shape);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003049
3050 static StringInputBuffer bufx;
3051 static StringInputBuffer bufy;
3052 bufx.Reset(x);
3053 bufy.Reset(y);
3054 while (bufx.has_more() && bufy.has_more()) {
3055 int d = bufx.GetNext() - bufy.GetNext();
3056 if (d < 0) return Smi::FromInt(LESS);
3057 else if (d > 0) return Smi::FromInt(GREATER);
3058 }
3059
3060 // x is (non-trivial) prefix of y:
3061 if (bufy.has_more()) return Smi::FromInt(LESS);
3062 // y is prefix of x:
3063 return Smi::FromInt(bufx.has_more() ? GREATER : EQUAL);
3064}
3065
3066
3067static Object* Runtime_Math_abs(Arguments args) {
3068 NoHandleAllocation ha;
3069 ASSERT(args.length() == 1);
3070
3071 CONVERT_DOUBLE_CHECKED(x, args[0]);
3072 return Heap::AllocateHeapNumber(fabs(x));
3073}
3074
3075
3076static Object* Runtime_Math_acos(Arguments args) {
3077 NoHandleAllocation ha;
3078 ASSERT(args.length() == 1);
3079
3080 CONVERT_DOUBLE_CHECKED(x, args[0]);
3081 return Heap::AllocateHeapNumber(acos(x));
3082}
3083
3084
3085static Object* Runtime_Math_asin(Arguments args) {
3086 NoHandleAllocation ha;
3087 ASSERT(args.length() == 1);
3088
3089 CONVERT_DOUBLE_CHECKED(x, args[0]);
3090 return Heap::AllocateHeapNumber(asin(x));
3091}
3092
3093
3094static Object* Runtime_Math_atan(Arguments args) {
3095 NoHandleAllocation ha;
3096 ASSERT(args.length() == 1);
3097
3098 CONVERT_DOUBLE_CHECKED(x, args[0]);
3099 return Heap::AllocateHeapNumber(atan(x));
3100}
3101
3102
3103static Object* Runtime_Math_atan2(Arguments args) {
3104 NoHandleAllocation ha;
3105 ASSERT(args.length() == 2);
3106
3107 CONVERT_DOUBLE_CHECKED(x, args[0]);
3108 CONVERT_DOUBLE_CHECKED(y, args[1]);
3109 double result;
3110 if (isinf(x) && isinf(y)) {
3111 // Make sure that the result in case of two infinite arguments
3112 // is a multiple of Pi / 4. The sign of the result is determined
3113 // by the first argument (x) and the sign of the second argument
3114 // determines the multiplier: one or three.
3115 static double kPiDividedBy4 = 0.78539816339744830962;
3116 int multiplier = (x < 0) ? -1 : 1;
3117 if (y < 0) multiplier *= 3;
3118 result = multiplier * kPiDividedBy4;
3119 } else {
3120 result = atan2(x, y);
3121 }
3122 return Heap::AllocateHeapNumber(result);
3123}
3124
3125
3126static Object* Runtime_Math_ceil(Arguments args) {
3127 NoHandleAllocation ha;
3128 ASSERT(args.length() == 1);
3129
3130 CONVERT_DOUBLE_CHECKED(x, args[0]);
3131 return Heap::NumberFromDouble(ceiling(x));
3132}
3133
3134
3135static Object* Runtime_Math_cos(Arguments args) {
3136 NoHandleAllocation ha;
3137 ASSERT(args.length() == 1);
3138
3139 CONVERT_DOUBLE_CHECKED(x, args[0]);
3140 return Heap::AllocateHeapNumber(cos(x));
3141}
3142
3143
3144static Object* Runtime_Math_exp(Arguments args) {
3145 NoHandleAllocation ha;
3146 ASSERT(args.length() == 1);
3147
3148 CONVERT_DOUBLE_CHECKED(x, args[0]);
3149 return Heap::AllocateHeapNumber(exp(x));
3150}
3151
3152
3153static Object* Runtime_Math_floor(Arguments args) {
3154 NoHandleAllocation ha;
3155 ASSERT(args.length() == 1);
3156
3157 CONVERT_DOUBLE_CHECKED(x, args[0]);
3158 return Heap::NumberFromDouble(floor(x));
3159}
3160
3161
3162static Object* Runtime_Math_log(Arguments args) {
3163 NoHandleAllocation ha;
3164 ASSERT(args.length() == 1);
3165
3166 CONVERT_DOUBLE_CHECKED(x, args[0]);
3167 return Heap::AllocateHeapNumber(log(x));
3168}
3169
3170
3171static Object* Runtime_Math_pow(Arguments args) {
3172 NoHandleAllocation ha;
3173 ASSERT(args.length() == 2);
3174
3175 CONVERT_DOUBLE_CHECKED(x, args[0]);
3176 CONVERT_DOUBLE_CHECKED(y, args[1]);
3177 if (isnan(y) || ((x == 1 || x == -1) && isinf(y))) {
3178 return Heap::nan_value();
3179 } else if (y == 0) {
3180 return Smi::FromInt(1);
3181 } else {
3182 return Heap::AllocateHeapNumber(pow(x, y));
3183 }
3184}
3185
3186// Returns a number value with positive sign, greater than or equal to
3187// 0 but less than 1, chosen randomly.
mads.s.ager31e71382008-08-13 09:32:07 +00003188static Object* Runtime_Math_random(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003189 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003190 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003191
3192 // To get much better precision, we combine the results of two
3193 // invocations of random(). The result is computed by normalizing a
3194 // double in the range [0, RAND_MAX + 1) obtained by adding the
3195 // high-order bits in the range [0, RAND_MAX] with the low-order
3196 // bits in the range [0, 1).
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003197 double lo = static_cast<double>(random()) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003198 double hi = static_cast<double>(random());
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003199 double result = (hi + lo) * (1.0 / (RAND_MAX + 1.0));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003200 ASSERT(result >= 0 && result < 1);
3201 return Heap::AllocateHeapNumber(result);
3202}
3203
3204
3205static Object* Runtime_Math_round(Arguments args) {
3206 NoHandleAllocation ha;
3207 ASSERT(args.length() == 1);
3208
3209 CONVERT_DOUBLE_CHECKED(x, args[0]);
3210 if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
3211 return Heap::NumberFromDouble(floor(x + 0.5));
3212}
3213
3214
3215static Object* Runtime_Math_sin(Arguments args) {
3216 NoHandleAllocation ha;
3217 ASSERT(args.length() == 1);
3218
3219 CONVERT_DOUBLE_CHECKED(x, args[0]);
3220 return Heap::AllocateHeapNumber(sin(x));
3221}
3222
3223
3224static Object* Runtime_Math_sqrt(Arguments args) {
3225 NoHandleAllocation ha;
3226 ASSERT(args.length() == 1);
3227
3228 CONVERT_DOUBLE_CHECKED(x, args[0]);
3229 return Heap::AllocateHeapNumber(sqrt(x));
3230}
3231
3232
3233static Object* Runtime_Math_tan(Arguments args) {
3234 NoHandleAllocation ha;
3235 ASSERT(args.length() == 1);
3236
3237 CONVERT_DOUBLE_CHECKED(x, args[0]);
3238 return Heap::AllocateHeapNumber(tan(x));
3239}
3240
3241
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003242// The NewArguments function is only used when constructing the
3243// arguments array when calling non-functions from JavaScript in
3244// runtime.js:CALL_NON_FUNCTION.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003245static Object* Runtime_NewArguments(Arguments args) {
3246 NoHandleAllocation ha;
3247 ASSERT(args.length() == 1);
3248
3249 // ECMA-262, 3rd., 10.1.8, p.39
3250 CONVERT_CHECKED(JSFunction, callee, args[0]);
3251
3252 // Compute the frame holding the arguments.
3253 JavaScriptFrameIterator it;
3254 it.AdvanceToArgumentsFrame();
3255 JavaScriptFrame* frame = it.frame();
3256
3257 const int length = frame->GetProvidedParametersCount();
3258 Object* result = Heap::AllocateArgumentsObject(callee, length);
3259 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003260 if (length > 0) {
3261 Object* obj = Heap::AllocateFixedArray(length);
3262 if (obj->IsFailure()) return obj;
3263 FixedArray* array = FixedArray::cast(obj);
3264 ASSERT(array->length() == length);
3265 WriteBarrierMode mode = array->GetWriteBarrierMode();
3266 for (int i = 0; i < length; i++) {
3267 array->set(i, frame->GetParameter(i), mode);
3268 }
3269 JSObject::cast(result)->set_elements(array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003270 }
3271 return result;
3272}
3273
3274
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003275static Object* Runtime_NewArgumentsFast(Arguments args) {
3276 NoHandleAllocation ha;
3277 ASSERT(args.length() == 3);
3278
3279 JSFunction* callee = JSFunction::cast(args[0]);
3280 Object** parameters = reinterpret_cast<Object**>(args[1]);
3281 const int length = Smi::cast(args[2])->value();
3282
3283 Object* result = Heap::AllocateArgumentsObject(callee, length);
3284 if (result->IsFailure()) return result;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003285 ASSERT(Heap::InNewSpace(result));
3286
3287 // Allocate the elements if needed.
3288 if (length > 0) {
3289 // Allocate the fixed array.
3290 Object* obj = Heap::AllocateRawFixedArray(length);
3291 if (obj->IsFailure()) return obj;
3292 reinterpret_cast<Array*>(obj)->set_map(Heap::fixed_array_map());
3293 FixedArray* array = FixedArray::cast(obj);
3294 array->set_length(length);
3295 WriteBarrierMode mode = array->GetWriteBarrierMode();
3296 for (int i = 0; i < length; i++) {
3297 array->set(i, *--parameters, mode);
3298 }
3299 JSObject::cast(result)->set_elements(FixedArray::cast(obj),
3300 SKIP_WRITE_BARRIER);
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00003301 }
3302 return result;
3303}
3304
3305
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003306static Object* Runtime_NewClosure(Arguments args) {
3307 HandleScope scope;
3308 ASSERT(args.length() == 2);
3309 CONVERT_ARG_CHECKED(JSFunction, boilerplate, 0);
3310 CONVERT_ARG_CHECKED(Context, context, 1);
3311
3312 Handle<JSFunction> result =
3313 Factory::NewFunctionFromBoilerplate(boilerplate, context);
3314 return *result;
3315}
3316
3317
3318static Object* Runtime_NewObject(Arguments args) {
3319 NoHandleAllocation ha;
3320 ASSERT(args.length() == 1);
3321
3322 Object* constructor = args[0];
3323 if (constructor->IsJSFunction()) {
3324 JSFunction* function = JSFunction::cast(constructor);
3325
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003326 // Handle steping into constructors if step into is active.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003327 if (Debug::StepInActive()) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003328 HandleScope scope;
3329 Debug::HandleStepIn(Handle<JSFunction>(function), 0, true);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003330 }
3331
3332 if (function->has_initial_map() &&
3333 function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
3334 // The 'Function' function ignores the receiver object when
3335 // called using 'new' and creates a new JSFunction object that
3336 // is returned. The receiver object is only used for error
3337 // reporting if an error occurs when constructing the new
3338 // JSFunction. AllocateJSObject should not be used to allocate
3339 // JSFunctions since it does not properly initialize the shared
3340 // part of the function. Since the receiver is ignored anyway,
3341 // we use the global object as the receiver instead of a new
3342 // JSFunction object. This way, errors are reported the same
3343 // way whether or not 'Function' is called using 'new'.
3344 return Top::context()->global();
3345 }
3346 return Heap::AllocateJSObject(function);
3347 }
3348
3349 HandleScope scope;
3350 Handle<Object> cons(constructor);
3351 // The constructor is not a function; throw a type error.
3352 Handle<Object> type_error =
3353 Factory::NewTypeError("not_constructor", HandleVector(&cons, 1));
3354 return Top::Throw(*type_error);
3355}
3356
3357
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003358static Object* Runtime_LazyCompile(Arguments args) {
3359 HandleScope scope;
3360 ASSERT(args.length() == 1);
3361
3362 Handle<JSFunction> function = args.at<JSFunction>(0);
3363#ifdef DEBUG
3364 if (FLAG_trace_lazy) {
3365 PrintF("[lazy: ");
3366 function->shared()->name()->Print();
3367 PrintF("]\n");
3368 }
3369#endif
3370
3371 // Compile the target function.
3372 ASSERT(!function->is_compiled());
3373 if (!CompileLazy(function, KEEP_EXCEPTION)) {
3374 return Failure::Exception();
3375 }
3376
3377 return function->code();
3378}
3379
3380
3381static Object* Runtime_GetCalledFunction(Arguments args) {
3382 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00003383 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003384 StackFrameIterator it;
3385 // Get past the JS-to-C exit frame.
3386 ASSERT(it.frame()->is_exit());
3387 it.Advance();
3388 // Get past the CALL_NON_FUNCTION activation frame.
3389 ASSERT(it.frame()->is_java_script());
3390 it.Advance();
3391 // Argument adaptor frames do not copy the function; we have to skip
3392 // past them to get to the real calling frame.
3393 if (it.frame()->is_arguments_adaptor()) it.Advance();
3394 // Get the function from the top of the expression stack of the
3395 // calling frame.
3396 StandardFrame* frame = StandardFrame::cast(it.frame());
3397 int index = frame->ComputeExpressionsCount() - 1;
3398 Object* result = frame->GetExpression(index);
3399 return result;
3400}
3401
3402
3403static Object* Runtime_GetFunctionDelegate(Arguments args) {
3404 HandleScope scope;
3405 ASSERT(args.length() == 1);
3406 RUNTIME_ASSERT(!args[0]->IsJSFunction());
3407 return *Execution::GetFunctionDelegate(args.at<Object>(0));
3408}
3409
3410
3411static Object* Runtime_NewContext(Arguments args) {
3412 NoHandleAllocation ha;
kasper.lund7276f142008-07-30 08:49:36 +00003413 ASSERT(args.length() == 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003414
kasper.lund7276f142008-07-30 08:49:36 +00003415 CONVERT_CHECKED(JSFunction, function, args[0]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003416 int length = ScopeInfo<>::NumberOfContextSlots(function->code());
3417 Object* result = Heap::AllocateFunctionContext(length, function);
3418 if (result->IsFailure()) return result;
3419
3420 Top::set_context(Context::cast(result));
3421
kasper.lund7276f142008-07-30 08:49:36 +00003422 return result; // non-failure
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003423}
3424
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003425static Object* PushContextHelper(Object* object, bool is_catch_context) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003426 // Convert the object to a proper JavaScript object.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003427 Object* js_object = object;
3428 if (!js_object->IsJSObject()) {
3429 js_object = js_object->ToObject();
3430 if (js_object->IsFailure()) {
3431 if (!Failure::cast(js_object)->IsInternalError()) return js_object;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003432 HandleScope scope;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003433 Handle<Object> handle(object);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003434 Handle<Object> result =
3435 Factory::NewTypeError("with_expression", HandleVector(&handle, 1));
3436 return Top::Throw(*result);
3437 }
3438 }
3439
3440 Object* result =
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003441 Heap::AllocateWithContext(Top::context(),
3442 JSObject::cast(js_object),
3443 is_catch_context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003444 if (result->IsFailure()) return result;
3445
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003446 Context* context = Context::cast(result);
3447 Top::set_context(context);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003448
kasper.lund7276f142008-07-30 08:49:36 +00003449 return result;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003450}
3451
3452
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003453static Object* Runtime_PushContext(Arguments args) {
3454 NoHandleAllocation ha;
3455 ASSERT(args.length() == 1);
3456 return PushContextHelper(args[0], false);
3457}
3458
3459
3460static Object* Runtime_PushCatchContext(Arguments args) {
3461 NoHandleAllocation ha;
3462 ASSERT(args.length() == 1);
3463 return PushContextHelper(args[0], true);
3464}
3465
3466
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003467static Object* Runtime_LookupContext(Arguments args) {
3468 HandleScope scope;
3469 ASSERT(args.length() == 2);
3470
3471 CONVERT_ARG_CHECKED(Context, context, 0);
3472 CONVERT_ARG_CHECKED(String, name, 1);
3473
3474 int index;
3475 PropertyAttributes attributes;
3476 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003477 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003478 context->Lookup(name, flags, &index, &attributes);
3479
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003480 if (index < 0 && !holder.is_null()) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003481 ASSERT(holder->IsJSObject());
3482 return *holder;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003483 }
3484
3485 // No intermediate context found. Use global object by default.
3486 return Top::context()->global();
3487}
3488
3489
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003490// A mechanism to return pairs of Object*'s. This is somewhat
3491// compiler-dependent as it assumes that a 64-bit value (a long long)
3492// is returned via two registers (edx:eax on ia32). Both the ia32 and
3493// arm platform support this; it is mostly an issue of "coaxing" the
3494// compiler to do the right thing.
3495//
3496// TODO(1236026): This is a non-portable hack that should be removed.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003497typedef uint64_t ObjectPair;
3498static inline ObjectPair MakePair(Object* x, Object* y) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003499 return reinterpret_cast<uint32_t>(x) |
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003500 (reinterpret_cast<ObjectPair>(y) << 32);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003501}
3502
3503
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003504static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003505 ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
3506 USE(attributes);
3507 return x->IsTheHole() ? Heap::undefined_value() : x;
3508}
3509
3510
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003511static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
3512 ASSERT(!holder->IsGlobalObject());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003513 Context* top = Top::context();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003514 // Get the context extension function.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003515 JSFunction* context_extension_function =
3516 top->global_context()->context_extension_function();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003517 // If the holder isn't a context extension object, we just return it
3518 // as the receiver. This allows arguments objects to be used as
3519 // receivers, but only if they are put in the context scope chain
3520 // explicitly via a with-statement.
3521 Object* constructor = holder->map()->constructor();
3522 if (constructor != context_extension_function) return holder;
3523 // Fall back to using the global object as the receiver if the
3524 // property turns out to be a local variable allocated in a context
3525 // extension object - introduced via eval.
3526 return top->global()->global_receiver();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003527}
3528
3529
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003530static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003531 HandleScope scope;
3532 ASSERT(args.length() == 2);
3533
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003534 if (!args[0]->IsContext() || !args[1]->IsString()) {
3535 return MakePair(IllegalOperation(), NULL);
3536 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003537 Handle<Context> context = args.at<Context>(0);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003538 Handle<String> name = args.at<String>(1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003539
3540 int index;
3541 PropertyAttributes attributes;
3542 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003543 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003544 context->Lookup(name, flags, &index, &attributes);
3545
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003546 // If the index is non-negative, the slot has been found in a local
3547 // variable or a parameter. Read it from the context object or the
3548 // arguments object.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003549 if (index >= 0) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003550 // If the "property" we were looking for is a local variable or an
3551 // argument in a context, the receiver is the global object; see
3552 // ECMA-262, 3rd., 10.1.6 and 10.2.3.
3553 JSObject* receiver = Top::context()->global()->global_receiver();
3554 Object* value = (holder->IsContext())
3555 ? Context::cast(*holder)->get(index)
3556 : JSObject::cast(*holder)->GetElement(index);
3557 return MakePair(Unhole(value, attributes), receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003558 }
3559
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003560 // If the holder is found, we read the property from it.
3561 if (!holder.is_null() && holder->IsJSObject()) {
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00003562 ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003563 JSObject* object = JSObject::cast(*holder);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003564 JSObject* receiver;
3565 if (object->IsGlobalObject()) {
3566 receiver = GlobalObject::cast(object)->global_receiver();
3567 } else if (context->is_exception_holder(*holder)) {
3568 receiver = Top::context()->global()->global_receiver();
3569 } else {
3570 receiver = ComputeReceiverForNonGlobal(object);
3571 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003572 // No need to unhole the value here. This is taken care of by the
3573 // GetProperty function.
3574 Object* value = object->GetProperty(*name);
3575 return MakePair(value, receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003576 }
3577
3578 if (throw_error) {
3579 // The property doesn't exist - throw exception.
3580 Handle<Object> reference_error =
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003581 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003582 return MakePair(Top::Throw(*reference_error), NULL);
3583 } else {
3584 // The property doesn't exist - return undefined
3585 return MakePair(Heap::undefined_value(), Heap::undefined_value());
3586 }
3587}
3588
3589
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003590static ObjectPair Runtime_LoadContextSlot(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003591 return LoadContextSlotHelper(args, true);
3592}
3593
3594
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003595static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003596 return LoadContextSlotHelper(args, false);
3597}
3598
3599
3600static Object* Runtime_StoreContextSlot(Arguments args) {
3601 HandleScope scope;
3602 ASSERT(args.length() == 3);
3603
3604 Handle<Object> value(args[0]);
3605 CONVERT_ARG_CHECKED(Context, context, 1);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00003606 CONVERT_ARG_CHECKED(String, name, 2);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003607
3608 int index;
3609 PropertyAttributes attributes;
3610 ContextLookupFlags flags = FOLLOW_CHAINS;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003611 Handle<Object> holder =
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003612 context->Lookup(name, flags, &index, &attributes);
3613
3614 if (index >= 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003615 if (holder->IsContext()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003616 // Ignore if read_only variable.
3617 if ((attributes & READ_ONLY) == 0) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003618 Handle<Context>::cast(holder)->set(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003619 }
3620 } else {
3621 ASSERT((attributes & READ_ONLY) == 0);
3622 Object* result =
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003623 Handle<JSObject>::cast(holder)->SetElement(index, *value);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003624 USE(result);
3625 ASSERT(!result->IsFailure());
3626 }
3627 return *value;
3628 }
3629
3630 // Slow case: The property is not in a FixedArray context.
3631 // It is either in an JSObject extension context or it was not found.
3632 Handle<JSObject> context_ext;
3633
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003634 if (!holder.is_null()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003635 // The property exists in the extension context.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003636 context_ext = Handle<JSObject>::cast(holder);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003637 } else {
3638 // The property was not found. It needs to be stored in the global context.
3639 ASSERT(attributes == ABSENT);
3640 attributes = NONE;
3641 context_ext = Handle<JSObject>(Top::context()->global());
3642 }
3643
3644 // Set the property, but ignore if read_only variable.
3645 if ((attributes & READ_ONLY) == 0) {
3646 Handle<Object> set = SetProperty(context_ext, name, value, attributes);
3647 if (set.is_null()) {
3648 // Failure::Exception is converted to a null handle in the
3649 // handle-based methods such as SetProperty. We therefore need
3650 // to convert null handles back to exceptions.
3651 ASSERT(Top::has_pending_exception());
3652 return Failure::Exception();
3653 }
3654 }
3655 return *value;
3656}
3657
3658
3659static Object* Runtime_Throw(Arguments args) {
3660 HandleScope scope;
3661 ASSERT(args.length() == 1);
3662
3663 return Top::Throw(args[0]);
3664}
3665
3666
3667static Object* Runtime_ReThrow(Arguments args) {
3668 HandleScope scope;
3669 ASSERT(args.length() == 1);
3670
3671 return Top::ReThrow(args[0]);
3672}
3673
3674
3675static Object* Runtime_ThrowReferenceError(Arguments args) {
3676 HandleScope scope;
3677 ASSERT(args.length() == 1);
3678
3679 Handle<Object> name(args[0]);
3680 Handle<Object> reference_error =
3681 Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
3682 return Top::Throw(*reference_error);
3683}
3684
3685
3686static Object* Runtime_StackOverflow(Arguments args) {
3687 NoHandleAllocation na;
3688 return Top::StackOverflow();
3689}
3690
3691
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003692static Object* Runtime_DebugBreak(Arguments args) {
3693 ASSERT(args.length() == 0);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003694 return Execution::DebugBreakHelper();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003695}
3696
3697
3698static Object* Runtime_StackGuard(Arguments args) {
3699 ASSERT(args.length() == 1);
3700
3701 // First check if this is a real stack overflow.
3702 if (StackGuard::IsStackOverflow()) return Runtime_StackOverflow(args);
3703
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00003704 return Execution::HandleStackGuardInterrupt();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003705}
3706
3707
3708// NOTE: These PrintXXX functions are defined for all builds (not just
3709// DEBUG builds) because we may want to be able to trace function
3710// calls in all modes.
3711static void PrintString(String* str) {
3712 // not uncommon to have empty strings
3713 if (str->length() > 0) {
3714 SmartPointer<char> s =
3715 str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
3716 PrintF("%s", *s);
3717 }
3718}
3719
3720
3721static void PrintObject(Object* obj) {
3722 if (obj->IsSmi()) {
3723 PrintF("%d", Smi::cast(obj)->value());
3724 } else if (obj->IsString() || obj->IsSymbol()) {
3725 PrintString(String::cast(obj));
3726 } else if (obj->IsNumber()) {
3727 PrintF("%g", obj->Number());
3728 } else if (obj->IsFailure()) {
3729 PrintF("<failure>");
3730 } else if (obj->IsUndefined()) {
3731 PrintF("<undefined>");
3732 } else if (obj->IsNull()) {
3733 PrintF("<null>");
3734 } else if (obj->IsTrue()) {
3735 PrintF("<true>");
3736 } else if (obj->IsFalse()) {
3737 PrintF("<false>");
3738 } else {
3739 PrintF("%p", obj);
3740 }
3741}
3742
3743
3744static int StackSize() {
3745 int n = 0;
3746 for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++;
3747 return n;
3748}
3749
3750
3751static void PrintTransition(Object* result) {
3752 // indentation
3753 { const int nmax = 80;
3754 int n = StackSize();
3755 if (n <= nmax)
3756 PrintF("%4d:%*s", n, n, "");
3757 else
3758 PrintF("%4d:%*s", n, nmax, "...");
3759 }
3760
3761 if (result == NULL) {
3762 // constructor calls
3763 JavaScriptFrameIterator it;
3764 JavaScriptFrame* frame = it.frame();
3765 if (frame->IsConstructor()) PrintF("new ");
3766 // function name
3767 Object* fun = frame->function();
3768 if (fun->IsJSFunction()) {
3769 PrintObject(JSFunction::cast(fun)->shared()->name());
3770 } else {
3771 PrintObject(fun);
3772 }
3773 // function arguments
3774 // (we are intentionally only printing the actually
3775 // supplied parameters, not all parameters required)
3776 PrintF("(this=");
3777 PrintObject(frame->receiver());
3778 const int length = frame->GetProvidedParametersCount();
3779 for (int i = 0; i < length; i++) {
3780 PrintF(", ");
3781 PrintObject(frame->GetParameter(i));
3782 }
3783 PrintF(") {\n");
3784
3785 } else {
3786 // function result
3787 PrintF("} -> ");
3788 PrintObject(result);
3789 PrintF("\n");
3790 }
3791}
3792
3793
3794static Object* Runtime_TraceEnter(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003795 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003796 NoHandleAllocation ha;
3797 PrintTransition(NULL);
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003798 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003799}
3800
3801
3802static Object* Runtime_TraceExit(Arguments args) {
3803 NoHandleAllocation ha;
3804 PrintTransition(args[0]);
3805 return args[0]; // return TOS
3806}
3807
3808
3809static Object* Runtime_DebugPrint(Arguments args) {
3810 NoHandleAllocation ha;
3811 ASSERT(args.length() == 1);
3812
3813#ifdef DEBUG
3814 if (args[0]->IsString()) {
3815 // If we have a string, assume it's a code "marker"
3816 // and print some interesting cpu debugging info.
3817 JavaScriptFrameIterator it;
3818 JavaScriptFrame* frame = it.frame();
3819 PrintF("fp = %p, sp = %p, pp = %p: ",
3820 frame->fp(), frame->sp(), frame->pp());
3821 } else {
3822 PrintF("DebugPrint: ");
3823 }
3824 args[0]->Print();
3825#else
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003826 // ShortPrint is available in release mode. Print is not.
3827 args[0]->ShortPrint();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003828#endif
3829 PrintF("\n");
ager@chromium.org236ad962008-09-25 09:45:57 +00003830 Flush();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003831
3832 return args[0]; // return TOS
3833}
3834
3835
3836static Object* Runtime_DebugTrace(Arguments args) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003837 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003838 NoHandleAllocation ha;
3839 Top::PrintStack();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00003840 return Heap::undefined_value();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003841}
3842
3843
mads.s.ager31e71382008-08-13 09:32:07 +00003844static Object* Runtime_DateCurrentTime(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003845 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003846 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003847
3848 // According to ECMA-262, section 15.9.1, page 117, the precision of
3849 // the number in a Date object representing a particular instant in
3850 // time is milliseconds. Therefore, we floor the result of getting
3851 // the OS time.
3852 double millis = floor(OS::TimeCurrentMillis());
3853 return Heap::NumberFromDouble(millis);
3854}
3855
3856
3857static Object* Runtime_DateParseString(Arguments args) {
3858 HandleScope scope;
3859 ASSERT(args.length() == 1);
3860
3861 CONVERT_CHECKED(String, string_object, args[0]);
3862
3863 Handle<String> str(string_object);
3864 Handle<FixedArray> output = Factory::NewFixedArray(DateParser::OUTPUT_SIZE);
3865 if (DateParser::Parse(*str, *output)) {
3866 return *Factory::NewJSArrayWithElements(output);
3867 } else {
3868 return *Factory::null_value();
3869 }
3870}
3871
3872
3873static Object* Runtime_DateLocalTimezone(Arguments args) {
3874 NoHandleAllocation ha;
3875 ASSERT(args.length() == 1);
3876
3877 CONVERT_DOUBLE_CHECKED(x, args[0]);
3878 char* zone = OS::LocalTimezone(x);
3879 return Heap::AllocateStringFromUtf8(CStrVector(zone));
3880}
3881
3882
3883static Object* Runtime_DateLocalTimeOffset(Arguments args) {
3884 NoHandleAllocation ha;
mads.s.ager31e71382008-08-13 09:32:07 +00003885 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003886
3887 return Heap::NumberFromDouble(OS::LocalTimeOffset());
3888}
3889
3890
3891static Object* Runtime_DateDaylightSavingsOffset(Arguments args) {
3892 NoHandleAllocation ha;
3893 ASSERT(args.length() == 1);
3894
3895 CONVERT_DOUBLE_CHECKED(x, args[0]);
3896 return Heap::NumberFromDouble(OS::DaylightSavingsOffset(x));
3897}
3898
3899
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003900static Object* Runtime_NumberIsFinite(Arguments args) {
3901 NoHandleAllocation ha;
3902 ASSERT(args.length() == 1);
3903
3904 CONVERT_DOUBLE_CHECKED(value, args[0]);
3905 Object* result;
3906 if (isnan(value) || (fpclassify(value) == FP_INFINITE)) {
3907 result = Heap::false_value();
3908 } else {
3909 result = Heap::true_value();
3910 }
3911 return result;
3912}
3913
3914
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00003915static Object* Runtime_GlobalReceiver(Arguments args) {
3916 ASSERT(args.length() == 1);
3917 Object* global = args[0];
3918 if (!global->IsJSGlobalObject()) return Heap::null_value();
3919 return JSGlobalObject::cast(global)->global_receiver();
3920}
3921
3922
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003923static Object* Runtime_CompileString(Arguments args) {
3924 HandleScope scope;
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003925 ASSERT(args.length() == 2);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003926 CONVERT_ARG_CHECKED(String, source, 0);
ager@chromium.org236ad962008-09-25 09:45:57 +00003927 CONVERT_ARG_CHECKED(Smi, line_offset, 1);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00003928
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003929 // Compile source string.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003930 Handle<JSFunction> boilerplate =
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003931 Compiler::CompileEval(source, line_offset->value(), true);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +00003932 if (boilerplate.is_null()) return Failure::Exception();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003933 Handle<Context> context(Top::context()->global_context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00003934 Handle<JSFunction> fun =
3935 Factory::NewFunctionFromBoilerplate(boilerplate, context);
3936 return *fun;
3937}
3938
3939
ager@chromium.orga74f0da2008-12-03 16:05:52 +00003940static Handle<JSFunction> GetBuiltinFunction(String* name) {
3941 LookupResult result;
3942 Top::global_context()->builtins()->LocalLookup(name, &result);
3943 return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
3944}
3945
3946
3947static Object* CompileDirectEval(Handle<String> source) {
3948 // Compute the eval context.
3949 HandleScope scope;
3950 StackFrameLocator locator;
3951 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
3952 Handle<Context> context(Context::cast(frame->context()));
3953 bool is_global = context->IsGlobalContext();
3954
3955 // Compile source string.
3956 Handle<JSFunction> boilerplate = Compiler::CompileEval(source, 0, is_global);
3957 if (boilerplate.is_null()) return Failure::Exception();
3958 Handle<JSFunction> fun =
3959 Factory::NewFunctionFromBoilerplate(boilerplate, context);
3960 return *fun;
3961}
3962
3963
3964static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
3965 ASSERT(args.length() == 2);
3966
3967 HandleScope scope;
3968
3969 CONVERT_ARG_CHECKED(JSFunction, callee, 0);
3970
3971 Handle<Object> receiver;
3972
3973 // Find where the 'eval' symbol is bound. It is unaliased only if
3974 // it is bound in the global context.
3975 StackFrameLocator locator;
3976 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
3977 Handle<Context> context(Context::cast(frame->context()));
3978 int index;
3979 PropertyAttributes attributes;
3980 while (!context.is_null()) {
3981 receiver = context->Lookup(Factory::eval_symbol(), FOLLOW_PROTOTYPE_CHAIN,
3982 &index, &attributes);
3983 if (attributes != ABSENT) break;
3984 if (context->is_function_context()) {
3985 context = Handle<Context>(Context::cast(context->closure()->context()));
3986 } else {
3987 context = Handle<Context>(context->previous());
3988 }
3989 }
3990
3991 if (context->IsGlobalContext()) {
3992 // 'eval' is bound in the global context, but it may have been overwritten.
3993 // Compare it to the builtin 'GlobalEval' function to make sure.
3994 Handle<JSFunction> global_eval =
3995 GetBuiltinFunction(Heap::global_eval_symbol());
3996 if (global_eval.is_identical_to(callee)) {
3997 // A direct eval call.
3998 if (args[1]->IsString()) {
3999 CONVERT_ARG_CHECKED(String, source, 1);
4000 // A normal eval call on a string. Compile it and return the
4001 // compiled function bound in the local context.
4002 Object* compiled_source = CompileDirectEval(source);
4003 if (compiled_source->IsFailure()) return compiled_source;
4004 receiver = Handle<Object>(frame->receiver());
4005 callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
4006 } else {
4007 // An eval call that is not called on a string. Global eval
4008 // deals better with this.
4009 receiver = Handle<Object>(Top::global_context()->global());
4010 }
4011 } else {
4012 // 'eval' is overwritten. Just call the function with the given arguments.
4013 receiver = Handle<Object>(Top::global_context()->global());
4014 }
4015 } else {
4016 // 'eval' is not bound in the global context. Just call the function
4017 // with the given arguments. This is not necessarily the global eval.
4018 if (receiver->IsContext()) {
4019 context = Handle<Context>::cast(receiver);
4020 receiver = Handle<Object>(context->get(index));
4021 }
4022 }
4023
4024 Handle<FixedArray> call = Factory::NewFixedArray(2);
4025 call->set(0, *callee);
4026 call->set(1, *receiver);
4027 return *call;
4028}
4029
4030
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004031static Object* Runtime_CompileScript(Arguments args) {
4032 HandleScope scope;
4033 ASSERT(args.length() == 4);
4034
4035 CONVERT_ARG_CHECKED(String, source, 0);
4036 CONVERT_ARG_CHECKED(String, script, 1);
4037 CONVERT_CHECKED(Smi, line_attrs, args[2]);
4038 int line = line_attrs->value();
4039 CONVERT_CHECKED(Smi, col_attrs, args[3]);
4040 int col = col_attrs->value();
4041 Handle<JSFunction> boilerplate =
4042 Compiler::Compile(source, script, line, col, NULL, NULL);
4043 if (boilerplate.is_null()) return Failure::Exception();
4044 Handle<JSFunction> fun =
4045 Factory::NewFunctionFromBoilerplate(boilerplate,
4046 Handle<Context>(Top::context()));
4047 return *fun;
4048}
4049
4050
4051static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
4052 // This utility adjusts the property attributes for newly created Function
4053 // object ("new Function(...)") by changing the map.
4054 // All it does is changing the prototype property to enumerable
4055 // as specified in ECMA262, 15.3.5.2.
4056 HandleScope scope;
4057 ASSERT(args.length() == 1);
4058 CONVERT_ARG_CHECKED(JSFunction, func, 0);
4059 ASSERT(func->map()->instance_type() ==
4060 Top::function_instance_map()->instance_type());
4061 ASSERT(func->map()->instance_size() ==
4062 Top::function_instance_map()->instance_size());
4063 func->set_map(*Top::function_instance_map());
4064 return *func;
4065}
4066
4067
ager@chromium.org9258b6b2008-09-11 09:11:10 +00004068// Push an array unto an array of arrays if it is not already in the
4069// array. Returns true if the element was pushed on the stack and
4070// false otherwise.
4071static Object* Runtime_PushIfAbsent(Arguments args) {
4072 ASSERT(args.length() == 2);
4073 CONVERT_CHECKED(JSArray, array, args[0]);
4074 CONVERT_CHECKED(JSArray, element, args[1]);
4075 RUNTIME_ASSERT(array->HasFastElements());
4076 int length = Smi::cast(array->length())->value();
4077 FixedArray* elements = FixedArray::cast(array->elements());
4078 for (int i = 0; i < length; i++) {
4079 if (elements->get(i) == element) return Heap::false_value();
4080 }
4081 Object* obj = array->SetFastElement(length, element);
4082 if (obj->IsFailure()) return obj;
4083 return Heap::true_value();
4084}
4085
4086
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00004087/**
4088 * A simple visitor visits every element of Array's.
4089 * The backend storage can be a fixed array for fast elements case,
4090 * or a dictionary for sparse array. Since Dictionary is a subtype
4091 * of FixedArray, the class can be used by both fast and slow cases.
4092 * The second parameter of the constructor, fast_elements, specifies
4093 * whether the storage is a FixedArray or Dictionary.
4094 *
4095 * An index limit is used to deal with the situation that a result array
4096 * length overflows 32-bit non-negative integer.
4097 */
4098class ArrayConcatVisitor {
4099 public:
4100 ArrayConcatVisitor(Handle<FixedArray> storage,
4101 uint32_t index_limit,
4102 bool fast_elements) :
4103 storage_(storage), index_limit_(index_limit),
4104 fast_elements_(fast_elements), index_offset_(0) { }
4105
4106 void visit(uint32_t i, Handle<Object> elm) {
4107 uint32_t index = i + index_offset_;
4108 if (index >= index_limit_) return;
4109
4110 if (fast_elements_) {
4111 ASSERT(index < static_cast<uint32_t>(storage_->length()));
4112 storage_->set(index, *elm);
4113
4114 } else {
4115 Handle<Dictionary> dict = Handle<Dictionary>::cast(storage_);
4116 Handle<Dictionary> result =
4117 Factory::DictionaryAtNumberPut(dict, index, elm);
4118 if (!result.is_identical_to(dict))
4119 storage_ = result;
4120 }
4121 }
4122
4123 void increase_index_offset(uint32_t delta) {
4124 index_offset_ += delta;
4125 }
4126
4127 private:
4128 Handle<FixedArray> storage_;
4129 uint32_t index_limit_;
4130 bool fast_elements_;
4131 uint32_t index_offset_;
4132};
4133
4134
4135/**
4136 * A helper function that visits elements of a JSObject. Only elements
4137 * whose index between 0 and range (exclusive) are visited.
4138 *
4139 * If the third parameter, visitor, is not NULL, the visitor is called
4140 * with parameters, 'visitor_index_offset + element index' and the element.
4141 *
4142 * It returns the number of visisted elements.
4143 */
4144static uint32_t IterateElements(Handle<JSObject> receiver,
4145 uint32_t range,
4146 ArrayConcatVisitor* visitor) {
4147 uint32_t num_of_elements = 0;
4148
4149 if (receiver->HasFastElements()) {
4150 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
4151 uint32_t len = elements->length();
4152 if (range < len) len = range;
4153
4154 for (uint32_t j = 0; j < len; j++) {
4155 Handle<Object> e(elements->get(j));
4156 if (!e->IsTheHole()) {
4157 num_of_elements++;
4158 if (visitor)
4159 visitor->visit(j, e);
4160 }
4161 }
4162
4163 } else {
4164 Handle<Dictionary> dict(receiver->element_dictionary());
4165 uint32_t capacity = dict->Capacity();
4166 for (uint32_t j = 0; j < capacity; j++) {
4167 Handle<Object> k(dict->KeyAt(j));
4168 if (dict->IsKey(*k)) {
4169 ASSERT(k->IsNumber());
4170 uint32_t index = static_cast<uint32_t>(k->Number());
4171 if (index < range) {
4172 num_of_elements++;
4173 if (visitor) {
4174 visitor->visit(index,
4175 Handle<Object>(dict->ValueAt(j)));
4176 }
4177 }
4178 }
4179 }
4180 }
4181
4182 return num_of_elements;
4183}
4184
4185
4186/**
4187 * A helper function that visits elements of an Array object, and elements
4188 * on its prototypes.
4189 *
4190 * Elements on prototypes are visited first, and only elements whose indices
4191 * less than Array length are visited.
4192 *
4193 * If a ArrayConcatVisitor object is given, the visitor is called with
4194 * parameters, element's index + visitor_index_offset and the element.
4195 */
4196static uint32_t IterateArrayAndPrototypeElements(Handle<JSArray> array,
4197 ArrayConcatVisitor* visitor) {
4198 uint32_t range = static_cast<uint32_t>(array->length()->Number());
4199 Handle<Object> obj = array;
4200
4201 static const int kEstimatedPrototypes = 3;
4202 List< Handle<JSObject> > objects(kEstimatedPrototypes);
4203
4204 // Visit prototype first. If an element on the prototype is shadowed by
4205 // the inheritor using the same index, the ArrayConcatVisitor visits
4206 // the prototype element before the shadowing element.
4207 // The visitor can simply overwrite the old value by new value using
4208 // the same index. This follows Array::concat semantics.
4209 while (!obj->IsNull()) {
4210 objects.Add(Handle<JSObject>::cast(obj));
4211 obj = Handle<Object>(obj->GetPrototype());
4212 }
4213
4214 uint32_t nof_elements = 0;
4215 for (int i = objects.length() - 1; i >= 0; i--) {
4216 Handle<JSObject> obj = objects[i];
4217 nof_elements +=
4218 IterateElements(Handle<JSObject>::cast(obj), range, visitor);
4219 }
4220
4221 return nof_elements;
4222}
4223
4224
4225/**
4226 * A helper function of Runtime_ArrayConcat.
4227 *
4228 * The first argument is an Array of arrays and objects. It is the
4229 * same as the arguments array of Array::concat JS function.
4230 *
4231 * If an argument is an Array object, the function visits array
4232 * elements. If an argument is not an Array object, the function
4233 * visits the object as if it is an one-element array.
4234 *
4235 * If the result array index overflows 32-bit integer, the rounded
4236 * non-negative number is used as new length. For example, if one
4237 * array length is 2^32 - 1, second array length is 1, the
4238 * concatenated array length is 0.
4239 */
4240static uint32_t IterateArguments(Handle<JSArray> arguments,
4241 ArrayConcatVisitor* visitor) {
4242 uint32_t visited_elements = 0;
4243 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
4244
4245 for (uint32_t i = 0; i < num_of_args; i++) {
4246 Handle<Object> obj(arguments->GetElement(i));
4247 if (obj->IsJSArray()) {
4248 Handle<JSArray> array = Handle<JSArray>::cast(obj);
4249 uint32_t len = static_cast<uint32_t>(array->length()->Number());
4250 uint32_t nof_elements =
4251 IterateArrayAndPrototypeElements(array, visitor);
4252 // Total elements of array and its prototype chain can be more than
4253 // the array length, but ArrayConcat can only concatenate at most
4254 // the array length number of elements.
4255 visited_elements += (nof_elements > len) ? len : nof_elements;
4256 if (visitor) visitor->increase_index_offset(len);
4257
4258 } else {
4259 if (visitor) {
4260 visitor->visit(0, obj);
4261 visitor->increase_index_offset(1);
4262 }
4263 visited_elements++;
4264 }
4265 }
4266 return visited_elements;
4267}
4268
4269
4270/**
4271 * Array::concat implementation.
4272 * See ECMAScript 262, 15.4.4.4.
4273 */
4274static Object* Runtime_ArrayConcat(Arguments args) {
4275 ASSERT(args.length() == 1);
4276 HandleScope handle_scope;
4277
4278 CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
4279 Handle<JSArray> arguments(arg_arrays);
4280
4281 // Pass 1: estimate the number of elements of the result
4282 // (it could be more than real numbers if prototype has elements).
4283 uint32_t result_length = 0;
4284 uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
4285
4286 { AssertNoAllocation nogc;
4287 for (uint32_t i = 0; i < num_of_args; i++) {
4288 Object* obj = arguments->GetElement(i);
4289 if (obj->IsJSArray()) {
4290 result_length +=
4291 static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
4292 } else {
4293 result_length++;
4294 }
4295 }
4296 }
4297
4298 // Allocate an empty array, will set length and content later.
4299 Handle<JSArray> result = Factory::NewJSArray(0);
4300
4301 uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
4302 // If estimated number of elements is more than half of length, a
4303 // fixed array (fast case) is more time and space-efficient than a
4304 // dictionary.
4305 bool fast_case = (estimate_nof_elements * 2) >= result_length;
4306
4307 Handle<FixedArray> storage;
4308 if (fast_case) {
4309 // The backing storage array must have non-existing elements to
4310 // preserve holes across concat operations.
4311 storage = Factory::NewFixedArrayWithHoles(result_length);
4312
4313 } else {
4314 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
4315 uint32_t at_least_space_for = estimate_nof_elements +
4316 (estimate_nof_elements >> 2);
4317 storage = Handle<FixedArray>::cast(
4318 Factory::NewDictionary(at_least_space_for));
4319 }
4320
4321 Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
4322
4323 ArrayConcatVisitor visitor(storage, result_length, fast_case);
4324
4325 IterateArguments(arguments, &visitor);
4326
4327 result->set_length(*len);
4328 result->set_elements(*storage);
4329
4330 return *result;
4331}
4332
4333
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004334// This will not allocate (flatten the string), but it may run
4335// very slowly for very deeply nested ConsStrings. For debugging use only.
4336static Object* Runtime_GlobalPrint(Arguments args) {
4337 NoHandleAllocation ha;
4338 ASSERT(args.length() == 1);
4339
4340 CONVERT_CHECKED(String, string, args[0]);
4341 StringInputBuffer buffer(string);
4342 while (buffer.has_more()) {
4343 uint16_t character = buffer.GetNext();
4344 PrintF("%c", character);
4345 }
4346 return string;
4347}
4348
4349
4350static Object* Runtime_RemoveArrayHoles(Arguments args) {
4351 ASSERT(args.length() == 1);
4352 // Ignore the case if this is not a JSArray.
4353 if (!args[0]->IsJSArray()) return args[0];
4354 return JSArray::cast(args[0])->RemoveHoles();
4355}
4356
4357
4358// Move contents of argument 0 (an array) to argument 1 (an array)
4359static Object* Runtime_MoveArrayContents(Arguments args) {
4360 ASSERT(args.length() == 2);
4361 CONVERT_CHECKED(JSArray, from, args[0]);
4362 CONVERT_CHECKED(JSArray, to, args[1]);
4363 to->SetContent(FixedArray::cast(from->elements()));
4364 to->set_length(from->length());
4365 from->SetContent(Heap::empty_fixed_array());
4366 from->set_length(0);
4367 return to;
4368}
4369
4370
4371// How many elements does this array have?
4372static Object* Runtime_EstimateNumberOfElements(Arguments args) {
4373 ASSERT(args.length() == 1);
4374 CONVERT_CHECKED(JSArray, array, args[0]);
4375 HeapObject* elements = array->elements();
4376 if (elements->IsDictionary()) {
4377 return Smi::FromInt(Dictionary::cast(elements)->NumberOfElements());
4378 } else {
4379 return array->length();
4380 }
4381}
4382
4383
4384// Returns an array that tells you where in the [0, length) interval an array
4385// might have elements. Can either return keys or intervals. Keys can have
4386// gaps in (undefined). Intervals can also span over some undefined keys.
4387static Object* Runtime_GetArrayKeys(Arguments args) {
4388 ASSERT(args.length() == 2);
4389 HandleScope scope;
4390 CONVERT_CHECKED(JSArray, raw_array, args[0]);
4391 Handle<JSArray> array(raw_array);
4392 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004393 if (array->elements()->IsDictionary()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004394 // Create an array and get all the keys into it, then remove all the
4395 // keys that are not integers in the range 0 to length-1.
4396 Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
4397 int keys_length = keys->length();
4398 for (int i = 0; i < keys_length; i++) {
4399 Object* key = keys->get(i);
4400 uint32_t index;
4401 if (!Array::IndexFromObject(key, &index) || index >= length) {
4402 // Zap invalid keys.
4403 keys->set_undefined(i);
4404 }
4405 }
4406 return *Factory::NewJSArrayWithElements(keys);
4407 } else {
4408 Handle<FixedArray> single_interval = Factory::NewFixedArray(2);
4409 // -1 means start of array.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004410 single_interval->set(0,
4411 Smi::FromInt(-1),
4412 SKIP_WRITE_BARRIER);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004413 Handle<Object> length_object =
4414 Factory::NewNumber(static_cast<double>(length));
4415 single_interval->set(1, *length_object);
4416 return *Factory::NewJSArrayWithElements(single_interval);
4417 }
4418}
4419
4420
4421// DefineAccessor takes an optional final argument which is the
4422// property attributes (eg, DONT_ENUM, DONT_DELETE). IMPORTANT: due
4423// to the way accessors are implemented, it is set for both the getter
4424// and setter on the first call to DefineAccessor and ignored on
4425// subsequent calls.
4426static Object* Runtime_DefineAccessor(Arguments args) {
4427 RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
4428 // Compute attributes.
4429 PropertyAttributes attributes = NONE;
4430 if (args.length() == 5) {
4431 CONVERT_CHECKED(Smi, attrs, args[4]);
4432 int value = attrs->value();
4433 // Only attribute bits should be set.
4434 ASSERT((value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
4435 attributes = static_cast<PropertyAttributes>(value);
4436 }
4437
4438 CONVERT_CHECKED(JSObject, obj, args[0]);
4439 CONVERT_CHECKED(String, name, args[1]);
4440 CONVERT_CHECKED(Smi, flag, args[2]);
4441 CONVERT_CHECKED(JSFunction, fun, args[3]);
4442 return obj->DefineAccessor(name, flag->value() == 0, fun, attributes);
4443}
4444
4445
4446static Object* Runtime_LookupAccessor(Arguments args) {
4447 ASSERT(args.length() == 3);
4448 CONVERT_CHECKED(JSObject, obj, args[0]);
4449 CONVERT_CHECKED(String, name, args[1]);
4450 CONVERT_CHECKED(Smi, flag, args[2]);
4451 return obj->LookupAccessor(name, flag->value() == 0);
4452}
4453
4454
4455// Helper functions for wrapping and unwrapping stack frame ids.
4456static Smi* WrapFrameId(StackFrame::Id id) {
4457 ASSERT(IsAligned(OffsetFrom(id), 4));
4458 return Smi::FromInt(id >> 2);
4459}
4460
4461
4462static StackFrame::Id UnwrapFrameId(Smi* wrapped) {
4463 return static_cast<StackFrame::Id>(wrapped->value() << 2);
4464}
4465
4466
4467// Adds a JavaScript function as a debug event listener.
4468// args[0]: debug event listener function
4469// args[1]: object supplied during callback
4470static Object* Runtime_AddDebugEventListener(Arguments args) {
4471 ASSERT(args.length() == 2);
4472 // Convert the parameters to API objects to call the API function for adding
4473 // a JavaScript function as debug event listener.
4474 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
4475 v8::Handle<v8::Function> fun(ToApi<v8::Function>(raw_fun));
4476 v8::Handle<v8::Value> data(ToApi<v8::Value>(args.at<Object>(0)));
4477 v8::Debug::AddDebugEventListener(fun, data);
4478
4479 return Heap::undefined_value();
4480}
4481
4482
4483// Removes a JavaScript function debug event listener.
4484// args[0]: debug event listener function
4485static Object* Runtime_RemoveDebugEventListener(Arguments args) {
4486 ASSERT(args.length() == 1);
4487 // Convert the parameter to an API object to call the API function for
4488 // removing a JavaScript function debug event listener.
4489 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
4490 v8::Handle<v8::Function> fun(ToApi<v8::Function>(raw_fun));
4491 v8::Debug::RemoveDebugEventListener(fun);
4492
4493 return Heap::undefined_value();
4494}
4495
4496
4497static Object* Runtime_Break(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00004498 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004499 StackGuard::DebugBreak();
4500 return Heap::undefined_value();
4501}
4502
4503
4504static Object* DebugLookupResultValue(LookupResult* result) {
4505 Object* value;
4506 switch (result->type()) {
4507 case NORMAL: {
4508 Dictionary* dict =
4509 JSObject::cast(result->holder())->property_dictionary();
4510 value = dict->ValueAt(result->GetDictionaryEntry());
4511 if (value->IsTheHole()) {
4512 return Heap::undefined_value();
4513 }
4514 return value;
4515 }
4516 case FIELD:
4517 value =
4518 JSObject::cast(
ager@chromium.org7c537e22008-10-16 08:43:32 +00004519 result->holder())->FastPropertyAt(result->GetFieldIndex());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004520 if (value->IsTheHole()) {
4521 return Heap::undefined_value();
4522 }
4523 return value;
4524 case CONSTANT_FUNCTION:
4525 return result->GetConstantFunction();
4526 case CALLBACKS:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004527 case INTERCEPTOR:
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004528 case MAP_TRANSITION:
4529 case CONSTANT_TRANSITION:
4530 case NULL_DESCRIPTOR:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004531 return Heap::undefined_value();
4532 default:
4533 UNREACHABLE();
4534 }
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004535 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004536 return Heap::undefined_value();
4537}
4538
4539
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004540static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004541 HandleScope scope;
4542
4543 ASSERT(args.length() == 2);
4544
4545 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4546 CONVERT_ARG_CHECKED(String, name, 1);
4547
4548 // Check if the name is trivially convertible to an index and get the element
4549 // if so.
4550 uint32_t index;
4551 if (name->AsArrayIndex(&index)) {
4552 Handle<FixedArray> details = Factory::NewFixedArray(2);
4553 details->set(0, Runtime::GetElementOrCharAt(obj, index));
4554 details->set(1, PropertyDetails(NONE, NORMAL).AsSmi());
4555 return *Factory::NewJSArrayWithElements(details);
4556 }
4557
4558 // Perform standard local lookup on the object.
4559 LookupResult result;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00004560 obj->Lookup(*name, &result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004561 if (result.IsProperty()) {
4562 Handle<Object> value(DebugLookupResultValue(&result));
4563 Handle<FixedArray> details = Factory::NewFixedArray(2);
4564 details->set(0, *value);
4565 details->set(1, result.GetPropertyDetails().AsSmi());
4566 return *Factory::NewJSArrayWithElements(details);
4567 }
4568 return Heap::undefined_value();
4569}
4570
4571
4572static Object* Runtime_DebugGetProperty(Arguments args) {
4573 HandleScope scope;
4574
4575 ASSERT(args.length() == 2);
4576
4577 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4578 CONVERT_ARG_CHECKED(String, name, 1);
4579
4580 LookupResult result;
4581 obj->Lookup(*name, &result);
4582 if (result.IsProperty()) {
4583 return DebugLookupResultValue(&result);
4584 }
4585 return Heap::undefined_value();
4586}
4587
4588
4589// Return the names of the local named properties.
4590// args[0]: object
4591static Object* Runtime_DebugLocalPropertyNames(Arguments args) {
4592 HandleScope scope;
4593 ASSERT(args.length() == 1);
4594 if (!args[0]->IsJSObject()) {
4595 return Heap::undefined_value();
4596 }
4597 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4598
4599 int n = obj->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
4600 Handle<FixedArray> names = Factory::NewFixedArray(n);
4601 obj->GetLocalPropertyNames(*names);
4602 return *Factory::NewJSArrayWithElements(names);
4603}
4604
4605
4606// Return the names of the local indexed properties.
4607// args[0]: object
4608static Object* Runtime_DebugLocalElementNames(Arguments args) {
4609 HandleScope scope;
4610 ASSERT(args.length() == 1);
4611 if (!args[0]->IsJSObject()) {
4612 return Heap::undefined_value();
4613 }
4614 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4615
4616 int n = obj->NumberOfLocalElements(static_cast<PropertyAttributes>(NONE));
4617 Handle<FixedArray> names = Factory::NewFixedArray(n);
4618 obj->GetLocalElementKeys(*names, static_cast<PropertyAttributes>(NONE));
4619 return *Factory::NewJSArrayWithElements(names);
4620}
4621
4622
4623// Return the property type calculated from the property details.
4624// args[0]: smi with property details.
4625static Object* Runtime_DebugPropertyTypeFromDetails(Arguments args) {
4626 ASSERT(args.length() == 1);
4627 CONVERT_CHECKED(Smi, details, args[0]);
4628 PropertyType type = PropertyDetails(details).type();
4629 return Smi::FromInt(static_cast<int>(type));
4630}
4631
4632
4633// Return the property attribute calculated from the property details.
4634// args[0]: smi with property details.
4635static Object* Runtime_DebugPropertyAttributesFromDetails(Arguments args) {
4636 ASSERT(args.length() == 1);
4637 CONVERT_CHECKED(Smi, details, args[0]);
4638 PropertyAttributes attributes = PropertyDetails(details).attributes();
4639 return Smi::FromInt(static_cast<int>(attributes));
4640}
4641
4642
4643// Return the property insertion index calculated from the property details.
4644// args[0]: smi with property details.
4645static Object* Runtime_DebugPropertyIndexFromDetails(Arguments args) {
4646 ASSERT(args.length() == 1);
4647 CONVERT_CHECKED(Smi, details, args[0]);
4648 int index = PropertyDetails(details).index();
4649 return Smi::FromInt(index);
4650}
4651
4652
4653// Return information on whether an object has a named or indexed interceptor.
4654// args[0]: object
4655static Object* Runtime_DebugInterceptorInfo(Arguments args) {
4656 HandleScope scope;
4657 ASSERT(args.length() == 1);
4658 if (!args[0]->IsJSObject()) {
4659 return Smi::FromInt(0);
4660 }
4661 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4662
4663 int result = 0;
4664 if (obj->HasNamedInterceptor()) result |= 2;
4665 if (obj->HasIndexedInterceptor()) result |= 1;
4666
4667 return Smi::FromInt(result);
4668}
4669
4670
4671// Return property names from named interceptor.
4672// args[0]: object
4673static Object* Runtime_DebugNamedInterceptorPropertyNames(Arguments args) {
4674 HandleScope scope;
4675 ASSERT(args.length() == 1);
4676 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4677 RUNTIME_ASSERT(obj->HasNamedInterceptor());
4678
4679 v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
4680 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4681 return Heap::undefined_value();
4682}
4683
4684
4685// Return element names from indexed interceptor.
4686// args[0]: object
4687static Object* Runtime_DebugIndexedInterceptorElementNames(Arguments args) {
4688 HandleScope scope;
4689 ASSERT(args.length() == 1);
4690 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4691 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
4692
4693 v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
4694 if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
4695 return Heap::undefined_value();
4696}
4697
4698
4699// Return property value from named interceptor.
4700// args[0]: object
4701// args[1]: property name
4702static Object* Runtime_DebugNamedInterceptorPropertyValue(Arguments args) {
4703 HandleScope scope;
4704 ASSERT(args.length() == 2);
4705 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4706 RUNTIME_ASSERT(obj->HasNamedInterceptor());
4707 CONVERT_ARG_CHECKED(String, name, 1);
4708
4709 PropertyAttributes attributes;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004710 return obj->GetPropertyWithInterceptor(*obj, *name, &attributes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004711}
4712
4713
4714// Return element value from indexed interceptor.
4715// args[0]: object
4716// args[1]: index
4717static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
4718 HandleScope scope;
4719 ASSERT(args.length() == 2);
4720 CONVERT_ARG_CHECKED(JSObject, obj, 0);
4721 RUNTIME_ASSERT(obj->HasIndexedInterceptor());
4722 CONVERT_NUMBER_CHECKED(uint32_t, index, Uint32, args[1]);
4723
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004724 return obj->GetElementWithInterceptor(*obj, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004725}
4726
4727
4728static Object* Runtime_CheckExecutionState(Arguments args) {
4729 ASSERT(args.length() >= 1);
4730 CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
ager@chromium.org8bb60582008-12-11 12:02:20 +00004731 // Check that the break id is valid.
4732 if (Top::break_id() == 0 || break_id != Top::break_id()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004733 return Top::Throw(Heap::illegal_execution_state_symbol());
4734 }
4735
4736 return Heap::true_value();
4737}
4738
4739
4740static Object* Runtime_GetFrameCount(Arguments args) {
4741 HandleScope scope;
4742 ASSERT(args.length() == 1);
4743
4744 // Check arguments.
4745 Object* result = Runtime_CheckExecutionState(args);
4746 if (result->IsFailure()) return result;
4747
4748 // Count all frames which are relevant to debugging stack trace.
4749 int n = 0;
4750 StackFrame::Id id = Top::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00004751 if (id == StackFrame::NO_ID) {
4752 // If there is no JavaScript stack frame count is 0.
4753 return Smi::FromInt(0);
4754 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004755 for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
4756 return Smi::FromInt(n);
4757}
4758
4759
4760static const int kFrameDetailsFrameIdIndex = 0;
4761static const int kFrameDetailsReceiverIndex = 1;
4762static const int kFrameDetailsFunctionIndex = 2;
4763static const int kFrameDetailsArgumentCountIndex = 3;
4764static const int kFrameDetailsLocalCountIndex = 4;
4765static const int kFrameDetailsSourcePositionIndex = 5;
4766static const int kFrameDetailsConstructCallIndex = 6;
4767static const int kFrameDetailsDebuggerFrameIndex = 7;
4768static const int kFrameDetailsFirstDynamicIndex = 8;
4769
4770// Return an array with frame details
4771// args[0]: number: break id
4772// args[1]: number: frame index
4773//
4774// The array returned contains the following information:
4775// 0: Frame id
4776// 1: Receiver
4777// 2: Function
4778// 3: Argument count
4779// 4: Local count
4780// 5: Source position
4781// 6: Constructor call
4782// 7: Debugger frame
4783// Arguments name, value
4784// Locals name, value
4785static Object* Runtime_GetFrameDetails(Arguments args) {
4786 HandleScope scope;
4787 ASSERT(args.length() == 2);
4788
4789 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00004790 Object* check = Runtime_CheckExecutionState(args);
4791 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004792 CONVERT_NUMBER_CHECKED(int, index, Int32, args[1]);
4793
4794 // Find the relevant frame with the requested index.
4795 StackFrame::Id id = Top::break_frame_id();
ager@chromium.org8bb60582008-12-11 12:02:20 +00004796 if (id == StackFrame::NO_ID) {
4797 // If there are no JavaScript stack frames return undefined.
4798 return Heap::undefined_value();
4799 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004800 int count = 0;
4801 JavaScriptFrameIterator it(id);
4802 for (; !it.done(); it.Advance()) {
4803 if (count == index) break;
4804 count++;
4805 }
4806 if (it.done()) return Heap::undefined_value();
4807
4808 // Traverse the saved contexts chain to find the active context for the
4809 // selected frame.
4810 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004811 while (save != NULL && !save->below(it.frame())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004812 save = save->prev();
4813 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +00004814 ASSERT(save != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004815
4816 // Get the frame id.
4817 Handle<Object> frame_id(WrapFrameId(it.frame()->id()));
4818
4819 // Find source position.
4820 int position = it.frame()->FindCode()->SourcePosition(it.frame()->pc());
4821
4822 // Check for constructor frame.
4823 bool constructor = it.frame()->IsConstructor();
4824
4825 // Get code and read scope info from it for local variable information.
4826 Handle<Code> code(it.frame()->FindCode());
4827 ScopeInfo<> info(*code);
4828
4829 // Get the context.
4830 Handle<Context> context(Context::cast(it.frame()->context()));
4831
4832 // Get the locals names and values into a temporary array.
4833 //
4834 // TODO(1240907): Hide compiler-introduced stack variables
4835 // (e.g. .result)? For users of the debugger, they will probably be
4836 // confusing.
4837 Handle<FixedArray> locals = Factory::NewFixedArray(info.NumberOfLocals() * 2);
4838 for (int i = 0; i < info.NumberOfLocals(); i++) {
4839 // Name of the local.
4840 locals->set(i * 2, *info.LocalName(i));
4841
4842 // Fetch the value of the local - either from the stack or from a
4843 // heap-allocated context.
4844 if (i < info.number_of_stack_slots()) {
4845 locals->set(i * 2 + 1, it.frame()->GetExpression(i));
4846 } else {
4847 Handle<String> name = info.LocalName(i);
4848 // Traverse the context chain to the function context as all local
4849 // variables stored in the context will be on the function context.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00004850 while (!context->is_function_context()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004851 context = Handle<Context>(context->previous());
4852 }
4853 ASSERT(context->is_function_context());
4854 locals->set(i * 2 + 1,
4855 context->get(ScopeInfo<>::ContextSlotIndex(*code, *name,
4856 NULL)));
4857 }
4858 }
4859
4860 // Now advance to the arguments adapter frame (if any). If contains all
4861 // the provided parameters and
4862
4863 // Now advance to the arguments adapter frame (if any). It contains all
4864 // the provided parameters whereas the function frame always have the number
4865 // of arguments matching the functions parameters. The rest of the
4866 // information (except for what is collected above) is the same.
4867 it.AdvanceToArgumentsFrame();
4868
4869 // Find the number of arguments to fill. At least fill the number of
4870 // parameters for the function and fill more if more parameters are provided.
4871 int argument_count = info.number_of_parameters();
4872 if (argument_count < it.frame()->GetProvidedParametersCount()) {
4873 argument_count = it.frame()->GetProvidedParametersCount();
4874 }
4875
4876 // Calculate the size of the result.
4877 int details_size = kFrameDetailsFirstDynamicIndex +
4878 2 * (argument_count + info.NumberOfLocals());
4879 Handle<FixedArray> details = Factory::NewFixedArray(details_size);
4880
4881 // Add the frame id.
4882 details->set(kFrameDetailsFrameIdIndex, *frame_id);
4883
4884 // Add the function (same as in function frame).
4885 details->set(kFrameDetailsFunctionIndex, it.frame()->function());
4886
4887 // Add the arguments count.
4888 details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
4889
4890 // Add the locals count
4891 details->set(kFrameDetailsLocalCountIndex,
4892 Smi::FromInt(info.NumberOfLocals()));
4893
4894 // Add the source position.
ager@chromium.org236ad962008-09-25 09:45:57 +00004895 if (position != RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00004896 details->set(kFrameDetailsSourcePositionIndex, Smi::FromInt(position));
4897 } else {
4898 details->set(kFrameDetailsSourcePositionIndex, Heap::undefined_value());
4899 }
4900
4901 // Add the constructor information.
4902 details->set(kFrameDetailsConstructCallIndex, Heap::ToBoolean(constructor));
4903
4904 // Add information on whether this frame is invoked in the debugger context.
4905 details->set(kFrameDetailsDebuggerFrameIndex,
4906 Heap::ToBoolean(*save->context() == *Debug::debug_context()));
4907
4908 // Fill the dynamic part.
4909 int details_index = kFrameDetailsFirstDynamicIndex;
4910
4911 // Add arguments name and value.
4912 for (int i = 0; i < argument_count; i++) {
4913 // Name of the argument.
4914 if (i < info.number_of_parameters()) {
4915 details->set(details_index++, *info.parameter_name(i));
4916 } else {
4917 details->set(details_index++, Heap::undefined_value());
4918 }
4919
4920 // Parameter value.
4921 if (i < it.frame()->GetProvidedParametersCount()) {
4922 details->set(details_index++, it.frame()->GetParameter(i));
4923 } else {
4924 details->set(details_index++, Heap::undefined_value());
4925 }
4926 }
4927
4928 // Add locals name and value from the temporary copy from the function frame.
4929 for (int i = 0; i < info.NumberOfLocals() * 2; i++) {
4930 details->set(details_index++, locals->get(i));
4931 }
4932
4933 // Add the receiver (same as in function frame).
4934 // THIS MUST BE DONE LAST SINCE WE MIGHT ADVANCE
4935 // THE FRAME ITERATOR TO WRAP THE RECEIVER.
4936 Handle<Object> receiver(it.frame()->receiver());
4937 if (!receiver->IsJSObject()) {
4938 // If the receiver is NOT a JSObject we have hit an optimization
4939 // where a value object is not converted into a wrapped JS objects.
4940 // To hide this optimization from the debugger, we wrap the receiver
4941 // by creating correct wrapper object based on the calling frame's
4942 // global context.
4943 it.Advance();
4944 Handle<Context> calling_frames_global_context(
4945 Context::cast(Context::cast(it.frame()->context())->global_context()));
4946 receiver = Factory::ToObject(receiver, calling_frames_global_context);
4947 }
4948 details->set(kFrameDetailsReceiverIndex, *receiver);
4949
4950 ASSERT_EQ(details_size, details_index);
4951 return *Factory::NewJSArrayWithElements(details);
4952}
4953
4954
4955static Object* Runtime_GetCFrames(Arguments args) {
4956 HandleScope scope;
4957 ASSERT(args.length() == 1);
4958 Object* result = Runtime_CheckExecutionState(args);
4959 if (result->IsFailure()) return result;
4960
4961 static const int kMaxCFramesSize = 200;
4962 OS::StackFrame frames[kMaxCFramesSize];
4963 int frames_count = OS::StackWalk(frames, kMaxCFramesSize);
4964 if (frames_count == OS::kStackWalkError) {
4965 return Heap::undefined_value();
4966 }
4967
4968 Handle<String> address_str = Factory::LookupAsciiSymbol("address");
4969 Handle<String> text_str = Factory::LookupAsciiSymbol("text");
4970 Handle<FixedArray> frames_array = Factory::NewFixedArray(frames_count);
4971 for (int i = 0; i < frames_count; i++) {
4972 Handle<JSObject> frame_value = Factory::NewJSObject(Top::object_function());
4973 frame_value->SetProperty(
4974 *address_str,
4975 *Factory::NewNumberFromInt(reinterpret_cast<int>(frames[i].address)),
4976 NONE);
4977
4978 // Get the stack walk text for this frame.
4979 Handle<String> frame_text;
4980 if (strlen(frames[i].text) > 0) {
4981 Vector<const char> str(frames[i].text, strlen(frames[i].text));
4982 frame_text = Factory::NewStringFromAscii(str);
4983 }
4984
4985 if (!frame_text.is_null()) {
4986 frame_value->SetProperty(*text_str, *frame_text, NONE);
4987 }
4988
4989 frames_array->set(i, *frame_value);
4990 }
4991 return *Factory::NewJSArrayWithElements(frames_array);
4992}
4993
4994
4995static Object* Runtime_GetBreakLocations(Arguments args) {
4996 HandleScope scope;
4997 ASSERT(args.length() == 1);
4998
4999 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
5000 Handle<SharedFunctionInfo> shared(raw_fun->shared());
5001 // Find the number of break points
5002 Handle<Object> break_locations = Debug::GetSourceBreakLocations(shared);
5003 if (break_locations->IsUndefined()) return Heap::undefined_value();
5004 // Return array as JS array
5005 return *Factory::NewJSArrayWithElements(
5006 Handle<FixedArray>::cast(break_locations));
5007}
5008
5009
5010// Set a break point in a function
5011// args[0]: function
5012// args[1]: number: break source position (within the function source)
5013// args[2]: number: break point object
5014static Object* Runtime_SetFunctionBreakPoint(Arguments args) {
5015 HandleScope scope;
5016 ASSERT(args.length() == 3);
5017 CONVERT_ARG_CHECKED(JSFunction, raw_fun, 0);
5018 Handle<SharedFunctionInfo> shared(raw_fun->shared());
5019 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
5020 RUNTIME_ASSERT(source_position >= 0);
5021 Handle<Object> break_point_object_arg = args.at<Object>(2);
5022
5023 // Set break point.
5024 Debug::SetBreakPoint(shared, source_position, break_point_object_arg);
5025
5026 return Heap::undefined_value();
5027}
5028
5029
5030static Object* FindSharedFunctionInfoInScript(Handle<Script> script,
5031 int position) {
5032 // Iterate the heap looking for SharedFunctionInfo generated from the
5033 // script. The inner most SharedFunctionInfo containing the source position
5034 // for the requested break point is found.
5035 // NOTE: This might reqire several heap iterations. If the SharedFunctionInfo
5036 // which is found is not compiled it is compiled and the heap is iterated
5037 // again as the compilation might create inner functions from the newly
5038 // compiled function and the actual requested break point might be in one of
5039 // these functions.
5040 bool done = false;
5041 // The current candidate for the source position:
ager@chromium.org236ad962008-09-25 09:45:57 +00005042 int target_start_position = RelocInfo::kNoPosition;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005043 Handle<SharedFunctionInfo> target;
5044 // The current candidate for the last function in script:
5045 Handle<SharedFunctionInfo> last;
5046 while (!done) {
5047 HeapIterator iterator;
5048 while (iterator.has_next()) {
5049 HeapObject* obj = iterator.next();
5050 ASSERT(obj != NULL);
5051 if (obj->IsSharedFunctionInfo()) {
5052 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(obj));
5053 if (shared->script() == *script) {
5054 // If the SharedFunctionInfo found has the requested script data and
5055 // contains the source position it is a candidate.
5056 int start_position = shared->function_token_position();
ager@chromium.org236ad962008-09-25 09:45:57 +00005057 if (start_position == RelocInfo::kNoPosition) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005058 start_position = shared->start_position();
5059 }
5060 if (start_position <= position &&
5061 position <= shared->end_position()) {
5062 // If there is no candidate or this function is within the currrent
5063 // candidate this is the new candidate.
5064 if (target.is_null()) {
5065 target_start_position = start_position;
5066 target = shared;
5067 } else {
5068 if (target_start_position < start_position &&
5069 shared->end_position() < target->end_position()) {
5070 target_start_position = start_position;
5071 target = shared;
5072 }
5073 }
5074 }
5075
5076 // Keep track of the last function in the script.
5077 if (last.is_null() ||
5078 shared->end_position() > last->start_position()) {
5079 last = shared;
5080 }
5081 }
5082 }
5083 }
5084
5085 // Make sure some candidate is selected.
5086 if (target.is_null()) {
5087 if (!last.is_null()) {
5088 // Position after the last function - use last.
5089 target = last;
5090 } else {
5091 // Unable to find function - possibly script without any function.
5092 return Heap::undefined_value();
5093 }
5094 }
5095
5096 // If the candidate found is compiled we are done. NOTE: when lazy
5097 // compilation of inner functions is introduced some additional checking
5098 // needs to be done here to compile inner functions.
5099 done = target->is_compiled();
5100 if (!done) {
5101 // If the candidate is not compiled compile it to reveal any inner
5102 // functions which might contain the requested source position.
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005103 CompileLazyShared(target, KEEP_EXCEPTION, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005104 }
5105 }
5106
5107 return *target;
5108}
5109
5110
5111// Change the state of a break point in a script. NOTE: Regarding performance
5112// see the NOTE for GetScriptFromScriptData.
5113// args[0]: script to set break point in
5114// args[1]: number: break source position (within the script source)
5115// args[2]: number: break point object
5116static Object* Runtime_SetScriptBreakPoint(Arguments args) {
5117 HandleScope scope;
5118 ASSERT(args.length() == 3);
5119 CONVERT_ARG_CHECKED(JSValue, wrapper, 0);
5120 CONVERT_NUMBER_CHECKED(int32_t, source_position, Int32, args[1]);
5121 RUNTIME_ASSERT(source_position >= 0);
5122 Handle<Object> break_point_object_arg = args.at<Object>(2);
5123
5124 // Get the script from the script wrapper.
5125 RUNTIME_ASSERT(wrapper->value()->IsScript());
5126 Handle<Script> script(Script::cast(wrapper->value()));
5127
5128 Object* result = FindSharedFunctionInfoInScript(script, source_position);
5129 if (!result->IsUndefined()) {
5130 Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result));
5131 // Find position within function. The script position might be before the
5132 // source position of the first function.
5133 int position;
5134 if (shared->start_position() > source_position) {
5135 position = 0;
5136 } else {
5137 position = source_position - shared->start_position();
5138 }
5139 Debug::SetBreakPoint(shared, position, break_point_object_arg);
5140 }
5141 return Heap::undefined_value();
5142}
5143
5144
5145// Clear a break point
5146// args[0]: number: break point object
5147static Object* Runtime_ClearBreakPoint(Arguments args) {
5148 HandleScope scope;
5149 ASSERT(args.length() == 1);
5150 Handle<Object> break_point_object_arg = args.at<Object>(0);
5151
5152 // Clear break point.
5153 Debug::ClearBreakPoint(break_point_object_arg);
5154
5155 return Heap::undefined_value();
5156}
5157
5158
5159// Change the state of break on exceptions
5160// args[0]: boolean indicating uncaught exceptions
5161// args[1]: boolean indicating on/off
5162static Object* Runtime_ChangeBreakOnException(Arguments args) {
5163 HandleScope scope;
5164 ASSERT(args.length() == 2);
5165 ASSERT(args[0]->IsNumber());
5166 ASSERT(args[1]->IsBoolean());
5167
5168 // Update break point state
5169 ExceptionBreakType type =
5170 static_cast<ExceptionBreakType>(NumberToUint32(args[0]));
5171 bool enable = args[1]->ToBoolean()->IsTrue();
5172 Debug::ChangeBreakOnException(type, enable);
5173 return Heap::undefined_value();
5174}
5175
5176
5177// Prepare for stepping
5178// args[0]: break id for checking execution state
5179// args[1]: step action from the enumeration StepAction
5180// args[2]: number of times to perform the step
5181static Object* Runtime_PrepareStep(Arguments args) {
5182 HandleScope scope;
5183 ASSERT(args.length() == 3);
5184 // Check arguments.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00005185 Object* check = Runtime_CheckExecutionState(args);
5186 if (check->IsFailure()) return check;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005187 if (!args[1]->IsNumber() || !args[2]->IsNumber()) {
5188 return Top::Throw(Heap::illegal_argument_symbol());
5189 }
5190
5191 // Get the step action and check validity.
5192 StepAction step_action = static_cast<StepAction>(NumberToInt32(args[1]));
5193 if (step_action != StepIn &&
5194 step_action != StepNext &&
5195 step_action != StepOut &&
5196 step_action != StepInMin &&
5197 step_action != StepMin) {
5198 return Top::Throw(Heap::illegal_argument_symbol());
5199 }
5200
5201 // Get the number of steps.
5202 int step_count = NumberToInt32(args[2]);
5203 if (step_count < 1) {
5204 return Top::Throw(Heap::illegal_argument_symbol());
5205 }
5206
5207 // Prepare step.
5208 Debug::PrepareStep(static_cast<StepAction>(step_action), step_count);
5209 return Heap::undefined_value();
5210}
5211
5212
5213// Clear all stepping set by PrepareStep.
5214static Object* Runtime_ClearStepping(Arguments args) {
5215 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00005216 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005217 Debug::ClearStepping();
5218 return Heap::undefined_value();
5219}
5220
5221
5222// Creates a copy of the with context chain. The copy of the context chain is
5223// is linked to the function context supplied.
5224static Handle<Context> CopyWithContextChain(Handle<Context> context_chain,
5225 Handle<Context> function_context) {
5226 // At the bottom of the chain. Return the function context to link to.
5227 if (context_chain->is_function_context()) {
5228 return function_context;
5229 }
5230
5231 // Recursively copy the with contexts.
5232 Handle<Context> previous(context_chain->previous());
5233 Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
5234 return Factory::NewWithContext(
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005235 CopyWithContextChain(function_context, previous),
5236 extension,
5237 context_chain->IsCatchContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005238}
5239
5240
5241// Helper function to find or create the arguments object for
5242// Runtime_DebugEvaluate.
5243static Handle<Object> GetArgumentsObject(JavaScriptFrame* frame,
5244 Handle<JSFunction> function,
5245 Handle<Code> code,
5246 const ScopeInfo<>* sinfo,
5247 Handle<Context> function_context) {
5248 // Try to find the value of 'arguments' to pass as parameter. If it is not
5249 // found (that is the debugged function does not reference 'arguments' and
5250 // does not support eval) then create an 'arguments' object.
5251 int index;
5252 if (sinfo->number_of_stack_slots() > 0) {
5253 index = ScopeInfo<>::StackSlotIndex(*code, Heap::arguments_symbol());
5254 if (index != -1) {
5255 return Handle<Object>(frame->GetExpression(index));
5256 }
5257 }
5258
5259 if (sinfo->number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
5260 index = ScopeInfo<>::ContextSlotIndex(*code, Heap::arguments_symbol(),
5261 NULL);
5262 if (index != -1) {
5263 return Handle<Object>(function_context->get(index));
5264 }
5265 }
5266
5267 const int length = frame->GetProvidedParametersCount();
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005268 Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
5269 Handle<FixedArray> array = Factory::NewFixedArray(length);
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005270 WriteBarrierMode mode = array->GetWriteBarrierMode();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005271 for (int i = 0; i < length; i++) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00005272 array->set(i, frame->GetParameter(i), mode);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005273 }
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005274 arguments->set_elements(*array);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005275 return arguments;
5276}
5277
5278
5279// Evaluate a piece of JavaScript in the context of a stack frame for
5280// debugging. This is acomplished by creating a new context which in its
5281// extension part has all the parameters and locals of the function on the
5282// stack frame. A function which calls eval with the code to evaluate is then
5283// compiled in this context and called in this context. As this context
5284// replaces the context of the function on the stack frame a new (empty)
5285// function is created as well to be used as the closure for the context.
5286// This function and the context acts as replacements for the function on the
5287// stack frame presenting the same view of the values of parameters and
5288// local variables as if the piece of JavaScript was evaluated at the point
5289// where the function on the stack frame is currently stopped.
5290static Object* Runtime_DebugEvaluate(Arguments args) {
5291 HandleScope scope;
5292
5293 // Check the execution state and decode arguments frame and source to be
5294 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005295 ASSERT(args.length() == 4);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005296 Object* check_result = Runtime_CheckExecutionState(args);
5297 if (check_result->IsFailure()) return check_result;
5298 CONVERT_CHECKED(Smi, wrapped_id, args[1]);
5299 CONVERT_ARG_CHECKED(String, source, 2);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005300 CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
5301
5302 // Handle the processing of break.
5303 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005304
5305 // Get the frame where the debugging is performed.
5306 StackFrame::Id id = UnwrapFrameId(wrapped_id);
5307 JavaScriptFrameIterator it(id);
5308 JavaScriptFrame* frame = it.frame();
5309 Handle<JSFunction> function(JSFunction::cast(frame->function()));
5310 Handle<Code> code(function->code());
5311 ScopeInfo<> sinfo(*code);
5312
5313 // Traverse the saved contexts chain to find the active context for the
5314 // selected frame.
5315 SaveContext* save = Top::save_context();
ager@chromium.orga74f0da2008-12-03 16:05:52 +00005316 while (save != NULL && !save->below(frame)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005317 save = save->prev();
5318 }
5319 ASSERT(save != NULL);
5320 SaveContext savex;
5321 Top::set_context(*(save->context()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005322
5323 // Create the (empty) function replacing the function on the stack frame for
5324 // the purpose of evaluating in the context created below. It is important
5325 // that this function does not describe any parameters and local variables
5326 // in the context. If it does then this will cause problems with the lookup
5327 // in Context::Lookup, where context slots for parameters and local variables
5328 // are looked at before the extension object.
5329 Handle<JSFunction> go_between =
5330 Factory::NewFunction(Factory::empty_string(), Factory::undefined_value());
5331 go_between->set_context(function->context());
5332#ifdef DEBUG
5333 ScopeInfo<> go_between_sinfo(go_between->shared()->code());
5334 ASSERT(go_between_sinfo.number_of_parameters() == 0);
5335 ASSERT(go_between_sinfo.number_of_context_slots() == 0);
5336#endif
5337
5338 // Allocate and initialize a context extension object with all the
5339 // arguments, stack locals heap locals and extension properties of the
5340 // debugged function.
5341 Handle<JSObject> context_ext = Factory::NewJSObject(Top::object_function());
5342 // First fill all parameters to the context extension.
5343 for (int i = 0; i < sinfo.number_of_parameters(); ++i) {
5344 SetProperty(context_ext,
5345 sinfo.parameter_name(i),
5346 Handle<Object>(frame->GetParameter(i)), NONE);
5347 }
5348 // Second fill all stack locals to the context extension.
5349 for (int i = 0; i < sinfo.number_of_stack_slots(); i++) {
5350 SetProperty(context_ext,
5351 sinfo.stack_slot_name(i),
5352 Handle<Object>(frame->GetExpression(i)), NONE);
5353 }
5354 // Third fill all context locals to the context extension.
5355 Handle<Context> frame_context(Context::cast(frame->context()));
5356 Handle<Context> function_context(frame_context->fcontext());
5357 for (int i = Context::MIN_CONTEXT_SLOTS;
5358 i < sinfo.number_of_context_slots();
5359 ++i) {
5360 int context_index =
5361 ScopeInfo<>::ContextSlotIndex(*code, *sinfo.context_slot_name(i), NULL);
5362 SetProperty(context_ext,
5363 sinfo.context_slot_name(i),
5364 Handle<Object>(function_context->get(context_index)), NONE);
5365 }
5366 // Finally copy any properties from the function context extension. This will
5367 // be variables introduced by eval.
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +00005368 if (function_context->has_extension() &&
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005369 !function_context->IsGlobalContext()) {
5370 Handle<JSObject> ext(JSObject::cast(function_context->extension()));
5371 Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
5372 for (int i = 0; i < keys->length(); i++) {
5373 // Names of variables introduced by eval are strings.
5374 ASSERT(keys->get(i)->IsString());
5375 Handle<String> key(String::cast(keys->get(i)));
5376 SetProperty(context_ext, key, GetProperty(ext, key), NONE);
5377 }
5378 }
5379
5380 // Allocate a new context for the debug evaluation and set the extension
5381 // object build.
5382 Handle<Context> context =
5383 Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, go_between);
5384 context->set_extension(*context_ext);
5385 // Copy any with contexts present and chain them in front of this context.
5386 context = CopyWithContextChain(frame_context, context);
5387
5388 // Wrap the evaluation statement in a new function compiled in the newly
5389 // created context. The function has one parameter which has to be called
5390 // 'arguments'. This it to have access to what would have been 'arguments' in
5391 // the function beeing debugged.
5392 // function(arguments,__source__) {return eval(__source__);}
5393 static const char* source_str =
5394 "function(arguments,__source__){return eval(__source__);}";
5395 static const int source_str_length = strlen(source_str);
5396 Handle<String> function_source =
5397 Factory::NewStringFromAscii(Vector<const char>(source_str,
5398 source_str_length));
5399 Handle<JSFunction> boilerplate =
ager@chromium.org236ad962008-09-25 09:45:57 +00005400 Compiler::CompileEval(function_source, 0, context->IsGlobalContext());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005401 if (boilerplate.is_null()) return Failure::Exception();
5402 Handle<JSFunction> compiled_function =
5403 Factory::NewFunctionFromBoilerplate(boilerplate, context);
5404
5405 // Invoke the result of the compilation to get the evaluation function.
5406 bool has_pending_exception;
5407 Handle<Object> receiver(frame->receiver());
5408 Handle<Object> evaluation_function =
5409 Execution::Call(compiled_function, receiver, 0, NULL,
5410 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005411 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005412
5413 Handle<Object> arguments = GetArgumentsObject(frame, function, code, &sinfo,
5414 function_context);
5415
5416 // Invoke the evaluation function and return the result.
5417 const int argc = 2;
5418 Object** argv[argc] = { arguments.location(),
5419 Handle<Object>::cast(source).location() };
5420 Handle<Object> result =
5421 Execution::Call(Handle<JSFunction>::cast(evaluation_function), receiver,
5422 argc, argv, &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005423 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005424 return *result;
5425}
5426
5427
5428static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
5429 HandleScope scope;
5430
5431 // Check the execution state and decode arguments frame and source to be
5432 // evaluated.
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005433 ASSERT(args.length() == 3);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005434 Object* check_result = Runtime_CheckExecutionState(args);
5435 if (check_result->IsFailure()) return check_result;
5436 CONVERT_ARG_CHECKED(String, source, 1);
kasper.lundbd3ec4e2008-07-09 11:06:54 +00005437 CONVERT_BOOLEAN_CHECKED(disable_break, args[2]);
5438
5439 // Handle the processing of break.
5440 DisableBreak disable_break_save(disable_break);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005441
5442 // Enter the top context from before the debugger was invoked.
5443 SaveContext save;
5444 SaveContext* top = &save;
5445 while (top != NULL && *top->context() == *Debug::debug_context()) {
5446 top = top->prev();
5447 }
5448 if (top != NULL) {
5449 Top::set_context(*top->context());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005450 }
5451
5452 // Get the global context now set to the top context from before the
5453 // debugger was invoked.
5454 Handle<Context> context = Top::global_context();
5455
5456 // Compile the source to be evaluated.
ager@chromium.org236ad962008-09-25 09:45:57 +00005457 Handle<JSFunction> boilerplate(Compiler::CompileEval(source, 0, true));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005458 if (boilerplate.is_null()) return Failure::Exception();
5459 Handle<JSFunction> compiled_function =
5460 Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
5461 context));
5462
5463 // Invoke the result of the compilation to get the evaluation function.
5464 bool has_pending_exception;
5465 Handle<Object> receiver = Top::global();
5466 Handle<Object> result =
5467 Execution::Call(compiled_function, receiver, 0, NULL,
5468 &has_pending_exception);
ager@chromium.org3bf7b912008-11-17 09:09:45 +00005469 if (has_pending_exception) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005470 return *result;
5471}
5472
5473
5474// Helper function used by Runtime_DebugGetLoadedScripts below.
5475static int DebugGetLoadedScripts(FixedArray* instances, int instances_size) {
5476 NoHandleAllocation ha;
5477 AssertNoAllocation no_alloc;
5478
5479 // Get hold of the current empty script.
5480 Context* context = Top::context()->global_context();
5481 Script* empty = context->empty_script();
5482
5483 // Scan heap for Script objects.
5484 int count = 0;
5485 HeapIterator iterator;
5486 while (iterator.has_next()) {
5487 HeapObject* obj = iterator.next();
5488 ASSERT(obj != NULL);
5489 if (obj->IsScript() && obj != empty) {
5490 if (instances != NULL && count < instances_size) {
5491 instances->set(count, obj);
5492 }
5493 count++;
5494 }
5495 }
5496
5497 return count;
5498}
5499
5500
5501static Object* Runtime_DebugGetLoadedScripts(Arguments args) {
5502 HandleScope scope;
mads.s.ager31e71382008-08-13 09:32:07 +00005503 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005504
5505 // Perform two GCs to get rid of all unreferenced scripts. The first GC gets
5506 // rid of all the cached script wrappes and the second gets rid of the
5507 // scripts which is no longer referenced.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005508 Heap::CollectAllGarbage();
5509 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005510
5511 // Get the number of scripts.
5512 int count;
5513 count = DebugGetLoadedScripts(NULL, 0);
5514
5515 // Allocate an array to hold the result.
5516 Handle<FixedArray> instances = Factory::NewFixedArray(count);
5517
5518 // Fill the script objects.
5519 count = DebugGetLoadedScripts(*instances, count);
5520
5521 // Convert the script objects to proper JS objects.
5522 for (int i = 0; i < count; i++) {
ager@chromium.org7c537e22008-10-16 08:43:32 +00005523 Handle<Script> script = Handle<Script>(Script::cast(instances->get(i)));
5524 // Get the script wrapper in a local handle before calling GetScriptWrapper,
5525 // because using
5526 // instances->set(i, *GetScriptWrapper(script))
5527 // is unsafe as GetScriptWrapper might call GC and the C++ compiler might
5528 // already have deferenced the instances handle.
5529 Handle<JSValue> wrapper = GetScriptWrapper(script);
5530 instances->set(i, *wrapper);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005531 }
5532
5533 // Return result as a JS array.
5534 Handle<JSObject> result = Factory::NewJSObject(Top::array_function());
5535 Handle<JSArray>::cast(result)->SetContent(*instances);
5536 return *result;
5537}
5538
5539
5540// Helper function used by Runtime_DebugReferencedBy below.
5541static int DebugReferencedBy(JSObject* target,
5542 Object* instance_filter, int max_references,
5543 FixedArray* instances, int instances_size,
5544 JSFunction* context_extension_function,
5545 JSFunction* arguments_function) {
5546 NoHandleAllocation ha;
5547 AssertNoAllocation no_alloc;
5548
5549 // Iterate the heap.
5550 int count = 0;
5551 JSObject* last = NULL;
5552 HeapIterator iterator;
5553 while (iterator.has_next() &&
5554 (max_references == 0 || count < max_references)) {
5555 // Only look at all JSObjects.
5556 HeapObject* heap_obj = iterator.next();
5557 if (heap_obj->IsJSObject()) {
5558 // Skip context extension objects and argument arrays as these are
5559 // checked in the context of functions using them.
5560 JSObject* obj = JSObject::cast(heap_obj);
5561 if (obj->map()->constructor() == context_extension_function ||
5562 obj->map()->constructor() == arguments_function) {
5563 continue;
5564 }
5565
5566 // Check if the JS object has a reference to the object looked for.
5567 if (obj->ReferencesObject(target)) {
5568 // Check instance filter if supplied. This is normally used to avoid
5569 // references from mirror objects (see Runtime_IsInPrototypeChain).
5570 if (!instance_filter->IsUndefined()) {
5571 Object* V = obj;
5572 while (true) {
5573 Object* prototype = V->GetPrototype();
5574 if (prototype->IsNull()) {
5575 break;
5576 }
5577 if (instance_filter == prototype) {
5578 obj = NULL; // Don't add this object.
5579 break;
5580 }
5581 V = prototype;
5582 }
5583 }
5584
5585 if (obj != NULL) {
5586 // Valid reference found add to instance array if supplied an update
5587 // count.
5588 if (instances != NULL && count < instances_size) {
5589 instances->set(count, obj);
5590 }
5591 last = obj;
5592 count++;
5593 }
5594 }
5595 }
5596 }
5597
5598 // Check for circular reference only. This can happen when the object is only
5599 // referenced from mirrors and has a circular reference in which case the
5600 // object is not really alive and would have been garbage collected if not
5601 // referenced from the mirror.
5602 if (count == 1 && last == target) {
5603 count = 0;
5604 }
5605
5606 // Return the number of referencing objects found.
5607 return count;
5608}
5609
5610
5611// Scan the heap for objects with direct references to an object
5612// args[0]: the object to find references to
5613// args[1]: constructor function for instances to exclude (Mirror)
5614// args[2]: the the maximum number of objects to return
5615static Object* Runtime_DebugReferencedBy(Arguments args) {
5616 ASSERT(args.length() == 3);
5617
5618 // First perform a full GC in order to avoid references from dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005619 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005620
5621 // Check parameters.
5622 CONVERT_CHECKED(JSObject, target, args[0]);
5623 Object* instance_filter = args[1];
5624 RUNTIME_ASSERT(instance_filter->IsUndefined() ||
5625 instance_filter->IsJSObject());
5626 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[2]);
5627 RUNTIME_ASSERT(max_references >= 0);
5628
5629 // Get the constructor function for context extension and arguments array.
5630 JSFunction* context_extension_function =
5631 Top::context()->global_context()->context_extension_function();
5632 JSObject* arguments_boilerplate =
5633 Top::context()->global_context()->arguments_boilerplate();
5634 JSFunction* arguments_function =
5635 JSFunction::cast(arguments_boilerplate->map()->constructor());
5636
5637 // Get the number of referencing objects.
5638 int count;
5639 count = DebugReferencedBy(target, instance_filter, max_references,
5640 NULL, 0,
5641 context_extension_function, arguments_function);
5642
5643 // Allocate an array to hold the result.
5644 Object* object = Heap::AllocateFixedArray(count);
5645 if (object->IsFailure()) return object;
5646 FixedArray* instances = FixedArray::cast(object);
5647
5648 // Fill the referencing objects.
5649 count = DebugReferencedBy(target, instance_filter, max_references,
5650 instances, count,
5651 context_extension_function, arguments_function);
5652
5653 // Return result as JS array.
5654 Object* result =
5655 Heap::AllocateJSObject(
5656 Top::context()->global_context()->array_function());
5657 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
5658 return result;
5659}
5660
5661
5662// Helper function used by Runtime_DebugConstructedBy below.
5663static int DebugConstructedBy(JSFunction* constructor, int max_references,
5664 FixedArray* instances, int instances_size) {
5665 AssertNoAllocation no_alloc;
5666
5667 // Iterate the heap.
5668 int count = 0;
5669 HeapIterator iterator;
5670 while (iterator.has_next() &&
5671 (max_references == 0 || count < max_references)) {
5672 // Only look at all JSObjects.
5673 HeapObject* heap_obj = iterator.next();
5674 if (heap_obj->IsJSObject()) {
5675 JSObject* obj = JSObject::cast(heap_obj);
5676 if (obj->map()->constructor() == constructor) {
5677 // Valid reference found add to instance array if supplied an update
5678 // count.
5679 if (instances != NULL && count < instances_size) {
5680 instances->set(count, obj);
5681 }
5682 count++;
5683 }
5684 }
5685 }
5686
5687 // Return the number of referencing objects found.
5688 return count;
5689}
5690
5691
5692// Scan the heap for objects constructed by a specific function.
5693// args[0]: the constructor to find instances of
5694// args[1]: the the maximum number of objects to return
5695static Object* Runtime_DebugConstructedBy(Arguments args) {
5696 ASSERT(args.length() == 2);
5697
5698 // First perform a full GC in order to avoid dead objects.
ager@chromium.org9258b6b2008-09-11 09:11:10 +00005699 Heap::CollectAllGarbage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005700
5701 // Check parameters.
5702 CONVERT_CHECKED(JSFunction, constructor, args[0]);
5703 CONVERT_NUMBER_CHECKED(int32_t, max_references, Int32, args[1]);
5704 RUNTIME_ASSERT(max_references >= 0);
5705
5706 // Get the number of referencing objects.
5707 int count;
5708 count = DebugConstructedBy(constructor, max_references, NULL, 0);
5709
5710 // Allocate an array to hold the result.
5711 Object* object = Heap::AllocateFixedArray(count);
5712 if (object->IsFailure()) return object;
5713 FixedArray* instances = FixedArray::cast(object);
5714
5715 // Fill the referencing objects.
5716 count = DebugConstructedBy(constructor, max_references, instances, count);
5717
5718 // Return result as JS array.
5719 Object* result =
5720 Heap::AllocateJSObject(
5721 Top::context()->global_context()->array_function());
5722 if (!result->IsFailure()) JSArray::cast(result)->SetContent(instances);
5723 return result;
5724}
5725
5726
5727static Object* Runtime_GetPrototype(Arguments args) {
5728 ASSERT(args.length() == 1);
5729
5730 CONVERT_CHECKED(JSObject, obj, args[0]);
5731
5732 return obj->GetPrototype();
5733}
5734
5735
5736static Object* Runtime_SystemBreak(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005737 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005738 CPU::DebugBreak();
5739 return Heap::undefined_value();
5740}
5741
5742
5743// Finds the script object from the script data. NOTE: This operation uses
5744// heap traversal to find the function generated for the source position
5745// for the requested break point. For lazily compiled functions several heap
5746// traversals might be required rendering this operation as a rather slow
5747// operation. However for setting break points which is normally done through
5748// some kind of user interaction the performance is not crucial.
5749static Handle<Object> Runtime_GetScriptFromScriptName(
5750 Handle<String> script_name) {
5751 // Scan the heap for Script objects to find the script with the requested
5752 // script data.
5753 Handle<Script> script;
5754 HeapIterator iterator;
5755 while (script.is_null() && iterator.has_next()) {
5756 HeapObject* obj = iterator.next();
5757 // If a script is found check if it has the script data requested.
5758 if (obj->IsScript()) {
5759 if (Script::cast(obj)->name()->IsString()) {
5760 if (String::cast(Script::cast(obj)->name())->Equals(*script_name)) {
5761 script = Handle<Script>(Script::cast(obj));
5762 }
5763 }
5764 }
5765 }
5766
5767 // If no script with the requested script data is found return undefined.
5768 if (script.is_null()) return Factory::undefined_value();
5769
5770 // Return the script found.
5771 return GetScriptWrapper(script);
5772}
5773
5774
5775// Get the script object from script data. NOTE: Regarding performance
5776// see the NOTE for GetScriptFromScriptData.
5777// args[0]: script data for the script to find the source for
5778static Object* Runtime_GetScript(Arguments args) {
5779 HandleScope scope;
5780
5781 ASSERT(args.length() == 1);
5782
5783 CONVERT_CHECKED(String, script_name, args[0]);
5784
5785 // Find the requested script.
5786 Handle<Object> result =
5787 Runtime_GetScriptFromScriptName(Handle<String>(script_name));
5788 return *result;
5789}
5790
5791
5792static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
5793#ifdef DEBUG
5794 HandleScope scope;
5795 ASSERT(args.length() == 1);
5796 // Get the function and make sure it is compiled.
5797 CONVERT_ARG_CHECKED(JSFunction, func, 0);
5798 if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
5799 return Failure::Exception();
5800 }
5801 func->code()->PrintLn();
5802#endif // DEBUG
5803 return Heap::undefined_value();
5804}
5805
5806
5807static Object* Runtime_Abort(Arguments args) {
5808 ASSERT(args.length() == 2);
5809 OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
5810 Smi::cast(args[1])->value());
5811 Top::PrintStack();
5812 OS::Abort();
5813 UNREACHABLE();
5814 return NULL;
5815}
5816
5817
kasper.lund44510672008-07-25 07:37:58 +00005818#ifdef DEBUG
5819// ListNatives is ONLY used by the fuzz-natives.js in debug mode
5820// Exclude the code in release mode.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005821static Object* Runtime_ListNatives(Arguments args) {
mads.s.ager31e71382008-08-13 09:32:07 +00005822 ASSERT(args.length() == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005823 HandleScope scope;
5824 Handle<JSArray> result = Factory::NewJSArray(0);
5825 int index = 0;
5826#define ADD_ENTRY(Name, argc) \
5827 { \
5828 HandleScope inner; \
5829 Handle<String> name = \
5830 Factory::NewStringFromAscii(Vector<const char>(#Name, strlen(#Name))); \
5831 Handle<JSArray> pair = Factory::NewJSArray(0); \
5832 SetElement(pair, 0, name); \
5833 SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
5834 SetElement(result, index++, pair); \
5835 }
5836 RUNTIME_FUNCTION_LIST(ADD_ENTRY)
5837#undef ADD_ENTRY
5838 return *result;
5839}
kasper.lund44510672008-07-25 07:37:58 +00005840#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005841
5842
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00005843static Object* Runtime_Log(Arguments args) {
5844 ASSERT(args.length() == 2);
5845 String* format = String::cast(args[0]);
5846 Vector<const char> chars = format->ToAsciiVector();
5847 JSArray* elms = JSArray::cast(args[1]);
5848 Logger::LogRuntime(chars, elms);
5849 return Heap::undefined_value();
5850}
5851
5852
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005853static Object* Runtime_IS_VAR(Arguments args) {
5854 UNREACHABLE(); // implemented as macro in the parser
5855 return NULL;
5856}
5857
5858
5859// ----------------------------------------------------------------------------
5860// Implementation of Runtime
5861
5862#define F(name, nargs) \
5863 { #name, "RuntimeStub_" #name, FUNCTION_ADDR(Runtime_##name), nargs, \
5864 static_cast<int>(Runtime::k##name) },
5865
5866static Runtime::Function Runtime_functions[] = {
5867 RUNTIME_FUNCTION_LIST(F)
5868 { NULL, NULL, NULL, 0, -1 }
5869};
5870
5871#undef F
5872
5873
5874Runtime::Function* Runtime::FunctionForId(FunctionId fid) {
5875 ASSERT(0 <= fid && fid < kNofFunctions);
5876 return &Runtime_functions[fid];
5877}
5878
5879
5880Runtime::Function* Runtime::FunctionForName(const char* name) {
5881 for (Function* f = Runtime_functions; f->name != NULL; f++) {
5882 if (strcmp(f->name, name) == 0) {
5883 return f;
5884 }
5885 }
5886 return NULL;
5887}
5888
5889
5890void Runtime::PerformGC(Object* result) {
5891 Failure* failure = Failure::cast(result);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00005892 if (failure->IsRetryAfterGC()) {
5893 // Try to do a garbage collection; ignore it if it fails. The C
5894 // entry stub will throw an out-of-memory exception in that case.
5895 Heap::CollectGarbage(failure->requested(), failure->allocation_space());
5896 } else {
5897 // Handle last resort GC and make sure to allow future allocations
5898 // to grow the heap without causing GCs (if possible).
5899 Counters::gc_last_resort_from_js.Increment();
5900 Heap::CollectAllGarbage();
5901 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00005902}
5903
5904
5905} } // namespace v8::internal