Maintain Subtyping when Adding Property on Prototype

Reduce some deoptimization in cocos.

Issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I77SBD

Signed-off-by: dingding <dingding5@huawei.com>
Change-Id: I1818d18fdf629d5f9d5e11d527e8280a0a414e5f
This commit is contained in:
dingding 2023-05-23 11:37:28 +08:00
parent 1c2e04399e
commit a47c0c72fb
5 changed files with 53 additions and 14 deletions

View File

@ -241,7 +241,7 @@ void JSHClass::AddProperty(const JSThread *thread, const JSHandle<JSObject> &obj
if (newClass != nullptr) { if (newClass != nullptr) {
obj->SetClass(newClass); obj->SetClass(newClass);
#if ECMASCRIPT_ENABLE_IC #if ECMASCRIPT_ENABLE_IC
JSHClass::NotifyHclassChanged(thread, jshclass, JSHandle<JSHClass>(thread, newClass)); JSHClass::NotifyHclassChanged(thread, jshclass, JSHandle<JSHClass>(thread, newClass), key.GetTaggedValue());
#endif #endif
return; return;
} }
@ -271,7 +271,7 @@ void JSHClass::AddProperty(const JSThread *thread, const JSHandle<JSObject> &obj
// 5. update hclass in object. // 5. update hclass in object.
#if ECMASCRIPT_ENABLE_IC #if ECMASCRIPT_ENABLE_IC
JSHClass::NotifyHclassChanged(thread, jshclass, newJsHClass); JSHClass::NotifyHclassChanged(thread, jshclass, newJsHClass, key.GetTaggedValue());
#endif #endif
obj->SetClass(*newJsHClass); obj->SetClass(*newJsHClass);
@ -459,7 +459,8 @@ JSHandle<JSTaggedValue> JSHClass::EnableProtoChangeMarker(const JSThread *thread
return JSHandle<JSTaggedValue>(markerHandle); return JSHandle<JSTaggedValue>(markerHandle);
} }
void JSHClass::NotifyHclassChanged(const JSThread *thread, JSHandle<JSHClass> oldHclass, JSHandle<JSHClass> newHclass) void JSHClass::NotifyHclassChanged(const JSThread *thread, JSHandle<JSHClass> oldHclass, JSHandle<JSHClass> newHclass,
JSTaggedValue addedKey)
{ {
if (!oldHclass->IsPrototype()) { if (!oldHclass->IsPrototype()) {
return; return;
@ -469,7 +470,7 @@ void JSHClass::NotifyHclassChanged(const JSThread *thread, JSHandle<JSHClass> ol
return; return;
} }
newHclass->SetIsPrototype(true); newHclass->SetIsPrototype(true);
JSHClass::NoticeThroughChain(thread, oldHclass); JSHClass::NoticeThroughChain(thread, oldHclass, addedKey);
JSHClass::RefreshUsers(thread, oldHclass, newHclass); JSHClass::RefreshUsers(thread, oldHclass, newHclass);
} }
@ -556,8 +557,10 @@ JSHandle<ProtoChangeDetails> JSHClass::GetProtoChangeDetails(const JSThread *thr
return GetProtoChangeDetails(thread, jshclass); return GetProtoChangeDetails(thread, jshclass);
} }
void JSHClass::MarkProtoChanged(const JSThread *thread, const JSHandle<JSHClass> &jshclass) void JSHClass::MarkProtoChanged(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
JSTaggedValue addedKey)
{ {
DISALLOW_GARBAGE_COLLECTION;
ASSERT(jshclass->IsPrototype() || jshclass->HasTSSubtyping()); ASSERT(jshclass->IsPrototype() || jshclass->HasTSSubtyping());
JSTaggedValue markerValue = jshclass->GetProtoChangeMarker(); JSTaggedValue markerValue = jshclass->GetProtoChangeMarker();
if (markerValue.IsProtoChangeMarker()) { if (markerValue.IsProtoChangeMarker()) {
@ -565,14 +568,19 @@ void JSHClass::MarkProtoChanged(const JSThread *thread, const JSHandle<JSHClass>
protoChangeMarker->SetHasChanged(true); protoChangeMarker->SetHasChanged(true);
} }
if (jshclass->HasTSSubtyping()) { if (jshclass->HasTSSubtyping() && addedKey.IsString()) {
jshclass->InitTSInheritInfo(thread); JSHandle<JSTaggedValue> key(thread, addedKey);
if (!SubtypingOperator::TryMaintainTSSubtypingOnPrototype(thread, jshclass, key)) {
jshclass->InitTSInheritInfo(thread);
}
} }
} }
void JSHClass::NoticeThroughChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass) void JSHClass::NoticeThroughChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
JSTaggedValue addedKey)
{ {
MarkProtoChanged(thread, jshclass); DISALLOW_GARBAGE_COLLECTION;
MarkProtoChanged(thread, jshclass, addedKey);
JSTaggedValue protoDetailsValue = jshclass->GetProtoChangeDetails(); JSTaggedValue protoDetailsValue = jshclass->GetProtoChangeDetails();
if (!protoDetailsValue.IsProtoChangeDetails()) { if (!protoDetailsValue.IsProtoChangeDetails()) {
return; return;
@ -585,7 +593,7 @@ void JSHClass::NoticeThroughChain(const JSThread *thread, const JSHandle<JSHClas
for (uint32_t i = 0; i < listeners->GetEnd(); i++) { for (uint32_t i = 0; i < listeners->GetEnd(); i++) {
JSTaggedValue temp = listeners->Get(i); JSTaggedValue temp = listeners->Get(i);
if (temp.IsJSHClass()) { if (temp.IsJSHClass()) {
NoticeThroughChain(thread, JSHandle<JSHClass>(thread, listeners->Get(i).GetTaggedObject())); NoticeThroughChain(thread, JSHandle<JSHClass>(thread, listeners->Get(i).GetTaggedObject()), addedKey);
} }
} }
} }

View File

@ -348,7 +348,8 @@ public:
static JSHandle<JSTaggedValue> EnableProtoChangeMarker(const JSThread *thread, const JSHandle<JSHClass> &jshclass); static JSHandle<JSTaggedValue> EnableProtoChangeMarker(const JSThread *thread, const JSHandle<JSHClass> &jshclass);
static void NotifyHclassChanged(const JSThread *thread, JSHandle<JSHClass> oldHclass, JSHandle<JSHClass> newHclass); static void NotifyHclassChanged(const JSThread *thread, JSHandle<JSHClass> oldHclass, JSHandle<JSHClass> newHclass,
JSTaggedValue addedKey = JSTaggedValue::Undefined());
static void RegisterOnProtoChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass); static void RegisterOnProtoChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass);
@ -362,9 +363,11 @@ public:
inline void UpdatePropertyMetaData(const JSThread *thread, const JSTaggedValue &key, inline void UpdatePropertyMetaData(const JSThread *thread, const JSTaggedValue &key,
const PropertyAttributes &metaData); const PropertyAttributes &metaData);
static void MarkProtoChanged(const JSThread *thread, const JSHandle<JSHClass> &jshclass); static void MarkProtoChanged(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
JSTaggedValue addedKey = JSTaggedValue::Undefined());
static void NoticeThroughChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass); static void NoticeThroughChain(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
JSTaggedValue addedKey = JSTaggedValue::Undefined());
static void RefreshUsers(const JSThread *thread, const JSHandle<JSHClass> &oldHclass, static void RefreshUsers(const JSThread *thread, const JSHandle<JSHClass> &oldHclass,
const JSHandle<JSHClass> &newHclass); const JSHandle<JSHClass> &newHclass);

