/* * Copyright (c) 2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/global_env.h" #include "ecmascript/ic/proto_change_details.h" #include "ecmascript/subtyping_operator-inl.h" #include "ecmascript/vtable.h" namespace panda::ecmascript { bool SubtypingOperator::CheckBaseClass(const JSThread *thread, const JSHandle &classType) { // the base class type does not need to check rules 2 and 4, because Object has no local properties TSObjectType *it = TSObjectType::Cast(classType->GetInstanceType().GetTaggedObject()); TSObjLayoutInfo *itLayout = TSObjLayoutInfo::Cast(it->GetObjLayoutInfo().GetTaggedObject()); TSObjectType *pt = TSObjectType::Cast(classType->GetPrototypeType().GetTaggedObject()); TSObjLayoutInfo *ptLayout = TSObjLayoutInfo::Cast(pt->GetObjLayoutInfo().GetTaggedObject()); // itLayout -> local, ptLayout -> vtable if (!SubtypingCondition(thread, itLayout, ptLayout, ConditionType::NO_OVERLAP_SUB_LOCAL_SUB_VTABLE)) { return false; } JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHClass *oProtoHClass = JSHClass::Cast(env->GetObjectFunctionPrototypeClass()->GetTaggedObject()); if (!SubtypingCondition(thread, ptLayout, oProtoHClass, ConditionType::SUB_VTABLE_CONTAIN_SUP_VTABLE) || !SubtypingCondition(thread, itLayout, oProtoHClass, ConditionType::NO_OVERLAP_SUP_VTABLE_SUB_LOCAL)) { return false; } return true; } bool SubtypingOperator::CheckSubtyping(const JSThread *thread, const JSHandle &classType) { TSManager *tsManager = const_cast(thread)->GetCurrentEcmaContext()->GetTSManager(); JSHandle eClassType = tsManager->GetExtendedClassType(classType); JSHClass *eIhc = JSHClass::Cast(tsManager->GetInstanceTSHClass(eClassType).GetTaggedObject()); WeakVector *eSupers = WeakVector::Cast(eIhc->GetSupers().GetTaggedObject()); // if the supers of extended IHClass is empty, means the chain is broken // will be overwritten by IsTSChainInfoFilled() in PR Part2. if (eSupers->Empty() || eIhc->GetLevel() + 1 == MAX_LEVEL) { return false; } TSObjectType *it = TSObjectType::Cast(classType->GetInstanceType().GetTaggedObject()); TSObjLayoutInfo *itLayout = TSObjLayoutInfo::Cast(it->GetObjLayoutInfo().GetTaggedObject()); TSObjectType *pt = TSObjectType::Cast(classType->GetPrototypeType().GetTaggedObject()); TSObjLayoutInfo *ptLayout = TSObjLayoutInfo::Cast(pt->GetObjLayoutInfo().GetTaggedObject()); TSObjectType *eIt = TSObjectType::Cast(eClassType->GetInstanceType().GetTaggedObject()); TSObjLayoutInfo *eItLayout = TSObjLayoutInfo::Cast(eIt->GetObjLayoutInfo().GetTaggedObject()); VTable *eVtable = VTable::Cast(eIhc->GetVTable().GetTaggedObject()); // itLayout -> local, eItLayout -> extended local // ptLayout -> vtable, eVtable -> extended vtable if (!SubtypingCondition(thread, itLayout, ptLayout, ConditionType::NO_OVERLAP_SUB_LOCAL_SUB_VTABLE) || !SubtypingCondition(thread, itLayout, eItLayout, ConditionType::SUB_LOCAL_CONTAIN_SUP_LOCAL) || !SubtypingCondition(thread, ptLayout, eVtable, ConditionType::SUB_VTABLE_CONTAIN_SUP_VTABLE) || !SubtypingCondition(thread, ptLayout, eItLayout, ConditionType::NO_OVERLAP_SUP_LOCAL_SUB_VTABLE) || !SubtypingCondition(thread, itLayout, eVtable, ConditionType::NO_OVERLAP_SUP_VTABLE_SUB_LOCAL)) { return false; } return true; } void SubtypingOperator::MergeClassField(const JSThread *thread, const JSHandle &classType) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); TSManager *tsManager = const_cast(thread)->GetCurrentEcmaContext()->GetTSManager(); JSHandle eClassType = tsManager->GetExtendedClassType(classType); JSHandle field(thread, classType->GetInstanceType()); TSObjectType *eField = TSObjectType::Cast(eClassType->GetInstanceType()); JSHandle layout(thread, field->GetObjLayoutInfo()); JSHandle eLayout(thread, eField->GetObjLayoutInfo()); uint32_t numSelfTypes = layout->GetNumOfProperties(); uint32_t numExtendTypes = eLayout->GetNumOfProperties(); uint32_t numTypes = numSelfTypes + numExtendTypes; JSHandle newLayout = factory->CreateTSObjLayoutInfo(numTypes); for (uint32_t index = 0; index < numExtendTypes; index++) { JSTaggedValue key = eLayout->GetKey(index); JSTaggedValue type = eLayout->GetTypeId(index); JSTaggedValue attribute = eLayout->GetAttribute(index); newLayout->AddProperty(thread, key, type, attribute); } for (uint32_t index = 0; index < numSelfTypes; index++) { JSTaggedValue key = layout->GetKey(index); if (eLayout->Find(key)) { continue; } JSTaggedValue type = layout->GetTypeId(index); JSTaggedValue attribute = layout->GetAttribute(index); newLayout->AddProperty(thread, key, type, attribute); } field->SetObjLayoutInfo(thread, newLayout); classType->SetHasLinked(true); } void SubtypingOperator::FillTSInheritInfo(JSThread *thread, const JSHandle &classType, const JSHandle &ihcHandle) { TSManager *tsManager = thread->GetCurrentEcmaContext()->GetTSManager(); JSObject *prototype = JSObject::Cast(ihcHandle->GetProto()); JSHandle phcHandle(thread, prototype->GetJSHClass()); JSHandle eIhcHandle(thread, JSTaggedValue::Undefined()); if (classType->IsBaseClassType()) { GenVTable(thread, ihcHandle, phcHandle, eIhcHandle); ihcHandle->SetLevel(0); AddSuper(thread, ihcHandle, eIhcHandle); } else { // get extended instance TSHclass JSHandle eClassType = tsManager->GetExtendedClassType(classType); JSTaggedValue eIhc = tsManager->GetInstanceTSHClass(eClassType); eIhcHandle = JSHandle(thread, eIhc); uint8_t eIhcLevel = eIhcHandle->GetLevel(); JSHandle ePrototype(thread, eIhcHandle->GetProto()); GenVTable(thread, ihcHandle, phcHandle, eIhcHandle); ihcHandle->SetLevel(eIhcLevel + 1); AddSuper(thread, ihcHandle, eIhcHandle); } } void SubtypingOperator::GenVTable(const JSThread *thread, const JSHandle &ihcHandle, const JSHandle &phcHandle, const JSHandle &eIhcHandle) { ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle owner(thread, ihcHandle->GetProto()); uint32_t propNumber = phcHandle->NumberOfProps(); JSMutableHandle eVTable(thread, JSTaggedValue::Undefined()); uint32_t ePropNumber = 0; if (!eIhcHandle.GetTaggedValue().IsUndefined()) { eVTable.Update(eIhcHandle->GetVTable()); ePropNumber = eVTable->GetNumberOfTuples(); } uint32_t loc = 0; uint32_t finalLength = propNumber + ePropNumber; JSHandle vtable = factory->NewVTable(finalLength); for (uint32_t index = 0; index < ePropNumber; ++index) { VTable::Tuple tuple = eVTable->GetTuple(thread, index); vtable->SetByIndex(thread, loc++, tuple); } for (uint32_t index = 0; index < propNumber; ++index) { VTable::Tuple tuple = VTable::CreateTuple(thread, phcHandle.GetTaggedValue(), owner, index); JSTaggedValue nameVal = tuple.GetItem(VTable::TupleItem::NAME).GetTaggedValue(); // When the vtable and the parent class vtable have the same name attribute, // the attribute of the parent class vtable should be overwritten int tIndex = vtable->GetTupleIndexByName(nameVal); if (tIndex == -1) { vtable->SetByIndex(thread, loc++, tuple); } else { vtable->SetByIndex(thread, tIndex, tuple); } } if (loc != finalLength) { vtable->Trim(thread, loc); } ihcHandle->SetVTable(thread, vtable); } void SubtypingOperator::AddSuper(const JSThread *thread, const JSHandle &iHClass, const JSHandle &superHClass) { JSHandle old; if (superHClass.GetTaggedValue().IsUndefined()) { old = JSHandle(thread, iHClass->GetSupers()); } else { old = JSHandle(thread, superHClass->GetSupers()); } JSHandle supersHandle = WeakVector::Copy(thread, old, old->Full()); JSHandle iHClassVal(iHClass); JSHandle newSupers = WeakVector::Append(thread, supersHandle, iHClassVal, WeakVector::ElementType::WEAK); iHClass->SetSupers(thread, newSupers); } // when add property in local, try maintain. void SubtypingOperator::TryMaintainTSSubtyping(const JSThread *thread, const JSHandle &oldHClass, JSHandle &newHClass, const JSHandle &key) { if (!key->IsString()) { // symbol return; } ASSERT(!oldHClass->IsPrototype()); // normal object hclass JSHandle vtable(thread, oldHClass->GetVTable()); ASSERT(!vtable.GetTaggedValue().IsUndefined()); ASSERT(vtable->GetNumberOfTuples() > 0); // there have default key 'constructor' at least if (vtable->Find(key.GetTaggedValue())) { // new key shadows vtable property LOG_ECMA(DEBUG) << "TryMaintainTSSubtyping failed, key: " << ConvertToString(EcmaString::Cast(key->GetTaggedObject())); return; } // Add newHClass to phc's listeners JSHandle prototype(thread, oldHClass->GetPrototype()); ASSERT(prototype->IsClassPrototype()); JSHandle phc(thread, prototype->GetTaggedObject()->GetClass()); // If hclass has inherit info, it had been registered on proto chain, details and listeners must not be Undefined. JSHandle details(thread, phc->GetProtoChangeDetails()); JSHandle listeners(thread, details->GetChangeListener()); uint32_t registerIndex = 0; JSHandle newListeners = ChangeListener::Add(thread, listeners, newHClass, ®isterIndex); if (UNLIKELY(registerIndex == TaggedArray::MAX_ARRAY_INDEX)) { return; } // maintaining succeeds details->SetChangeListener(thread, newListeners); JSHClass::CopyTSInheritInfo(thread, oldHClass, newHClass); } // when add property on prototype, try maintain. bool SubtypingOperator::TryMaintainTSSubtypingOnPrototype(const JSThread *thread, const JSHandle &hclass, const JSHandle &key) { ASSERT(key->IsString()); JSHandle 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