| Index: src/runtime.cc
|
| diff --git a/src/runtime.cc b/src/runtime.cc
|
| index 28481383a5fbd846a6940deb9a1b08ee64dab56e..aba2d75ef5f40e1d5272ddf7b781db96246b7ce9 100644
|
| --- a/src/runtime.cc
|
| +++ b/src/runtime.cc
|
| @@ -640,8 +640,8 @@ RUNTIME_FUNCTION(Runtime_CreateGlobalPrivateSymbol) {
|
| ASSERT(symbol->IsUndefined());
|
| symbol = isolate->factory()->NewPrivateSymbol();
|
| Handle<Symbol>::cast(symbol)->set_name(*name);
|
| - JSObject::SetProperty(Handle<JSObject>::cast(privates),
|
| - name, symbol, NONE, STRICT).Assert();
|
| + JSObject::SetProperty(Handle<JSObject>::cast(privates), name, symbol, NONE,
|
| + STRICT).Assert();
|
| }
|
| return *symbol;
|
| }
|
| @@ -2103,6 +2103,50 @@ static Object* ThrowRedeclarationError(Isolate* isolate, Handle<String> name) {
|
| }
|
|
|
|
|
| +// May throw a RedeclarationError.
|
| +static Object* DeclareGlobals(Isolate* isolate, Handle<GlobalObject> global,
|
| + Handle<String> name, Handle<Object> value,
|
| + PropertyAttributes attr, bool is_var,
|
| + bool is_const, bool is_function) {
|
| + // Do the lookup own properties only, see ES5 erratum.
|
| + LookupIterator it(global, name, LookupIterator::CHECK_HIDDEN);
|
| + PropertyAttributes old_attributes = JSReceiver::GetPropertyAttributes(&it);
|
| +
|
| + if (old_attributes != ABSENT) {
|
| + // The name was declared before; check for conflicting re-declarations.
|
| + if (is_const) return ThrowRedeclarationError(isolate, name);
|
| +
|
| + // Skip var re-declarations.
|
| + if (is_var) return isolate->heap()->undefined_value();
|
| +
|
| + ASSERT(is_function);
|
| + if ((old_attributes & DONT_DELETE) != 0) {
|
| + // Only allow reconfiguring globals to functions in user code (no
|
| + // natives, which are marked as read-only).
|
| + ASSERT((attr & READ_ONLY) == 0);
|
| +
|
| + // Check whether we can reconfigure the existing property into a
|
| + // function.
|
| + PropertyDetails old_details = it.property_details();
|
| + // TODO(verwaest): CALLBACKS invalidly includes ExecutableAccessInfo,
|
| + // which are actually data properties, not accessor properties.
|
| + if (old_details.IsReadOnly() || old_details.IsDontEnum() ||
|
| + old_details.type() == CALLBACKS) {
|
| + return ThrowRedeclarationError(isolate, name);
|
| + }
|
| + // If the existing property is not configurable, keep its attributes. Do
|
| + attr = old_attributes;
|
| + }
|
| + }
|
| +
|
| + // Define or redefine own property.
|
| + RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
| + global, name, value, attr));
|
| +
|
| + return isolate->heap()->undefined_value();
|
| +}
|
| +
|
| +
|
| RUNTIME_FUNCTION(Runtime_DeclareGlobals) {
|
| HandleScope scope(isolate);
|
| ASSERT(args.length() == 3);
|
| @@ -2117,181 +2161,42 @@ RUNTIME_FUNCTION(Runtime_DeclareGlobals) {
|
| for (int i = 0; i < length; i += 2) {
|
| HandleScope scope(isolate);
|
| Handle<String> name(String::cast(pairs->get(i)));
|
| - Handle<Object> value(pairs->get(i + 1), isolate);
|
| + Handle<Object> initial_value(pairs->get(i + 1), isolate);
|
|
|
| // We have to declare a global const property. To capture we only
|
| // assign to it when evaluating the assignment for "const x =
|
| // <expr>" the initial value is the hole.
|
| - bool is_var = value->IsUndefined();
|
| - bool is_const = value->IsTheHole();
|
| - bool is_function = value->IsSharedFunctionInfo();
|
| + bool is_var = initial_value->IsUndefined();
|
| + bool is_const = initial_value->IsTheHole();
|
| + bool is_function = initial_value->IsSharedFunctionInfo();
|
| ASSERT(is_var + is_const + is_function == 1);
|
|
|
| - if (is_var || is_const) {
|
| - // Lookup the property in the global object, and don't set the
|
| - // value of the variable if the property is already there.
|
| - // Do the lookup own properties only, see ES5 erratum.
|
| - LookupResult lookup(isolate);
|
| - global->LookupOwn(name, &lookup, true);
|
| - if (lookup.IsFound()) {
|
| - // We found an existing property. Unless it was an interceptor
|
| - // that claims the property is absent, skip this declaration.
|
| - if (!lookup.IsInterceptor()) continue;
|
| - if (JSReceiver::GetPropertyAttributes(global, name) != ABSENT) continue;
|
| - // Fall-through and introduce the absent property by using
|
| - // SetProperty.
|
| - }
|
| - } else if (is_function) {
|
| + Handle<Object> value;
|
| + if (is_function) {
|
| // Copy the function and update its context. Use it as value.
|
| Handle<SharedFunctionInfo> shared =
|
| - Handle<SharedFunctionInfo>::cast(value);
|
| + Handle<SharedFunctionInfo>::cast(initial_value);
|
| Handle<JSFunction> function =
|
| - isolate->factory()->NewFunctionFromSharedFunctionInfo(
|
| - shared, context, TENURED);
|
| + isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context,
|
| + TENURED);
|
| value = function;
|
| + } else {
|
| + value = isolate->factory()->undefined_value();
|
| }
|
|
|
| - LookupResult lookup(isolate);
|
| - global->LookupOwn(name, &lookup, true);
|
| -
|
| // Compute the property attributes. According to ECMA-262,
|
| // the property must be non-configurable except in eval.
|
| - int attr = NONE;
|
| - bool is_eval = DeclareGlobalsEvalFlag::decode(flags);
|
| - if (!is_eval) {
|
| - attr |= DONT_DELETE;
|
| - }
|
| bool is_native = DeclareGlobalsNativeFlag::decode(flags);
|
| - if (is_const || (is_native && is_function)) {
|
| - attr |= READ_ONLY;
|
| - }
|
| -
|
| - StrictMode strict_mode = DeclareGlobalsStrictMode::decode(flags);
|
| -
|
| - if (!lookup.IsFound() || is_function) {
|
| - // If the own property exists, check that we can reconfigure it
|
| - // as required for function declarations.
|
| - if (lookup.IsFound() && lookup.IsDontDelete()) {
|
| - if (lookup.IsReadOnly() || lookup.IsDontEnum() ||
|
| - lookup.IsPropertyCallbacks()) {
|
| - return ThrowRedeclarationError(isolate, name);
|
| - }
|
| - // If the existing property is not configurable, keep its attributes.
|
| - attr = lookup.GetAttributes();
|
| - }
|
| - // Define or redefine own property.
|
| - RETURN_FAILURE_ON_EXCEPTION(isolate,
|
| - JSObject::SetOwnPropertyIgnoreAttributes(
|
| - global, name, value, static_cast<PropertyAttributes>(attr)));
|
| - } else {
|
| - // Do a [[Put]] on the existing (own) property.
|
| - RETURN_FAILURE_ON_EXCEPTION(
|
| - isolate,
|
| - JSObject::SetProperty(
|
| - global, name, value, static_cast<PropertyAttributes>(attr),
|
| - strict_mode));
|
| - }
|
| - }
|
| -
|
| - ASSERT(!isolate->has_pending_exception());
|
| - return isolate->heap()->undefined_value();
|
| -}
|
| -
|
| -
|
| -RUNTIME_FUNCTION(Runtime_DeclareContextSlot) {
|
| - HandleScope scope(isolate);
|
| - ASSERT(args.length() == 4);
|
| -
|
| - // Declarations are always made in a function or native context. In the
|
| - // case of eval code, the context passed is the context of the caller,
|
| - // which may be some nested context and not the declaration context.
|
| - CONVERT_ARG_HANDLE_CHECKED(Context, context_arg, 0);
|
| - Handle<Context> context(context_arg->declaration_context());
|
| - CONVERT_ARG_HANDLE_CHECKED(String, name, 1);
|
| - CONVERT_SMI_ARG_CHECKED(mode_arg, 2);
|
| - PropertyAttributes mode = static_cast<PropertyAttributes>(mode_arg);
|
| - RUNTIME_ASSERT(mode == READ_ONLY || mode == NONE);
|
| - CONVERT_ARG_HANDLE_CHECKED(Object, initial_value, 3);
|
| -
|
| - int index;
|
| - PropertyAttributes attributes;
|
| - ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
|
| - BindingFlags binding_flags;
|
| - Handle<Object> holder =
|
| - context->Lookup(name, flags, &index, &attributes, &binding_flags);
|
| -
|
| - if (attributes != ABSENT) {
|
| - // The name was declared before; check for conflicting re-declarations.
|
| - // Note: this is actually inconsistent with what happens for globals (where
|
| - // we silently ignore such declarations).
|
| - if (((attributes & READ_ONLY) != 0) || (mode == READ_ONLY)) {
|
| - // Functions are not read-only.
|
| - ASSERT(mode != READ_ONLY || initial_value->IsTheHole());
|
| - return ThrowRedeclarationError(isolate, name);
|
| - }
|
| -
|
| - // Initialize it if necessary.
|
| - if (*initial_value != NULL) {
|
| - if (index >= 0) {
|
| - ASSERT(holder.is_identical_to(context));
|
| - if (((attributes & READ_ONLY) == 0) ||
|
| - context->get(index)->IsTheHole()) {
|
| - context->set(index, *initial_value);
|
| - }
|
| - } else {
|
| - // Slow case: The property is in the context extension object of a
|
| - // function context or the global object of a native context.
|
| - Handle<JSObject> object = Handle<JSObject>::cast(holder);
|
| - RETURN_FAILURE_ON_EXCEPTION(
|
| - isolate,
|
| - JSReceiver::SetProperty(object, name, initial_value, mode, SLOPPY));
|
| - }
|
| - }
|
| + bool is_eval = DeclareGlobalsEvalFlag::decode(flags);
|
| + int attr = NONE;
|
| + if (is_const) attr |= READ_ONLY;
|
| + if (is_function && is_native) attr |= READ_ONLY;
|
| + if (!is_const && !is_eval) attr |= DONT_DELETE;
|
|
|
| - } else {
|
| - // The property is not in the function context. It needs to be
|
| - // "declared" in the function context's extension context or as a
|
| - // property of the the global object.
|
| - Handle<JSObject> object;
|
| - if (context->has_extension()) {
|
| - object = Handle<JSObject>(JSObject::cast(context->extension()));
|
| - } else {
|
| - // Context extension objects are allocated lazily.
|
| - ASSERT(context->IsFunctionContext());
|
| - object = isolate->factory()->NewJSObject(
|
| - isolate->context_extension_function());
|
| - context->set_extension(*object);
|
| - }
|
| - ASSERT(*object != NULL);
|
| -
|
| - // Declare the property by setting it to the initial value if provided,
|
| - // or undefined, and use the correct mode (e.g. READ_ONLY attribute for
|
| - // constant declarations).
|
| - ASSERT(!JSReceiver::HasOwnProperty(object, name));
|
| - Handle<Object> value(isolate->heap()->undefined_value(), isolate);
|
| - if (*initial_value != NULL) value = initial_value;
|
| - // 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() &&
|
| - !object->IsJSContextExtensionObject()) {
|
| - LookupResult lookup(isolate);
|
| - object->Lookup(name, &lookup);
|
| - if (lookup.IsPropertyCallbacks()) {
|
| - return ThrowRedeclarationError(isolate, name);
|
| - }
|
| - }
|
| - if (object->IsJSGlobalObject()) {
|
| - // Define own property on the global object.
|
| - RETURN_FAILURE_ON_EXCEPTION(isolate,
|
| - JSObject::SetOwnPropertyIgnoreAttributes(object, name, value, mode));
|
| - } else {
|
| - RETURN_FAILURE_ON_EXCEPTION(isolate,
|
| - JSReceiver::SetProperty(object, name, value, mode, SLOPPY));
|
| - }
|
| + Object* result = DeclareGlobals(isolate, global, name, value,
|
| + static_cast<PropertyAttributes>(attr),
|
| + is_var, is_const, is_function);
|
| + if (isolate->has_pending_exception()) return result;
|
| }
|
|
|
| return isolate->heap()->undefined_value();
|
| @@ -2306,60 +2211,23 @@ RUNTIME_FUNCTION(Runtime_InitializeVarGlobal) {
|
|
|
| // Determine if we need to assign to the variable if it already
|
| // exists (based on the number of arguments).
|
| - RUNTIME_ASSERT(args.length() == 2 || args.length() == 3);
|
| - bool assign = args.length() == 3;
|
| + RUNTIME_ASSERT(args.length() == 3);
|
|
|
| CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
|
| CONVERT_STRICT_MODE_ARG_CHECKED(strict_mode, 1);
|
| + CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
|
|
|
| - // According to ECMA-262, section 12.2, page 62, the property must
|
| - // not be deletable.
|
| - PropertyAttributes attributes = DONT_DELETE;
|
| -
|
| - // Lookup the property as own on the global object. If it isn't
|
| - // there, there is a property with this name in the prototype chain.
|
| - // We follow Safari and Firefox behavior and only set the property
|
| - // if there is an explicit initialization value that we have
|
| - // 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 an 'own' lookup.
|
| - LookupResult lookup(isolate);
|
| - isolate->context()->global_object()->LookupOwn(name, &lookup, true);
|
| - if (lookup.IsInterceptor()) {
|
| - Handle<JSObject> holder(lookup.holder());
|
| - PropertyAttributes intercepted =
|
| - JSReceiver::GetPropertyAttributes(holder, name);
|
| - if (intercepted != ABSENT && (intercepted & READ_ONLY) == 0) {
|
| - // Found an interceptor that's not read only.
|
| - if (assign) {
|
| - CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
|
| - Handle<Object> result;
|
| - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
| - isolate, result,
|
| - JSObject::SetPropertyForResult(
|
| - holder, &lookup, name, value, attributes, strict_mode));
|
| - return *result;
|
| - } else {
|
| - return isolate->heap()->undefined_value();
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (assign) {
|
| - CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
|
| - Handle<GlobalObject> global(isolate->context()->global_object());
|
| - Handle<Object> result;
|
| - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
| - isolate, result,
|
| - JSReceiver::SetProperty(global, name, value, attributes, strict_mode));
|
| - return *result;
|
| - }
|
| - return isolate->heap()->undefined_value();
|
| + Handle<GlobalObject> global(isolate->context()->global_object());
|
| + Handle<Object> result;
|
| + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
| + isolate, result,
|
| + JSReceiver::SetProperty(global, name, value, NONE, strict_mode));
|
| + return *result;
|
| }
|
|
|
|
|
| RUNTIME_FUNCTION(Runtime_InitializeConstGlobal) {
|
| - SealHandleScope shs(isolate);
|
| + HandleScope handle_scope(isolate);
|
| // All constants are declared with an initial value. The name
|
| // of the constant is the first argument and the initial value
|
| // is the second.
|
| @@ -2367,71 +2235,112 @@ RUNTIME_FUNCTION(Runtime_InitializeConstGlobal) {
|
| CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
|
| CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
|
|
|
| - // Get the current global object from top.
|
| - GlobalObject* global = isolate->context()->global_object();
|
| + Handle<GlobalObject> global = isolate->global_object();
|
|
|
| - // According to ECMA-262, section 12.2, page 62, the property must
|
| - // not be deletable. Since it's a const, it must be READ_ONLY too.
|
| - PropertyAttributes attributes =
|
| - static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
|
| + // Lookup the property as own on the global object.
|
| + LookupIterator it(global, name, LookupIterator::CHECK_HIDDEN);
|
| + PropertyAttributes old_attributes = JSReceiver::GetPropertyAttributes(&it);
|
|
|
| - // Lookup the property as own on the global object. If it isn't
|
| - // there, we add the property and take special precautions to always
|
| - // add it even in case of callbacks in the prototype chain (this rules
|
| - // out using SetProperty). We use SetOwnPropertyIgnoreAttributes instead
|
| - LookupResult lookup(isolate);
|
| - global->LookupOwn(name, &lookup);
|
| - if (!lookup.IsFound()) {
|
| - HandleScope handle_scope(isolate);
|
| - Handle<GlobalObject> global(isolate->context()->global_object());
|
| - JSObject::AddProperty(global, name, value, attributes);
|
| - return *value;
|
| + PropertyAttributes attr =
|
| + static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
|
| + // Set the value if the property is either missing, or the property attributes
|
| + // allow setting the value without invoking an accessor.
|
| + if (it.IsFound()) {
|
| + // Ignore if we can't reconfigure the value.
|
| + if ((old_attributes & DONT_DELETE) != 0) {
|
| + if ((old_attributes & READ_ONLY) != 0 ||
|
| + it.property_kind() == LookupIterator::ACCESSOR) {
|
| + return *value;
|
| + }
|
| + attr = static_cast<PropertyAttributes>(old_attributes | READ_ONLY);
|
| + }
|
| }
|
|
|
| - if (!lookup.IsReadOnly()) {
|
| - // Restore global object from context (in case of GC) and continue
|
| - // with setting the value.
|
| - HandleScope handle_scope(isolate);
|
| - Handle<GlobalObject> global(isolate->context()->global_object());
|
| + RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
| + global, name, value, attr));
|
|
|
| - // 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...
|
| - // Passing sloppy mode because the property is writable.
|
| - RETURN_FAILURE_ON_EXCEPTION(
|
| - isolate,
|
| - JSReceiver::SetProperty(global, name, value, attributes, SLOPPY));
|
| - return *value;
|
| + return *value;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_DeclareLookupSlot) {
|
| + HandleScope scope(isolate);
|
| + ASSERT(args.length() == 4);
|
| +
|
| + // Declarations are always made in a function, native, or global context. In
|
| + // the case of eval code, the context passed is the context of the caller,
|
| + // which may be some nested context and not the declaration context.
|
| + CONVERT_ARG_HANDLE_CHECKED(Context, context_arg, 0);
|
| + Handle<Context> context(context_arg->declaration_context());
|
| + CONVERT_ARG_HANDLE_CHECKED(String, name, 1);
|
| + CONVERT_SMI_ARG_CHECKED(attr_arg, 2);
|
| + PropertyAttributes attr = static_cast<PropertyAttributes>(attr_arg);
|
| + RUNTIME_ASSERT(attr == READ_ONLY || attr == NONE);
|
| + CONVERT_ARG_HANDLE_CHECKED(Object, initial_value, 3);
|
| +
|
| + // TODO(verwaest): Unify the encoding indicating "var" with DeclareGlobals.
|
| + bool is_var = *initial_value == NULL;
|
| + bool is_const = initial_value->IsTheHole();
|
| + bool is_function = initial_value->IsJSFunction();
|
| + ASSERT(is_var + is_const + is_function == 1);
|
| +
|
| + int index;
|
| + PropertyAttributes attributes;
|
| + ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
|
| + BindingFlags binding_flags;
|
| + Handle<Object> holder =
|
| + context->Lookup(name, flags, &index, &attributes, &binding_flags);
|
| +
|
| + Handle<JSObject> object;
|
| + Handle<Object> value =
|
| + is_function ? initial_value
|
| + : Handle<Object>::cast(isolate->factory()->undefined_value());
|
| +
|
| + // TODO(verwaest): This case should probably not be covered by this function,
|
| + // but by DeclareGlobals instead.
|
| + if ((attributes != ABSENT && holder->IsJSGlobalObject()) ||
|
| + (context_arg->has_extension() &&
|
| + context_arg->extension()->IsJSGlobalObject())) {
|
| + return DeclareGlobals(isolate, Handle<JSGlobalObject>::cast(holder), name,
|
| + value, attr, is_var, is_const, is_function);
|
| }
|
|
|
| - // Set the value, but only if 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 is disallowed in strict mode).
|
| - if (lookup.IsField()) {
|
| - FixedArray* properties = global->properties();
|
| - int index = lookup.GetFieldIndex().outobject_array_index();
|
| - if (properties->get(index)->IsTheHole() || !lookup.IsReadOnly()) {
|
| - properties->set(index, *value);
|
| + if (attributes != ABSENT) {
|
| + // The name was declared before; check for conflicting re-declarations.
|
| + if (is_const || (attributes & READ_ONLY) != 0) {
|
| + return ThrowRedeclarationError(isolate, name);
|
| }
|
| - } else if (lookup.IsNormal()) {
|
| - if (global->GetNormalizedProperty(&lookup)->IsTheHole() ||
|
| - !lookup.IsReadOnly()) {
|
| - HandleScope scope(isolate);
|
| - JSObject::SetNormalizedProperty(Handle<JSObject>(global), &lookup, value);
|
| +
|
| + // Skip var re-declarations.
|
| + if (is_var) return isolate->heap()->undefined_value();
|
| +
|
| + ASSERT(is_function);
|
| + if (index >= 0) {
|
| + ASSERT(holder.is_identical_to(context));
|
| + context->set(index, *initial_value);
|
| + return isolate->heap()->undefined_value();
|
| }
|
| +
|
| + object = Handle<JSObject>::cast(holder);
|
| +
|
| + } else if (context->has_extension()) {
|
| + object = handle(JSObject::cast(context->extension()));
|
| + ASSERT(object->IsJSContextExtensionObject() || object->IsJSGlobalObject());
|
| } else {
|
| - // Ignore re-initialization of constants that have already been
|
| - // assigned a constant value.
|
| - ASSERT(lookup.IsReadOnly() && lookup.IsConstant());
|
| + ASSERT(context->IsFunctionContext());
|
| + object =
|
| + isolate->factory()->NewJSObject(isolate->context_extension_function());
|
| + context->set_extension(*object);
|
| }
|
|
|
| - // Use the set value as the result of the operation.
|
| - return *value;
|
| + RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
| + object, name, value, attr));
|
| +
|
| + return isolate->heap()->undefined_value();
|
| }
|
|
|
|
|
| -RUNTIME_FUNCTION(Runtime_InitializeConstContextSlot) {
|
| +RUNTIME_FUNCTION(Runtime_InitializeLegacyConstLookupSlot) {
|
| HandleScope scope(isolate);
|
| ASSERT(args.length() == 3);
|
|
|
| @@ -2444,86 +2353,56 @@ RUNTIME_FUNCTION(Runtime_InitializeConstContextSlot) {
|
|
|
| int index;
|
| PropertyAttributes attributes;
|
| - ContextLookupFlags flags = FOLLOW_CHAINS;
|
| + ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
|
| BindingFlags binding_flags;
|
| Handle<Object> holder =
|
| context->Lookup(name, flags, &index, &attributes, &binding_flags);
|
|
|
| if (index >= 0) {
|
| ASSERT(holder->IsContext());
|
| - // Property was found in a context. Perform the assignment if we
|
| - // found some non-constant or an uninitialized constant.
|
| + // Property was found in a context. Perform the assignment if the constant
|
| + // was uninitialized.
|
| Handle<Context> context = Handle<Context>::cast(holder);
|
| - if ((attributes & READ_ONLY) == 0 || context->get(index)->IsTheHole()) {
|
| - context->set(index, *value);
|
| - }
|
| + ASSERT((attributes & READ_ONLY) != 0);
|
| + if (context->get(index)->IsTheHole()) context->set(index, *value);
|
| return *value;
|
| }
|
|
|
| - // The property could not be found, we introduce it as a property of the
|
| - // global object.
|
| - if (attributes == ABSENT) {
|
| - Handle<JSObject> global = Handle<JSObject>(
|
| - isolate->context()->global_object());
|
| - // Strict mode not needed (const disallowed in strict mode).
|
| - RETURN_FAILURE_ON_EXCEPTION(
|
| - isolate,
|
| - JSReceiver::SetProperty(global, name, value, NONE, SLOPPY));
|
| - return *value;
|
| - }
|
| + PropertyAttributes attr =
|
| + static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
|
|
|
| - // The property was present in some function's context extension object,
|
| - // as a property on the subject of a with, or as a property of the global
|
| - // object.
|
| - //
|
| - // In most situations, eval-introduced consts should still be present in
|
| - // the context extension object. However, because declaration and
|
| - // initialization are separate, the property might have been deleted
|
| - // before we reach the initialization point.
|
| - //
|
| - // Example:
|
| - //
|
| - // function f() { eval("delete x; const x;"); }
|
| - //
|
| - // In that case, the initialization behaves like a normal assignment.
|
| - Handle<JSObject> object = Handle<JSObject>::cast(holder);
|
| + // Strict mode handling not needed (legacy const is disallowed in strict
|
| + // mode).
|
|
|
| - if (*object == context->extension()) {
|
| - // This is the property that was introduced by the const declaration.
|
| - // Set it if it hasn't been set before. NOTE: We cannot use
|
| - // GetProperty() to get the current value as it 'unholes' the value.
|
| - LookupResult lookup(isolate);
|
| - object->LookupOwnRealNamedProperty(name, &lookup);
|
| - ASSERT(lookup.IsFound()); // the property was declared
|
| - ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
|
| -
|
| - if (lookup.IsField()) {
|
| - FixedArray* properties = object->properties();
|
| - FieldIndex index = lookup.GetFieldIndex();
|
| - ASSERT(!index.is_inobject());
|
| - if (properties->get(index.outobject_array_index())->IsTheHole()) {
|
| - properties->set(index.outobject_array_index(), *value);
|
| - }
|
| - } else if (lookup.IsNormal()) {
|
| - if (object->GetNormalizedProperty(&lookup)->IsTheHole()) {
|
| - JSObject::SetNormalizedProperty(object, &lookup, value);
|
| - }
|
| - } else {
|
| - // We should not reach here. Any real, named property should be
|
| - // either a field or a dictionary slot.
|
| - UNREACHABLE();
|
| - }
|
| + // The declared const was configurable, and may have been deleted in the
|
| + // meanwhile. If so, re-introduce the variable in the context extension.
|
| + ASSERT(context_arg->has_extension());
|
| + if (attributes == ABSENT) {
|
| + holder = handle(context_arg->extension(), isolate);
|
| } else {
|
| - // The property was found on some other 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_FAILURE_ON_EXCEPTION(
|
| - isolate,
|
| - JSReceiver::SetProperty(object, name, value, attributes, SLOPPY));
|
| + // For JSContextExtensionObjects, the initializer can be run multiple times
|
| + // if in a for loop: for (var i = 0; i < 2; i++) { const x = i; }. Only the
|
| + // first assignment should go through. For JSGlobalObjects, additionally any
|
| + // code can run in between that modifies the declared property.
|
| + ASSERT(holder->IsJSGlobalObject() || holder->IsJSContextExtensionObject());
|
| +
|
| + LookupIterator it(holder, name, LookupIterator::CHECK_HIDDEN);
|
| + PropertyAttributes old_attributes = JSReceiver::GetPropertyAttributes(&it);
|
| +
|
| + // Ignore if we can't reconfigure the value.
|
| + if ((old_attributes & DONT_DELETE) != 0) {
|
| + if ((old_attributes & READ_ONLY) != 0 ||
|
| + it.property_kind() == LookupIterator::ACCESSOR) {
|
| + return *value;
|
| + }
|
| + attr = static_cast<PropertyAttributes>(old_attributes | READ_ONLY);
|
| }
|
| }
|
|
|
| + RETURN_FAILURE_ON_EXCEPTION(
|
| + isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
| + Handle<JSObject>::cast(holder), name, value, attr));
|
| +
|
| return *value;
|
| }
|
|
|
| @@ -9322,7 +9201,7 @@ RUNTIME_FUNCTION_RETURN_PAIR(Runtime_LoadContextSlotNoReferenceError) {
|
| }
|
|
|
|
|
| -RUNTIME_FUNCTION(Runtime_StoreContextSlot) {
|
| +RUNTIME_FUNCTION(Runtime_StoreLookupSlot) {
|
| HandleScope scope(isolate);
|
| ASSERT(args.length() == 4);
|
|
|
| @@ -9340,22 +9219,13 @@ RUNTIME_FUNCTION(Runtime_StoreContextSlot) {
|
| &index,
|
| &attributes,
|
| &binding_flags);
|
| + // In case of JSProxy, an exception might have been thrown.
|
| if (isolate->has_pending_exception()) return isolate->heap()->exception();
|
|
|
| + // The property was found in a context slot.
|
| if (index >= 0) {
|
| - // The property was found in a context slot.
|
| - Handle<Context> context = Handle<Context>::cast(holder);
|
| - if (binding_flags == MUTABLE_CHECK_INITIALIZED &&
|
| - context->get(index)->IsTheHole()) {
|
| - Handle<Object> error =
|
| - isolate->factory()->NewReferenceError("not_defined",
|
| - HandleVector(&name, 1));
|
| - return isolate->Throw(*error);
|
| - }
|
| - // Ignore if read_only variable.
|
| if ((attributes & READ_ONLY) == 0) {
|
| - // Context is a fixed array and set cannot fail.
|
| - context->set(index, *value);
|
| + Handle<Context>::cast(holder)->set(index, *value);
|
| } else if (strict_mode == STRICT) {
|
| // Setting read only property in strict mode.
|
| Handle<Object> error =
|
| @@ -9370,39 +9240,22 @@ RUNTIME_FUNCTION(Runtime_StoreContextSlot) {
|
| // context extension object, a property of the subject of a with, or a
|
| // property of the global object.
|
| Handle<JSReceiver> object;
|
| -
|
| - if (!holder.is_null()) {
|
| + if (attributes != ABSENT) {
|
| // The property exists on the holder.
|
| object = Handle<JSReceiver>::cast(holder);
|
| + } else if (strict_mode == STRICT) {
|
| + // If absent in strict mode: throw.
|
| + Handle<Object> error = isolate->factory()->NewReferenceError(
|
| + "not_defined", HandleVector(&name, 1));
|
| + return isolate->Throw(*error);
|
| } else {
|
| - // The property was not found.
|
| - ASSERT(attributes == ABSENT);
|
| -
|
| - if (strict_mode == STRICT) {
|
| - // Throw in strict mode (assignment to undefined variable).
|
| - Handle<Object> error =
|
| - isolate->factory()->NewReferenceError(
|
| - "not_defined", HandleVector(&name, 1));
|
| - return isolate->Throw(*error);
|
| - }
|
| - // In sloppy mode, the property is added to the global object.
|
| - attributes = NONE;
|
| - object = Handle<JSReceiver>(isolate->context()->global_object());
|
| + // If absent in sloppy mode: add the property to the global object.
|
| + object = Handle<JSReceiver>(context->global_object());
|
| }
|
|
|
| - // Set the property if it's not read only or doesn't yet exist.
|
| - if ((attributes & READ_ONLY) == 0 ||
|
| - (JSReceiver::GetOwnPropertyAttributes(object, name) == ABSENT)) {
|
| - RETURN_FAILURE_ON_EXCEPTION(
|
| - isolate,
|
| - JSReceiver::SetProperty(object, name, value, NONE, strict_mode));
|
| - } else if (strict_mode == STRICT && (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_FAILURE_ON_EXCEPTION(
|
| + isolate, JSReceiver::SetProperty(object, name, value, NONE, strict_mode));
|
| +
|
| return *value;
|
| }
|
|
|
|
|