• 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/compiler/ts_hclass_generator.h"
17 #include "ecmascript/subtyping_operator.h"
18 #include "ecmascript/jspandafile/class_info_extractor.h"
19 
20 namespace panda::ecmascript::kungfu {
21 using ClassInfoExtractor = panda::ecmascript::ClassInfoExtractor;
GenerateTSHClasses() const22 void TSHClassGenerator::GenerateTSHClasses() const
23 {
24     const JSThread *thread = tsManager_->GetThread();
25     const std::set<GlobalTSTypeRef> &collectedGT = GetCollectedGT();
26     for (const auto &gt : collectedGT) {
27         JSHandle<JSTaggedValue> tsType = tsManager_->GetTSType(gt);
28         if (tsType->IsUndefined()) {
29             continue;
30         }
31         ASSERT(tsType->IsTSClassType());
32         JSHandle<TSClassType> classType(tsType);
33         if (tsManager_->HasTSHClass(classType)) {
34             continue;
35         } else if (tsManager_->IsUserDefinedClassTypeKind(gt)) {
36             RecursiveGenerate(classType);
37         } else if (!classType->GetHasLinked()) {
38             SubtypingOperator::MergeClassField(thread, classType);
39         }
40     }
41 }
42 
UpdateTSHClassFromPGO(const kungfu::GateType & type,const PGOHClassLayoutDesc & desc,bool enableOptTrackField) const43 void TSHClassGenerator::UpdateTSHClassFromPGO(const kungfu::GateType &type, const PGOHClassLayoutDesc &desc,
44     bool enableOptTrackField) const
45 {
46     DISALLOW_GARBAGE_COLLECTION;
47     auto hclassValue = tsManager_->GetTSHClass(type);
48     if (!hclassValue.IsJSHClass()) {
49         return;
50     }
51 
52     tsManager_->InsertPtToGtMap(desc.GetClassType(), type);
53     if (!enableOptTrackField) {
54         return;
55     }
56 
57     std::vector<JSHClass *> superHClasses;
58     kungfu::GateType current = type;
59     while (tsManager_->GetSuperGateType(current)) {
60         auto superHClassValue = tsManager_->GetTSHClass(current);
61         if (!superHClassValue.IsJSHClass()) {
62             break;
63         }
64         superHClasses.emplace_back(JSHClass::Cast(superHClassValue.GetTaggedObject()));
65     }
66 
67     auto hclass = JSHClass::Cast(hclassValue.GetTaggedObject());
68     const JSThread *thread = tsManager_->GetThread();
69     LayoutInfo *layoutInfo = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
70     int element = layoutInfo->NumberOfElements();
71     for (int i = 0; i < element; i++) {
72         auto key = layoutInfo->GetKey(i);
73         if (!key.IsString()) {
74             continue;
75         }
76         auto keyString = EcmaStringAccessor(key).ToCString();
77         PGOHandler newHandler;
78         if (desc.FindDescWithKey(keyString, newHandler)) {
79             auto attr = layoutInfo->GetAttr(i);
80             if (newHandler.SetAttribute(attr)) {
81                 hclass->SetIsAllTaggedProp(false);
82             }
83             layoutInfo->SetNormalAttr(thread, i, attr);
84 
85             // Update super class representation
86             for (auto superHClass : superHClasses) {
87                 int entry = JSHClass::FindPropertyEntry(thread, superHClass, key);
88                 if (entry == -1) {
89                     continue;
90                 }
91                 auto superLayout = LayoutInfo::Cast(superHClass->GetLayout().GetTaggedObject());
92                 PropertyAttributes superAttr = superLayout->GetAttr(entry);
93                 if (newHandler.SetAttribute(superAttr)) {
94                     superHClass->SetIsAllTaggedProp(false);
95                 }
96                 superLayout->SetNormalAttr(thread, entry, superAttr);
97             }
98         }
99     }
100 }
101 
RecursiveGenerate(const JSHandle<TSClassType> & classType) const102 void TSHClassGenerator::RecursiveGenerate(const JSHandle<TSClassType> &classType) const
103 {
104     if (!classType->IsBaseClassType()) {
105         JSHandle<TSClassType> extendedClassType(tsManager_->GetExtendedClassType(classType));
106         if (!tsManager_->HasTSHClass(extendedClassType)) {
107             RecursiveGenerate(extendedClassType);
108         }
109     }
110 
111     JSThread *thread = tsManager_->GetThread();
112     bool passed = false;
113     if (classType->IsBaseClassType()) {
114         passed = SubtypingOperator::CheckBaseClass(thread, classType);
115     } else {
116         passed = SubtypingOperator::CheckSubtyping(thread, classType);
117     }
118 
119     classType->SetHasPassedSubtypingCheck(passed);
120 
121     if (passed) {
122         if (!classType->IsBaseClassType()) {
123             SubtypingOperator::MergeClassField(thread, classType);
124         }
125         JSHandle<JSHClass> ihclass = Generate(classType);
126         SubtypingOperator::FillTSInheritInfo(thread, classType, ihclass);
127         return;
128     }
129     Generate(classType);
130 }
131 
Generate(const JSHandle<TSClassType> & classType) const132 JSHandle<JSHClass> TSHClassGenerator::Generate(const JSHandle<TSClassType> &classType) const
133 {
134     const JSThread *thread = tsManager_->GetThread();
135     JSHandle<JSHClass> ihclass = CreateHClass(thread, classType, Kind::INSTANCE);
136     JSHandle<JSHClass> phclass = CreateHClass(thread, classType, Kind::PROTOTYPE);
137     JSHandle<JSHClass> chclass = CreateHClass(thread, classType, Kind::CONSTRUCTOR);
138     JSHandle<JSObject> prototype = thread->GetEcmaVM()->GetFactory()->NewJSObject(phclass);
139     ihclass->SetProto(thread, prototype);
140 
141     GlobalTSTypeRef gt = classType->GetGT();
142     tsManager_->AddInstanceTSHClass(gt, ihclass);
143     tsManager_->AddConstructorTSHClass(gt, chclass);
144     return ihclass;
145 }
146 
CreateHClass(const JSThread * thread,const JSHandle<TSClassType> & classType,Kind kind) const147 JSHandle<JSHClass> TSHClassGenerator::CreateHClass(const JSThread *thread, const JSHandle<TSClassType> &classType,
148                                                    Kind kind) const
149 {
150     JSHandle<JSHClass> hclass(thread->GlobalConstants()->GetHandledUndefined());
151     switch (kind) {
152         case Kind::INSTANCE: {
153             hclass = CreateIHClass(thread, classType);
154             break;
155         }
156         case Kind::PROTOTYPE: {
157             hclass = CreatePHClass(thread, classType);
158             break;
159         }
160         case Kind::CONSTRUCTOR: {
161             hclass = CreateCHClass(thread, classType);
162             break;
163         }
164         default:
165             LOG_ECMA(FATAL) << "this branch is unreachable";
166             UNREACHABLE();
167     }
168 
169     return hclass;
170 }
171 
CreateIHClass(const JSThread * thread,const JSHandle<TSClassType> & classType) const172 JSHandle<JSHClass> TSHClassGenerator::CreateIHClass(const JSThread *thread,
173                                                     const JSHandle<TSClassType> &classType) const
174 {
175     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
176 
177     JSHandle<TSObjectType> instanceType(thread, classType->GetInstanceType());
178     JSHandle<TSObjLayoutInfo> tsLayout(thread, instanceType->GetObjLayoutInfo());
179     uint32_t numOfProps = tsLayout->GetNumOfProperties();
180     JSHandle<JSHClass> hclass;
181     if (LIKELY(numOfProps <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) {
182         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
183         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(numOfProps);
184         for (uint32_t index = 0; index < numOfProps; ++index) {
185             JSTaggedValue tsPropKey = tsLayout->GetKey(index);
186             key.Update(tsPropKey);
187             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
188             PropertyAttributes attributes = PropertyAttributes::Default();
189             attributes.SetIsInlinedProps(true);
190             attributes.SetRepresentation(Representation::NONE);
191             attributes.SetOffset(index);
192             layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
193         }
194         hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, numOfProps);
195         hclass->SetLayout(thread, layout);
196         hclass->SetNumberOfProps(numOfProps);
197     } else {
198         // dictionary mode
199         hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_OBJECT, 0);  // without in-obj
200         hclass->SetIsDictionaryMode(true);
201         hclass->SetNumberOfProps(0);
202     }
203 
204     hclass->SetTS(true);
205 
206     return hclass;
207 }
208 
CreatePHClass(const JSThread * thread,const JSHandle<TSClassType> & classType) const209 JSHandle<JSHClass> TSHClassGenerator::CreatePHClass(const JSThread *thread,
210                                                     const JSHandle<TSClassType> &classType) const
211 {
212     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
213 
214     JSHandle<TSObjectType> prototypeType(thread, classType->GetPrototypeType());
215     JSHandle<TSObjLayoutInfo> tsLayout(thread, prototypeType->GetObjLayoutInfo());
216     uint32_t numOfProps = tsLayout->GetNumOfProperties();
217     JSHandle<JSHClass> hclass;
218     if (LIKELY(numOfProps <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) {
219         TSManager *tsManager = thread->GetCurrentEcmaContext()->GetTSManager();
220         const GlobalEnvConstants *globalConst = thread->GlobalConstants();
221         JSHandle<JSTaggedValue> ctor = globalConst->GetHandledConstructorString();
222         CVector<std::pair<JSHandle<JSTaggedValue>, GlobalTSTypeRef>> sortedPrototype {{ctor, GlobalTSTypeRef()}};
223         CUnorderedMap<std::string, uint32_t> keysMap;
224         for (uint32_t index = 0; index < numOfProps; ++index) {
225             JSHandle<JSTaggedValue> key(thread, tsLayout->GetKey(index));
226             auto value = GlobalTSTypeRef(tsLayout->GetTypeId(index).GetInt());
227             // Usually, abstract methods in abstract class have no specific implementation,
228             // and method signatures will be added after class scope.
229             // Strategy: ignore abstract method, and rearrange the order of method signature to be at the end.
230             bool isSame = JSTaggedValue::SameValue(key, ctor);
231             bool isAbs = tsManager->IsAbstractMethod(value);
232             if (!isSame && !isAbs) {
233                 bool isSign = tsManager->IsMethodSignature(value);
234                 if (isSign) {
235                     continue;
236                 }
237                 std::string keyStr = EcmaStringAccessor(key.GetTaggedValue()).ToStdString();
238                 auto it = keysMap.find(keyStr);
239                 if (it != keysMap.end()) {
240                     sortedPrototype[it->second] = std::make_pair(key, value);
241                     continue;
242                 }
243                 keysMap[keyStr] = sortedPrototype.size();
244                 sortedPrototype.emplace_back(std::make_pair(key, value));
245             }
246         }
247 
248         uint32_t keysLen = sortedPrototype.size();
249         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
250         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(keysLen);
251 
252         for (uint32_t index = 0; index < keysLen; ++index) {
253             key.Update(sortedPrototype[index].first);
254             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
255             PropertyAttributes attributes = PropertyAttributes::Default(true, false, true);
256             if (tsManager->IsGetterSetterFunc(sortedPrototype[index].second)) {
257                 attributes.SetIsAccessor(true);
258             }
259             attributes.SetIsInlinedProps(true);
260             attributes.SetRepresentation(Representation::NONE);
261             attributes.SetOffset(index);
262             layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
263         }
264         hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, keysLen);
265         hclass->SetLayout(thread, layout);
266         hclass->SetNumberOfProps(keysLen);
267     } else {
268         // dictionary mode
269         hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, 0);  // without in-obj
270         hclass->SetIsDictionaryMode(true);
271         hclass->SetNumberOfProps(0);
272     }
273 
274     hclass->SetTS(true);
275     hclass->SetClassPrototype(true);
276     hclass->SetIsPrototype(true);
277 
278     return hclass;
279 }
280 
CreateCHClass(const JSThread * thread,const JSHandle<TSClassType> & classType) const281 JSHandle<JSHClass> TSHClassGenerator::CreateCHClass(const JSThread *thread,
282                                                     const JSHandle<TSClassType> &classType) const
283 {
284     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
285 
286     JSHandle<TSObjectType> constructorType(thread, classType->GetConstructorType());
287     JSHandle<TSObjLayoutInfo> tsLayout(thread, constructorType->GetObjLayoutInfo());
288     uint32_t numOfProps = tsLayout->GetNumOfProperties() + ClassInfoExtractor::STATIC_RESERVED_LENGTH;
289     JSHandle<JSHClass> hclass;
290     uint32_t functionFirstIndex = numOfProps;
291     uint32_t numNonStaticFunc = 0;
292     bool hasFunction = false;
293     if (LIKELY(numOfProps <= PropertyAttributes::MAX_CAPACITY_OF_PROPERTIES)) {
294         TSManager *tsManager = thread->GetCurrentEcmaContext()->GetTSManager();
295         const GlobalEnvConstants *globalConst = thread->GlobalConstants();
296         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(numOfProps);
297         for (uint32_t index = 0; index < numOfProps; ++index) {
298             JSTaggedValue tsPropKey;
299             PropertyAttributes attributes;
300             switch (index) {
301                 case ClassInfoExtractor::LENGTH_INDEX:
302                     attributes = PropertyAttributes::Default(false, false, true);
303                     tsPropKey = globalConst->GetLengthString();
304                     break;
305                 case ClassInfoExtractor::NAME_INDEX:
306                     attributes = PropertyAttributes::Default(false, false, true);
307                     tsPropKey = globalConst->GetNameString();
308                     break;
309                 case ClassInfoExtractor::PROTOTYPE_INDEX:
310                     attributes = PropertyAttributes::DefaultAccessor(false, false, false);
311                     tsPropKey = globalConst->GetPrototypeString();
312                     break;
313                 default:
314                     attributes = PropertyAttributes::Default(true, false, true);
315                     tsPropKey = tsLayout->GetKey(index - ClassInfoExtractor::STATIC_RESERVED_LENGTH);
316                     JSTaggedValue typeId = tsLayout->GetTypeId(index - ClassInfoExtractor::STATIC_RESERVED_LENGTH);
317                     GlobalTSTypeRef gt(static_cast<uint32_t>(typeId.GetNumber()));
318                     if (!tsManager->IsFunctionTypeKind(gt)) {
319                         continue;
320                     }
321                     if (!hasFunction) {
322                         hasFunction = true;
323                         functionFirstIndex = index;
324                         numNonStaticFunc = functionFirstIndex - ClassInfoExtractor::STATIC_RESERVED_LENGTH;
325                     }
326                     if (tsManager->IsGetterSetterFunc(gt)) {
327                         attributes.SetIsAccessor(true);
328                     }
329                     break;
330             }
331             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(JSHandle<JSTaggedValue>(thread, tsPropKey)),
332                          "Key is not a property key");
333             attributes.SetIsInlinedProps(true);
334             attributes.SetRepresentation(Representation::NONE);
335             attributes.SetOffset(index - numNonStaticFunc);
336             layout->AddKey(thread, index - numNonStaticFunc, tsPropKey, attributes);
337         }
338         uint32_t numStaticFunc = numOfProps - functionFirstIndex;
339         for (uint32_t index = ClassInfoExtractor::STATIC_RESERVED_LENGTH; index < functionFirstIndex; index++) {
340             JSTaggedValue tsPropKey = tsLayout->GetKey(index - ClassInfoExtractor::STATIC_RESERVED_LENGTH);
341             PropertyAttributes attributes = PropertyAttributes::Default();
342             attributes.SetIsInlinedProps(true);
343             attributes.SetRepresentation(Representation::NONE);
344             attributes.SetOffset(index + numStaticFunc);
345             layout->AddKey(thread, index + numStaticFunc, tsPropKey, attributes);
346         }
347         hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, numOfProps);
348         hclass->SetLayout(thread, layout);
349         hclass->SetNumberOfProps(numOfProps);
350     } else {
351         // dictionary mode
352         hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, 0);  // without in-obj
353         hclass->SetIsDictionaryMode(true);
354         hclass->SetNumberOfProps(0);
355     }
356 
357     hclass->SetTS(true);
358     hclass->SetClassConstructor(true);
359     hclass->SetConstructor(true);
360 
361     return hclass;
362 }
363 }  // namespace panda::ecmascript::kungfu
364