| Index: src/runtime.cc
|
| ===================================================================
|
| --- src/runtime.cc (revision 7180)
|
| +++ 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"
|
| @@ -160,7 +162,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;
|
| }
|
| }
|
| @@ -169,7 +172,7 @@
|
|
|
| // Deep copy local elements.
|
| // Pixel elements cannot be created using an object literal.
|
| - ASSERT(!copy->HasPixelElements() && !copy->HasExternalArrayElements());
|
| + ASSERT(!copy->HasExternalArrayElements());
|
| switch (copy->GetElementsKind()) {
|
| case JSObject::FAST_ELEMENTS: {
|
| FixedArray* elements = FixedArray::cast(copy->elements());
|
| @@ -332,7 +335,10 @@
|
| if (key->IsSymbol()) {
|
| if (Handle<String>::cast(key)->AsArrayIndex(&element_index)) {
|
| // Array index as string (uint32).
|
| - result = SetOwnElement(boilerplate, element_index, value);
|
| + result = SetOwnElement(boilerplate,
|
| + element_index,
|
| + value,
|
| + kNonStrictMode);
|
| } else {
|
| Handle<String> name(String::cast(*key));
|
| ASSERT(!name->AsArrayIndex(&element_index));
|
| @@ -341,7 +347,10 @@
|
| }
|
| } else if (key->ToArrayIndex(&element_index)) {
|
| // Array index (uint32).
|
| - result = SetOwnElement(boilerplate, element_index, value);
|
| + result = SetOwnElement(boilerplate,
|
| + element_index,
|
| + value,
|
| + kNonStrictMode);
|
| } else {
|
| // Non-uint32 number.
|
| ASSERT(key->IsNumber());
|
| @@ -546,7 +555,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;
|
| @@ -783,7 +794,9 @@
|
| 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);
|
| + RETURN_IF_EMPTY_HANDLE(value);
|
| + 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());
|
| @@ -816,12 +829,15 @@
|
| }
|
| 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);
|
| + ASSERT(!value.is_null());
|
| + elms->set(VALUE_INDEX, *value);
|
| elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
|
| break;
|
| + }
|
| default:
|
| UNREACHABLE();
|
| break;
|
| @@ -994,12 +1010,16 @@
|
|
|
|
|
| static MaybeObject* Runtime_DeclareGlobals(Arguments args) {
|
| + ASSERT(args.length() == 4);
|
| HandleScope scope;
|
| Handle<GlobalObject> global = Handle<GlobalObject>(Top::context()->global());
|
|
|
| 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
|
| @@ -1051,6 +1071,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("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(type, name);
|
| @@ -1076,29 +1102,43 @@
|
| ? 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(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(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));
|
| }
|
| - RETURN_IF_EMPTY_HANDLE(SetProperty(global, name, value, attributes));
|
| + RETURN_IF_EMPTY_HANDLE(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.
|
| - RETURN_IF_EMPTY_HANDLE(
|
| - SetLocalPropertyIgnoreAttributes(global, name, value, attributes));
|
| + RETURN_IF_EMPTY_HANDLE(SetProperty(global,
|
| + name,
|
| + value,
|
| + attributes,
|
| + strict_mode));
|
| }
|
| }
|
|
|
| @@ -1152,14 +1192,16 @@
|
| } else {
|
| // The holder is an arguments object.
|
| Handle<JSObject> arguments(Handle<JSObject>::cast(holder));
|
| - Handle<Object> result = SetElement(arguments, index, initial_value);
|
| + Handle<Object> result = SetElement(arguments, index, initial_value,
|
| + kNonStrictMode);
|
| if (result.is_null()) return Failure::Exception();
|
| }
|
| } else {
|
| // Slow case: The property is not in the FixedArray part of the context.
|
| Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
|
| RETURN_IF_EMPTY_HANDLE(
|
| - SetProperty(context_ext, name, initial_value, mode));
|
| + SetProperty(context_ext, name, initial_value,
|
| + mode, kNonStrictMode));
|
| }
|
| }
|
|
|
| @@ -1186,7 +1228,22 @@
|
| ASSERT(!context_ext->HasLocalProperty(*name));
|
| Handle<Object> value(Heap::undefined_value());
|
| if (*initial_value != NULL) value = initial_value;
|
| - RETURN_IF_EMPTY_HANDLE(SetProperty(context_ext, name, value, mode));
|
| + // 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("const", name);
|
| + }
|
| + }
|
| + RETURN_IF_EMPTY_HANDLE(SetProperty(context_ext, name, value, mode,
|
| + kNonStrictMode));
|
| }
|
|
|
| return Heap::undefined_value();
|
| @@ -1195,14 +1252,21 @@
|
|
|
| static MaybeObject* Runtime_InitializeVarGlobal(Arguments args) {
|
| 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 = Top::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.
|
| @@ -1212,11 +1276,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;
|
| @@ -1262,8 +1322,9 @@
|
| }
|
|
|
| // Assign the value (or undefined) to the property.
|
| - Object* value = (assign) ? args[1] : Heap::undefined_value();
|
| - return real_holder->SetProperty(&lookup, *name, value, attributes);
|
| + Object* value = (assign) ? args[2] : Heap::undefined_value();
|
| + return real_holder->SetProperty(
|
| + &lookup, *name, value, attributes, strict_mode);
|
| }
|
|
|
| Object* proto = real_holder->GetPrototype();
|
| @@ -1278,9 +1339,7 @@
|
|
|
| global = Top::context()->global();
|
| if (assign) {
|
| - return global->SetLocalPropertyIgnoreAttributes(*name,
|
| - args[1],
|
| - attributes);
|
| + return global->SetProperty(*name, args[2], attributes, strict_mode);
|
| }
|
| return Heap::undefined_value();
|
| }
|
| @@ -1340,13 +1399,19 @@
|
| // 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...
|
| - RETURN_IF_EMPTY_HANDLE(SetProperty(global, name, value, attributes));
|
| + // Passing non-strict mode because the property is writable.
|
| + RETURN_IF_EMPTY_HANDLE(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();
|
| @@ -1413,7 +1478,8 @@
|
| // The holder is an arguments object.
|
| ASSERT((attributes & READ_ONLY) == 0);
|
| Handle<JSObject> arguments(Handle<JSObject>::cast(holder));
|
| - SetElement(arguments, index, value);
|
| + RETURN_IF_EMPTY_HANDLE(
|
| + SetElement(arguments, index, value, kNonStrictMode));
|
| }
|
| return *value;
|
| }
|
| @@ -1422,7 +1488,9 @@
|
| // context.
|
| if (attributes == ABSENT) {
|
| Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
|
| - RETURN_IF_EMPTY_HANDLE(SetProperty(global, name, value, NONE));
|
| + // Strict mode not needed (const disallowed in strict mode).
|
| + RETURN_IF_EMPTY_HANDLE(
|
| + SetProperty(global, name, value, NONE, kNonStrictMode));
|
| return *value;
|
| }
|
|
|
| @@ -1459,8 +1527,9 @@
|
| // 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) {
|
| + // Strict mode not needed (const disallowed in strict mode).
|
| RETURN_IF_EMPTY_HANDLE(
|
| - SetProperty(context_ext, name, value, attributes));
|
| + SetProperty(context_ext, name, value, attributes, kNonStrictMode));
|
| }
|
| }
|
|
|
| @@ -1626,7 +1695,7 @@
|
| code,
|
| false);
|
| optimized->shared()->DontAdaptArguments();
|
| - SetProperty(holder, key, optimized, NONE);
|
| + SetProperty(holder, key, optimized, NONE, kStrictMode);
|
| return optimized;
|
| }
|
|
|
| @@ -3673,6 +3742,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.
|
| @@ -3691,6 +3762,14 @@
|
| LookupResult result;
|
| js_object->LookupRealNamedProperty(*name, &result);
|
|
|
| + // To be compatible with safari we do not change the value on API objects
|
| + // in defineProperty. Firefox disagrees here, and actually changes the value.
|
| + if (result.IsProperty() &&
|
| + (result.type() == CALLBACKS) &&
|
| + result.GetCallbackObject()->IsAccessorInfo()) {
|
| + return Heap::undefined_value();
|
| + }
|
| +
|
| // Take special care when attributes are different and there is already
|
| // a property. For simplicity we normalize the property which enables us
|
| // to not worry about changing the instance_descriptor and creating a new
|
| @@ -3720,7 +3799,8 @@
|
| MaybeObject* Runtime::SetObjectProperty(Handle<Object> object,
|
| Handle<Object> key,
|
| Handle<Object> value,
|
| - PropertyAttributes attr) {
|
| + PropertyAttributes attr,
|
| + StrictModeFlag strict_mode) {
|
| HandleScope scope;
|
|
|
| if (object->IsUndefined() || object->IsNull()) {
|
| @@ -3750,7 +3830,7 @@
|
| return *value;
|
| }
|
|
|
| - Handle<Object> result = SetElement(js_object, index, value);
|
| + Handle<Object> result = SetElement(js_object, index, value, strict_mode);
|
| if (result.is_null()) return Failure::Exception();
|
| return *value;
|
| }
|
| @@ -3758,11 +3838,11 @@
|
| if (key->IsString()) {
|
| Handle<Object> result;
|
| if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
|
| - result = SetElement(js_object, index, value);
|
| + result = SetElement(js_object, index, value, strict_mode);
|
| } 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_mode);
|
| }
|
| if (result.is_null()) return Failure::Exception();
|
| return *value;
|
| @@ -3775,9 +3855,9 @@
|
| Handle<String> name = Handle<String>::cast(converted);
|
|
|
| if (name->AsArrayIndex(&index)) {
|
| - return js_object->SetElement(index, *value);
|
| + return js_object->SetElement(index, *value, strict_mode);
|
| } else {
|
| - return js_object->SetProperty(*name, *value, attr);
|
| + return js_object->SetProperty(*name, *value, attr, strict_mode);
|
| }
|
| }
|
|
|
| @@ -3802,12 +3882,12 @@
|
| return *value;
|
| }
|
|
|
| - return js_object->SetElement(index, *value);
|
| + return js_object->SetElement(index, *value, kNonStrictMode);
|
| }
|
|
|
| if (key->IsString()) {
|
| if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
|
| - return js_object->SetElement(index, *value);
|
| + return js_object->SetElement(index, *value, kNonStrictMode);
|
| } else {
|
| Handle<String> key_string = Handle<String>::cast(key);
|
| key_string->TryFlatten();
|
| @@ -3824,7 +3904,7 @@
|
| Handle<String> name = Handle<String>::cast(converted);
|
|
|
| if (name->AsArrayIndex(&index)) {
|
| - return js_object->SetElement(index, *value);
|
| + return js_object->SetElement(index, *value, kNonStrictMode);
|
| } else {
|
| return js_object->SetLocalPropertyIgnoreAttributes(*name, *value, attr);
|
| }
|
| @@ -3869,23 +3949,31 @@
|
|
|
| static MaybeObject* Runtime_SetProperty(Arguments args) {
|
| 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_mode = kNonStrictMode;
|
| + if (args.length() == 5) {
|
| + CONVERT_SMI_CHECKED(strict_unchecked, args[4]);
|
| + RUNTIME_ASSERT(strict_unchecked == kStrictMode ||
|
| + strict_unchecked == kNonStrictMode);
|
| + strict_mode = static_cast<StrictModeFlag>(strict_unchecked);
|
| }
|
| - return Runtime::SetObjectProperty(object, key, value, attributes);
|
| +
|
| + return Runtime::SetObjectProperty(object,
|
| + key,
|
| + value,
|
| + attributes,
|
| + strict_mode);
|
| }
|
|
|
|
|
| @@ -3919,7 +4007,7 @@
|
| CONVERT_CHECKED(JSObject, object, args[0]);
|
| CONVERT_CHECKED(String, key, args[1]);
|
| CONVERT_SMI_CHECKED(strict, args[2]);
|
| - return object->DeleteProperty(key, strict == kStrictMode
|
| + return object->DeleteProperty(key, (strict == kStrictMode)
|
| ? JSObject::STRICT_DELETION
|
| : JSObject::NORMAL_DELETION);
|
| }
|
| @@ -4264,7 +4352,7 @@
|
| JavaScriptFrame* frame = it.frame();
|
|
|
| // Get the actual number of provided arguments.
|
| - const uint32_t n = frame->GetProvidedParametersCount();
|
| + const uint32_t n = frame->ComputeParametersCount();
|
|
|
| // Try to convert the key to an index. If successful and within
|
| // index return the the argument from the frame.
|
| @@ -6825,7 +6913,7 @@
|
| ASSERT(!frame->is_optimized());
|
| it.AdvanceToArgumentsFrame();
|
| frame = it.frame();
|
| - int argc = frame->GetProvidedParametersCount();
|
| + int argc = frame->ComputeParametersCount();
|
|
|
| // Prepend bound arguments to caller's arguments.
|
| int total_argc = bound_argc + argc;
|
| @@ -6933,6 +7021,7 @@
|
|
|
| bool first_allocation = !shared->live_objects_may_exist();
|
| Handle<JSObject> result = Factory::NewJSObject(function);
|
| + RETURN_IF_EMPTY_HANDLE(result);
|
| // Delay setting the stub if inobject slack tracking is in progress.
|
| if (first_allocation && !shared->IsInobjectSlackTrackingInProgress()) {
|
| TrySettingInlineConstructStub(function);
|
| @@ -7006,7 +7095,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) {
|
| @@ -7015,7 +7104,7 @@
|
| PrintF(": optimized compilation failed]\n");
|
| }
|
| function->ReplaceCode(function->shared()->code());
|
| - return Failure::Exception();
|
| + return function->code();
|
| }
|
|
|
|
|
| @@ -7175,7 +7264,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) {
|
| @@ -7218,6 +7308,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);
|
| }
|
| }
|
| @@ -7468,11 +7561,15 @@
|
|
|
| static MaybeObject* Runtime_StoreContextSlot(Arguments args) {
|
| HandleScope scope;
|
| - ASSERT(args.length() == 3);
|
| + ASSERT(args.length() == 4);
|
|
|
| Handle<Object> value(args[0]);
|
| 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_mode = static_cast<StrictModeFlag>(strict_unchecked);
|
|
|
| int index;
|
| PropertyAttributes attributes;
|
| @@ -7485,11 +7582,17 @@
|
| if ((attributes & READ_ONLY) == 0) {
|
| // Context is a fixed array and set cannot fail.
|
| Context::cast(*holder)->set(index, *value);
|
| + } else if (strict_mode == kStrictMode) {
|
| + // Setting read only property in strict mode.
|
| + Handle<Object> error =
|
| + Factory::NewTypeError("strict_cannot_assign",
|
| + HandleVector(&name, 1));
|
| + return Top::Throw(*error);
|
| }
|
| } else {
|
| ASSERT((attributes & READ_ONLY) == 0);
|
| Handle<Object> result =
|
| - SetElement(Handle<JSObject>::cast(holder), index, value);
|
| + SetElement(Handle<JSObject>::cast(holder), index, value, strict_mode);
|
| if (result.is_null()) {
|
| ASSERT(Top::has_pending_exception());
|
| return Failure::Exception();
|
| @@ -7516,7 +7619,13 @@
|
| // extension object itself.
|
| if ((attributes & READ_ONLY) == 0 ||
|
| (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
|
| - RETURN_IF_EMPTY_HANDLE(SetProperty(context_ext, name, value, NONE));
|
| + RETURN_IF_EMPTY_HANDLE(
|
| + SetProperty(context_ext, name, value, NONE, strict_mode));
|
| + } else if (strict_mode == kStrictMode && (attributes & READ_ONLY) != 0) {
|
| + // Setting read only property in strict mode.
|
| + Handle<Object> error =
|
| + Factory::NewTypeError("strict_cannot_assign", HandleVector(&name, 1));
|
| + return Top::Throw(*error);
|
| }
|
| return *value;
|
| }
|
| @@ -7643,7 +7752,7 @@
|
| // supplied parameters, not all parameters required)
|
| PrintF("(this=");
|
| PrintObject(frame->receiver());
|
| - const int length = frame->GetProvidedParametersCount();
|
| + const int length = frame->ComputeParametersCount();
|
| for (int i = 0; i < length; i++) {
|
| PrintF(", ");
|
| PrintObject(frame->GetParameter(i));
|
| @@ -7826,14 +7935,14 @@
|
|
|
| static ObjectPair CompileGlobalEval(Handle<String> source,
|
| Handle<Object> receiver,
|
| - StrictModeFlag mode) {
|
| + StrictModeFlag strict_mode) {
|
| // Deal with a normal eval call with a string argument. Compile it
|
| // and return the compiled function bound in the local context.
|
| Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
|
| source,
|
| Handle<Context>(Top::context()),
|
| Top::context()->IsGlobalContext(),
|
| - mode);
|
| + strict_mode);
|
| if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
|
| Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
|
| shared,
|
| @@ -7845,12 +7954,9 @@
|
|
|
| static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
|
| ASSERT(args.length() == 4);
|
| - if (!args[0]->IsJSFunction()) {
|
| - return MakePair(Top::ThrowIllegalOperation(), NULL);
|
| - }
|
|
|
| HandleScope scope;
|
| - Handle<JSFunction> callee = args.at<JSFunction>(0);
|
| + Handle<Object> callee = args.at<Object>(0);
|
| Handle<Object> receiver; // Will be overwritten.
|
|
|
| // Compute the calling context.
|
| @@ -7918,12 +8024,9 @@
|
|
|
| static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
|
| ASSERT(args.length() == 4);
|
| - if (!args[0]->IsJSFunction()) {
|
| - return MakePair(Top::ThrowIllegalOperation(), NULL);
|
| - }
|
|
|
| HandleScope scope;
|
| - 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.
|
| @@ -7993,7 +8096,9 @@
|
| if (elements->get(i) == element) return Heap::false_value();
|
| }
|
| Object* obj;
|
| - { MaybeObject* maybe_obj = array->SetFastElement(length, element);
|
| + // Strict not needed. Used for cycle detection in Array join implementation.
|
| + { MaybeObject* maybe_obj = array->SetFastElement(length, element,
|
| + kNonStrictMode);
|
| if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| }
|
| return Heap::true_value();
|
| @@ -8014,377 +8119,472 @@
|
| class ArrayConcatVisitor {
|
| public:
|
| ArrayConcatVisitor(Handle<FixedArray> storage,
|
| - uint32_t index_limit,
|
| bool fast_elements) :
|
| - storage_(storage), index_limit_(index_limit),
|
| - index_offset_(0), fast_elements_(fast_elements) { }
|
| + storage_(Handle<FixedArray>::cast(GlobalHandles::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 =
|
| + 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 =
|
| - 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 = Factory::NewJSArray(0);
|
| + Handle<Object> length =
|
| + Factory::NewNumber(static_cast<double>(index_offset_));
|
| + Handle<Map> map;
|
| + if (fast_elements_) {
|
| + map = Factory::GetFastElementsMap(Handle<Map>(array->map()));
|
| } else {
|
| - index_offset_ += delta;
|
| + map = 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(
|
| + 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 =
|
| + 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() {
|
| + GlobalHandles::Destroy(Handle<Object>::cast(storage_).location());
|
| + }
|
| +
|
| + inline void set_storage(FixedArray* storage) {
|
| + storage_ = Handle<FixedArray>::cast(GlobalHandles::Create(storage));
|
| + }
|
| +
|
| + 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(Handle<JSObject> receiver,
|
| - bool elements_are_ints,
|
| - bool elements_are_guaranteed_smis,
|
| - uint32_t range,
|
| - ArrayConcatVisitor* visitor) {
|
| +static void IterateExternalArrayElements(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 =
|
| + 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 =
|
| - Factory::NewNumber(static_cast<ElementType>(val));
|
| - visitor->visit(j, e);
|
| + }
|
| + }
|
| + } else {
|
| + for (uint32_t j = 0; j < len; j++) {
|
| + HandleScope loop_scope;
|
| + Handle<Object> e = 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 = Factory::NewNumber(array->get(j));
|
| - visitor->visit(j, e);
|
| + break;
|
| + }
|
| + default: {
|
| + int dense_elements_length;
|
| + switch (kind) {
|
| + case JSObject::EXTERNAL_PIXEL_ELEMENTS: {
|
| + dense_elements_length =
|
| + ExternalPixelArray::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.
|
| + * Returns false if any access threw an exception, otherwise true.
|
| */
|
| -static uint32_t IterateElements(Handle<JSObject> receiver,
|
| - uint32_t range,
|
| - ArrayConcatVisitor* visitor) {
|
| - uint32_t num_of_elements = 0;
|
| -
|
| +static bool IterateElements(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));
|
| - 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;
|
| + Handle<Object> element_value(elements->get(j));
|
| + 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);
|
| + if (element_value.is_null()) return false;
|
| + visitor->visit(j, element_value);
|
| }
|
| }
|
| break;
|
| }
|
| - case JSObject::PIXEL_ELEMENTS: {
|
| - Handle<PixelArray> pixels(PixelArray::cast(receiver->elements()));
|
| - uint32_t len = pixels->length();
|
| - if (range < len) {
|
| - len = range;
|
| + 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);
|
| + if (element.is_null()) return false;
|
| + visitor->visit(index, element);
|
| + // Skip to next different index (i.e., omit duplicates).
|
| + do {
|
| + j++;
|
| + } while (j < n && indices[j] == index);
|
| }
|
| -
|
| - 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_PIXEL_ELEMENTS: {
|
| + Handle<ExternalPixelArray> pixels(ExternalPixelArray::cast(
|
| + receiver->elements()));
|
| + for (uint32_t j = 0; j < length; j++) {
|
| + 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>(
|
| - receiver, true, true, range, visitor);
|
| + IterateExternalArrayElements<ExternalByteArray, int8_t>(
|
| + receiver, true, true, visitor);
|
| break;
|
| }
|
| case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: {
|
| - num_of_elements =
|
| - IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
|
| - receiver, true, true, range, visitor);
|
| + IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>(
|
| + receiver, true, true, visitor);
|
| break;
|
| }
|
| case JSObject::EXTERNAL_SHORT_ELEMENTS: {
|
| - num_of_elements =
|
| - IterateExternalArrayElements<ExternalShortArray, int16_t>(
|
| - receiver, true, true, range, visitor);
|
| + IterateExternalArrayElements<ExternalShortArray, int16_t>(
|
| + receiver, true, true, visitor);
|
| break;
|
| }
|
| case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: {
|
| - num_of_elements =
|
| - IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
|
| - receiver, true, true, range, visitor);
|
| + IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>(
|
| + receiver, true, true, visitor);
|
| break;
|
| }
|
| case JSObject::EXTERNAL_INT_ELEMENTS: {
|
| - num_of_elements =
|
| - IterateExternalArrayElements<ExternalIntArray, int32_t>(
|
| - receiver, true, false, range, visitor);
|
| + IterateExternalArrayElements<ExternalIntArray, int32_t>(
|
| + receiver, true, false, visitor);
|
| break;
|
| }
|
| case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: {
|
| - num_of_elements =
|
| - IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
|
| - receiver, true, false, range, visitor);
|
| + IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>(
|
| + receiver, true, false, visitor);
|
| break;
|
| }
|
| case JSObject::EXTERNAL_FLOAT_ELEMENTS: {
|
| - num_of_elements =
|
| - IterateExternalArrayElements<ExternalFloatArray, float>(
|
| - receiver, false, false, range, visitor);
|
| + IterateExternalArrayElements<ExternalFloatArray, float>(
|
| + 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));
|
| - 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)));
|
| - }
|
| - }
|
| - }
|
| - }
|
| - break;
|
| - }
|
| default:
|
| UNREACHABLE();
|
| break;
|
| }
|
| -
|
| - return num_of_elements;
|
| + visitor->increase_index_offset(length);
|
| + return true;
|
| }
|
|
|
|
|
| /**
|
| - * 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(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());
|
| - }
|
| -
|
| - uint32_t nof_elements = 0;
|
| - for (int i = objects.length() - 1; i >= 0; i--) {
|
| - Handle<JSObject> obj = objects[i];
|
| - uint32_t encountered_elements =
|
| - IterateElements(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(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);
|
| - 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(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(Arguments args) {
|
| ASSERT(args.length() == 1);
|
| HandleScope handle_scope;
|
|
|
| - 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 map, length, and content later.
|
| - Handle<JSArray> result = Factory::NewJSArray(0);
|
| -
|
| - uint32_t estimate_nof_elements = IterateArguments(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<Map> map;
|
| Handle<FixedArray> storage;
|
| if (fast_case) {
|
| // The backing storage array must have non-existing elements to
|
| // preserve holes across concat operations.
|
| - map = Factory::GetFastElementsMap(Handle<Map>(result->map()));
|
| - storage = Factory::NewFixedArrayWithHoles(result_length);
|
| + storage = Factory::NewFixedArrayWithHoles(estimate_result_length);
|
| } else {
|
| - map = Factory::GetSlowElementsMap(Handle<Map>(result->map()));
|
| // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
|
| uint32_t at_least_space_for = estimate_nof_elements +
|
| (estimate_nof_elements >> 2);
|
| @@ -8392,21 +8592,22 @@
|
| Factory::NewNumberDictionary(at_least_space_for));
|
| }
|
|
|
| - Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
|
| + ArrayConcatVisitor visitor(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);
|
| + if (!IterateElements(array, &visitor)) {
|
| + return Failure::Exception();
|
| + }
|
| + } else {
|
| + visitor.visit(0, obj);
|
| + visitor.increase_index_offset(1);
|
| + }
|
| + }
|
|
|
| - IterateArguments(arguments, &visitor);
|
| -
|
| - // Please note:
|
| - // - the storage might have been changed in the visitor;
|
| - // - the map and the storage must be set together to avoid breaking
|
| - // the invariant that the map describes the array's elements.
|
| - result->set_map(*map);
|
| - result->set_length(*len);
|
| - result->set_elements(*visitor.storage());
|
| -
|
| - return *result;
|
| + return *visitor.ToArray();
|
| }
|
|
|
|
|
| @@ -8497,10 +8698,12 @@
|
|
|
| Handle<JSObject> jsobject = Handle<JSObject>::cast(object);
|
| Handle<Object> tmp1 = GetElement(jsobject, index1);
|
| + RETURN_IF_EMPTY_HANDLE(tmp1);
|
| Handle<Object> tmp2 = GetElement(jsobject, index2);
|
| + RETURN_IF_EMPTY_HANDLE(tmp2);
|
|
|
| - SetElement(jsobject, index1, tmp2);
|
| - SetElement(jsobject, index2, tmp1);
|
| + RETURN_IF_EMPTY_HANDLE(SetElement(jsobject, index1, tmp2, kStrictMode));
|
| + RETURN_IF_EMPTY_HANDLE(SetElement(jsobject, index2, tmp1, kStrictMode));
|
|
|
| return Heap::undefined_value();
|
| }
|
| @@ -9063,8 +9266,8 @@
|
| // Find the number of arguments to fill. At least fill the number of
|
| // parameters for the function and fill more if more parameters are provided.
|
| int argument_count = info.number_of_parameters();
|
| - if (argument_count < it.frame()->GetProvidedParametersCount()) {
|
| - argument_count = it.frame()->GetProvidedParametersCount();
|
| + if (argument_count < it.frame()->ComputeParametersCount()) {
|
| + argument_count = it.frame()->ComputeParametersCount();
|
| }
|
|
|
| // Calculate the size of the result.
|
| @@ -9121,7 +9324,7 @@
|
| // TODO(3141533): We should be able to get the actual parameter
|
| // value for optimized frames.
|
| if (!is_optimized_frame &&
|
| - (i < it.frame()->GetProvidedParametersCount())) {
|
| + (i < it.frame()->ComputeParametersCount())) {
|
| details->set(details_index++, it.frame()->GetParameter(i));
|
| } else {
|
| details->set(details_index++, Heap::undefined_value());
|
| @@ -9178,7 +9381,9 @@
|
| RETURN_IF_EMPTY_HANDLE_VALUE(
|
| SetProperty(scope_object,
|
| scope_info.context_slot_name(i),
|
| - Handle<Object>(context->get(context_index)), NONE),
|
| + Handle<Object>(context->get(context_index)),
|
| + NONE,
|
| + kNonStrictMode),
|
| false);
|
| }
|
| }
|
| @@ -9204,7 +9409,9 @@
|
| RETURN_IF_EMPTY_HANDLE_VALUE(
|
| SetProperty(local_scope,
|
| scope_info.parameter_name(i),
|
| - Handle<Object>(frame->GetParameter(i)), NONE),
|
| + Handle<Object>(frame->GetParameter(i)),
|
| + NONE,
|
| + kNonStrictMode),
|
| Handle<JSObject>());
|
| }
|
|
|
| @@ -9213,7 +9420,9 @@
|
| RETURN_IF_EMPTY_HANDLE_VALUE(
|
| SetProperty(local_scope,
|
| scope_info.stack_slot_name(i),
|
| - Handle<Object>(frame->GetExpression(i)), NONE),
|
| + Handle<Object>(frame->GetExpression(i)),
|
| + NONE,
|
| + kNonStrictMode),
|
| Handle<JSObject>());
|
| }
|
|
|
| @@ -9237,7 +9446,11 @@
|
| ASSERT(keys->get(i)->IsString());
|
| Handle<String> key(String::cast(keys->get(i)));
|
| RETURN_IF_EMPTY_HANDLE_VALUE(
|
| - SetProperty(local_scope, key, GetProperty(ext, key), NONE),
|
| + SetProperty(local_scope,
|
| + key,
|
| + GetProperty(ext, key),
|
| + NONE,
|
| + kNonStrictMode),
|
| Handle<JSObject>());
|
| }
|
| }
|
| @@ -9275,7 +9488,8 @@
|
| SetProperty(closure_scope,
|
| scope_info.parameter_name(i),
|
| Handle<Object>(element),
|
| - NONE),
|
| + NONE,
|
| + kNonStrictMode),
|
| Handle<JSObject>());
|
| }
|
| }
|
| @@ -9296,7 +9510,11 @@
|
| ASSERT(keys->get(i)->IsString());
|
| Handle<String> key(String::cast(keys->get(i)));
|
| RETURN_IF_EMPTY_HANDLE_VALUE(
|
| - SetProperty(closure_scope, key, GetProperty(ext, key), NONE),
|
| + SetProperty(closure_scope,
|
| + key,
|
| + GetProperty(ext, key),
|
| + NONE,
|
| + kNonStrictMode),
|
| Handle<JSObject>());
|
| }
|
| }
|
| @@ -9986,7 +10204,7 @@
|
| }
|
| }
|
|
|
| - const int length = frame->GetProvidedParametersCount();
|
| + const int length = frame->ComputeParametersCount();
|
| Handle<JSObject> arguments = Factory::NewArgumentsObject(function, length);
|
| Handle<FixedArray> array = Factory::NewFixedArray(length);
|
|
|
| @@ -10774,6 +10992,207 @@
|
| }
|
| return Smi::FromInt(usage);
|
| }
|
| +
|
| +
|
| +// Captures a live object list from the present heap.
|
| +static MaybeObject* Runtime_HasLOLEnabled(Arguments args) {
|
| +#ifdef LIVE_OBJECT_LIST
|
| + return Heap::true_value();
|
| +#else
|
| + return Heap::false_value();
|
| +#endif
|
| +}
|
| +
|
| +
|
| +// Captures a live object list from the present heap.
|
| +static MaybeObject* Runtime_CaptureLOL(Arguments args) {
|
| +#ifdef LIVE_OBJECT_LIST
|
| + return LiveObjectList::Capture();
|
| +#else
|
| + return Heap::undefined_value();
|
| +#endif
|
| +}
|
| +
|
| +
|
| +// Deletes the specified live object list.
|
| +static MaybeObject* Runtime_DeleteLOL(Arguments args) {
|
| +#ifdef LIVE_OBJECT_LIST
|
| + CONVERT_SMI_CHECKED(id, args[0]);
|
| + bool success = LiveObjectList::Delete(id);
|
| + return success ? Heap::true_value() : Heap::false_value();
|
| +#else
|
| + return 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(Arguments args) {
|
| +#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 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(Arguments args) {
|
| +#ifdef LIVE_OBJECT_LIST
|
| + CONVERT_SMI_CHECKED(obj_id, args[0]);
|
| + Object* result = LiveObjectList::GetObj(obj_id);
|
| + return result;
|
| +#else
|
| + return 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(Arguments args) {
|
| +#ifdef LIVE_OBJECT_LIST
|
| + HandleScope scope;
|
| + CONVERT_ARG_CHECKED(String, address, 0);
|
| + Object* result = LiveObjectList::GetObjId(address);
|
| + return result;
|
| +#else
|
| + return Heap::undefined_value();
|
| +#endif
|
| +}
|
| +
|
| +
|
| +// Gets the retainers that references the specified object alive.
|
| +static MaybeObject* Runtime_GetLOLObjRetainers(Arguments args) {
|
| +#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 Heap::undefined_value();
|
| +#endif
|
| +}
|
| +
|
| +
|
| +// Gets the reference path between 2 objects.
|
| +static MaybeObject* Runtime_GetLOLPath(Arguments args) {
|
| +#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 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(Arguments args) {
|
| +#ifdef LIVE_OBJECT_LIST
|
| + CONVERT_SMI_CHECKED(start, args[0]);
|
| + CONVERT_SMI_CHECKED(count, args[1]);
|
| + return LiveObjectList::Info(start, count);
|
| +#else
|
| + return 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(Arguments args) {
|
| +#ifdef LIVE_OBJECT_LIST
|
| + HandleScope scope;
|
| + CONVERT_SMI_CHECKED(obj_id, args[0]);
|
| + Object* result = LiveObjectList::PrintObj(obj_id);
|
| + return result;
|
| +#else
|
| + return Heap::undefined_value();
|
| +#endif
|
| +}
|
| +
|
| +
|
| +// Resets and releases all previously captured live object lists.
|
| +static MaybeObject* Runtime_ResetLOL(Arguments args) {
|
| +#ifdef LIVE_OBJECT_LIST
|
| + LiveObjectList::Reset();
|
| + return Heap::undefined_value();
|
| +#else
|
| + return 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(Arguments args) {
|
| +#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 Heap::undefined_value();
|
| +#endif
|
| +}
|
| +
|
| #endif // ENABLE_DEBUGGER_SUPPORT
|
|
|
|
|
| @@ -10890,7 +11309,8 @@
|
|
|
| limit = Max(limit, 0); // Ensure that limit is not negative.
|
| int initial_size = Min(limit, 10);
|
| - Handle<JSArray> result = Factory::NewJSArray(initial_size * 4);
|
| + Handle<FixedArray> elements =
|
| + Factory::NewFixedArrayWithHoles(initial_size * 4);
|
|
|
| StackFrameIterator iter;
|
| // If the caller parameter is a function we skip frames until we're
|
| @@ -10906,27 +11326,30 @@
|
| List<FrameSummary> frames(3); // Max 2 levels of inlining.
|
| frame->Summarize(&frames);
|
| for (int i = frames.length() - 1; i >= 0; i--) {
|
| + if (cursor + 4 > elements->length()) {
|
| + int new_capacity = JSObject::NewElementsCapacity(elements->length());
|
| + Handle<FixedArray> new_elements =
|
| + Factory::NewFixedArrayWithHoles(new_capacity);
|
| + for (int i = 0; i < cursor; i++) {
|
| + new_elements->set(i, elements->get(i));
|
| + }
|
| + elements = new_elements;
|
| + }
|
| + ASSERT(cursor + 4 <= elements->length());
|
| +
|
| Handle<Object> recv = frames[i].receiver();
|
| Handle<JSFunction> fun = frames[i].function();
|
| Handle<Code> code = frames[i].code();
|
| Handle<Smi> offset(Smi::FromInt(frames[i].offset()));
|
| - FixedArray* elements = FixedArray::cast(result->elements());
|
| - if (cursor + 3 < elements->length()) {
|
| - elements->set(cursor++, *recv);
|
| - elements->set(cursor++, *fun);
|
| - elements->set(cursor++, *code);
|
| - elements->set(cursor++, *offset);
|
| - } else {
|
| - SetElement(result, cursor++, recv);
|
| - SetElement(result, cursor++, fun);
|
| - SetElement(result, cursor++, code);
|
| - SetElement(result, cursor++, offset);
|
| - }
|
| + elements->set(cursor++, *recv);
|
| + elements->set(cursor++, *fun);
|
| + elements->set(cursor++, *code);
|
| + elements->set(cursor++, *offset);
|
| }
|
| }
|
| iter.Advance();
|
| }
|
| -
|
| + Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
|
| result->set_length(Smi::FromInt(cursor));
|
| return *result;
|
| }
|
| @@ -11091,7 +11514,13 @@
|
| static MaybeObject* Runtime_ListNatives(Arguments args) {
|
| ASSERT(args.length() == 0);
|
| HandleScope scope;
|
| - Handle<JSArray> result = Factory::NewJSArray(0);
|
| +#define COUNT_ENTRY(Name, argc, ressize) + 1
|
| + int entry_count = 0
|
| + RUNTIME_FUNCTION_LIST(COUNT_ENTRY)
|
| + INLINE_FUNCTION_LIST(COUNT_ENTRY)
|
| + INLINE_RUNTIME_FUNCTION_LIST(COUNT_ENTRY);
|
| +#undef COUNT_ENTRY
|
| + Handle<FixedArray> elements = Factory::NewFixedArray(entry_count);
|
| int index = 0;
|
| bool inline_runtime_functions = false;
|
| #define ADD_ENTRY(Name, argc, ressize) \
|
| @@ -11106,10 +11535,11 @@
|
| name = Factory::NewStringFromAscii( \
|
| Vector<const char>(#Name, StrLength(#Name))); \
|
| } \
|
| - Handle<JSArray> pair = Factory::NewJSArray(0); \
|
| - SetElement(pair, 0, name); \
|
| - SetElement(pair, 1, Handle<Smi>(Smi::FromInt(argc))); \
|
| - SetElement(result, index++, pair); \
|
| + Handle<FixedArray> pair_elements = Factory::NewFixedArray(2); \
|
| + pair_elements->set(0, *name); \
|
| + pair_elements->set(1, Smi::FromInt(argc)); \
|
| + Handle<JSArray> pair = Factory::NewJSArrayWithElements(pair_elements); \
|
| + elements->set(index++, *pair); \
|
| }
|
| inline_runtime_functions = false;
|
| RUNTIME_FUNCTION_LIST(ADD_ENTRY)
|
| @@ -11117,6 +11547,8 @@
|
| INLINE_FUNCTION_LIST(ADD_ENTRY)
|
| INLINE_RUNTIME_FUNCTION_LIST(ADD_ENTRY)
|
| #undef ADD_ENTRY
|
| + ASSERT_EQ(index, entry_count);
|
| + Handle<JSArray> result = Factory::NewJSArrayWithElements(elements);
|
| return *result;
|
| }
|
| #endif
|
|
|