| Index: src/objects.cc
|
| diff --git a/src/objects.cc b/src/objects.cc
|
| index a608da440bebc815517f96934e9b201d5580e645..e837e58d8f0ff56adbaf5c24972de5f54d5d6d6b 100644
|
| --- a/src/objects.cc
|
| +++ b/src/objects.cc
|
| @@ -536,10 +536,11 @@ static bool FindAllCanWriteHolder(LookupIterator* it) {
|
|
|
|
|
| MaybeHandle<Object> JSObject::SetPropertyWithFailedAccessCheck(
|
| - LookupIterator* it, Handle<Object> value, LanguageMode language_mode) {
|
| + LookupIterator* it, Handle<Object> value) {
|
| Handle<JSObject> checked = it->GetHolder<JSObject>();
|
| if (FindAllCanWriteHolder(it)) {
|
| - return SetPropertyWithAccessor(it, value, language_mode);
|
| + // The supplied language-mode is ignored by SetPropertyWithAccessor.
|
| + return SetPropertyWithAccessor(it, value, SLOPPY);
|
| }
|
|
|
| it->isolate()->ReportFailedAccessCheck(checked);
|
| @@ -3044,8 +3045,7 @@ MaybeHandle<Object> Object::SetPropertyInternal(LookupIterator* it,
|
| if (it->HasAccess()) break;
|
| // Check whether it makes sense to reuse the lookup iterator. Here it
|
| // might still call into setters up the prototype chain.
|
| - return JSObject::SetPropertyWithFailedAccessCheck(it, value,
|
| - language_mode);
|
| + return JSObject::SetPropertyWithFailedAccessCheck(it, value);
|
|
|
| case LookupIterator::JSPROXY:
|
| if (it->HolderIsReceiverOrHiddenPrototype()) {
|
| @@ -3165,9 +3165,8 @@ MaybeHandle<Object> Object::SetSuperProperty(LookupIterator* it,
|
| for (; own_lookup.IsFound(); own_lookup.Next()) {
|
| switch (own_lookup.state()) {
|
| case LookupIterator::ACCESS_CHECK:
|
| - if (!it->isolate()->MayAccess(own_lookup.GetHolder<JSObject>())) {
|
| - return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value,
|
| - language_mode);
|
| + if (!own_lookup.HasAccess()) {
|
| + return JSObject::SetPropertyWithFailedAccessCheck(&own_lookup, value);
|
| }
|
| break;
|
|
|
| @@ -3177,8 +3176,8 @@ MaybeHandle<Object> Object::SetSuperProperty(LookupIterator* it,
|
| case LookupIterator::DATA: {
|
| PropertyDetails details = own_lookup.property_details();
|
| if (details.IsConfigurable() || !details.IsReadOnly()) {
|
| - return JSObject::ReconfigureAsDataProperty(&own_lookup, value,
|
| - details.attributes());
|
| + return JSObject::DefineOwnPropertyIgnoreAttributes(
|
| + &own_lookup, value, details.attributes());
|
| }
|
| return WriteToReadOnlyProperty(&own_lookup, value, language_mode);
|
| }
|
| @@ -3186,8 +3185,8 @@ MaybeHandle<Object> Object::SetSuperProperty(LookupIterator* it,
|
| case LookupIterator::ACCESSOR: {
|
| PropertyDetails details = own_lookup.property_details();
|
| if (details.IsConfigurable()) {
|
| - return JSObject::ReconfigureAsDataProperty(&own_lookup, value,
|
| - details.attributes());
|
| + return JSObject::DefineOwnPropertyIgnoreAttributes(
|
| + &own_lookup, value, details.attributes());
|
| }
|
|
|
| return RedefineNonconfigurableProperty(it->isolate(), it->GetName(),
|
| @@ -4131,7 +4130,11 @@ void ExecutableAccessorInfo::ClearSetter(Handle<ExecutableAccessorInfo> info) {
|
| }
|
|
|
|
|
| -MaybeHandle<Object> JSObject::ReconfigureAsDataProperty(
|
| +// Reconfigures a property to a data property with attributes, even if it is not
|
| +// reconfigurable.
|
| +// Requires a LookupIterator that does not look at the prototype chain beyond
|
| +// hidden prototypes.
|
| +MaybeHandle<Object> JSObject::DefineOwnPropertyIgnoreAttributes(
|
| LookupIterator* it, Handle<Object> value, PropertyAttributes attributes,
|
| ExecutableAccessorInfoHandling handling) {
|
| Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver());
|
| @@ -4139,54 +4142,81 @@ MaybeHandle<Object> JSObject::ReconfigureAsDataProperty(
|
| (it->IsElement() ||
|
| !it->isolate()->IsInternallyUsedPropertyName(it->name()));
|
|
|
| - switch (it->state()) {
|
| - case LookupIterator::INTERCEPTOR:
|
| - case LookupIterator::JSPROXY:
|
| - case LookupIterator::NOT_FOUND:
|
| - case LookupIterator::TRANSITION:
|
| - case LookupIterator::ACCESS_CHECK:
|
| - UNREACHABLE();
|
| + for (; it->IsFound(); it->Next()) {
|
| + switch (it->state()) {
|
| + case LookupIterator::JSPROXY:
|
| + case LookupIterator::NOT_FOUND:
|
| + case LookupIterator::TRANSITION:
|
| + UNREACHABLE();
|
|
|
| - case LookupIterator::INTEGER_INDEXED_EXOTIC:
|
| - return value;
|
| + case LookupIterator::ACCESS_CHECK:
|
| + if (!it->HasAccess()) {
|
| + return SetPropertyWithFailedAccessCheck(it, value);
|
| + }
|
| + break;
|
|
|
| - case LookupIterator::ACCESSOR: {
|
| - PropertyDetails details = it->property_details();
|
| - // Ensure the context isn't changed after calling into accessors.
|
| - AssertNoContextChange ncc(it->isolate());
|
| + // If there's an interceptor, try to store the property with the
|
| + // interceptor.
|
| + // In case of success, the attributes will have been reset to the default
|
| + // attributes of the interceptor, rather than the incoming attributes.
|
| + //
|
| + // TODO(verwaest): JSProxy afterwards verify the attributes that the
|
| + // JSProxy claims it has, and verifies that they are compatible. If not,
|
| + // they throw. Here we should do the same.
|
| + case LookupIterator::INTERCEPTOR:
|
| + if (handling == DONT_FORCE_FIELD) {
|
| + MaybeHandle<Object> maybe_result =
|
| + JSObject::SetPropertyWithInterceptor(it, value);
|
| + if (!maybe_result.is_null()) return maybe_result;
|
| + if (it->isolate()->has_pending_exception()) return maybe_result;
|
| + }
|
| + break;
|
|
|
| - Handle<Object> accessors = it->GetAccessors();
|
| + case LookupIterator::INTEGER_INDEXED_EXOTIC:
|
| + return value;
|
|
|
| - // Special handling for ExecutableAccessorInfo, which behaves like a
|
| - // data property.
|
| - if (accessors->IsExecutableAccessorInfo() &&
|
| - handling == DONT_FORCE_FIELD) {
|
| - Handle<Object> result;
|
| - ASSIGN_RETURN_ON_EXCEPTION(
|
| - it->isolate(), result,
|
| - JSObject::SetPropertyWithAccessor(it, value, STRICT), Object);
|
| - DCHECK(result->SameValue(*value));
|
| -
|
| - if (details.attributes() == attributes) return value;
|
| -
|
| - // Reconfigure the accessor if attributes mismatch.
|
| - Handle<ExecutableAccessorInfo> new_data = Accessors::CloneAccessor(
|
| - it->isolate(), Handle<ExecutableAccessorInfo>::cast(accessors));
|
| - new_data->set_property_attributes(attributes);
|
| - // By clearing the setter we don't have to introduce a lookup to
|
| - // the setter, simply make it unavailable to reflect the
|
| - // attributes.
|
| - if (attributes & READ_ONLY) {
|
| - ExecutableAccessorInfo::ClearSetter(new_data);
|
| - }
|
| + case LookupIterator::ACCESSOR: {
|
| + Handle<Object> accessors = it->GetAccessors();
|
|
|
| - if (it->IsElement()) {
|
| - SetElementCallback(it->GetHolder<JSObject>(), it->index(), new_data,
|
| - attributes);
|
| + // Special handling for ExecutableAccessorInfo, which behaves like a
|
| + // data property.
|
| + if (accessors->IsExecutableAccessorInfo() &&
|
| + handling == DONT_FORCE_FIELD) {
|
| + PropertyDetails details = it->property_details();
|
| + // Ensure the context isn't changed after calling into accessors.
|
| + AssertNoContextChange ncc(it->isolate());
|
| +
|
| + Handle<Object> result;
|
| + ASSIGN_RETURN_ON_EXCEPTION(
|
| + it->isolate(), result,
|
| + JSObject::SetPropertyWithAccessor(it, value, STRICT), Object);
|
| + DCHECK(result->SameValue(*value));
|
| +
|
| + if (details.attributes() == attributes) return value;
|
| +
|
| + // Reconfigure the accessor if attributes mismatch.
|
| + Handle<ExecutableAccessorInfo> new_data = Accessors::CloneAccessor(
|
| + it->isolate(), Handle<ExecutableAccessorInfo>::cast(accessors));
|
| + new_data->set_property_attributes(attributes);
|
| + // By clearing the setter we don't have to introduce a lookup to
|
| + // the setter, simply make it unavailable to reflect the
|
| + // attributes.
|
| + if (attributes & READ_ONLY) {
|
| + ExecutableAccessorInfo::ClearSetter(new_data);
|
| + }
|
| +
|
| + if (it->IsElement()) {
|
| + SetElementCallback(it->GetHolder<JSObject>(), it->index(), new_data,
|
| + attributes);
|
| + } else {
|
| + SetPropertyCallback(it->GetHolder<JSObject>(), it->name(), new_data,
|
| + attributes);
|
| + }
|
| } else {
|
| - SetPropertyCallback(it->GetHolder<JSObject>(), it->name(), new_data,
|
| - attributes);
|
| + it->ReconfigureDataProperty(value, attributes);
|
| + it->WriteDataValue(value);
|
| }
|
| +
|
| if (is_observed) {
|
| RETURN_ON_EXCEPTION(
|
| it->isolate(),
|
| @@ -4194,81 +4224,56 @@ MaybeHandle<Object> JSObject::ReconfigureAsDataProperty(
|
| it->factory()->the_hole_value()),
|
| Object);
|
| }
|
| - return value;
|
| - }
|
| -
|
| - it->ReconfigureDataProperty(value, attributes);
|
| - it->WriteDataValue(value);
|
| -
|
| - if (is_observed) {
|
| - RETURN_ON_EXCEPTION(
|
| - it->isolate(),
|
| - EnqueueChangeRecord(object, "reconfigure", it->GetName(),
|
| - it->factory()->the_hole_value()),
|
| - Object);
|
| - }
|
|
|
| - return value;
|
| - }
|
| -
|
| - case LookupIterator::DATA: {
|
| - PropertyDetails details = it->property_details();
|
| - Handle<Object> old_value = it->factory()->the_hole_value();
|
| - // Regular property update if the attributes match.
|
| - if (details.attributes() == attributes) {
|
| - return SetDataProperty(it, value);
|
| + return value;
|
| }
|
| + case LookupIterator::DATA: {
|
| + PropertyDetails details = it->property_details();
|
| + Handle<Object> old_value = it->factory()->the_hole_value();
|
| + // Regular property update if the attributes match.
|
| + if (details.attributes() == attributes) {
|
| + return SetDataProperty(it, value);
|
| + }
|
|
|
| - // Special case: properties of typed arrays cannot be reconfigured to
|
| - // non-writable nor to non-enumerable.
|
| - if (it->IsElement() && (object->HasExternalArrayElements() ||
|
| - object->HasFixedTypedArrayElements())) {
|
| - return RedefineNonconfigurableProperty(it->isolate(), it->GetName(),
|
| - value, STRICT);
|
| - }
|
| + // Special case: properties of typed arrays cannot be reconfigured to
|
| + // non-writable nor to non-enumerable.
|
| + if (it->IsElement() && (object->HasExternalArrayElements() ||
|
| + object->HasFixedTypedArrayElements())) {
|
| + return RedefineNonconfigurableProperty(it->isolate(), it->GetName(),
|
| + value, STRICT);
|
| + }
|
|
|
| - // Reconfigure the data property if the attributes mismatch.
|
| - if (is_observed) old_value = it->GetDataValue();
|
| + // Reconfigure the data property if the attributes mismatch.
|
| + if (is_observed) old_value = it->GetDataValue();
|
|
|
| - it->ReconfigureDataProperty(value, attributes);
|
| - it->WriteDataValue(value);
|
| + it->ReconfigureDataProperty(value, attributes);
|
| + it->WriteDataValue(value);
|
|
|
| - if (is_observed) {
|
| - if (old_value->SameValue(*value)) {
|
| - old_value = it->factory()->the_hole_value();
|
| + if (is_observed) {
|
| + if (old_value->SameValue(*value)) {
|
| + old_value = it->factory()->the_hole_value();
|
| + }
|
| + RETURN_ON_EXCEPTION(it->isolate(),
|
| + EnqueueChangeRecord(object, "reconfigure",
|
| + it->GetName(), old_value),
|
| + Object);
|
| }
|
| - RETURN_ON_EXCEPTION(it->isolate(),
|
| - EnqueueChangeRecord(object, "reconfigure",
|
| - it->GetName(), old_value),
|
| - Object);
|
| + return value;
|
| }
|
| }
|
| }
|
|
|
| - return value;
|
| + return AddDataProperty(it, value, attributes, STRICT,
|
| + CERTAINLY_NOT_STORE_FROM_KEYED);
|
| }
|
|
|
|
|
| -// Reconfigures a property to a data property with attributes, even if it is not
|
| -// reconfigurable.
|
| MaybeHandle<Object> JSObject::SetOwnPropertyIgnoreAttributes(
|
| Handle<JSObject> object, Handle<Name> name, Handle<Object> value,
|
| PropertyAttributes attributes, ExecutableAccessorInfoHandling handling) {
|
| DCHECK(!value->IsTheHole());
|
| - LookupIterator it(object, name, LookupIterator::OWN_SKIP_INTERCEPTOR);
|
| - if (it.state() == LookupIterator::ACCESS_CHECK) {
|
| - if (!it.HasAccess()) {
|
| - return SetPropertyWithFailedAccessCheck(&it, value, SLOPPY);
|
| - }
|
| - it.Next();
|
| - }
|
| -
|
| - if (it.IsFound()) {
|
| - return ReconfigureAsDataProperty(&it, value, attributes, handling);
|
| - }
|
| -
|
| - return AddDataProperty(&it, value, attributes, STRICT,
|
| - CERTAINLY_NOT_STORE_FROM_KEYED);
|
| + LookupIterator it(object, name, LookupIterator::OWN);
|
| + return DefineOwnPropertyIgnoreAttributes(&it, value, attributes, handling);
|
| }
|
|
|
|
|
| @@ -4276,21 +4281,21 @@ MaybeHandle<Object> JSObject::SetOwnElementIgnoreAttributes(
|
| Handle<JSObject> object, uint32_t index, Handle<Object> value,
|
| PropertyAttributes attributes, ExecutableAccessorInfoHandling handling) {
|
| Isolate* isolate = object->GetIsolate();
|
| - LookupIterator it(isolate, object, index,
|
| - LookupIterator::OWN_SKIP_INTERCEPTOR);
|
| - if (it.state() == LookupIterator::ACCESS_CHECK) {
|
| - if (!it.HasAccess()) {
|
| - return SetPropertyWithFailedAccessCheck(&it, value, STRICT);
|
| - }
|
| - it.Next();
|
| - }
|
| + LookupIterator it(isolate, object, index, LookupIterator::OWN);
|
| + return DefineOwnPropertyIgnoreAttributes(&it, value, attributes, handling);
|
| +}
|
|
|
| - if (it.IsFound()) {
|
| - return ReconfigureAsDataProperty(&it, value, attributes, handling);
|
| - }
|
|
|
| - return AddDataProperty(&it, value, attributes, STRICT,
|
| - MAY_BE_STORE_FROM_KEYED);
|
| +MaybeHandle<Object> JSObject::DefinePropertyOrElementIgnoreAttributes(
|
| + Handle<JSObject> object, Handle<Name> name, Handle<Object> value,
|
| + PropertyAttributes attributes, ExecutableAccessorInfoHandling handling) {
|
| + Isolate* isolate = object->GetIsolate();
|
| + uint32_t index;
|
| + LookupIterator it =
|
| + name->AsArrayIndex(&index)
|
| + ? LookupIterator(isolate, object, index, LookupIterator::OWN)
|
| + : LookupIterator(object, name, LookupIterator::OWN);
|
| + return DefineOwnPropertyIgnoreAttributes(&it, value, attributes, handling);
|
| }
|
|
|
|
|
|
|