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 > : 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