View File

@ -236,4 +236,28 @@ void SubtypingOperator::TryMaintainTSSubtyping(const JSThread *thread, const JSH
JSHClass::CopyTSInheritInfo(thread, oldHClass, newHClass); JSHClass::CopyTSInheritInfo(thread, oldHClass, newHClass);
} }
// when add property on prototype, try maintain.
bool SubtypingOperator::TryMaintainTSSubtypingOnPrototype(const JSThread *thread, const JSHandle<JSHClass> &hclass,
const JSHandle<JSTaggedValue> &key)
{
ASSERT(key->IsString());
JSHandle<VTable> vtable(thread, hclass->GetVTable());
ASSERT(vtable->GetNumberOfTuples() > 0); // there have default key 'constructor' at least
if (vtable->Find(key.GetTaggedValue())) { // new key shadows vtable property
LOG_ECMA(DEBUG) << "TryMaintainTSSubtypingOnPrototype failed, key: "
<< ConvertToString(EcmaString::Cast(key->GetTaggedObject()));
return false;
}
int entry = JSHClass::FindPropertyEntry(thread, *hclass, key.GetTaggedValue());
if (entry != -1) { // new key shadows loacl property
LOG_ECMA(DEBUG) << "TryMaintainTSSubtypingOnPrototype failed, key: "
<< ConvertToString(EcmaString::Cast(key->GetTaggedObject()));
return false;
}
return true;
}
} // namespace panda::ecmascript } // namespace panda::ecmascript

View File

@ -37,6 +37,10 @@ public:
static void TryMaintainTSSubtyping(const JSThread *thread, const JSHandle<JSHClass> &oldHClass, static void TryMaintainTSSubtyping(const JSThread *thread, const JSHandle<JSHClass> &oldHClass,
JSHandle<JSHClass> &newHClass, const JSHandle<JSTaggedValue> &key); JSHandle<JSHClass> &newHClass, const JSHandle<JSTaggedValue> &key);
static bool TryMaintainTSSubtypingOnPrototype(const JSThread *thread, const JSHandle<JSHClass> &hclass,
const JSHandle<JSTaggedValue> &key);
private: private:
static constexpr uint8_t MAX_LEVEL = 1 << JSHClass::LEVEL_BTTFIELD_NUM; static constexpr uint8_t MAX_LEVEL = 1 << JSHClass::LEVEL_BTTFIELD_NUM;

View File

@ -17,7 +17,7 @@ true
z z
123 123
456 456
false true
1 1
2 2
z z