diff options
author | Michaël Zasso <targos@protonmail.com> | 2022-01-18 14:12:53 +0100 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2022-01-20 10:40:26 +0100 |
commit | e23e345b6c6b66c7fac5c76741bbfef722953e3a (patch) | |
tree | 5af12593fb5f52c4c95300e25d962c57274d99b2 /deps/v8/src/objects | |
parent | 696ce7df261f9d773adddb30d2a94eed1d577b5d (diff) | |
download | node-new-e23e345b6c6b66c7fac5c76741bbfef722953e3a.tar.gz |
deps: V8: cherry-pick 80bbbb143c24
Original commit message:
[class] handle existing readonly properties in StoreOwnIC
Previously, StoreOwnIC incorrectly reuses the [[Set]] semantics
when initializing public literal class fields and object literals in
certain cases (e.g. when there's no feedback).
This was less of an issue for object literals, but with public class
fields it's possible to define property attributes while the
instance is still being initialized, or to encounter existing static
"name" or "length" properties that should be readonly. This patch
fixes it by
1) Emitting code that calls into the slow stub when
handling StoreOwnIC with existing read-only properties.
2) Adding extra steps in StoreIC::Store to handle such stores
properly with [[DefineOwnProperty]] semantics.
Bug: v8:12421, v8:9888
Change-Id: I6547320a1caba58c66ee1043cd3183a2de7cefef
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3300092
Reviewed-by: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#78659}
Refs: https://github.com/v8/v8/commit/80bbbb143c2449f7a2109424a937d1887043c9ee
PR-URL: https://github.com/nodejs/node/pull/40907
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Diffstat (limited to 'deps/v8/src/objects')
-rw-r--r-- | deps/v8/src/objects/js-objects.cc | 82 | ||||
-rw-r--r-- | deps/v8/src/objects/js-objects.h | 22 | ||||
-rw-r--r-- | deps/v8/src/objects/lookup.cc | 3 | ||||
-rw-r--r-- | deps/v8/src/objects/objects.cc | 12 | ||||
-rw-r--r-- | deps/v8/src/objects/objects.h | 7 |
5 files changed, 90 insertions, 36 deletions
diff --git a/deps/v8/src/objects/js-objects.cc b/deps/v8/src/objects/js-objects.cc index 4c6809b56f..933c3cda0e 100644 --- a/deps/v8/src/objects/js-objects.cc +++ b/deps/v8/src/objects/js-objects.cc @@ -184,6 +184,27 @@ Maybe<bool> JSReceiver::HasInPrototypeChain(Isolate* isolate, } } +// static +Maybe<bool> JSReceiver::CheckIfCanDefine(Isolate* isolate, LookupIterator* it, + Handle<Object> value, + Maybe<ShouldThrow> should_throw) { + if (it->IsFound()) { + Maybe<PropertyAttributes> attributes = GetPropertyAttributes(it); + MAYBE_RETURN(attributes, Nothing<bool>()); + if ((attributes.FromJust() & DONT_DELETE) != 0) { + RETURN_FAILURE( + isolate, GetShouldThrow(isolate, should_throw), + NewTypeError(MessageTemplate::kRedefineDisallowed, it->GetName())); + } + } else if (!JSObject::IsExtensible( + Handle<JSObject>::cast(it->GetReceiver()))) { + RETURN_FAILURE( + isolate, GetShouldThrow(isolate, should_throw), + NewTypeError(MessageTemplate::kDefineDisallowed, it->GetName())); + } + return Just(true); +} + namespace { bool HasExcludedProperty( @@ -3365,23 +3386,25 @@ void JSObject::AddProperty(Isolate* isolate, Handle<JSObject> object, // hidden prototypes. MaybeHandle<Object> JSObject::DefineOwnPropertyIgnoreAttributes( LookupIterator* it, Handle<Object> value, PropertyAttributes attributes, - AccessorInfoHandling handling) { + AccessorInfoHandling handling, EnforceDefineSemantics semantics) { MAYBE_RETURN_NULL(DefineOwnPropertyIgnoreAttributes( - it, value, attributes, Just(ShouldThrow::kThrowOnError), handling)); + it, value, attributes, Just(ShouldThrow::kThrowOnError), handling, + semantics)); return value; } Maybe<bool> JSObject::DefineOwnPropertyIgnoreAttributes( LookupIterator* it, Handle<Object> value, PropertyAttributes attributes, - Maybe<ShouldThrow> should_throw, AccessorInfoHandling handling) { + Maybe<ShouldThrow> should_throw, AccessorInfoHandling handling, + EnforceDefineSemantics semantics) { it->UpdateProtector(); Handle<JSObject> object = Handle<JSObject>::cast(it->GetReceiver()); for (; it->IsFound(); it->Next()) { switch (it->state()) { case LookupIterator::JSPROXY: - case LookupIterator::NOT_FOUND: case LookupIterator::TRANSITION: + case LookupIterator::NOT_FOUND: UNREACHABLE(); case LookupIterator::ACCESS_CHECK: @@ -3400,13 +3423,36 @@ Maybe<bool> JSObject::DefineOwnPropertyIgnoreAttributes( // 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) { - Maybe<bool> result = - JSObject::SetPropertyWithInterceptor(it, should_throw, value); - if (result.IsNothing() || result.FromJust()) return result; + case LookupIterator::INTERCEPTOR: { + Maybe<bool> result = Just(false); + if (semantics == EnforceDefineSemantics::kDefine) { + PropertyDescriptor descriptor; + descriptor.set_configurable((attributes & DONT_DELETE) != 0); + descriptor.set_enumerable((attributes & DONT_ENUM) != 0); + descriptor.set_writable((attributes & READ_ONLY) != 0); + descriptor.set_value(value); + result = DefinePropertyWithInterceptorInternal( + it, it->GetInterceptor(), should_throw, &descriptor); + } else { + DCHECK_EQ(semantics, EnforceDefineSemantics::kSet); + if (handling == DONT_FORCE_FIELD) { + result = + JSObject::SetPropertyWithInterceptor(it, should_throw, value); + } + } + if (result.IsNothing() || result.FromJust()) return result; + + if (semantics == EnforceDefineSemantics::kDefine) { + it->Restart(); + Maybe<bool> can_define = JSReceiver::CheckIfCanDefine( + it->isolate(), it, value, should_throw); + if (can_define.IsNothing() || !can_define.FromJust()) { + return can_define; + } + it->Restart(); } break; + } case LookupIterator::ACCESSOR: { Handle<Object> accessors = it->GetAccessors(); @@ -3824,20 +3870,10 @@ Maybe<bool> JSObject::CreateDataProperty(LookupIterator* it, Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(it->GetReceiver()); Isolate* isolate = receiver->GetIsolate(); - if (it->IsFound()) { - Maybe<PropertyAttributes> attributes = GetPropertyAttributes(it); - MAYBE_RETURN(attributes, Nothing<bool>()); - if ((attributes.FromJust() & DONT_DELETE) != 0) { - RETURN_FAILURE( - isolate, GetShouldThrow(isolate, should_throw), - NewTypeError(MessageTemplate::kRedefineDisallowed, it->GetName())); - } - } else { - if (!JSObject::IsExtensible(Handle<JSObject>::cast(it->GetReceiver()))) { - RETURN_FAILURE( - isolate, GetShouldThrow(isolate, should_throw), - NewTypeError(MessageTemplate::kDefineDisallowed, it->GetName())); - } + Maybe<bool> can_define = + JSReceiver::CheckIfCanDefine(isolate, it, value, should_throw); + if (can_define.IsNothing() || !can_define.FromJust()) { + return can_define; } RETURN_ON_EXCEPTION_VALUE(it->isolate(), diff --git a/deps/v8/src/objects/js-objects.h b/deps/v8/src/objects/js-objects.h index 8eff066bb1..3904144e40 100644 --- a/deps/v8/src/objects/js-objects.h +++ b/deps/v8/src/objects/js-objects.h @@ -160,6 +160,12 @@ class JSReceiver : public TorqueGeneratedJSReceiver<JSReceiver, HeapObject> { Isolate* isolate, Handle<JSReceiver> object, Handle<Object> key, PropertyDescriptor* desc, Maybe<ShouldThrow> should_throw); + // Check if a data property can be created on the object. It will fail with + // an error when it cannot. + V8_WARN_UNUSED_RESULT static Maybe<bool> CheckIfCanDefine( + Isolate* isolate, LookupIterator* it, Handle<Object> value, + Maybe<ShouldThrow> should_throw); + // ES6 7.3.4 (when passed kDontThrow) V8_WARN_UNUSED_RESULT static Maybe<bool> CreateDataProperty( Isolate* isolate, Handle<JSReceiver> object, Handle<Name> key, @@ -399,15 +405,27 @@ class JSObject : public TorqueGeneratedJSObject<JSObject, JSReceiver> { // to the default behavior that calls the setter. enum AccessorInfoHandling { FORCE_FIELD, DONT_FORCE_FIELD }; + // Currently DefineOwnPropertyIgnoreAttributes invokes the setter + // interceptor and user-defined setters during define operations, + // even in places where it makes more sense to invoke the definer + // interceptor and not invoke the setter: e.g. both the definer and + // the setter interceptors are called in Object.defineProperty(). + // kDefine allows us to implement the define semantics correctly + // in selected locations. + // TODO(joyee): see if we can deprecate the old behavior. + enum class EnforceDefineSemantics { kSet, kDefine }; + V8_WARN_UNUSED_RESULT static MaybeHandle<Object> DefineOwnPropertyIgnoreAttributes( LookupIterator* it, Handle<Object> value, PropertyAttributes attributes, - AccessorInfoHandling handling = DONT_FORCE_FIELD); + AccessorInfoHandling handling = DONT_FORCE_FIELD, + EnforceDefineSemantics semantics = EnforceDefineSemantics::kSet); V8_WARN_UNUSED_RESULT static Maybe<bool> DefineOwnPropertyIgnoreAttributes( LookupIterator* it, Handle<Object> value, PropertyAttributes attributes, Maybe<ShouldThrow> should_throw, - AccessorInfoHandling handling = DONT_FORCE_FIELD); + AccessorInfoHandling handling = DONT_FORCE_FIELD, + EnforceDefineSemantics semantics = EnforceDefineSemantics::kSet); V8_WARN_UNUSED_RESULT static MaybeHandle<Object> V8_EXPORT_PRIVATE SetOwnPropertyIgnoreAttributes(Handle<JSObject> object, Handle<Name> name, diff --git a/deps/v8/src/objects/lookup.cc b/deps/v8/src/objects/lookup.cc index b53bbeb0e7..5b10337e41 100644 --- a/deps/v8/src/objects/lookup.cc +++ b/deps/v8/src/objects/lookup.cc @@ -167,7 +167,8 @@ Handle<Map> LookupIterator::GetReceiverMap() const { } bool LookupIterator::HasAccess() const { - DCHECK_EQ(ACCESS_CHECK, state_); + // TRANSITION is true when being called from StoreOwnIC. + DCHECK(state_ == ACCESS_CHECK || state_ == TRANSITION); return isolate_->MayAccess(handle(isolate_->context(), isolate_), GetHolder<JSObject>()); } diff --git a/deps/v8/src/objects/objects.cc b/deps/v8/src/objects/objects.cc index 2da580ea4b..3d4e6cf399 100644 --- a/deps/v8/src/objects/objects.cc +++ b/deps/v8/src/objects/objects.cc @@ -2579,14 +2579,8 @@ Maybe<bool> Object::SetPropertyInternal(LookupIterator* it, return Nothing<bool>(); } -namespace { - -// If the receiver is the JSGlobalObject, the store was contextual. In case -// the property did not exist yet on the global object itself, we have to -// throw a reference error in strict mode. In sloppy mode, we continue. -// Returns false if the exception was thrown, otherwise true. -bool CheckContextualStoreToJSGlobalObject(LookupIterator* it, - Maybe<ShouldThrow> should_throw) { +bool Object::CheckContextualStoreToJSGlobalObject( + LookupIterator* it, Maybe<ShouldThrow> should_throw) { Isolate* isolate = it->isolate(); if (it->GetReceiver()->IsJSGlobalObject(isolate) && @@ -2605,8 +2599,6 @@ bool CheckContextualStoreToJSGlobalObject(LookupIterator* it, return true; } -} // namespace - Maybe<bool> Object::SetProperty(LookupIterator* it, Handle<Object> value, StoreOrigin store_origin, Maybe<ShouldThrow> should_throw) { diff --git a/deps/v8/src/objects/objects.h b/deps/v8/src/objects/objects.h index 4daaafead3..9fc636365d 100644 --- a/deps/v8/src/objects/objects.h +++ b/deps/v8/src/objects/objects.h @@ -721,6 +721,13 @@ class Object : public TaggedImpl<HeapObjectReferenceType::STRONG, Address> { inline void WriteExternalPointerField(size_t offset, Isolate* isolate, Address value, ExternalPointerTag tag); + // If the receiver is the JSGlobalObject, the store was contextual. In case + // the property did not exist yet on the global object itself, we have to + // throw a reference error in strict mode. In sloppy mode, we continue. + // Returns false if the exception was thrown, otherwise true. + static bool CheckContextualStoreToJSGlobalObject( + LookupIterator* it, Maybe<ShouldThrow> should_throw); + protected: inline Address field_address(size_t offset) const { return ptr() + offset - kHeapObjectTag; |