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/jspandafile/type_literal_extractor.h"
17
18 #include <iomanip>
19 #include "libpandafile/literal_data_accessor-inl.h"
20 #include "libpandafile/method_data_accessor-inl.h"
21
22 namespace panda::ecmascript {
23 using LiteralTag = panda_file::LiteralTag;
24 using LiteralValue = panda_file::LiteralDataAccessor::LiteralValue;
25 using StringData = panda_file::StringData;
26 using EntityId = panda_file::File::EntityId;
27 using LiteralDataAccessor = panda_file::LiteralDataAccessor;
28
29 static constexpr const char *TYPE_ANNO_RECORD_NAME = "L_ESTypeAnnotation;";
30
IsLegalOffset(uint32_t offset)31 static bool IsLegalOffset(uint32_t offset)
32 {
33 return offset != 0U;
34 }
35
GetMethodAnnoOffset(const JSPandaFile * jsPandaFile,uint32_t methodOffset,const char * annoName)36 static uint32_t GetMethodAnnoOffset(const JSPandaFile *jsPandaFile, uint32_t methodOffset, const char *annoName)
37 {
38 const panda_file::File &pf = *jsPandaFile->GetPandaFile();
39 EntityId methodId(methodOffset);
40 panda_file::MethodDataAccessor mda(pf, methodId);
41
42 uint32_t annoOffset = 0;
43 mda.EnumerateAnnotations([&jsPandaFile, &annoName, &pf, &annoOffset](EntityId annotationId) {
44 panda_file::AnnotationDataAccessor ada(pf, annotationId);
45 auto *annotationName = reinterpret_cast<const char *>(jsPandaFile->GetStringData(ada.GetClassId()).data);
46 ASSERT(annotationName != nullptr);
47 if (::strcmp(TYPE_ANNO_RECORD_NAME, annotationName) != 0) {
48 return;
49 }
50 uint32_t length = ada.GetCount();
51 for (uint32_t i = 0; i < length; i++) {
52 panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i);
53 auto *elemName = reinterpret_cast<const char *>(jsPandaFile->GetStringData(adae.GetNameId()).data);
54 ASSERT(elemName != nullptr);
55
56 if (::strcmp(annoName, elemName) != 0) {
57 continue;
58 }
59
60 panda_file::ScalarValue sv = adae.GetScalarValue();
61 annoOffset = sv.GetValue();
62 }
63 });
64 return annoOffset;
65 }
66
TypeLiteralExtractor(const JSPandaFile * jsPandaFile,const uint32_t typeOffset)67 TypeLiteralExtractor::TypeLiteralExtractor(const JSPandaFile *jsPandaFile, const uint32_t typeOffset)
68 : typeOffset_(typeOffset)
69 {
70 ProcessTypeLiteral(jsPandaFile, typeOffset);
71 }
72
ProcessTypeLiteral(const JSPandaFile * jsPandaFile,const uint32_t typeOffset)73 void TypeLiteralExtractor::ProcessTypeLiteral(const JSPandaFile *jsPandaFile, const uint32_t typeOffset)
74 {
75 EntityId literalId(typeOffset);
76 LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
77 bool isFirst = true;
78 lda.EnumerateLiteralVals(literalId,
79 [this, &jsPandaFile, &isFirst](const LiteralValue &value, const LiteralTag &tag) {
80 if (isFirst) {
81 uint32_t kindValue = std::get<uint32_t>(value);
82 if (UNLIKELY(!IsVaildKind(kindValue))) {
83 LOG_COMPILER(FATAL) << "Load type literal failure.";
84 return;
85 }
86 kind_ = static_cast<TSTypeKind>(std::get<uint32_t>(value));
87 isFirst = false;
88 return;
89 }
90 switch (tag) {
91 case LiteralTag::INTEGER: {
92 uint32_t valueValue = std::get<uint32_t>(value);
93 if (static_cast<int32_t>(valueValue) < 0) {
94 isGenerics_ = true;
95 }
96 array_.emplace_back(valueValue);
97 break;
98 }
99 case LiteralTag::LITERALARRAY: {
100 array_.emplace_back(std::get<uint32_t>(value));
101 break;
102 }
103 case LiteralTag::BUILTINTYPEINDEX: {
104 array_.emplace_back(static_cast<uint32_t>(std::get<uint8_t>(value)));
105 break;
106 }
107 case LiteralTag::STRING: {
108 StringData sd = jsPandaFile->GetStringData(EntityId(std::get<uint32_t>(value)));
109 CString stringValue = utf::Mutf8AsCString(sd.data);
110 array_.emplace_back(stringValue);
111 break;
112 }
113 default: {
114 LOG_COMPILER(FATAL) << "TypeLiteral should not exist LiteralTag: " << static_cast<uint32_t>(tag);
115 break;
116 }
117 }
118 });
119 }
120
Print() const121 void TypeLiteralExtractor::Print() const
122 {
123 std::string log("[TypeLiteral] TypeOffset: ");
124 log += (std::to_string(typeOffset_) + ", " + PrintTypeKind(kind_));
125 if (isGenerics_) {
126 log += ", generics";
127 }
128 uint32_t length = array_.size();
129 log += ", [";
130 for (uint32_t i = 0; i < length; ++i) {
131 if (std::holds_alternative<uint32_t>(array_[i])) {
132 log += std::to_string(std::get<uint32_t>(array_[i]));
133 } else if (std::holds_alternative<CString>(array_[i])) {
134 log += std::get<CString>(array_[i]);
135 }
136 if (i == length - 1) {
137 log += "]";
138 } else {
139 log += ", ";
140 }
141 }
142 LOG_COMPILER(INFO) << log;
143 }
144
PrintTypeKind(TSTypeKind typeKind) const145 std::string TypeLiteralExtractor::PrintTypeKind(TSTypeKind typeKind) const
146 {
147 switch (typeKind) {
148 case TSTypeKind::CLASS:
149 return "class";
150 case TSTypeKind::CLASS_INSTANCE:
151 return "class_instance";
152 case TSTypeKind::FUNCTION:
153 return "function";
154 case TSTypeKind::UNION:
155 return "union";
156 case TSTypeKind::ARRAY:
157 return "array";
158 case TSTypeKind::OBJECT:
159 return "object";
160 case TSTypeKind::IMPORT:
161 return "import";
162 case TSTypeKind::INTERFACE:
163 return "interface";
164 case TSTypeKind::BUILTIN_INSTANCE:
165 return "builtin_instance";
166 case TSTypeKind::GENERIC_INSTANCE:
167 return "generic_instance";
168 case TSTypeKind::INDEXSIG:
169 return "index_signature";
170 default:
171 LOG_ECMA(FATAL) << "this branch is unreachable";
172 UNREACHABLE();
173 }
174 }
175
TypeSummaryExtractor(const JSPandaFile * jsPandaFile,const CString & recordName)176 TypeSummaryExtractor::TypeSummaryExtractor(const JSPandaFile *jsPandaFile, const CString &recordName)
177 : jsPandaFile_(jsPandaFile)
178 {
179 ProcessTypeSummary(jsPandaFile, jsPandaFile->GetTypeSummaryOffset(recordName));
180 }
181
ProcessTypeSummary(const JSPandaFile * jsPandaFile,const uint32_t summaryOffset)182 void TypeSummaryExtractor::ProcessTypeSummary(const JSPandaFile *jsPandaFile, const uint32_t summaryOffset)
183 {
184 EntityId summaryId(summaryOffset);
185 LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
186 bool isFirstIndex = true;
187 lda.EnumerateLiteralVals(summaryId,
188 [this, &isFirstIndex, &jsPandaFile](const LiteralValue &value, const LiteralTag &tag) {
189 switch (tag) {
190 case LiteralTag::LITERALARRAY: {
191 typeOffsets_.emplace_back(std::get<uint32_t>(value));
192 break;
193 }
194 case LiteralTag::BUILTINTYPEINDEX: {
195 typeOffsets_.emplace_back(static_cast<uint32_t>(std::get<uint8_t>(value)));
196 break;
197 }
198 case LiteralTag::INTEGER: {
199 if (isFirstIndex) {
200 numOfTypes_ = std::get<uint32_t>(value);
201 typeOffsets_.emplace_back(numOfTypes_);
202 isFirstIndex = false;
203 } else {
204 numOfRedirects_ = std::get<uint32_t>(value);
205 }
206 break;
207 }
208 case LiteralTag::STRING: {
209 StringData sd = jsPandaFile->GetStringData(EntityId(std::get<uint32_t>(value)));
210 CString stringValue = utf::Mutf8AsCString(sd.data);
211 reDirects_.emplace_back(stringValue);
212 break;
213 }
214 default: {
215 LOG_COMPILER(FATAL) << "TypeSummary should not exist LiteralTag: " << static_cast<uint32_t>(tag);
216 break;
217 }
218 }
219 });
220 ASSERT(typeOffsets_.size() == numOfTypes_ + 1);
221 ASSERT(reDirects_.size() == numOfRedirects_);
222 }
223
Print() const224 void TypeSummaryExtractor::Print() const
225 {
226 for (uint32_t i = 1; i <= numOfTypes_; ++i) { // 1: start index of typeOffsets
227 TypeLiteralExtractor(jsPandaFile_, typeOffsets_[i]).Print();
228 }
229 }
230
TypeAnnotationExtractor(const JSPandaFile * jsPandaFile,const uint32_t methodOffset)231 TypeAnnotationExtractor::TypeAnnotationExtractor(const JSPandaFile *jsPandaFile, const uint32_t methodOffset)
232 {
233 ProcessTypeAnnotation(jsPandaFile, methodOffset);
234 CollectTSMethodKind();
235 }
236
ProcessTypeAnnotation(const JSPandaFile * jsPandaFile,const uint32_t methodOffset)237 void TypeAnnotationExtractor::ProcessTypeAnnotation(const JSPandaFile *jsPandaFile, const uint32_t methodOffset)
238 {
239 uint32_t annoOffset = GetMethodAnnoOffset(jsPandaFile, methodOffset, TYPE_ANNO_ELEMENT_NAME);
240 if (!IsLegalOffset(annoOffset)) {
241 return;
242 }
243
244 EntityId annoId(annoOffset);
245 LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
246 lda.EnumerateLiteralVals(annoId, [this](const LiteralValue &value, const LiteralTag &tag) {
247 switch (tag) {
248 case LiteralTag::INTEGER: {
249 int32_t valueValue = base::bit_cast<int32_t>(std::get<uint32_t>(value));
250 bcOffsets_.emplace_back(valueValue);
251 break;
252 }
253 case LiteralTag::LITERALARRAY: {
254 typeIds_.emplace_back(std::get<uint32_t>(value));
255 tags_.emplace_back(tag);
256 break;
257 }
258 case LiteralTag::BUILTINTYPEINDEX: {
259 typeIds_.emplace_back(static_cast<uint32_t>(std::get<uint8_t>(value)));
260 tags_.emplace_back(tag);
261 break;
262 }
263 default: {
264 LOG_COMPILER(FATAL) << "TypeAnnotation should not exist LiteralTag: " << static_cast<uint32_t>(tag);
265 break;
266 }
267 }
268 });
269 ASSERT(bcOffsets_.size() == typeIds_.size());
270 ASSERT(tags_.size() == typeIds_.size());
271 }
272
CollectTSMethodKind()273 void TypeAnnotationExtractor::CollectTSMethodKind()
274 {
275 ASSERT(bcOffsets_.size() == typeIds_.size());
276 uint32_t length = bcOffsets_.size();
277 for (uint32_t i = 0; i < length; ++i) {
278 if (bcOffsets_[i] != METHOD_ANNOTATION_FUNCTION_TYPE_OFFSET) {
279 continue;
280 }
281
282 if (tags_[i] != LiteralTag::BUILTINTYPEINDEX) {
283 methodTypeOffset_ = typeIds_[i];
284 return;
285 }
286
287 if (typeIds_[i] == METHOD_ANNOTATION_NAMESPACE) {
288 isNamespace_ = true;
289 }
290 typeIds_[i] = 0; // set default value
291 }
292 }
293
Print() const294 void TypeAnnotationExtractor::Print() const
295 {
296 const uint32_t typeRightAdjustment = 6;
297 ASSERT(bcOffsets_.size() == typeIds_.size());
298 ASSERT(tags_.size() == typeIds_.size());
299 LOG_COMPILER(INFO) << "====================================================================";
300 LOG_COMPILER(INFO) << "[TypeAnnotation]";
301 uint32_t length = bcOffsets_.size();
302 for (uint32_t i = 0; i < length; ++i) {
303 LOG_COMPILER(INFO) << "Order of bytecodes: " << std::setw(typeRightAdjustment) << bcOffsets_[i] << ", "
304 << "typeId: " << std::setw(typeRightAdjustment) << typeIds_[i] << ", "
305 << "tag of typeId: " << PrintTag(tags_[i]);
306 }
307 }
308
PrintTag(LiteralTag tag) const309 std::string TypeAnnotationExtractor::PrintTag(LiteralTag tag) const
310 {
311 switch (tag) {
312 case LiteralTag::LITERALARRAY: {
313 return "type literal offset";
314 }
315 case LiteralTag::BUILTINTYPEINDEX: {
316 return "builtin type index";
317 }
318 default: {
319 return "none";
320 }
321 }
322 }
323
ExportTypeTableExtractor(const JSPandaFile * jsPandaFile,const CString & recordName,bool isBuiltinTable)324 ExportTypeTableExtractor::ExportTypeTableExtractor(const JSPandaFile *jsPandaFile,
325 const CString &recordName,
326 bool isBuiltinTable)
327 {
328 ProcessExportTable(jsPandaFile, recordName, isBuiltinTable);
329 }
330
ProcessExportTable(const JSPandaFile * jsPandaFile,const CString & recordName,bool isBuiltinTable)331 void ExportTypeTableExtractor::ProcessExportTable(const JSPandaFile *jsPandaFile,
332 const CString &recordName,
333 bool isBuiltinTable)
334 {
335 const char *name = isBuiltinTable ? DECLARED_SYMBOL_TYPES : EXPORTED_SYMBOL_TYPES;
336 uint32_t methodOffset = jsPandaFile->GetMainMethodIndex(recordName);
337 uint32_t annoOffset = GetMethodAnnoOffset(jsPandaFile, methodOffset, name);
338 if (!IsLegalOffset(annoOffset)) {
339 return;
340 }
341
342 EntityId annoId(annoOffset);
343 LiteralDataAccessor lda = jsPandaFile->GetLiteralDataAccessor();
344 lda.EnumerateLiteralVals(annoId, [this, &jsPandaFile](const LiteralValue &value, const LiteralTag &tag) {
345 switch (tag) {
346 case LiteralTag::LITERALARRAY: {
347 typeIds_.emplace_back(std::get<uint32_t>(value));
348 break;
349 }
350 case LiteralTag::BUILTINTYPEINDEX: {
351 typeIds_.emplace_back(static_cast<uint32_t>(std::get<uint8_t>(value)));
352 break;
353 }
354 case LiteralTag::STRING: {
355 StringData sd = jsPandaFile->GetStringData(EntityId(std::get<uint32_t>(value)));
356 exportVars_.emplace_back(utf::Mutf8AsCString(sd.data));
357 break;
358 }
359 default: {
360 LOG_COMPILER(FATAL) << "TypeExportTable should not exist LiteralTag: " << static_cast<uint32_t>(tag);
361 break;
362 }
363 }
364 });
365 ASSERT(exportVars_.size() == typeIds_.size());
366 }
367
Print() const368 void ExportTypeTableExtractor::Print() const
369 {
370 ASSERT(exportVars_.size() == typeIds_.size());
371 LOG_COMPILER(INFO) << "[ExportTypeTable]";
372 uint32_t length = typeIds_.size();
373 for (uint32_t i = 0; i < length; ++i) {
374 LOG_COMPILER(INFO) << "Export variable: " << exportVars_[i] << ", "
375 << "typeId: " << typeIds_[i];
376 }
377 }
378 } // namespace panda::ecmascript
379