• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/global_env.h"
17 #include "ecmascript/ic/proto_change_details.h"
18 #include "ecmascript/subtyping_operator-inl.h"
19 #include "ecmascript/vtable.h"
20 
21 namespace panda::ecmascript {
CheckBaseClass(const JSThread * thread,const JSHandle<TSClassType> & classType)22 bool SubtypingOperator::CheckBaseClass(const JSThread *thread, const JSHandle<TSClassType> &classType)
23 {
24     // the base class type does not need to check rules 2 and 4, because Object has no local properties
25     TSObjectType *it = TSObjectType::Cast(classType->GetInstanceType().GetTaggedObject());
26     TSObjLayoutInfo *itLayout = TSObjLayoutInfo::Cast(it->GetObjLayoutInfo().GetTaggedObject());
27     TSObjectType *pt = TSObjectType::Cast(classType->GetPrototypeType().GetTaggedObject());
28     TSObjLayoutInfo *ptLayout = TSObjLayoutInfo::Cast(pt->GetObjLayoutInfo().GetTaggedObject());
29 
30     // itLayout  -> local, ptLayout  -> vtable
31     if (!SubtypingCondition(thread, itLayout, ptLayout, ConditionType::NO_OVERLAP_SUB_LOCAL_SUB_VTABLE)) {
32         return false;
33     }
34 
35     JSHandle<panda::ecmascript::GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
36     JSHClass *oProtoHClass = JSHClass::Cast(env->GetObjectFunctionPrototypeClass()->GetTaggedObject());
37 
38     if (!SubtypingCondition(thread, ptLayout, oProtoHClass, ConditionType::SUB_VTABLE_CONTAIN_SUP_VTABLE) ||
39         !SubtypingCondition(thread, itLayout, oProtoHClass, ConditionType::NO_OVERLAP_SUP_VTABLE_SUB_LOCAL)) {
40         return false;
41     }
42 
43     return true;
44 }
45 
CheckSubtyping(const JSThread * thread,const JSHandle<TSClassType> & classType)46 bool SubtypingOperator::CheckSubtyping(const JSThread *thread, const JSHandle<TSClassType> &classType)
47 {
48     TSManager *tsManager = const_cast<JSThread*>(thread)->GetCurrentEcmaContext()->GetTSManager();
49     JSHandle<TSClassType> eClassType = tsManager->GetExtendedClassType(classType);
50     JSHClass *eIhc = JSHClass::Cast(tsManager->GetInstanceTSHClass(eClassType).GetTaggedObject());
51     WeakVector *eSupers = WeakVector::Cast(eIhc->GetSupers().GetTaggedObject());
52 
53     // if the supers of extended IHClass is empty, means the chain is broken
54     // will be overwritten by IsTSChainInfoFilled() in PR Part2.
55     if (eSupers->Empty() || eIhc->GetLevel() + 1 == MAX_LEVEL) {
56         return false;
57     }
58 
59     TSObjectType *it = TSObjectType::Cast(classType->GetInstanceType().GetTaggedObject());
60     TSObjLayoutInfo *itLayout = TSObjLayoutInfo::Cast(it->GetObjLayoutInfo().GetTaggedObject());
61     TSObjectType *pt = TSObjectType::Cast(classType->GetPrototypeType().GetTaggedObject());
62     TSObjLayoutInfo *ptLayout = TSObjLayoutInfo::Cast(pt->GetObjLayoutInfo().GetTaggedObject());
63 
64     TSObjectType *eIt = TSObjectType::Cast(eClassType->GetInstanceType().GetTaggedObject());
65     TSObjLayoutInfo *eItLayout = TSObjLayoutInfo::Cast(eIt->GetObjLayoutInfo().GetTaggedObject());
66     VTable *eVtable = VTable::Cast(eIhc->GetVTable().GetTaggedObject());
67 
68     // itLayout  -> local, eItLayout -> extended local
69     // ptLayout  -> vtable, eVtable   -> extended vtable
70     if (!SubtypingCondition(thread, itLayout, ptLayout, ConditionType::NO_OVERLAP_SUB_LOCAL_SUB_VTABLE) ||
71         !SubtypingCondition(thread, itLayout, eItLayout, ConditionType::SUB_LOCAL_CONTAIN_SUP_LOCAL) ||
72         !SubtypingCondition(thread, ptLayout, eVtable, ConditionType::SUB_VTABLE_CONTAIN_SUP_VTABLE) ||
73         !SubtypingCondition(thread, ptLayout, eItLayout, ConditionType::NO_OVERLAP_SUP_LOCAL_SUB_VTABLE) ||
74         !SubtypingCondition(thread, itLayout, eVtable, ConditionType::NO_OVERLAP_SUP_VTABLE_SUB_LOCAL)) {
75         return false;
76     }
77 
78     return true;
79 }
80 
MergeClassField(const JSThread * thread,const JSHandle<TSClassType> & classType)81 void SubtypingOperator::MergeClassField(const JSThread *thread, const JSHandle<TSClassType> &classType)
82 {
83     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
84     TSManager *tsManager = const_cast<JSThread*>(thread)->GetCurrentEcmaContext()->GetTSManager();
85 
86     JSHandle<TSClassType> eClassType = tsManager->GetExtendedClassType(classType);
87     JSHandle<TSObjectType> field(thread, classType->GetInstanceType());
88     TSObjectType *eField = TSObjectType::Cast(eClassType->GetInstanceType());
89 
90     JSHandle<TSObjLayoutInfo> layout(thread, field->GetObjLayoutInfo());
91     JSHandle<TSObjLayoutInfo> eLayout(thread, eField->GetObjLayoutInfo());
92 
93     uint32_t numSelfTypes = layout->GetNumOfProperties();
94     uint32_t numExtendTypes = eLayout->GetNumOfProperties();
95     uint32_t numTypes = numSelfTypes + numExtendTypes;
96     JSHandle<TSObjLayoutInfo> newLayout = factory->CreateTSObjLayoutInfo(numTypes);
97 
98     for (uint32_t index = 0; index < numExtendTypes; index++) {
99         JSTaggedValue key = eLayout->GetKey(index);
100         JSTaggedValue type = eLayout->GetTypeId(index);
101         JSTaggedValue attribute = eLayout->GetAttribute(index);
102         newLayout->AddProperty(thread, key, type, attribute);
103     }
104 
105     for (uint32_t index = 0; index < numSelfTypes; index++) {
106         JSTaggedValue key = layout->GetKey(index);
107         if (eLayout->Find(key)) {
108             continue;
109         }
110         JSTaggedValue type = layout->GetTypeId(index);
111         JSTaggedValue attribute = layout->GetAttribute(index);
112         newLayout->AddProperty(thread, key, type, attribute);
113     }
114 
115     field->SetObjLayoutInfo(thread, newLayout);
116     classType->SetHasLinked(true);
117 }
118 
FillTSInheritInfo(JSThread * thread,const JSHandle<TSClassType> & classType,const JSHandle<JSHClass> & ihcHandle)119 void SubtypingOperator::FillTSInheritInfo(JSThread *thread, const JSHandle<TSClassType> &classType,
120                                           const JSHandle<JSHClass> &ihcHandle)
121 {
122     TSManager *tsManager = thread->GetCurrentEcmaContext()->GetTSManager();
123     JSObject *prototype = JSObject::Cast(ihcHandle->GetProto());
124     JSHandle<JSHClass> phcHandle(thread, prototype->GetJSHClass());
125     JSHandle<JSHClass> eIhcHandle(thread, JSTaggedValue::Undefined());
126 
127     if (classType->IsBaseClassType()) {
128         GenVTable(thread, ihcHandle, phcHandle, eIhcHandle);
129         ihcHandle->SetLevel(0);
130         AddSuper(thread, ihcHandle, eIhcHandle);
131     } else {
132         // get extended instance TSHclass
133         JSHandle<TSClassType> eClassType = tsManager->GetExtendedClassType(classType);
134         JSTaggedValue eIhc = tsManager->GetInstanceTSHClass(eClassType);
135         eIhcHandle = JSHandle<JSHClass>(thread, eIhc);
136 
137         uint8_t eIhcLevel = eIhcHandle->GetLevel();
138         JSHandle<JSObject> ePrototype(thread, eIhcHandle->GetProto());
139 
140         GenVTable(thread, ihcHandle, phcHandle, eIhcHandle);
141         ihcHandle->SetLevel(eIhcLevel + 1);
142         AddSuper(thread, ihcHandle, eIhcHandle);
143     }
144 }
145 
GenVTable(const JSThread * thread,const JSHandle<JSHClass> & ihcHandle,const JSHandle<JSHClass> & phcHandle,const JSHandle<JSHClass> & eIhcHandle)146 void SubtypingOperator::GenVTable(const JSThread *thread, const JSHandle<JSHClass> &ihcHandle,
147                                   const JSHandle<JSHClass> &phcHandle, const JSHandle<JSHClass> &eIhcHandle)
148 {
149     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
150 
151     JSHandle<JSTaggedValue> owner(thread, ihcHandle->GetProto());
152     uint32_t propNumber = phcHandle->NumberOfProps();
153     JSMutableHandle<VTable> eVTable(thread, JSTaggedValue::Undefined());
154     uint32_t ePropNumber = 0;
155     if (!eIhcHandle.GetTaggedValue().IsUndefined()) {
156         eVTable.Update(eIhcHandle->GetVTable());
157         ePropNumber = eVTable->GetNumberOfTuples();
158     }
159 
160     uint32_t loc = 0;
161     uint32_t finalLength = propNumber + ePropNumber;
162     JSHandle<VTable> vtable = factory->NewVTable(finalLength);
163 
164     for (uint32_t index = 0; index < ePropNumber; ++index) {
165         VTable::Tuple tuple = eVTable->GetTuple(thread, index);
166         vtable->SetByIndex(thread, loc++, tuple);
167     }
168 
169     for (uint32_t index = 0; index < propNumber; ++index) {
170         VTable::Tuple tuple = VTable::CreateTuple(thread, phcHandle.GetTaggedValue(), owner, index);
171         JSTaggedValue nameVal = tuple.GetItem(VTable::TupleItem::NAME).GetTaggedValue();
172         // When the vtable and the parent class vtable have the same name attribute,
173         // the attribute of the parent class vtable should be overwritten
174         int tIndex = vtable->GetTupleIndexByName(nameVal);
175         if (tIndex == -1) {
176             vtable->SetByIndex(thread, loc++, tuple);
177         } else {
178             vtable->SetByIndex(thread, tIndex, tuple);
179         }
180     }
181 
182     if (loc != finalLength) {
183         vtable->Trim(thread, loc);
184     }
185     ihcHandle->SetVTable(thread, vtable);
186 }
187 
AddSuper(const JSThread * thread,const JSHandle<JSHClass> & iHClass,const JSHandle<JSHClass> & superHClass)188 void SubtypingOperator::AddSuper(const JSThread *thread, const JSHandle<JSHClass> &iHClass,
189                                  const JSHandle<JSHClass> &superHClass)
190 {
191     JSHandle<WeakVector> old;
192     if (superHClass.GetTaggedValue().IsUndefined()) {
193         old = JSHandle<WeakVector>(thread, iHClass->GetSupers());
194     } else {
195         old = JSHandle<WeakVector>(thread, superHClass->GetSupers());
196     }
197     JSHandle<WeakVector> supersHandle = WeakVector::Copy(thread, old, old->Full());
198 
199     JSHandle<JSTaggedValue> iHClassVal(iHClass);
200     JSHandle<WeakVector> newSupers = WeakVector::Append(thread, supersHandle,
201         iHClassVal, WeakVector::ElementType::WEAK);
202     iHClass->SetSupers(thread, newSupers);
203 }
204 
205 // when add property in local, try maintain.
TryMaintainTSSubtyping(const JSThread * thread,const JSHandle<JSHClass> & oldHClass,JSHandle<JSHClass> & newHClass,const JSHandle<JSTaggedValue> & key)206 void SubtypingOperator::TryMaintainTSSubtyping(const JSThread *thread, const JSHandle<JSHClass> &oldHClass,
207                                                JSHandle<JSHClass> &newHClass, const JSHandle<JSTaggedValue> &key)
208 {
209     if (!key->IsString()) {  // symbol
210         return;
211     }
212 
213     ASSERT(!oldHClass->IsPrototype());  // normal object hclass
214     JSHandle<VTable> vtable(thread, oldHClass->GetVTable());
215     ASSERT(!vtable.GetTaggedValue().IsUndefined());
216     ASSERT(vtable->GetNumberOfTuples() > 0);   // there have default key 'constructor' at least
217 
218     if (vtable->Find(key.GetTaggedValue())) {  // new key shadows vtable property
219         LOG_ECMA(DEBUG) << "TryMaintainTSSubtyping failed, key: "
220                         << ConvertToString(EcmaString::Cast(key->GetTaggedObject()));
221         return;
222     }
223 
224     // Add newHClass to phc's listeners
225     JSHandle<JSTaggedValue> prototype(thread, oldHClass->GetPrototype());
226     ASSERT(prototype->IsClassPrototype());
227     JSHandle<JSHClass> phc(thread, prototype->GetTaggedObject()->GetClass());
228     // If hclass has inherit info, it had been registered on proto chain, details and listeners must not be Undefined.
229     JSHandle<ProtoChangeDetails> details(thread, phc->GetProtoChangeDetails());
230     JSHandle<ChangeListener> listeners(thread, details->GetChangeListener());
231     uint32_t registerIndex = 0;
232     JSHandle<ChangeListener> newListeners = ChangeListener::Add(thread, listeners, newHClass, &registerIndex);
233     if (UNLIKELY(registerIndex == TaggedArray::MAX_ARRAY_INDEX)) {
234         return;
235     }
236 
237     // maintaining succeeds
238     details->SetChangeListener(thread, newListeners);
239 
240     JSHClass::CopyTSInheritInfo(thread, oldHClass, newHClass);
241 }
242 
243 // when add property on prototype, try maintain.
TryMaintainTSSubtypingOnPrototype(const JSThread * thread,const JSHandle<JSHClass> & hclass,const JSHandle<JSTaggedValue> & key)244 bool SubtypingOperator::TryMaintainTSSubtypingOnPrototype(const JSThread *thread, const JSHandle<JSHClass> &hclass,
245                                                           const JSHandle<JSTaggedValue> &key)
246 {
247     ASSERT(key->IsString());
248     JSHandle<VTable> vtable(thread, hclass->GetVTable());
249     ASSERT(vtable->GetNumberOfTuples() > 0);   // there have default key 'constructor' at least
250 
251     if (vtable->Find(key.GetTaggedValue())) {  // new key shadows vtable property
252         LOG_ECMA(DEBUG) << "TryMaintainTSSubtypingOnPrototype failed, key: "
253                         << ConvertToString(EcmaString::Cast(key->GetTaggedObject()));
254         return false;
255     }
256 
257     int entry = JSHClass::FindPropertyEntry(thread, *hclass, key.GetTaggedValue());
258     if (entry != -1) {  // new key shadows loacl property
259         LOG_ECMA(DEBUG) << "TryMaintainTSSubtypingOnPrototype failed, key: "
260                         << ConvertToString(EcmaString::Cast(key->GetTaggedObject()));
261         return false;
262     }
263 
264     return true;
265 }
266 }  // namespace panda::ecmascript
267