summaryrefslogtreecommitdiff
path: root/deps/v8/src/objects
diff options
context:
space:
mode:
authorMichaël Zasso <targos@protonmail.com>2022-01-18 14:12:53 +0100
committerMichaël Zasso <targos@protonmail.com>2022-01-20 10:40:26 +0100
commite23e345b6c6b66c7fac5c76741bbfef722953e3a (patch)
tree5af12593fb5f52c4c95300e25d962c57274d99b2 /deps/v8/src/objects
parent696ce7df261f9d773adddb30d2a94eed1d577b5d (diff)
downloadnode-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.cc82
-rw-r--r--deps/v8/src/objects/js-objects.h22
-rw-r--r--deps/v8/src/objects/lookup.cc3
-rw-r--r--deps/v8/src/objects/objects.cc12
-rw-r--r--deps/v8/src/objects/objects.h7
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;