Chromium Code Reviews| Index: src/runtime.cc |
| diff --git a/src/runtime.cc b/src/runtime.cc |
| index 28481383a5fbd846a6940deb9a1b08ee64dab56e..250957e5ea559003fdc23c8a42ae8d2d9be56bf3 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 *value; |
|
rossberg
2014/07/11 12:54:48
Maybe have a comment saying what this value is use
Toon Verwaest
2014/07/14 07:39:55
Not sure what to say here. It's the result of the
rossberg
2014/07/14 12:04:22
But declarations don't have a result (other than u
|
| +} |
| + |
| + |
| 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,67 +2235,108 @@ 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, 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. |
|
rossberg
2014/07/11 12:54:49
Update comment?
Toon Verwaest
2014/07/14 07:39:55
Done.
|
| + // Strict mode handling not needed (const is disallowed in strict mode). |
| + 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_DeclareContextSlot) { |
|
rossberg
2014/07/11 12:54:49
Wasn't the plan to rename this to DeclareLookupSlo
Toon Verwaest
2014/07/14 07:39:55
Done.
|
| + HandleScope scope(isolate); |
| + ASSERT(args.length() == 4); |
| + |
| + // Declarations are always made in a function or native context. In the case |
|
rossberg
2014/07/11 12:54:49
Can also be a global context.
Toon Verwaest
2014/07/14 07:39:55
Done.
|
| + // 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); |
| + |
| + bool is_var = *initial_value == NULL; |
|
rossberg
2014/07/11 12:54:49
Maybe add a TODO to clean this up also.
Toon Verwaest
2014/07/14 07:39:55
Done.
|
| + 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); |
| - } |
| - } else if (lookup.IsNormal()) { |
| - if (global->GetNormalizedProperty(&lookup)->IsTheHole() || |
| - !lookup.IsReadOnly()) { |
| - HandleScope scope(isolate); |
| - JSObject::SetNormalizedProperty(Handle<JSObject>(global), &lookup, 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); |
| + } |
| + |
| + // 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(); |
| } |
| @@ -2444,86 +2353,55 @@ 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 (const is disallowed in strict mode). |
|
rossberg
2014/07/11 12:54:48
s/const/legacy const/
Toon Verwaest
2014/07/14 07:39:55
Done.
|
| - 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 +9200,7 @@ RUNTIME_FUNCTION_RETURN_PAIR(Runtime_LoadContextSlotNoReferenceError) { |
| } |
| -RUNTIME_FUNCTION(Runtime_StoreContextSlot) { |
| +RUNTIME_FUNCTION(Runtime_StoreLookupSlot) { |
| HandleScope scope(isolate); |
| ASSERT(args.length() == 4); |
| @@ -9340,22 +9218,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 +9239,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; |
| } |