• 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/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