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, ®isterIndex);
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