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