diff --git a/ecmascript/builtins/builtins_object.cpp b/ecmascript/builtins/builtins_object.cpp index 8a13c4a150..296a5b0a18 100644 --- a/ecmascript/builtins/builtins_object.cpp +++ b/ecmascript/builtins/builtins_object.cpp @@ -1042,7 +1042,7 @@ JSTaggedValue BuiltinsObject::ProtoSetter(EcmaRuntimeCallInfo *argv) } // 5. Let status be O.[[SetPrototypeOf]](proto). - bool status = JSTaggedValue::SetPrototype(thread, obj, proto); + bool status = JSTaggedValue::SetPrototype(thread, obj, proto, true); // 6. ReturnIfAbrupt(status). RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); diff --git a/ecmascript/js_hclass.cpp b/ecmascript/js_hclass.cpp index 7368e128cb..3591001c2e 100644 --- a/ecmascript/js_hclass.cpp +++ b/ecmascript/js_hclass.cpp @@ -348,7 +348,7 @@ JSHandle JSHClass::TransitionExtension(const JSThread *thread, const J } JSHandle JSHClass::TransitionProto(const JSThread *thread, const JSHandle &jshclass, - const JSHandle &proto) + const JSHandle &proto, bool isChangeProto) { JSHandle key(thread->GlobalConstants()->GetHandledPrototypeString()); @@ -366,7 +366,7 @@ JSHandle JSHClass::TransitionProto(const JSThread *thread, const JSHan ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); // 2. new a hclass JSHandle newJsHClass = JSHClass::Clone(thread, jshclass); - newJsHClass->SetPrototype(thread, proto.GetTaggedValue()); + newJsHClass->SetPrototype(thread, proto.GetTaggedValue(), isChangeProto); JSTaggedValue layout = newJsHClass->GetLayout(); { @@ -461,21 +461,22 @@ JSHandle JSHClass::TransProtoWithoutLayout(const JSThread *thread, con return CloneWithAddProto(thread, jshclass, key, proto); } -void JSHClass::SetPrototype(const JSThread *thread, JSTaggedValue proto) +void JSHClass::SetPrototype(const JSThread *thread, JSTaggedValue proto, bool isChangeProto) { // Because the heap-space of hclass is non-movable, this function can be non-static. JSHandle protoHandle(thread, proto); - SetPrototype(thread, protoHandle); + SetPrototype(thread, protoHandle, isChangeProto); } JSHandle JSHClass::SetPrototypeWithNotification(const JSThread *thread, const JSHandle &hclass, - const JSHandle &proto) + const JSHandle &proto, + bool isChangeProto) { // `hclass` can become prototype inside `TransitionProto` if `hclass` is HClass of `proto`. // In this case we don't need to notify auto wasPrototype = hclass->IsPrototype(); - JSHandle newClass = JSHClass::TransitionProto(thread, hclass, proto); + JSHandle newClass = JSHClass::TransitionProto(thread, hclass, proto, isChangeProto); if (wasPrototype) { ASSERT(hclass->IsPrototype()); JSHClass::NotifyHclassChanged(thread, hclass, newClass); @@ -484,21 +485,21 @@ JSHandle JSHClass::SetPrototypeWithNotification(const JSThread *thread } void JSHClass::SetPrototypeTransition(JSThread *thread, const JSHandle &object, - const JSHandle &proto) + const JSHandle &proto, bool isChangeProto) { JSHandle hclass(thread, object->GetJSHClass()); - auto newClass = SetPrototypeWithNotification(thread, hclass, proto); + auto newClass = SetPrototypeWithNotification(thread, hclass, proto, isChangeProto); RestoreElementsKindToGeneric(*newClass); object->SynchronizedSetClass(thread, *newClass); thread->NotifyStableArrayElementsGuardians(object, StableArrayChangeKind::PROTO); ObjectOperator::UpdateDetectorOnSetPrototype(thread, object.GetTaggedValue()); } -void JSHClass::SetPrototype(const JSThread *thread, const JSHandle &proto) +void JSHClass::SetPrototype(const JSThread *thread, const JSHandle &proto, bool isChangeProto) { // Because the heap-space of hclass is non-movable, this function can be non-static. if (proto->IsJSObject()) { - OptimizePrototypeForIC(thread, proto); + OptimizePrototypeForIC(thread, proto, isChangeProto); } SetProto(thread, proto); } diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index b78044986a..04972bb98c 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -458,7 +458,7 @@ public: const JSHandle &transIhc, const JSHandle &transPhc); static JSHandle TransitionProto(const JSThread *thread, const JSHandle &jshclass, - const JSHandle &proto); + const JSHandle &proto, bool isChangeProto = false); static JSHClass *FindTransitionProtoForAOT(const JSThread *thread, const JSHandle &jshclass, const JSHandle &proto); static JSHandle TransProtoWithoutLayout(const JSThread *thread, const JSHandle &jshclass, @@ -2004,12 +2004,17 @@ public: static JSHandle SetPrototypeWithNotification(const JSThread *thread, const JSHandle &hclass, - const JSHandle &proto); + const JSHandle &proto, + bool isChangeProto = false); static void SetPrototypeTransition(JSThread *thread, const JSHandle &object, - const JSHandle &proto); - void SetPrototype(const JSThread *thread, JSTaggedValue proto); - void PUBLIC_API SetPrototype(const JSThread *thread, const JSHandle &proto); - static void OptimizePrototypeForIC(const JSThread *thread, const JSHandle &proto, + const JSHandle &proto, + bool isChangeProto = false); + void SetPrototype(const JSThread *thread, JSTaggedValue proto, bool isChangeProto = false); + void PUBLIC_API SetPrototype(const JSThread *thread, + const JSHandle &proto, + bool isChangeProto = false); + static void OptimizePrototypeForIC(const JSThread *thread, + const JSHandle &proto, bool isChangeProto = false); inline JSTaggedValue GetPrototype() const { diff --git a/ecmascript/js_object.cpp b/ecmascript/js_object.cpp index 2f2d2c5f42..6f6ce48513 100644 --- a/ecmascript/js_object.cpp +++ b/ecmascript/js_object.cpp @@ -1490,7 +1490,9 @@ JSTaggedValue JSObject::GetPrototype(const JSHandle &obj) return hclass->GetPrototype(); } -bool JSObject::SetPrototype(JSThread *thread, const JSHandle &obj, const JSHandle &proto) +bool JSObject::SetPrototype(JSThread *thread, const JSHandle &obj, + const JSHandle &proto, + bool isChangeProto) { ASSERT_PRINT(proto->IsECMAObject() || proto->IsNull(), "proto must be object or null"); JSTaggedValue current = JSObject::GetPrototype(obj); @@ -1517,7 +1519,7 @@ bool JSObject::SetPrototype(JSThread *thread, const JSHandle &obj, con } ElementsKind oldKind = obj->GetJSHClass()->GetElementsKind(); // map transition - JSHClass::SetPrototypeTransition(thread, obj, proto); + JSHClass::SetPrototypeTransition(thread, obj, proto, isChangeProto); TryMigrateToGenericKindForJSObject(thread, obj, oldKind); return true; } diff --git a/ecmascript/js_object.h b/ecmascript/js_object.h index f613ab75d6..e88a31c06a 100644 --- a/ecmascript/js_object.h +++ b/ecmascript/js_object.h @@ -483,7 +483,9 @@ public: static JSTaggedValue GetPrototype(JSTaggedValue obj); // [[SetPrototypeOf]] - static bool SetPrototype(JSThread *thread, const JSHandle &obj, const JSHandle &proto); + static bool SetPrototype(JSThread *thread, const JSHandle &obj, + const JSHandle &proto, + bool isChangeProto = false); // [[IsExtensible]] bool IsExtensible() const; diff --git a/ecmascript/js_tagged_value.cpp b/ecmascript/js_tagged_value.cpp index 1a16883ba2..07d1280b43 100644 --- a/ecmascript/js_tagged_value.cpp +++ b/ecmascript/js_tagged_value.cpp @@ -1230,7 +1230,7 @@ bool JSTaggedValue::GetOwnProperty(JSThread *thread, const JSHandle &obj, - const JSHandle &proto) + const JSHandle &proto, bool isChangeProto) { if (obj->IsJSShared() || proto->IsJSShared()) { THROW_TYPE_ERROR_AND_RETURN(thread, GET_MESSAGE_STRING(SetProtoWithSendable), false); @@ -1254,7 +1254,7 @@ bool JSTaggedValue::SetPrototype(JSThread *thread, const JSHandle } } - return JSObject::SetPrototype(thread, JSHandle(obj), proto); + return JSObject::SetPrototype(thread, JSHandle(obj), proto, isChangeProto); } JSTaggedValue JSTaggedValue::GetPrototype(JSThread *thread, const JSHandle &obj) diff --git a/ecmascript/js_tagged_value.h b/ecmascript/js_tagged_value.h index adfdf8ad43..743c181b83 100644 --- a/ecmascript/js_tagged_value.h +++ b/ecmascript/js_tagged_value.h @@ -450,7 +450,7 @@ public: static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, PropertyDescriptor &desc); static bool SetPrototype(JSThread *thread, const JSHandle &obj, - const JSHandle &proto); + const JSHandle &proto, bool isChangeProto = false); static JSTaggedValue GetPrototype(JSThread *thread, const JSHandle &obj); static bool PreventExtensions(JSThread *thread, const JSHandle &obj); static JSHandle GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj); diff --git a/test/aottest/pgo_object_prototype/expect_output.txt b/test/aottest/pgo_object_prototype/expect_output.txt index 32f5c4b5af..0851dcbfc4 100644 --- a/test/aottest/pgo_object_prototype/expect_output.txt +++ b/test/aottest/pgo_object_prototype/expect_output.txt @@ -13,3 +13,4 @@ -1 50 +success \ No newline at end of file diff --git a/test/aottest/pgo_object_prototype/pgo_expect_output.txt b/test/aottest/pgo_object_prototype/pgo_expect_output.txt index 32f5c4b5af..0851dcbfc4 100644 --- a/test/aottest/pgo_object_prototype/pgo_expect_output.txt +++ b/test/aottest/pgo_object_prototype/pgo_expect_output.txt @@ -13,3 +13,4 @@ -1 50 +success \ No newline at end of file diff --git a/test/aottest/pgo_object_prototype/pgo_object_prototype.js b/test/aottest/pgo_object_prototype/pgo_object_prototype.js index ce7d0fb3a4..1c076f5558 100644 --- a/test/aottest/pgo_object_prototype/pgo_object_prototype.js +++ b/test/aottest/pgo_object_prototype/pgo_object_prototype.js @@ -29,3 +29,25 @@ Object.setPrototypeOf(object, proto); // Check usage of proto's HClass after AOT compile print(proto.method(50)); print(object.method(50)); + +function F0() { + this.f0 = 0; +} +let v3 = new F0(); +let v4 = new F0(); +class C27 { + c270 = 0; +} +class C28 { + c270 = 0; +} +class C42 extends C27 { +} + + +C27.__proto__ = v4; + +v3.__proto__ = C27; + +Object.defineProperty(C27, "d", { configurable: true, value: 1 }); +print("success") \ No newline at end of file