• 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/global_env_constants-inl.h"
18 #include "ecmascript/layout_info-inl.h"
19 #include "ecmascript/js_function.h"
20 #include "ecmascript/jspandafile/class_info_extractor.h"
21 #include "ecmascript/subtyping_operator.h"
22 
23 namespace panda::ecmascript::kungfu {
24 using ClassInfoExtractor = panda::ecmascript::ClassInfoExtractor;
GenerateTSHClasses() const25 void TSHClassGenerator::GenerateTSHClasses() const
26 {
27     const JSThread *thread = tsManager_->GetThread();
28     const std::set<GlobalTSTypeRef> &collectedGT = GetCollectedGT();
29     for (const auto &gt : collectedGT) {
30         JSHandle<JSTaggedValue> tsType = tsManager_->GetTSType(gt);
31         if (tsType->IsUndefined()) {
32             continue;
33         }
34         ASSERT(tsType->IsTSClassType());
35         JSHandle<TSClassType> classType(tsType);
36         if (tsManager_->HasTSHClass(classType)) {
37             continue;
38         } else if (tsManager_->IsUserDefinedClassTypeKind(gt)) {
39             RecursiveGenerate(classType);
40         } else if (!classType->GetHasLinked()) {
41             SubtypingOperator::MergeClassField(thread, classType);
42         }
43     }
44 }
45 
RecursiveGenerate(const JSHandle<TSClassType> & classType) const46 void TSHClassGenerator::RecursiveGenerate(const JSHandle<TSClassType> &classType) const
47 {
48     if (!classType->IsBaseClassType()) {
49         JSHandle<TSClassType> extendedClassType(tsManager_->GetExtendedClassType(classType));
50         if (!tsManager_->HasTSHClass(extendedClassType)) {
51             RecursiveGenerate(extendedClassType);
52         }
53     }
54 
55     JSThread *thread = tsManager_->GetThread();
56     bool passed = false;
57     if (classType->IsBaseClassType()) {
58         passed = SubtypingOperator::CheckBaseClass(thread, classType);
59     } else {
60         passed = SubtypingOperator::CheckSubtyping(thread, classType);
61     }
62 
63     classType->SetHasPassedSubtypingCheck(passed);
64 
65     if (passed) {
66         if (!classType->IsBaseClassType()) {
67             SubtypingOperator::MergeClassField(thread, classType);
68         }
69         JSHandle<JSHClass> ihclass = Generate(classType);
70         SubtypingOperator::FillTSInheritInfo(thread, classType, ihclass);
71         return;
72     }
73     Generate(classType);
74 }
75 
Generate(const JSHandle<TSClassType> & classType) const76 JSHandle<JSHClass> TSHClassGenerator::Generate(const JSHandle<TSClassType> &classType) const
77 {
78     const JSThread *thread = tsManager_->GetThread();
79     JSHandle<JSHClass> ihclass = CreateHClass(thread, classType, Kind::INSTANCE);
80     JSHandle<JSHClass> phclass = CreateHClass(thread, classType, Kind::PROTOTYPE);
81     JSHandle<JSHClass> chclass = CreateHClass(thread, classType, Kind::CONSTRUCTOR);
82     JSHandle<JSObject> prototype = thread->GetEcmaVM()->GetFactory()->NewJSObject(phclass);
83     ihclass->SetProto(thread, prototype);
84 
85     GlobalTSTypeRef gt = classType->GetGT();
86     tsManager_->AddInstanceTSHClass(gt, ihclass);
87     tsManager_->AddConstructorTSHClass(gt, chclass);
88     return ihclass;
89 }
90 
CreateHClass(const JSThread * thread,const JSHandle<TSClassType> & classType,Kind kind) const91 JSHandle<JSHClass> TSHClassGenerator::CreateHClass(const JSThread *thread, const JSHandle<TSClassType> &classType,
92                                                    Kind kind) const
93 {
94     JSHandle<JSHClass> hclass(thread->GlobalConstants()->GetHandledUndefined());
95     switch (kind) {
96         case Kind::INSTANCE: {
97             hclass = CreateIHClass(thread, classType);
98             break;
99         }
100         case Kind::PROTOTYPE: {
101             hclass = CreatePHClass(thread, classType);
102             break;
103         }
104         case Kind::CONSTRUCTOR: {
105             hclass = CreateCHClass(thread, classType);
106             break;
107         }
108         default:
109             LOG_ECMA(FATAL) << "this branch is unreachable";
110             UNREACHABLE();
111     }
112 
113     return hclass;
114 }
115 
CreateIHClass(const JSThread * thread,const JSHandle<TSClassType> & classType) const116 JSHandle<JSHClass> TSHClassGenerator::CreateIHClass(const JSThread *thread,
117                                                     const JSHandle<TSClassType> &classType) const
118 {
119     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
120 
121     JSHandle<TSObjectType> instanceType(thread, classType->GetInstanceType());
122     JSHandle<TSObjLayoutInfo> tsLayout(thread, instanceType->GetObjLayoutInfo());
123     uint32_t numOfProps = tsLayout->GetNumOfProperties();
124     JSHandle<JSHClass> hclass;
125     if (LIKELY(numOfProps <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
126         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
127         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(numOfProps);
128         for (uint32_t index = 0; index < numOfProps; ++index) {
129             JSTaggedValue tsPropKey = tsLayout->GetKey(index);
130             key.Update(tsPropKey);
131             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
132             PropertyAttributes attributes = PropertyAttributes::Default();
133             attributes.SetIsInlinedProps(true);
134             attributes.SetRepresentation(Representation::NONE);
135             attributes.SetOffset(index);
136             layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
137         }
138         hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, numOfProps);
139         hclass->SetLayout(thread, layout);
140         hclass->SetNumberOfProps(numOfProps);
141     } else {
142         // dictionary mode
143         hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_OBJECT, 0);  // without in-obj
144         hclass->SetIsDictionaryMode(true);
145         hclass->SetNumberOfProps(0);
146     }
147 
148     hclass->SetTS(true);
149 
150     return hclass;
151 }
152 
CreatePHClass(const JSThread * thread,const JSHandle<TSClassType> & classType) const153 JSHandle<JSHClass> TSHClassGenerator::CreatePHClass(const JSThread *thread,
154                                                     const JSHandle<TSClassType> &classType) const
155 {
156     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
157 
158     JSHandle<TSObjectType> prototypeType(thread, classType->GetPrototypeType());
159     JSHandle<TSObjLayoutInfo> tsLayout(thread, prototypeType->GetObjLayoutInfo());
160     uint32_t numOfProps = tsLayout->GetNumOfProperties();
161     JSHandle<JSHClass> hclass;
162     // There is only function information in abc, which needs to be brought up to the front for processing.
163     // Other fields should be collected first and then optimized to the end.
164     // Please note that there may be a problem with order in this way
165     if (LIKELY(numOfProps <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
166         TSManager *tsManager = thread->GetCurrentEcmaContext()->GetTSManager();
167         const GlobalEnvConstants *globalConst = thread->GlobalConstants();
168         JSHandle<JSTaggedValue> ctor = globalConst->GetHandledConstructorString();
169         CVector<std::pair<JSHandle<JSTaggedValue>, GlobalTSTypeRef>> sortedPrototype {{ctor, GlobalTSTypeRef()}};
170         CUnorderedMap<std::string, uint32_t> keysMap;
171         for (uint32_t index = 0; index < numOfProps; ++index) {
172             JSHandle<JSTaggedValue> key(thread, tsLayout->GetKey(index));
173             auto value = GlobalTSTypeRef(tsLayout->GetTypeId(index).GetInt());
174             // Usually, abstract methods in abstract class have no specific implementation,
175             // and method signatures will be added after class scope.
176             // Strategy: ignore abstract method, and rearrange the order of method signature to be at the end.
177             bool isSame = JSTaggedValue::SameValue(key, ctor);
178             bool isAbs = tsManager->IsAbstractMethod(value);
179             if (!isSame && !isAbs) {
180                 bool isSign = tsManager->IsMethodSignature(value);
181                 if (isSign) {
182                     continue;
183                 }
184                 std::string keyStr = EcmaStringAccessor(key.GetTaggedValue()).ToStdString();
185                 auto it = keysMap.find(keyStr);
186                 if (it != keysMap.end()) {
187                     sortedPrototype[it->second] = std::make_pair(key, value);
188                     continue;
189                 }
190                 keysMap[keyStr] = sortedPrototype.size();
191                 sortedPrototype.emplace_back(std::make_pair(key, value));
192             }
193         }
194 
195         uint32_t keysLen = sortedPrototype.size();
196         JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
197         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(keysLen);
198 
199         for (uint32_t index = 0; index < keysLen; ++index) {
200             key.Update(sortedPrototype[index].first);
201             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(key), "Key is not a property key");
202             PropertyAttributes attributes = PropertyAttributes::Default(true, false, true);
203             if (tsManager->IsGetterSetterFunc(sortedPrototype[index].second)) {
204                 attributes.SetIsAccessor(true);
205             }
206             attributes.SetIsInlinedProps(true);
207             attributes.SetRepresentation(Representation::NONE);
208             attributes.SetOffset(index);
209             layout->AddKey(thread, index, key.GetTaggedValue(), attributes);
210         }
211         hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, keysLen);
212         hclass->SetLayout(thread, layout);
213         hclass->SetNumberOfProps(keysLen);
214     } else {
215         // dictionary mode
216         hclass = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, 0);  // without in-obj
217         hclass->SetIsDictionaryMode(true);
218         hclass->SetNumberOfProps(0);
219     }
220 
221     hclass->SetTS(true);
222     hclass->SetClassPrototype(true);
223     hclass->SetIsPrototype(true);
224 
225     return hclass;
226 }
227 
CreateCHClass(const JSThread * thread,const JSHandle<TSClassType> & classType) const228 JSHandle<JSHClass> TSHClassGenerator::CreateCHClass(const JSThread *thread,
229                                                     const JSHandle<TSClassType> &classType) const
230 {
231     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
232 
233     JSHandle<TSObjectType> constructorType(thread, classType->GetConstructorType());
234     JSHandle<TSObjLayoutInfo> tsLayout(thread, constructorType->GetObjLayoutInfo());
235     uint32_t numOfProps = tsLayout->GetNumOfProperties() + ClassInfoExtractor::STATIC_RESERVED_LENGTH;
236     JSHandle<JSHClass> hclass;
237     if (LIKELY(numOfProps <= PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
238         TSManager *tsManager = thread->GetCurrentEcmaContext()->GetTSManager();
239         const GlobalEnvConstants *globalConst = thread->GlobalConstants();
240         JSHandle<LayoutInfo> layout = factory->CreateLayoutInfo(numOfProps);
241         std::vector<JSHandle<JSTaggedValue>> noFunc;
242         int functionCount = -1;
243         for (uint32_t index = 0; index < numOfProps; ++index) {
244             JSTaggedValue tsPropKey;
245             PropertyAttributes attributes;
246             switch (index) {
247                 case ClassInfoExtractor::LENGTH_INDEX:
248                     attributes = PropertyAttributes::Default(false, false, true);
249                     tsPropKey = globalConst->GetLengthString();
250                     functionCount++;
251                     break;
252                 case ClassInfoExtractor::NAME_INDEX:
253                     attributes = PropertyAttributes::Default(false, false, true);
254                     tsPropKey = globalConst->GetNameString();
255                     functionCount++;
256                     break;
257                 case ClassInfoExtractor::PROTOTYPE_INDEX:
258                     attributes = PropertyAttributes::DefaultAccessor(false, false, false);
259                     tsPropKey = globalConst->GetPrototypeString();
260                     functionCount++;
261                     break;
262                 default:
263                     attributes = PropertyAttributes::Default(true, false, true);
264                     tsPropKey = tsLayout->GetKey(index - ClassInfoExtractor::STATIC_RESERVED_LENGTH);
265                     JSTaggedValue typeId = tsLayout->GetTypeId(index - ClassInfoExtractor::STATIC_RESERVED_LENGTH);
266                     GlobalTSTypeRef gt(static_cast<uint32_t>(typeId.GetNumber()));
267                     if (!tsManager->IsFunctionTypeKind(gt)) {
268                         noFunc.emplace_back(JSHandle<JSTaggedValue>(thread, tsPropKey));
269                         continue;
270                     }
271                     if (tsManager->IsGetterSetterFunc(gt)) {
272                         attributes.SetIsAccessor(true);
273                     }
274                     functionCount++;
275                     break;
276             }
277             ASSERT_PRINT(JSTaggedValue::IsPropertyKey(JSHandle<JSTaggedValue>(thread, tsPropKey)),
278                          "Key is not a property key");
279             attributes.SetIsInlinedProps(true);
280             attributes.SetRepresentation(Representation::NONE);
281             attributes.SetOffset(functionCount);
282             layout->AddKey(thread, functionCount, tsPropKey, attributes);
283         }
284         for (uint32_t index = 1; index <= noFunc.size(); index++) {
285             PropertyAttributes attributes = PropertyAttributes::Default();
286             attributes.SetIsInlinedProps(true);
287             attributes.SetRepresentation(Representation::NONE);
288             attributes.SetOffset(index + functionCount);
289             layout->AddKey(thread, index + functionCount, noFunc[index - 1].GetTaggedValue(), attributes);
290         }
291         hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, numOfProps);
292         hclass->SetLayout(thread, layout);
293         hclass->SetNumberOfProps(numOfProps);
294     } else {
295         // dictionary mode
296         hclass = factory->NewEcmaHClass(JSFunction::SIZE, JSType::JS_FUNCTION, 0);  // without in-obj
297         hclass->SetIsDictionaryMode(true);
298         hclass->SetNumberOfProps(0);
299     }
300 
301     hclass->SetTS(true);
302     hclass->SetClassConstructor(true);
303     hclass->SetConstructor(true);
304 
305     return hclass;
306 }
307 }  // namespace panda::ecmascript::kungfu
308