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