Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(932)

Unified Diff: src/runtime.cc

Issue 6614010: [Isolates] Merge 6700:7030 from bleeding_edge to isolates. (Closed) Base URL: http://v8.googlecode.com/svn/branches/experimental/isolates/
Patch Set: '' Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/runtime.h ('k') | src/runtime.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/runtime.cc
===================================================================
--- src/runtime.cc (revision 7031)
+++ src/runtime.cc (working copy)
@@ -40,8 +40,10 @@
#include "debug.h"
#include "deoptimizer.h"
#include "execution.h"
+#include "global-handles.h"
#include "jsregexp.h"
#include "liveedit.h"
+#include "liveobjectlist-inl.h"
#include "parser.h"
#include "platform.h"
#include "runtime.h"
@@ -159,7 +161,8 @@
if (!maybe_result->ToObject(&result)) return maybe_result;
}
{ MaybeObject* maybe_result =
- copy->SetProperty(key_string, result, NONE);
+ // Creating object copy for literals. No strict mode needed.
+ copy->SetProperty(key_string, result, NONE, kNonStrictMode);
if (!maybe_result->ToObject(&result)) return maybe_result;
}
}
@@ -573,7 +576,9 @@
// Assign the exception value to the catch variable and make sure
// that the catch variable is DontDelete.
{ MaybeObject* maybe_value =
- JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
+ // Passing non-strict per ECMA-262 5th Ed. 12.14. Catch, bullet #4.
+ JSObject::cast(object)->SetProperty(
+ key, value, DONT_DELETE, kNonStrictMode);
if (!maybe_value->ToObject(&value)) return maybe_value;
}
return object;
@@ -817,7 +822,8 @@
case JSObject::INTERCEPTED_ELEMENT:
case JSObject::FAST_ELEMENT: {
elms->set(IS_ACCESSOR_INDEX, heap->false_value());
- elms->set(VALUE_INDEX, *GetElement(obj, index));
+ Handle<Object> value = GetElement(obj, index);
+ elms->set(VALUE_INDEX, *value);
elms->set(WRITABLE_INDEX, heap->true_value());
elms->set(ENUMERABLE_INDEX, heap->true_value());
elms->set(CONFIGURABLE_INDEX, heap->true_value());
@@ -850,12 +856,14 @@
}
break;
}
- case NORMAL:
+ case NORMAL: {
// This is a data property.
elms->set(IS_ACCESSOR_INDEX, heap->false_value());
- elms->set(VALUE_INDEX, *GetElement(obj, index));
+ Handle<Object> value = GetElement(obj, index);
+ elms->set(VALUE_INDEX, *value);
elms->set(WRITABLE_INDEX, heap->ToBoolean(!details.IsReadOnly()));
break;
+ }
default:
UNREACHABLE();
break;
@@ -1041,6 +1049,7 @@
static MaybeObject* Runtime_DeclareGlobals(RUNTIME_CALLING_CONVENTION) {
RUNTIME_GET_ISOLATE;
+ ASSERT(args.length() == 4);
HandleScope scope(isolate);
Handle<GlobalObject> global = Handle<GlobalObject>(
isolate->context()->global());
@@ -1048,6 +1057,9 @@
Handle<Context> context = args.at<Context>(0);
CONVERT_ARG_CHECKED(FixedArray, pairs, 1);
bool is_eval = Smi::cast(args[2])->value() == 1;
+ StrictModeFlag strict_mode =
+ static_cast<StrictModeFlag>(Smi::cast(args[3])->value());
+ ASSERT(strict_mode == kStrictMode || strict_mode == kNonStrictMode);
// Compute the property attributes. According to ECMA-262, section
// 13, page 71, the property must be read-only and
@@ -1099,6 +1111,12 @@
// Fall-through and introduce the absent property by using
// SetProperty.
} else {
+ // For const properties, we treat a callback with this name
+ // even in the prototype as a conflicting declaration.
+ if (is_const_property && (lookup.type() == CALLBACKS)) {
+ return ThrowRedeclarationError(isolate, "const", name);
+ }
+ // Otherwise, we check for locally conflicting declarations.
if (is_local && (is_read_only || is_const_property)) {
const char* type = (is_read_only) ? "const" : "var";
return ThrowRedeclarationError(isolate, type, name);
@@ -1126,37 +1144,45 @@
? static_cast<PropertyAttributes>(base | READ_ONLY)
: base;
- if (lookup.IsProperty()) {
- // There's a local property that we need to overwrite because
- // we're either declaring a function or there's an interceptor
- // that claims the property is absent.
+ // There's a local property that we need to overwrite because
+ // we're either declaring a function or there's an interceptor
+ // that claims the property is absent.
+ //
+ // Check for conflicting re-declarations. We cannot have
+ // conflicting types in case of intercepted properties because
+ // they are absent.
+ if (lookup.IsProperty() &&
+ (lookup.type() != INTERCEPTOR) &&
+ (lookup.IsReadOnly() || is_const_property)) {
+ const char* type = (lookup.IsReadOnly()) ? "const" : "var";
+ return ThrowRedeclarationError(isolate, type, name);
+ }
- // Check for conflicting re-declarations. We cannot have
- // conflicting types in case of intercepted properties because
- // they are absent.
- if (lookup.type() != INTERCEPTOR &&
- (lookup.IsReadOnly() || is_const_property)) {
- const char* type = (lookup.IsReadOnly()) ? "const" : "var";
- return ThrowRedeclarationError(isolate, type, name);
+ // Safari does not allow the invocation of callback setters for
+ // function declarations. To mimic this behavior, we do not allow
+ // the invocation of setters for function values. This makes a
+ // difference for global functions with the same names as event
+ // handlers such as "function onload() {}". Firefox does call the
+ // onload setter in those case and Safari does not. We follow
+ // Safari for compatibility.
+ if (value->IsJSFunction()) {
+ // Do not change DONT_DELETE to false from true.
+ if (lookup.IsProperty() && (lookup.type() != INTERCEPTOR)) {
+ attributes = static_cast<PropertyAttributes>(
+ attributes | (lookup.GetAttributes() & DONT_DELETE));
}
- Handle<Object> result = SetProperty(global, name, value, attributes);
- if (result.is_null()) {
- ASSERT(isolate->has_pending_exception());
- return Failure::Exception();
- }
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ SetLocalPropertyIgnoreAttributes(global,
+ name,
+ value,
+ attributes));
} else {
- // If a property with this name does not already exist on the
- // global object add the property locally. We take special
- // precautions to always add it as a local property even in case
- // of callbacks in the prototype chain (this rules out using
- // SetProperty). Also, we must use the handle-based version to
- // avoid GC issues.
- Handle<Object> result =
- SetLocalPropertyIgnoreAttributes(global, name, value, attributes);
- if (result.is_null()) {
- ASSERT(isolate->has_pending_exception());
- return Failure::Exception();
- }
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ SetProperty(global,
+ name,
+ value,
+ attributes,
+ strict_mode));
}
}
@@ -1217,9 +1243,10 @@
} else {
// Slow case: The property is not in the FixedArray part of the context.
Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
- Handle<Object> result =
- SetProperty(context_ext, name, initial_value, mode);
- if (result.is_null()) return Failure::Exception();
+ RETURN_IF_EMPTY_HANDLE(
+ isolate,
+ SetProperty(context_ext, name, initial_value,
+ mode, kNonStrictMode));
}
}
@@ -1247,8 +1274,23 @@
ASSERT(!context_ext->HasLocalProperty(*name));
Handle<Object> value(isolate->heap()->undefined_value(), isolate);
if (*initial_value != NULL) value = initial_value;
- Handle<Object> result = SetProperty(context_ext, name, value, mode);
- if (result.is_null()) return Failure::Exception();
+ // Declaring a const context slot is a conflicting declaration if
+ // there is a callback with that name in a prototype. It is
+ // allowed to introduce const variables in
+ // JSContextExtensionObjects. They are treated specially in
+ // SetProperty and no setters are invoked for those since they are
+ // not real JSObjects.
+ if (initial_value->IsTheHole() &&
+ !context_ext->IsJSContextExtensionObject()) {
+ LookupResult lookup;
+ context_ext->Lookup(*name, &lookup);
+ if (lookup.IsProperty() && (lookup.type() == CALLBACKS)) {
+ return ThrowRedeclarationError(isolate, "const", name);
+ }
+ }
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ SetProperty(context_ext, name, value, mode,
+ kNonStrictMode));
}
return isolate->heap()->undefined_value();
@@ -1258,14 +1300,21 @@
static MaybeObject* Runtime_InitializeVarGlobal(RUNTIME_CALLING_CONVENTION) {
RUNTIME_GET_ISOLATE;
NoHandleAllocation nha;
+ // args[0] == name
+ // args[1] == strict_mode
+ // args[2] == value (optional)
// Determine if we need to assign to the variable if it already
// exists (based on the number of arguments).
- RUNTIME_ASSERT(args.length() == 1 || args.length() == 2);
- bool assign = args.length() == 2;
+ RUNTIME_ASSERT(args.length() == 2 || args.length() == 3);
+ bool assign = args.length() == 3;
CONVERT_ARG_CHECKED(String, name, 0);
GlobalObject* global = isolate->context()->global();
+ RUNTIME_ASSERT(args[1]->IsSmi());
+ StrictModeFlag strict_mode =
+ static_cast<StrictModeFlag>(Smi::cast(args[1])->value());
+ ASSERT(strict_mode == kStrictMode || strict_mode == kNonStrictMode);
// According to ECMA-262, section 12.2, page 62, the property must
// not be deletable.
@@ -1275,11 +1324,7 @@
// there, there is a property with this name in the prototype chain.
// We follow Safari and Firefox behavior and only set the property
// locally if there is an explicit initialization value that we have
- // to assign to the property. When adding the property we take
- // special precautions to always add it as a local property even in
- // case of callbacks in the prototype chain (this rules out using
- // SetProperty). We have SetLocalPropertyIgnoreAttributes for
- // this.
+ // to assign to the property.
// Note that objects can have hidden prototypes, so we need to traverse
// the whole chain of hidden prototypes to do a 'local' lookup.
JSObject* real_holder = global;
@@ -1325,8 +1370,9 @@
}
// Assign the value (or undefined) to the property.
- Object* value = (assign) ? args[1] : isolate->heap()->undefined_value();
- return real_holder->SetProperty(&lookup, *name, value, attributes);
+ Object* value = (assign) ? args[2] : isolate->heap()->undefined_value();
+ return real_holder->SetProperty(
+ &lookup, *name, value, attributes, strict_mode);
}
Object* proto = real_holder->GetPrototype();
@@ -1341,9 +1387,7 @@
global = isolate->context()->global();
if (assign) {
- return global->SetLocalPropertyIgnoreAttributes(*name,
- args[1],
- attributes);
+ return global->SetProperty(*name, args[2], attributes, strict_mode);
}
return isolate->heap()->undefined_value();
}
@@ -1401,16 +1445,23 @@
HandleScope handle_scope(isolate);
Handle<GlobalObject> global(isolate->context()->global());
- // BUG 1213579: Handle the case where we have to set a read-only
+ // BUG 1213575: Handle the case where we have to set a read-only
// property through an interceptor and only do it if it's
// uninitialized, e.g. the hole. Nirk...
- SetProperty(global, name, value, attributes);
+ // Passing non-strict mode because the property is writable.
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ SetProperty(global,
+ name,
+ value,
+ attributes,
+ kNonStrictMode));
return *value;
}
// Set the value, but only we're assigning the initial value to a
// constant. For now, we determine this by checking if the
// current value is the hole.
+ // Strict mode handling not needed (const disallowed in strict mode).
PropertyType type = lookup.type();
if (type == FIELD) {
FixedArray* properties = global->properties();
@@ -1489,7 +1540,10 @@
if (attributes == ABSENT) {
Handle<JSObject> global = Handle<JSObject>(
isolate->context()->global());
- SetProperty(global, name, value, NONE);
+ // Strict mode not needed (const disallowed in strict mode).
+ RETURN_IF_EMPTY_HANDLE(
+ isolate,
+ SetProperty(global, name, value, NONE, kNonStrictMode));
return *value;
}
@@ -1526,14 +1580,10 @@
// The property was found in a different context extension object.
// Set it if it is not a read-only property.
if ((attributes & READ_ONLY) == 0) {
- Handle<Object> set = SetProperty(context_ext, name, value, attributes);
- // Setting a property might throw an exception. Exceptions
- // are converted to empty handles in handle operations. We
- // need to convert back to exceptions here.
- if (set.is_null()) {
- ASSERT(isolate->has_pending_exception());
- return Failure::Exception();
- }
+ // Strict mode not needed (const disallowed in strict mode).
+ RETURN_IF_EMPTY_HANDLE(
+ isolate,
+ SetProperty(context_ext, name, value, attributes, kNonStrictMode));
}
}
@@ -1705,7 +1755,7 @@
code,
false);
optimized->shared()->DontAdaptArguments();
- SetProperty(holder, key, optimized, NONE);
+ SetProperty(holder, key, optimized, NONE, kStrictMode);
return optimized;
}
@@ -3850,6 +3900,8 @@
is_element) {
// Normalize the elements to enable attributes on the property.
if (js_object->IsJSGlobalProxy()) {
+ // We do not need to do access checks here since these has already
+ // been performed by the call to GetOwnProperty.
Handle<Object> proto(js_object->GetPrototype());
// If proxy is detached, ignore the assignment. Alternatively,
// we could throw an exception.
@@ -3890,7 +3942,11 @@
attr);
}
- return Runtime::SetObjectProperty(isolate, js_object, name, obj_value, attr);
+ return Runtime::ForceSetObjectProperty(isolate,
+ js_object,
+ name,
+ obj_value,
+ attr);
}
@@ -3898,7 +3954,8 @@
Handle<Object> object,
Handle<Object> key,
Handle<Object> value,
- PropertyAttributes attr) {
+ PropertyAttributes attr,
+ StrictModeFlag strict) {
HandleScope scope(isolate);
if (object->IsUndefined() || object->IsNull()) {
@@ -3928,6 +3985,7 @@
return *value;
}
+ // TODO(1220): Implement SetElement strict mode.
Handle<Object> result = SetElement(js_object, index, value);
if (result.is_null()) return Failure::Exception();
return *value;
@@ -3940,7 +3998,7 @@
} else {
Handle<String> key_string = Handle<String>::cast(key);
key_string->TryFlatten();
- result = SetProperty(js_object, key_string, value, attr);
+ result = SetProperty(js_object, key_string, value, attr, strict);
}
if (result.is_null()) return Failure::Exception();
return *value;
@@ -3953,9 +4011,10 @@
Handle<String> name = Handle<String>::cast(converted);
if (name->AsArrayIndex(&index)) {
+ // TODO(1220): Implement SetElement strict mode.
return js_object->SetElement(index, *value);
} else {
- return js_object->SetProperty(*name, *value, attr);
+ return js_object->SetProperty(*name, *value, attr, strict);
}
}
@@ -4050,23 +4109,32 @@
static MaybeObject* Runtime_SetProperty(RUNTIME_CALLING_CONVENTION) {
RUNTIME_GET_ISOLATE;
NoHandleAllocation ha;
- RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
+ RUNTIME_ASSERT(args.length() == 4 || args.length() == 5);
Handle<Object> object = args.at<Object>(0);
Handle<Object> key = args.at<Object>(1);
Handle<Object> value = args.at<Object>(2);
+ CONVERT_SMI_CHECKED(unchecked_attributes, args[3]);
+ RUNTIME_ASSERT(
+ (unchecked_attributes & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
+ // Compute attributes.
+ PropertyAttributes attributes =
+ static_cast<PropertyAttributes>(unchecked_attributes);
- // Compute attributes.
- PropertyAttributes attributes = NONE;
- if (args.length() == 4) {
- CONVERT_CHECKED(Smi, value_obj, args[3]);
- int unchecked_value = value_obj->value();
- // Only attribute bits should be set.
- RUNTIME_ASSERT(
- (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
- attributes = static_cast<PropertyAttributes>(unchecked_value);
+ StrictModeFlag strict = kNonStrictMode;
+ if (args.length() == 5) {
+ CONVERT_SMI_CHECKED(strict_unchecked, args[4]);
+ RUNTIME_ASSERT(strict_unchecked == kStrictMode ||
+ strict_unchecked == kNonStrictMode);
+ strict = static_cast<StrictModeFlag>(strict_unchecked);
}
- return Runtime::SetObjectProperty(isolate, object, key, value, attributes);
+
+ return Runtime::SetObjectProperty(isolate,
+ object,
+ key,
+ value,
+ attributes,
+ strict);
}
@@ -4098,11 +4166,14 @@
static MaybeObject* Runtime_DeleteProperty(RUNTIME_CALLING_CONVENTION) {
RUNTIME_GET_ISOLATE;
NoHandleAllocation ha;
- ASSERT(args.length() == 2);
+ ASSERT(args.length() == 3);
CONVERT_CHECKED(JSObject, object, args[0]);
CONVERT_CHECKED(String, key, args[1]);
- return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
+ CONVERT_SMI_CHECKED(strict, args[2]);
+ return object->DeleteProperty(key, (strict == kStrictMode)
+ ? JSObject::STRICT_DELETION
+ : JSObject::NORMAL_DELETION);
}
@@ -4417,6 +4488,22 @@
CONVERT_CHECKED(JSObject, raw_object, args[0]);
HandleScope scope(isolate);
Handle<JSObject> object(raw_object);
+
+ if (object->IsJSGlobalProxy()) {
+ // Do access checks before going to the global object.
+ if (object->IsAccessCheckNeeded() &&
+ !isolate->MayNamedAccess(*object, isolate->heap()->undefined_value(),
+ v8::ACCESS_KEYS)) {
+ isolate->ReportFailedAccessCheck(*object, v8::ACCESS_KEYS);
+ return *isolate->factory()->NewJSArray(0);
+ }
+
+ Handle<Object> proto(object->GetPrototype());
+ // If proxy is detached we simply return an empty array.
+ if (proto->IsNull()) return *isolate->factory()->NewJSArray(0);
+ object = Handle<JSObject>::cast(proto);
+ }
+
Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
LOCAL_ONLY);
// Some fast paths through GetKeysInFixedArrayFor reuse a cached
@@ -6058,6 +6145,91 @@
}
+static MaybeObject* Runtime_StringBuilderJoin(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+ NoHandleAllocation ha;
+ ASSERT(args.length() == 3);
+ CONVERT_CHECKED(JSArray, array, args[0]);
+ if (!args[1]->IsSmi()) {
+ isolate->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException();
+ }
+ int array_length = Smi::cast(args[1])->value();
+ CONVERT_CHECKED(String, separator, args[2]);
+
+ if (!array->HasFastElements()) {
+ return isolate->Throw(isolate->heap()->illegal_argument_symbol());
+ }
+ FixedArray* fixed_array = FixedArray::cast(array->elements());
+ if (fixed_array->length() < array_length) {
+ array_length = fixed_array->length();
+ }
+
+ if (array_length == 0) {
+ return isolate->heap()->empty_string();
+ } else if (array_length == 1) {
+ Object* first = fixed_array->get(0);
+ if (first->IsString()) return first;
+ }
+
+ int separator_length = separator->length();
+ int max_nof_separators =
+ (String::kMaxLength + separator_length - 1) / separator_length;
+ if (max_nof_separators < (array_length - 1)) {
+ isolate->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException();
+ }
+ int length = (array_length - 1) * separator_length;
+ for (int i = 0; i < array_length; i++) {
+ Object* element_obj = fixed_array->get(i);
+ if (!element_obj->IsString()) {
+ // TODO(1161): handle this case.
+ return isolate->Throw(isolate->heap()->illegal_argument_symbol());
+ }
+ String* element = String::cast(element_obj);
+ int increment = element->length();
+ if (increment > String::kMaxLength - length) {
+ isolate->context()->mark_out_of_memory();
+ return Failure::OutOfMemoryException();
+ }
+ length += increment;
+ }
+
+ Object* object;
+ { MaybeObject* maybe_object =
+ isolate->heap()->AllocateRawTwoByteString(length);
+ if (!maybe_object->ToObject(&object)) return maybe_object;
+ }
+ SeqTwoByteString* answer = SeqTwoByteString::cast(object);
+
+ uc16* sink = answer->GetChars();
+#ifdef DEBUG
+ uc16* end = sink + length;
+#endif
+
+ String* first = String::cast(fixed_array->get(0));
+ int first_length = first->length();
+ String::WriteToFlat(first, sink, 0, first_length);
+ sink += first_length;
+
+ for (int i = 1; i < array_length; i++) {
+ ASSERT(sink + separator_length <= end);
+ String::WriteToFlat(separator, sink, 0, separator_length);
+ sink += separator_length;
+
+ String* element = String::cast(fixed_array->get(i));
+ int element_length = element->length();
+ ASSERT(sink + element_length <= end);
+ String::WriteToFlat(element, sink, 0, element_length);
+ sink += element_length;
+ }
+ ASSERT(sink == end);
+
+ ASSERT(!answer->HasOnlyAsciiChars()); // Use %_FastAsciiArrayJoin instead.
+ return answer;
+}
+
+
static MaybeObject* Runtime_NumberOr(RUNTIME_CALLING_CONVENTION) {
RUNTIME_GET_ISOLATE;
NoHandleAllocation ha;
@@ -7132,6 +7304,7 @@
bool first_allocation = !shared->live_objects_may_exist();
Handle<JSObject> result = isolate->factory()->NewJSObject(function);
+ RETURN_IF_EMPTY_HANDLE(isolate, result);
// Delay setting the stub if inobject slack tracking is in progress.
if (first_allocation && !shared->IsInobjectSlackTrackingInProgress()) {
TrySettingInlineConstructStub(isolate, function);
@@ -7208,7 +7381,7 @@
function->ReplaceCode(function->shared()->code());
return function->code();
}
- if (CompileOptimized(function, AstNode::kNoNumber)) {
+ if (CompileOptimized(function, AstNode::kNoNumber, CLEAR_EXCEPTION)) {
return function->code();
}
if (FLAG_trace_opt) {
@@ -7217,7 +7390,7 @@
PrintF(": optimized compilation failed]\n");
}
function->ReplaceCode(function->shared()->code());
- return Failure::Exception();
+ return function->code();
}
@@ -7382,7 +7555,8 @@
// Try to compile the optimized code. A true return value from
// CompileOptimized means that compilation succeeded, not necessarily
// that optimization succeeded.
- if (CompileOptimized(function, ast_id) && function->IsOptimized()) {
+ if (CompileOptimized(function, ast_id, CLEAR_EXCEPTION) &&
+ function->IsOptimized()) {
DeoptimizationInputData* data = DeoptimizationInputData::cast(
function->code()->deoptimization_data());
if (data->OsrPcOffset()->value() >= 0) {
@@ -7425,6 +7599,9 @@
ASSERT(function->code()->kind() == Code::OPTIMIZED_FUNCTION);
return Smi::FromInt(ast_id);
} else {
+ if (function->IsMarkedForLazyRecompilation()) {
+ function->ReplaceCode(function->shared()->code());
+ }
return Smi::FromInt(-1);
}
}
@@ -7693,12 +7870,17 @@
static MaybeObject* Runtime_StoreContextSlot(RUNTIME_CALLING_CONVENTION) {
RUNTIME_GET_ISOLATE;
HandleScope scope(isolate);
- ASSERT(args.length() == 3);
+ ASSERT(args.length() == 4);
Handle<Object> value(args[0], isolate);
CONVERT_ARG_CHECKED(Context, context, 1);
CONVERT_ARG_CHECKED(String, name, 2);
+ CONVERT_SMI_CHECKED(strict_unchecked, args[3]);
+ RUNTIME_ASSERT(strict_unchecked == kStrictMode ||
+ strict_unchecked == kNonStrictMode);
+ StrictModeFlag strict = static_cast<StrictModeFlag>(strict_unchecked);
+
int index;
PropertyAttributes attributes;
ContextLookupFlags flags = FOLLOW_CHAINS;
@@ -7741,14 +7923,14 @@
// extension object itself.
if ((attributes & READ_ONLY) == 0 ||
(context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
- Handle<Object> result = SetProperty(context_ext, name, value, NONE);
- if (result.is_null()) {
- // Failure::Exception is converted to a null handle in the
- // handle-based methods such as SetProperty. We therefore need
- // to convert null handles back to exceptions.
- ASSERT(isolate->has_pending_exception());
- return Failure::Exception();
- }
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ SetProperty(context_ext, name, value, NONE, strict));
+ } else if (strict == kStrictMode && (attributes & READ_ONLY) != 0) {
+ // Setting read only property in strict mode.
+ Handle<Object> error =
+ isolate->factory()->NewTypeError(
+ "strict_cannot_assign", HandleVector(&name, 1));
+ return isolate->Throw(*error);
}
return *value;
}
@@ -8095,12 +8277,9 @@
RUNTIME_CALLING_CONVENTION) {
RUNTIME_GET_ISOLATE;
ASSERT(args.length() == 4);
- if (!args[0]->IsJSFunction()) {
- return MakePair(isolate->ThrowIllegalOperation(), NULL);
- }
HandleScope scope(isolate);
- Handle<JSFunction> callee = args.at<JSFunction>(0);
+ Handle<Object> callee = args.at<Object>(0);
Handle<Object> receiver; // Will be overwritten.
// Compute the calling context.
@@ -8176,12 +8355,9 @@
RUNTIME_CALLING_CONVENTION) {
RUNTIME_GET_ISOLATE;
ASSERT(args.length() == 4);
- if (!args[0]->IsJSFunction()) {
- return MakePair(isolate->ThrowIllegalOperation(), NULL);
- }
HandleScope scope(isolate);
- Handle<JSFunction> callee = args.at<JSFunction>(0);
+ Handle<Object> callee = args.at<Object>(0);
// 'eval' is bound in the global context, but it may have been overwritten.
// Compare it to the builtin 'GlobalEval' function to make sure.
@@ -8277,329 +8453,415 @@
*/
class ArrayConcatVisitor {
public:
- ArrayConcatVisitor(Handle<FixedArray> storage,
- uint32_t index_limit,
+ ArrayConcatVisitor(Isolate* isolate,
+ Handle<FixedArray> storage,
bool fast_elements) :
- storage_(storage), index_limit_(index_limit),
- index_offset_(0), fast_elements_(fast_elements) { }
+ isolate_(isolate),
+ storage_(Handle<FixedArray>::cast(
+ isolate->global_handles()->Create(*storage))),
+ index_offset_(0u),
+ fast_elements_(fast_elements) { }
+ ~ArrayConcatVisitor() {
+ clear_storage();
+ }
+
void visit(uint32_t i, Handle<Object> elm) {
- if (i >= index_limit_ - index_offset_) return;
+ if (i >= JSObject::kMaxElementCount - index_offset_) return;
uint32_t index = index_offset_ + i;
if (fast_elements_) {
- ASSERT(index < static_cast<uint32_t>(storage_->length()));
- storage_->set(index, *elm);
+ if (index < static_cast<uint32_t>(storage_->length())) {
+ storage_->set(index, *elm);
+ return;
+ }
+ // Our initial estimate of length was foiled, possibly by
+ // getters on the arrays increasing the length of later arrays
+ // during iteration.
+ // This shouldn't happen in anything but pathological cases.
+ SetDictionaryMode(index);
+ // Fall-through to dictionary mode.
+ }
+ ASSERT(!fast_elements_);
+ Handle<NumberDictionary> dict(NumberDictionary::cast(*storage_));
+ Handle<NumberDictionary> result =
+ isolate_->factory()->DictionaryAtNumberPut(dict, index, elm);
+ if (!result.is_identical_to(dict)) {
+ // Dictionary needed to grow.
+ clear_storage();
+ set_storage(*result);
+ }
+}
+ void increase_index_offset(uint32_t delta) {
+ if (JSObject::kMaxElementCount - index_offset_ < delta) {
+ index_offset_ = JSObject::kMaxElementCount;
} else {
- Handle<NumberDictionary> dict = Handle<NumberDictionary>::cast(storage_);
- Handle<NumberDictionary> result =
- storage_->GetIsolate()->factory()->DictionaryAtNumberPut(dict,
- index,
- elm);
- if (!result.is_identical_to(dict))
- storage_ = result;
+ index_offset_ += delta;
}
}
- void increase_index_offset(uint32_t delta) {
- if (index_limit_ - index_offset_ < delta) {
- index_offset_ = index_limit_;
+ Handle<JSArray> ToArray() {
+ Handle<JSArray> array = isolate_->factory()->NewJSArray(0);
+ Handle<Object> length =
+ isolate_->factory()->NewNumber(static_cast<double>(index_offset_));
+ Handle<Map> map;
+ if (fast_elements_) {
+ map = isolate_->factory()->GetFastElementsMap(Handle<Map>(array->map()));
} else {
- index_offset_ += delta;
+ map = isolate_->factory()->GetSlowElementsMap(Handle<Map>(array->map()));
}
+ array->set_map(*map);
+ array->set_length(*length);
+ array->set_elements(*storage_);
+ return array;
}
- Handle<FixedArray> storage() { return storage_; }
+ private:
+ // Convert storage to dictionary mode.
+ void SetDictionaryMode(uint32_t index) {
+ ASSERT(fast_elements_);
+ Handle<FixedArray> current_storage(*storage_);
+ Handle<NumberDictionary> slow_storage(
+ isolate_->factory()->NewNumberDictionary(current_storage->length()));
+ uint32_t current_length = static_cast<uint32_t>(current_storage->length());
+ for (uint32_t i = 0; i < current_length; i++) {
+ HandleScope loop_scope;
+ Handle<Object> element(current_storage->get(i));
+ if (!element->IsTheHole()) {
+ Handle<NumberDictionary> new_storage =
+ isolate_->factory()->DictionaryAtNumberPut(slow_storage, i, element);
+ if (!new_storage.is_identical_to(slow_storage)) {
+ slow_storage = loop_scope.CloseAndEscape(new_storage);
+ }
+ }
+ }
+ clear_storage();
+ set_storage(*slow_storage);
+ fast_elements_ = false;
+ }
- private:
- Handle<FixedArray> storage_;
- // Limit on the accepted indices. Elements with indices larger than the
- // limit are ignored by the visitor.
- uint32_t index_limit_;
- // Index after last seen index. Always less than or equal to index_limit_.
+ inline void clear_storage() {
+ isolate_->global_handles()->Destroy(
+ Handle<Object>::cast(storage_).location());
+ }
+
+ inline void set_storage(FixedArray* storage) {
+ storage_ = Handle<FixedArray>::cast(
+ isolate_->global_handles()->Create(storage));
+ }
+
+ Isolate* isolate_;
+ Handle<FixedArray> storage_; // Always a global handle.
+ // Index after last seen index. Always less than or equal to
+ // JSObject::kMaxElementCount.
uint32_t index_offset_;
- const bool fast_elements_;
+ bool fast_elements_;
};
+static uint32_t EstimateElementCount(Handle<JSArray> array) {
+ uint32_t length = static_cast<uint32_t>(array->length()->Number());
+ int element_count = 0;
+ switch (array->GetElementsKind()) {
+ case JSObject::FAST_ELEMENTS: {
+ // Fast elements can't have lengths that are not representable by
+ // a 32-bit signed integer.
+ ASSERT(static_cast<int32_t>(FixedArray::kMaxLength) >= 0);
+ int fast_length = static_cast<int>(length);
+ Handle<FixedArray> elements(FixedArray::cast(array->elements()));
+ for (int i = 0; i < fast_length; i++) {
+ if (!elements->get(i)->IsTheHole()) element_count++;
+ }
+ break;
+ }
+ case JSObject::DICTIONARY_ELEMENTS: {
+ Handle<NumberDictionary> dictionary(
+ NumberDictionary::cast(array->elements()));
+ int capacity = dictionary->Capacity();
+ for (int i = 0; i < capacity; i++) {
+ Handle<Object> key(dictionary->KeyAt(i));
+ if (dictionary->IsKey(*key)) {
+ element_count++;
+ }
+ }
+ break;
+ }
+ default:
+ // External arrays are always dense.
+ return length;
+ }
+ // As an estimate, we assume that the prototype doesn't contain any
+ // inherited elements.
+ return element_count;
+}
+
+
+
template<class ExternalArrayClass, class ElementType>
-static uint32_t IterateExternalArrayElements(Isolate* isolate,
- Handle<JSObject> receiver,
- bool elements_are_ints,
- bool elements_are_guaranteed_smis,
- uint32_t range,
- ArrayConcatVisitor* visitor) {
+static void IterateExternalArrayElements(Isolate* isolate,
+ Handle<JSObject> receiver,
+ bool elements_are_ints,
+ bool elements_are_guaranteed_smis,
+ ArrayConcatVisitor* visitor) {
Handle<ExternalArrayClass> array(
ExternalArrayClass::cast(receiver->elements()));
- uint32_t len = Min(static_cast<uint32_t>(array->length()), range);
+ uint32_t len = static_cast<uint32_t>(array->length());
- if (visitor != NULL) {
- if (elements_are_ints) {
- if (elements_are_guaranteed_smis) {
- for (uint32_t j = 0; j < len; j++) {
- Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
+ ASSERT(visitor != NULL);
+ if (elements_are_ints) {
+ if (elements_are_guaranteed_smis) {
+ for (uint32_t j = 0; j < len; j++) {
+ HandleScope loop_scope;
+ Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get(j))));
+ visitor->visit(j, e);
+ }
+ } else {
+ for (uint32_t j = 0; j < len; j++) {
+ HandleScope loop_scope;
+ int64_t val = static_cast<int64_t>(array->get(j));
+ if (Smi::IsValid(static_cast<intptr_t>(val))) {
+ Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
visitor->visit(j, e);
+ } else {
+ Handle<Object> e =
+ isolate->factory()->NewNumber(static_cast<ElementType>(val));
+ visitor->visit(j, e);
}
- } else {
- for (uint32_t j = 0; j < len; j++) {
- int64_t val = static_cast<int64_t>(array->get(j));
- if (Smi::IsValid(static_cast<intptr_t>(val))) {
- Handle<Smi> e(Smi::FromInt(static_cast<int>(val)));
- visitor->visit(j, e);
- } else {
- Handle<Object> e =
- isolate->factory()->NewNumber(static_cast<ElementType>(val));
- visitor->visit(j, e);
+ }
+ }
+ } else {
+ for (uint32_t j = 0; j < len; j++) {
+ HandleScope loop_scope(isolate);
+ Handle<Object> e = isolate->factory()->NewNumber(array->get(j));
+ visitor->visit(j, e);
+ }
+ }
+}
+
+
+// Used for sorting indices in a List<uint32_t>.
+static int compareUInt32(const uint32_t* ap, const uint32_t* bp) {
+ uint32_t a = *ap;
+ uint32_t b = *bp;
+ return (a == b) ? 0 : (a < b) ? -1 : 1;
+}
+
+
+static void CollectElementIndices(Handle<JSObject> object,
+ uint32_t range,
+ List<uint32_t>* indices) {
+ JSObject::ElementsKind kind = object->GetElementsKind();
+ switch (kind) {
+ case JSObject::FAST_ELEMENTS: {
+ Handle<FixedArray> elements(FixedArray::cast(object->elements()));
+ uint32_t length = static_cast<uint32_t>(elements->length());
+ if (range < length) length = range;
+ for (uint32_t i = 0; i < length; i++) {
+ if (!elements->get(i)->IsTheHole()) {
+ indices->Add(i);
+ }
+ }
+ break;
+ }
+ case JSObject::DICTIONARY_ELEMENTS: {
+ Handle<NumberDictionary> dict(NumberDictionary::cast(object->elements()));
+ uint32_t capacity = dict->Capacity();
+ for (uint32_t j = 0; j < capacity; j++) {
+ HandleScope loop_scope;
+ Handle<Object> k(dict->KeyAt(j));
+ if (dict->IsKey(*k)) {
+ ASSERT(k->IsNumber());
+ uint32_t index = static_cast<uint32_t>(k->Number());
+ if (index < range) {
+ indices->Add(index);
}
}
}
- } else {
- for (uint32_t j = 0; j < len; j++) {
- Handle<Object> e = isolate->factory()->NewNumber(array->get(j));
- visitor->visit(j, e);
+ break;
+ }
+ default: {
+ int dense_elements_length;
+ switch (kind) {
+ case JSObject::PIXEL_ELEMENTS: {
+ dense_elements_length =
+ PixelArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_BYTE_ELEMENTS: {
+ dense_elements_length =
+ ExternalByteArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
+ dense_elements_length =
+ ExternalUnsignedByteArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_SHORT_ELEMENTS: {
+ dense_elements_length =
+ ExternalShortArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
+ dense_elements_length =
+ ExternalUnsignedShortArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_INT_ELEMENTS: {
+ dense_elements_length =
+ ExternalIntArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
+ dense_elements_length =
+ ExternalUnsignedIntArray::cast(object->elements())->length();
+ break;
+ }
+ case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
+ dense_elements_length =
+ ExternalFloatArray::cast(object->elements())->length();
+ break;
+ }
+ default:
+ UNREACHABLE();
+ dense_elements_length = 0;
+ break;
}
+ uint32_t length = static_cast<uint32_t>(dense_elements_length);
+ if (range <= length) {
+ length = range;
+ // We will add all indices, so we might as well clear it first
+ // and avoid duplicates.
+ indices->Clear();
+ }
+ for (uint32_t i = 0; i < length; i++) {
+ indices->Add(i);
+ }
+ if (length == range) return; // All indices accounted for already.
+ break;
}
}
- return len;
+ Handle<Object> prototype(object->GetPrototype());
+ if (prototype->IsJSObject()) {
+ // The prototype will usually have no inherited element indices,
+ // but we have to check.
+ CollectElementIndices(Handle<JSObject>::cast(prototype), range, indices);
+ }
}
+
/**
- * A helper function that visits elements of a JSObject. Only elements
- * whose index between 0 and range (exclusive) are visited.
+ * A helper function that visits elements of a JSArray in numerical
+ * order.
*
- * If the third parameter, visitor, is not NULL, the visitor is called
- * with parameters, 'visitor_index_offset + element index' and the element.
- *
- * It returns the number of visisted elements.
+ * The visitor argument called for each existing element in the array
+ * with the element index and the element's value.
+ * Afterwards it increments the base-index of the visitor by the array
+ * length.
*/
-static uint32_t IterateElements(Isolate* isolate,
- Handle<JSObject> receiver,
- uint32_t range,
- ArrayConcatVisitor* visitor) {
- uint32_t num_of_elements = 0;
-
+static void IterateElements(Isolate* isolate,
+ Handle<JSArray> receiver,
+ ArrayConcatVisitor* visitor) {
+ uint32_t length = static_cast<uint32_t>(receiver->length()->Number());
switch (receiver->GetElementsKind()) {
case JSObject::FAST_ELEMENTS: {
+ // Run through the elements FixedArray and use HasElement and GetElement
+ // to check the prototype for missing elements.
Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
- uint32_t len = elements->length();
- if (range < len) {
- len = range;
- }
-
- for (uint32_t j = 0; j < len; j++) {
- Handle<Object> e(elements->get(j), isolate);
- if (!e->IsTheHole()) {
- num_of_elements++;
- if (visitor) {
- visitor->visit(j, e);
- }
+ int fast_length = static_cast<int>(length);
+ ASSERT(fast_length <= elements->length());
+ for (int j = 0; j < fast_length; j++) {
+ HandleScope loop_scope(isolate);
+ Handle<Object> element_value(elements->get(j), isolate);
+ if (!element_value->IsTheHole()) {
+ visitor->visit(j, element_value);
+ } else if (receiver->HasElement(j)) {
+ // Call GetElement on receiver, not its prototype, or getters won't
+ // have the correct receiver.
+ element_value = GetElement(receiver, j);
+ visitor->visit(j, element_value);
}
}
break;
}
+ case JSObject::DICTIONARY_ELEMENTS: {
+ Handle<NumberDictionary> dict(receiver->element_dictionary());
+ List<uint32_t> indices(dict->Capacity() / 2);
+ // Collect all indices in the object and the prototypes less
+ // than length. This might introduce duplicates in the indices list.
+ CollectElementIndices(receiver, length, &indices);
+ indices.Sort(&compareUInt32);
+ int j = 0;
+ int n = indices.length();
+ while (j < n) {
+ HandleScope loop_scope;
+ uint32_t index = indices[j];
+ Handle<Object> element = GetElement(receiver, index);
+ visitor->visit(index, element);
+ // Skip to next different index (i.e., omit duplicates).
+ do {
+ j++;
+ } while (j < n && indices[j] == index);
+ }
+ break;
+ }
case JSObject::PIXEL_ELEMENTS: {
Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
- uint32_t len = pixels->length();
- if (range < len) {
- len = range;
+ for (uint32_t j = 0; j < length; j++) {
+ Handle<Smi> e(Smi::FromInt(pixels->get(j)));
+ visitor->visit(j, e);
}
-
- for (uint32_t j = 0; j < len; j++) {
- num_of_elements++;
- if (visitor != NULL) {
- Handle<Smi> e(Smi::FromInt(pixels->get(j)));
- visitor->visit(j, e);
- }
- }
break;
}
case JSObject::EXTERNAL_BYTE_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalByteArray, int8_t>(
- isolate, receiver, true, true, range, visitor);
+ IterateExternalArrayElements<ExternalByteArray, int8_t>(
+ isolate, receiver, true, true, visitor);
break;
}
case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
- isolate, receiver, true, true, range, visitor);
+ IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
+ isolate, receiver, true, true, visitor);
break;
}
case JSObject::EXTERNAL_SHORT_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalShortArray, int16_t>(
- isolate, receiver, true, true, range, visitor);
+ IterateExternalArrayElements<ExternalShortArray, int16_t>(
+ isolate, receiver, true, true, visitor);
break;
}
case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
- isolate, receiver, true, true, range, visitor);
+ IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
+ isolate, receiver, true, true, visitor);
break;
}
case JSObject::EXTERNAL_INT_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalIntArray, int32_t>(
- isolate, receiver, true, false, range, visitor);
+ IterateExternalArrayElements<ExternalIntArray, int32_t>(
+ isolate, receiver, true, false, visitor);
break;
}
case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
- isolate, receiver, true, false, range, visitor);
+ IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
+ isolate, receiver, true, false, visitor);
break;
}
case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
- num_of_elements =
- IterateExternalArrayElements<ExternalFloatArray, float>(
- isolate, receiver, false, false, range, visitor);
+ IterateExternalArrayElements<ExternalFloatArray, float>(
+ isolate, receiver, false, false, visitor);
break;
}
- case JSObject::DICTIONARY_ELEMENTS: {
- Handle<NumberDictionary> dict(receiver->element_dictionary());
- uint32_t capacity = dict->Capacity();
- for (uint32_t j = 0; j < capacity; j++) {
- Handle<Object> k(dict->KeyAt(j), isolate);
- if (dict->IsKey(*k)) {
- ASSERT(k->IsNumber());
- uint32_t index = static_cast<uint32_t>(k->Number());
- if (index < range) {
- num_of_elements++;
- if (visitor) {
- visitor->visit(index, Handle<Object>(dict->ValueAt(j), isolate));
- }
- }
- }
- }
- break;
- }
default:
UNREACHABLE();
break;
}
-
- return num_of_elements;
+ visitor->increase_index_offset(length);
}
/**
- * A helper function that visits elements of an Array object, and elements
- * on its prototypes.
- *
- * Elements on prototypes are visited first, and only elements whose indices
- * less than Array length are visited.
- *
- * If a ArrayConcatVisitor object is given, the visitor is called with
- * parameters, element's index + visitor_index_offset and the element.
- *
- * The returned number of elements is an upper bound on the actual number
- * of elements added. If the same element occurs in more than one object
- * in the array's prototype chain, it will be counted more than once, but
- * will only occur once in the result.
- */
-static uint32_t IterateArrayAndPrototypeElements(Isolate* isolate,
- Handle<JSArray> array,
- ArrayConcatVisitor* visitor) {
- uint32_t range = static_cast<uint32_t>(array->length()->Number());
- Handle<Object> obj = array;
-
- static const int kEstimatedPrototypes = 3;
- List< Handle<JSObject> > objects(kEstimatedPrototypes);
-
- // Visit prototype first. If an element on the prototype is shadowed by
- // the inheritor using the same index, the ArrayConcatVisitor visits
- // the prototype element before the shadowing element.
- // The visitor can simply overwrite the old value by new value using
- // the same index. This follows Array::concat semantics.
- while (!obj->IsNull()) {
- objects.Add(Handle<JSObject>::cast(obj));
- obj = Handle<Object>(obj->GetPrototype(), isolate);
- }
-
- uint32_t nof_elements = 0;
- for (int i = objects.length() - 1; i >= 0; i--) {
- Handle<JSObject> obj = objects[i];
- uint32_t encountered_elements =
- IterateElements(isolate, Handle<JSObject>::cast(obj), range, visitor);
-
- if (encountered_elements > JSObject::kMaxElementCount - nof_elements) {
- nof_elements = JSObject::kMaxElementCount;
- } else {
- nof_elements += encountered_elements;
- }
- }
-
- return nof_elements;
-}
-
-
-/**
- * A helper function of Runtime_ArrayConcat.
- *
- * The first argument is an Array of arrays and objects. It is the
- * same as the arguments array of Array::concat JS function.
- *
- * If an argument is an Array object, the function visits array
- * elements. If an argument is not an Array object, the function
- * visits the object as if it is an one-element array.
- *
- * If the result array index overflows 32-bit unsigned integer, the rounded
- * non-negative number is used as new length. For example, if one
- * array length is 2^32 - 1, second array length is 1, the
- * concatenated array length is 0.
- * TODO(lrn) Change length behavior to ECMAScript 5 specification (length
- * is one more than the last array index to get a value assigned).
- */
-static uint32_t IterateArguments(Isolate* isolate,
- Handle<JSArray> arguments,
- ArrayConcatVisitor* visitor) {
- uint32_t visited_elements = 0;
- uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
-
- for (uint32_t i = 0; i < num_of_args; i++) {
- Object* element;
- MaybeObject* maybe_element = arguments->GetElement(i);
- // This if() is not expected to fail, but we have the check in the
- // interest of hardening the runtime calls.
- if (maybe_element->ToObject(&element)) {
- Handle<Object> obj(element, isolate);
- if (obj->IsJSArray()) {
- Handle<JSArray> array = Handle<JSArray>::cast(obj);
- uint32_t len = static_cast<uint32_t>(array->length()->Number());
- uint32_t nof_elements =
- IterateArrayAndPrototypeElements(isolate, array, visitor);
- // Total elements of array and its prototype chain can be more than
- // the array length, but ArrayConcat can only concatenate at most
- // the array length number of elements. We use the length as an estimate
- // for the actual number of elements added.
- uint32_t added_elements = (nof_elements > len) ? len : nof_elements;
- if (JSArray::kMaxElementCount - visited_elements < added_elements) {
- visited_elements = JSArray::kMaxElementCount;
- } else {
- visited_elements += added_elements;
- }
- if (visitor) visitor->increase_index_offset(len);
- } else {
- if (visitor) {
- visitor->visit(0, obj);
- visitor->increase_index_offset(1);
- }
- if (visited_elements < JSArray::kMaxElementCount) {
- visited_elements++;
- }
- }
- }
- }
- return visited_elements;
-}
-
-
-/**
* Array::concat implementation.
* See ECMAScript 262, 15.4.4.4.
- * TODO(lrn): Fix non-compliance for very large concatenations and update to
+ * TODO(581): Fix non-compliance for very large concatenations and update to
* following the ECMAScript 5 specification.
*/
static MaybeObject* Runtime_ArrayConcat(RUNTIME_CALLING_CONVENTION) {
@@ -8607,77 +8869,83 @@
ASSERT(args.length() == 1);
HandleScope handle_scope(isolate);
- CONVERT_CHECKED(JSArray, arg_arrays, args[0]);
- Handle<JSArray> arguments(arg_arrays);
+ CONVERT_ARG_CHECKED(JSArray, arguments, 0);
+ int argument_count = static_cast<int>(arguments->length()->Number());
+ RUNTIME_ASSERT(arguments->HasFastElements());
+ Handle<FixedArray> elements(FixedArray::cast(arguments->elements()));
- // Pass 1: estimate the number of elements of the result
- // (it could be more than real numbers if prototype has elements).
- uint32_t result_length = 0;
- uint32_t num_of_args = static_cast<uint32_t>(arguments->length()->Number());
+ // Pass 1: estimate the length and number of elements of the result.
+ // The actual length can be larger if any of the arguments have getters
+ // that mutate other arguments (but will otherwise be precise).
+ // The number of elements is precise if there are no inherited elements.
- { AssertNoAllocation nogc;
- for (uint32_t i = 0; i < num_of_args; i++) {
- Object* obj;
- MaybeObject* maybe_object = arguments->GetElement(i);
- // This if() is not expected to fail, but we have the check in the
- // interest of hardening the runtime calls.
- if (maybe_object->ToObject(&obj)) {
- uint32_t length_estimate;
- if (obj->IsJSArray()) {
- length_estimate =
- static_cast<uint32_t>(JSArray::cast(obj)->length()->Number());
- } else {
- length_estimate = 1;
- }
- if (JSObject::kMaxElementCount - result_length < length_estimate) {
- result_length = JSObject::kMaxElementCount;
- break;
- }
- result_length += length_estimate;
+ uint32_t estimate_result_length = 0;
+ uint32_t estimate_nof_elements = 0;
+ {
+ for (int i = 0; i < argument_count; i++) {
+ HandleScope loop_scope;
+ Handle<Object> obj(elements->get(i));
+ uint32_t length_estimate;
+ uint32_t element_estimate;
+ if (obj->IsJSArray()) {
+ Handle<JSArray> array(Handle<JSArray>::cast(obj));
+ length_estimate =
+ static_cast<uint32_t>(array->length()->Number());
+ element_estimate =
+ EstimateElementCount(array);
+ } else {
+ length_estimate = 1;
+ element_estimate = 1;
}
+ // Avoid overflows by capping at kMaxElementCount.
+ if (JSObject::kMaxElementCount - estimate_result_length <
+ length_estimate) {
+ estimate_result_length = JSObject::kMaxElementCount;
+ } else {
+ estimate_result_length += length_estimate;
+ }
+ if (JSObject::kMaxElementCount - estimate_nof_elements <
+ element_estimate) {
+ estimate_nof_elements = JSObject::kMaxElementCount;
+ } else {
+ estimate_nof_elements += element_estimate;
+ }
}
}
- // Allocate an empty array, will set length and content later.
- Handle<JSArray> result = isolate->factory()->NewJSArray(0);
-
- uint32_t estimate_nof_elements = IterateArguments(isolate, arguments, NULL);
// If estimated number of elements is more than half of length, a
// fixed array (fast case) is more time and space-efficient than a
// dictionary.
- bool fast_case = (estimate_nof_elements * 2) >= result_length;
+ bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length;
Handle<FixedArray> storage;
if (fast_case) {
// The backing storage array must have non-existing elements to
// preserve holes across concat operations.
- storage = isolate->factory()->NewFixedArrayWithHoles(result_length);
- Handle<Map> fast_map =
- isolate->factory()->GetFastElementsMap(Handle<Map>(result->map()));
- result->set_map(*fast_map);
+ storage = isolate->factory()->NewFixedArrayWithHoles(
+ estimate_result_length);
} else {
// TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
uint32_t at_least_space_for = estimate_nof_elements +
(estimate_nof_elements >> 2);
storage = Handle<FixedArray>::cast(
- isolate->factory()->NewNumberDictionary(at_least_space_for));
- Handle<Map> slow_map =
- isolate->factory()->GetSlowElementsMap(Handle<Map>(result->map()));
- result->set_map(*slow_map);
+ isolate->factory()->NewNumberDictionary(at_least_space_for));
}
- Handle<Object> len =
- isolate->factory()->NewNumber(static_cast<double>(result_length));
+ ArrayConcatVisitor visitor(isolate, storage, fast_case);
- ArrayConcatVisitor visitor(storage, result_length, fast_case);
+ for (int i = 0; i < argument_count; i++) {
+ Handle<Object> obj(elements->get(i));
+ if (obj->IsJSArray()) {
+ Handle<JSArray> array = Handle<JSArray>::cast(obj);
+ IterateElements(isolate, array, &visitor);
+ } else {
+ visitor.visit(0, obj);
+ visitor.increase_index_offset(1);
+ }
+ }
- IterateArguments(isolate, arguments, &visitor);
-
- result->set_length(*len);
- // Please note the storage might have changed in the visitor.
- result->set_elements(*visitor.storage());
-
- return *result;
+ return *visitor.ToArray();
}
@@ -9304,7 +9572,7 @@
// If we are inspecting an optimized frame use undefined as the
// value for all locals.
//
- // TODO(3141533): We should be able to get the correct values
+ // TODO(1140): We should be able to get the correct values
// for locals in optimized frames.
locals->set(i * 2 + 1, isolate->heap()->undefined_value());
} else if (i < info.number_of_stack_slots()) {
@@ -9471,7 +9739,7 @@
// Copy all the context locals into an object used to materialize a scope.
-static void CopyContextLocalsToScopeObject(
+static bool CopyContextLocalsToScopeObject(
Isolate* isolate,
Handle<SerializedScopeInfo> serialized_scope_info,
ScopeInfo<>& scope_info,
@@ -9487,11 +9755,18 @@
// Don't include the arguments shadow (.arguments) context variable.
if (*scope_info.context_slot_name(i) !=
isolate->heap()->arguments_shadow_symbol()) {
- SetProperty(scope_object,
- scope_info.context_slot_name(i),
- Handle<Object>(context->get(context_index), isolate), NONE);
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate,
+ SetProperty(scope_object,
+ scope_info.context_slot_name(i),
+ Handle<Object>(context->get(context_index), isolate),
+ NONE,
+ kNonStrictMode),
+ false);
}
}
+
+ return true;
}
@@ -9511,23 +9786,36 @@
// First fill all parameters.
for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
- SetProperty(local_scope,
- scope_info.parameter_name(i),
- Handle<Object>(frame->GetParameter(i), isolate), NONE);
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate,
+ SetProperty(local_scope,
+ scope_info.parameter_name(i),
+ Handle<Object>(frame->GetParameter(i), isolate),
+ NONE,
+ kNonStrictMode),
+ Handle<JSObject>());
}
// Second fill all stack locals.
for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
- SetProperty(local_scope,
- scope_info.stack_slot_name(i),
- Handle<Object>(frame->GetExpression(i), isolate), NONE);
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate,
+ SetProperty(local_scope,
+ scope_info.stack_slot_name(i),
+ Handle<Object>(frame->GetExpression(i), isolate),
+ NONE,
+ kNonStrictMode),
+ Handle<JSObject>());
}
// Third fill all context locals.
Handle<Context> frame_context(Context::cast(frame->context()));
Handle<Context> function_context(frame_context->fcontext());
- CopyContextLocalsToScopeObject(isolate, serialized_scope_info, scope_info,
- function_context, local_scope);
+ if (!CopyContextLocalsToScopeObject(isolate,
+ serialized_scope_info, scope_info,
+ function_context, local_scope)) {
+ return Handle<JSObject>();
+ }
// Finally copy any properties from the function context extension. This will
// be variables introduced by eval.
@@ -9540,7 +9828,14 @@
// Names of variables introduced by eval are strings.
ASSERT(keys->get(i)->IsString());
Handle<String> key(String::cast(keys->get(i)));
- SetProperty(local_scope, key, GetProperty(ext, key), NONE);
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate,
+ SetProperty(local_scope,
+ key,
+ GetProperty(ext, key),
+ NONE,
+ kNonStrictMode),
+ Handle<JSObject>());
}
}
}
@@ -9575,16 +9870,23 @@
for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
// We don't expect exception-throwing getters on the arguments shadow.
Object* element = arguments_shadow->GetElement(i)->ToObjectUnchecked();
- SetProperty(closure_scope,
- scope_info.parameter_name(i),
- Handle<Object>(element, isolate),
- NONE);
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate,
+ SetProperty(closure_scope,
+ scope_info.parameter_name(i),
+ Handle<Object>(element, isolate),
+ NONE,
+ kNonStrictMode),
+ Handle<JSObject>());
}
}
// Fill all context locals to the context extension.
- CopyContextLocalsToScopeObject(isolate, serialized_scope_info, scope_info,
- context, closure_scope);
+ if (!CopyContextLocalsToScopeObject(isolate,
+ serialized_scope_info, scope_info,
+ context, closure_scope)) {
+ return Handle<JSObject>();
+ }
// Finally copy any properties from the function context extension. This will
// be variables introduced by eval.
@@ -9595,7 +9897,14 @@
// Names of variables introduced by eval are strings.
ASSERT(keys->get(i)->IsString());
Handle<String> key(String::cast(keys->get(i)));
- SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
+ RETURN_IF_EMPTY_HANDLE_VALUE(
+ isolate,
+ SetProperty(closure_scope,
+ key,
+ GetProperty(ext, key),
+ NONE,
+ kNonStrictMode),
+ Handle<JSObject>());
}
}
@@ -9886,6 +10195,7 @@
// Fill in scope details.
details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
Handle<JSObject> scope_object = it.ScopeObject();
+ RETURN_IF_EMPTY_HANDLE(isolate, scope_object);
details->set(kScopeDetailsObjectIndex, *scope_object);
return *isolate->factory()->NewJSArrayWithElements(details);
@@ -10397,6 +10707,7 @@
// Materialize the content of the local scope into a JSObject.
Handle<JSObject> local_scope = MaterializeLocalScope(isolate, frame);
+ RETURN_IF_EMPTY_HANDLE(isolate, local_scope);
// Allocate a new context for the debug evaluation and set the extension
// object build.
@@ -11143,6 +11454,220 @@
}
return Smi::FromInt(usage);
}
+
+
+// Captures a live object list from the present heap.
+static MaybeObject* Runtime_HasLOLEnabled(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+#ifdef LIVE_OBJECT_LIST
+ return isolate->heap()->true_value();
+#else
+ return isolate->heap()->false_value();
+#endif
+}
+
+
+// Captures a live object list from the present heap.
+static MaybeObject* Runtime_CaptureLOL(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+#ifdef LIVE_OBJECT_LIST
+ return LiveObjectList::Capture();
+#else
+ return isolate->heap()->undefined_value();
+#endif
+}
+
+
+// Deletes the specified live object list.
+static MaybeObject* Runtime_DeleteLOL(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+#ifdef LIVE_OBJECT_LIST
+ CONVERT_SMI_CHECKED(id, args[0]);
+ bool success = LiveObjectList::Delete(id);
+ return success ? isolate->heap()->true_value() :
+ isolate->heap()->false_value();
+#else
+ return isolate->heap()->undefined_value();
+#endif
+}
+
+
+// Generates the response to a debugger request for a dump of the objects
+// contained in the difference between the captured live object lists
+// specified by id1 and id2.
+// If id1 is 0 (i.e. not a valid lol), then the whole of lol id2 will be
+// dumped.
+static MaybeObject* Runtime_DumpLOL(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+#ifdef LIVE_OBJECT_LIST
+ HandleScope scope;
+ CONVERT_SMI_CHECKED(id1, args[0]);
+ CONVERT_SMI_CHECKED(id2, args[1]);
+ CONVERT_SMI_CHECKED(start, args[2]);
+ CONVERT_SMI_CHECKED(count, args[3]);
+ CONVERT_ARG_CHECKED(JSObject, filter_obj, 4);
+ EnterDebugger enter_debugger;
+ return LiveObjectList::Dump(id1, id2, start, count, filter_obj);
+#else
+ return isolate->heap()->undefined_value();
+#endif
+}
+
+
+// Gets the specified object as requested by the debugger.
+// This is only used for obj ids shown in live object lists.
+static MaybeObject* Runtime_GetLOLObj(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+#ifdef LIVE_OBJECT_LIST
+ CONVERT_SMI_CHECKED(obj_id, args[0]);
+ Object* result = LiveObjectList::GetObj(obj_id);
+ return result;
+#else
+ return isolate->heap()->undefined_value();
+#endif
+}
+
+
+// Gets the obj id for the specified address if valid.
+// This is only used for obj ids shown in live object lists.
+static MaybeObject* Runtime_GetLOLObjId(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+#ifdef LIVE_OBJECT_LIST
+ HandleScope scope;
+ CONVERT_ARG_CHECKED(String, address, 0);
+ Object* result = LiveObjectList::GetObjId(address);
+ return result;
+#else
+ return isolate->heap()->undefined_value();
+#endif
+}
+
+
+// Gets the retainers that references the specified object alive.
+static MaybeObject* Runtime_GetLOLObjRetainers(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+#ifdef LIVE_OBJECT_LIST
+ HandleScope scope;
+ CONVERT_SMI_CHECKED(obj_id, args[0]);
+ RUNTIME_ASSERT(args[1]->IsUndefined() || args[1]->IsJSObject());
+ RUNTIME_ASSERT(args[2]->IsUndefined() || args[2]->IsBoolean());
+ RUNTIME_ASSERT(args[3]->IsUndefined() || args[3]->IsSmi());
+ RUNTIME_ASSERT(args[4]->IsUndefined() || args[4]->IsSmi());
+ CONVERT_ARG_CHECKED(JSObject, filter_obj, 5);
+
+ Handle<JSObject> instance_filter;
+ if (args[1]->IsJSObject()) {
+ instance_filter = args.at<JSObject>(1);
+ }
+ bool verbose = false;
+ if (args[2]->IsBoolean()) {
+ verbose = args[2]->IsTrue();
+ }
+ int start = 0;
+ if (args[3]->IsSmi()) {
+ start = Smi::cast(args[3])->value();
+ }
+ int limit = Smi::kMaxValue;
+ if (args[4]->IsSmi()) {
+ limit = Smi::cast(args[4])->value();
+ }
+
+ return LiveObjectList::GetObjRetainers(obj_id,
+ instance_filter,
+ verbose,
+ start,
+ limit,
+ filter_obj);
+#else
+ return isolate->heap()->undefined_value();
+#endif
+}
+
+
+// Gets the reference path between 2 objects.
+static MaybeObject* Runtime_GetLOLPath(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+#ifdef LIVE_OBJECT_LIST
+ HandleScope scope;
+ CONVERT_SMI_CHECKED(obj_id1, args[0]);
+ CONVERT_SMI_CHECKED(obj_id2, args[1]);
+ RUNTIME_ASSERT(args[2]->IsUndefined() || args[2]->IsJSObject());
+
+ Handle<JSObject> instance_filter;
+ if (args[2]->IsJSObject()) {
+ instance_filter = args.at<JSObject>(2);
+ }
+
+ Object* result =
+ LiveObjectList::GetPath(obj_id1, obj_id2, instance_filter);
+ return result;
+#else
+ return isolate->heap()->undefined_value();
+#endif
+}
+
+
+// Generates the response to a debugger request for a list of all
+// previously captured live object lists.
+static MaybeObject* Runtime_InfoLOL(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+#ifdef LIVE_OBJECT_LIST
+ CONVERT_SMI_CHECKED(start, args[0]);
+ CONVERT_SMI_CHECKED(count, args[1]);
+ return LiveObjectList::Info(start, count);
+#else
+ return isolate->heap()->undefined_value();
+#endif
+}
+
+
+// Gets a dump of the specified object as requested by the debugger.
+// This is only used for obj ids shown in live object lists.
+static MaybeObject* Runtime_PrintLOLObj(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+#ifdef LIVE_OBJECT_LIST
+ HandleScope scope;
+ CONVERT_SMI_CHECKED(obj_id, args[0]);
+ Object* result = LiveObjectList::PrintObj(obj_id);
+ return result;
+#else
+ return isolate->heap()->undefined_value();
+#endif
+}
+
+
+// Resets and releases all previously captured live object lists.
+static MaybeObject* Runtime_ResetLOL(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+#ifdef LIVE_OBJECT_LIST
+ LiveObjectList::Reset();
+ return isolate->heap()->undefined_value();
+#else
+ return isolate->heap()->undefined_value();
+#endif
+}
+
+
+// Generates the response to a debugger request for a summary of the types
+// of objects in the difference between the captured live object lists
+// specified by id1 and id2.
+// If id1 is 0 (i.e. not a valid lol), then the whole of lol id2 will be
+// summarized.
+static MaybeObject* Runtime_SummarizeLOL(RUNTIME_CALLING_CONVENTION) {
+ RUNTIME_GET_ISOLATE;
+#ifdef LIVE_OBJECT_LIST
+ HandleScope scope;
+ CONVERT_SMI_CHECKED(id1, args[0]);
+ CONVERT_SMI_CHECKED(id2, args[1]);
+ CONVERT_ARG_CHECKED(JSObject, filter_obj, 2);
+
+ EnterDebugger enter_debugger;
+ return LiveObjectList::Summarize(id1, id2, filter_obj);
+#else
+ return isolate->heap()->undefined_value();
+#endif
+}
+
#endif // ENABLE_DEBUGGER_SUPPORT
« no previous file with comments | « src/runtime.h ('k') | src/runtime.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698