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; |
} |