1 /*
2 * Copyright (c) 2024 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 "evaluate/helpers.h"
17 #include "checker/types/globalTypesHolder.h"
18 #include "ir/ets/etsPrimitiveType.h"
19 #include "ir/ets/etsTypeReference.h"
20 #include "ir/ets/etsTypeReferencePart.h"
21 #include "ir/expressions/identifier.h"
22 #include "ir/statements/blockStatement.h"
23 #include "ir/ts/tsArrayType.h"
24 #include "ir/typeNode.h"
25
26 #include "assembler/assembly-type.h"
27 #include "libpandafile/field_data_accessor-inl.h"
28 #include "libpandafile/file-inl.h"
29
30 #include <algorithm>
31 #include <unordered_map>
32
33 namespace ark::es2panda::evaluate::helpers {
34
35 namespace {
36
PrimitiveToTypeNode(panda_file::Type::TypeId typeId,checker::ETSChecker * checker)37 ir::TypeNode *PrimitiveToTypeNode(panda_file::Type::TypeId typeId, checker::ETSChecker *checker)
38 {
39 ir::PrimitiveType irType;
40 switch (typeId) {
41 case panda_file::Type::TypeId::VOID:
42 irType = ir::PrimitiveType::VOID;
43 break;
44 case panda_file::Type::TypeId::U1:
45 irType = ir::PrimitiveType::BOOLEAN;
46 break;
47 case panda_file::Type::TypeId::I8:
48 irType = ir::PrimitiveType::BYTE;
49 break;
50 case panda_file::Type::TypeId::U16:
51 irType = ir::PrimitiveType::CHAR;
52 break;
53 case panda_file::Type::TypeId::I16:
54 irType = ir::PrimitiveType::SHORT;
55 break;
56 case panda_file::Type::TypeId::I32:
57 irType = ir::PrimitiveType::INT;
58 break;
59 case panda_file::Type::TypeId::I64:
60 irType = ir::PrimitiveType::LONG;
61 break;
62 case panda_file::Type::TypeId::F32:
63 irType = ir::PrimitiveType::FLOAT;
64 break;
65 case panda_file::Type::TypeId::F64:
66 irType = ir::PrimitiveType::DOUBLE;
67 break;
68 default:
69 UNREACHABLE();
70 }
71
72 return checker->AllocNode<ir::ETSPrimitiveType>(irType);
73 }
74
ClassReferenceToTypeNode(std::string_view name,checker::ETSChecker * checker)75 ir::TypeNode *ClassReferenceToTypeNode(std::string_view name, checker::ETSChecker *checker)
76 {
77 util::UString typeName(name, checker->Allocator());
78 return CreateETSTypeReference(checker, typeName.View());
79 }
80
ReferenceToTypeNode(std::string_view typeSignature,checker::ETSChecker * checker)81 ir::TypeNode *ReferenceToTypeNode(std::string_view typeSignature, checker::ETSChecker *checker)
82 {
83 ASSERT(checker);
84 ASSERT(!typeSignature.empty());
85 switch (typeSignature[0]) {
86 case 'L': {
87 // Variable is a reference.
88 ASSERT(typeSignature.back() == ';');
89 // Required to remove "std/core/" prefix, otherwise type name won't be parsed.
90 auto startPos = typeSignature.find_last_of('/');
91 if (startPos == std::string_view::npos) {
92 startPos = 1;
93 } else {
94 startPos += 1;
95 }
96 return ClassReferenceToTypeNode(typeSignature.substr(startPos, typeSignature.size() - 1 - startPos),
97 checker);
98 }
99 case '[': {
100 // Variable is an array.
101 size_t rank = std::count(typeSignature.begin(), typeSignature.end(), '[');
102 auto *elementType = ToTypeNode(typeSignature.substr(rank), checker);
103 if (elementType != nullptr) {
104 for (size_t i = 0; i < rank; ++i) {
105 elementType = checker->AllocNode<ir::TSArrayType>(elementType);
106 }
107 return elementType;
108 }
109 return nullptr;
110 }
111 default:
112 return nullptr;
113 }
114 return nullptr;
115 }
116
117 } // namespace
118
ToTypeNode(std::string_view typeSignature,checker::ETSChecker * checker)119 ir::TypeNode *ToTypeNode(std::string_view typeSignature, checker::ETSChecker *checker)
120 {
121 ASSERT(checker);
122 ASSERT(!typeSignature.empty());
123
124 if (typeSignature[0] == 'L' || typeSignature[0] == '[') {
125 return ReferenceToTypeNode(typeSignature, checker);
126 }
127
128 pandasm::Type type = pandasm::Type::FromDescriptor(typeSignature);
129
130 return PrimitiveToTypeNode(type.GetId(), checker);
131 }
132
PandaTypeToTypeNode(const panda_file::File & pf,panda_file::FieldDataAccessor & fda,checker::ETSChecker * checker)133 ir::TypeNode *PandaTypeToTypeNode(const panda_file::File &pf, panda_file::FieldDataAccessor &fda,
134 checker::ETSChecker *checker)
135 {
136 auto pandaType = panda_file::Type::GetTypeFromFieldEncoding(fda.GetType());
137 if (pandaType.IsReference()) {
138 auto typeId = panda_file::FieldDataAccessor::GetTypeId(pf, fda.GetFieldId());
139 std::string_view refSignature = utf::Mutf8AsCString(pf.GetStringData(typeId).data);
140 return ReferenceToTypeNode(refSignature, checker);
141 }
142 return PrimitiveToTypeNode(pandaType.GetId(), checker);
143 }
144
PandaTypeToTypeNode(const panda_file::File & pf,panda_file::Type pandaType,panda_file::File::EntityId classId,checker::ETSChecker * checker)145 ir::TypeNode *PandaTypeToTypeNode(const panda_file::File &pf, panda_file::Type pandaType,
146 panda_file::File::EntityId classId, checker::ETSChecker *checker)
147 {
148 if (pandaType.IsReference()) {
149 ASSERT(classId.IsValid());
150 std::string_view refSignature = utf::Mutf8AsCString(pf.GetStringData(classId).data);
151 return ReferenceToTypeNode(refSignature, checker);
152 }
153 return PrimitiveToTypeNode(pandaType.GetId(), checker);
154 }
155
PrimitiveToCheckerType(panda_file::Type::TypeId typeId,checker::GlobalTypesHolder * globalTypes)156 static checker::Type *PrimitiveToCheckerType(panda_file::Type::TypeId typeId, checker::GlobalTypesHolder *globalTypes)
157 {
158 ASSERT(globalTypes);
159 switch (typeId) {
160 case panda_file::Type::TypeId::VOID:
161 return globalTypes->GlobalETSVoidType();
162 case panda_file::Type::TypeId::U1:
163 return globalTypes->GlobalBooleanType();
164 case panda_file::Type::TypeId::I8:
165 return globalTypes->GlobalCharType();
166 case panda_file::Type::TypeId::U8:
167 return globalTypes->GlobalByteType();
168 case panda_file::Type::TypeId::I16:
169 [[fallthrough]];
170 case panda_file::Type::TypeId::U16:
171 return globalTypes->GlobalShortType();
172 case panda_file::Type::TypeId::I32:
173 [[fallthrough]];
174 case panda_file::Type::TypeId::U32:
175 return globalTypes->GlobalIntType();
176 case panda_file::Type::TypeId::F32:
177 return globalTypes->GlobalFloatType();
178 case panda_file::Type::TypeId::F64:
179 return globalTypes->GlobalDoubleType();
180 case panda_file::Type::TypeId::I64:
181 [[fallthrough]];
182 case panda_file::Type::TypeId::U64:
183 return globalTypes->GlobalLongType();
184 default:
185 return nullptr;
186 }
187 return nullptr;
188 }
189
ReferenceToName(std::string_view typeSignature,checker::GlobalTypesHolder * globalTypes)190 static std::optional<std::string> ReferenceToName(std::string_view typeSignature,
191 checker::GlobalTypesHolder *globalTypes)
192 {
193 static constexpr const size_t ARRAY_RANK_SYMBOLS = 2;
194
195 ASSERT(globalTypes);
196 ASSERT(!typeSignature.empty());
197
198 switch (typeSignature[0]) {
199 case 'L': {
200 // Variable is a reference.
201 ASSERT(typeSignature.back() == ';');
202 // Required to remove "std/core/" prefix, otherwise type name won't be parsed.
203 auto startPos = typeSignature.find_last_of('/');
204 if (startPos == std::string_view::npos) {
205 startPos = 1;
206 } else {
207 startPos += 1;
208 }
209 return std::string(typeSignature.substr(startPos, typeSignature.size() - 1 - startPos));
210 }
211 case '[': {
212 // Variable is an array.
213 auto rank = std::count(typeSignature.begin(), typeSignature.end(), '[');
214 auto elementType = ToTypeName(typeSignature.substr(rank), globalTypes);
215 if (!elementType) {
216 return elementType;
217 }
218
219 auto &arrayType = *elementType;
220 auto subtypeSize = arrayType.size();
221 arrayType.resize(subtypeSize + rank * ARRAY_RANK_SYMBOLS);
222 for (size_t i = subtypeSize, end = arrayType.size(); i < end; i += ARRAY_RANK_SYMBOLS) {
223 arrayType[i] = '[';
224 arrayType[i + 1] = ']';
225 }
226 return arrayType;
227 }
228 default:
229 UNREACHABLE();
230 }
231 return {};
232 }
233
ToTypeName(std::string_view typeSignature,checker::GlobalTypesHolder * globalTypes)234 std::optional<std::string> ToTypeName(std::string_view typeSignature, checker::GlobalTypesHolder *globalTypes)
235 {
236 ASSERT(globalTypes);
237 ASSERT(!typeSignature.empty());
238
239 if (typeSignature[0] == 'L' || typeSignature[0] == '[') {
240 return ReferenceToName(typeSignature, globalTypes);
241 }
242
243 pandasm::Type type = pandasm::Type::FromDescriptor(typeSignature);
244
245 auto *checkerType = PrimitiveToCheckerType(type.GetId(), globalTypes);
246 ASSERT(checkerType);
247 return checkerType->ToString();
248 }
249
GetTypeId(std::string_view typeSignature)250 panda_file::Type::TypeId GetTypeId(std::string_view typeSignature)
251 {
252 if (typeSignature.empty()) {
253 return panda_file::Type::TypeId::INVALID;
254 }
255 if (typeSignature[0] == 'L' || typeSignature[0] == '[') {
256 return panda_file::Type::TypeId::REFERENCE;
257 }
258 pandasm::Type type = pandasm::Type::FromDescriptor(typeSignature);
259 return type.GetId();
260 }
261
GetEnclosingBlock(ir::Identifier * ident)262 ir::BlockStatement *GetEnclosingBlock(ir::Identifier *ident)
263 {
264 ASSERT(ident);
265
266 ir::AstNode *iter = ident;
267
268 while (iter->Parent() != nullptr && !iter->IsBlockStatement()) {
269 iter = iter->Parent();
270 }
271
272 ASSERT(iter);
273 return iter->AsBlockStatement();
274 }
275
SafeStateScope(checker::ETSChecker * checker,varbinder::ETSBinder * varBinder)276 SafeStateScope::SafeStateScope(checker::ETSChecker *checker, varbinder::ETSBinder *varBinder)
277 : checker_(checker),
278 varBinder_(varBinder),
279 checkerScope_(checker->Scope()),
280 binderTopScope_(varBinder->TopScope()),
281 binderVarScope_(varBinder->VarScope()),
282 binderScope_(varBinder->GetScope()),
283 binderProgram_(varBinder->Program()),
284 recordTable_(varBinder->GetRecordTable())
285 {
286 }
287
~SafeStateScope()288 SafeStateScope::~SafeStateScope()
289 {
290 (void)checker_;
291 (void)varBinder_;
292 (void)checkerScope_;
293 (void)binderTopScope_;
294 (void)binderVarScope_;
295 (void)binderScope_;
296 (void)binderProgram_;
297 (void)recordTable_;
298 ASSERT(checkerScope_ == checker_->Scope());
299 ASSERT(binderTopScope_ == varBinder_->TopScope());
300 ASSERT(binderVarScope_ == varBinder_->VarScope());
301 ASSERT(binderScope_ == varBinder_->GetScope());
302 ASSERT(binderProgram_ == varBinder_->Program());
303 ASSERT(recordTable_ == varBinder_->GetRecordTable());
304 }
305
AddExternalProgram(parser::Program * program,parser::Program * extProgram,std::string_view moduleName)306 void AddExternalProgram(parser::Program *program, parser::Program *extProgram, std::string_view moduleName)
307 {
308 ASSERT(program);
309 ASSERT(extProgram);
310
311 auto &extSources = program->ExternalSources();
312 if (extSources.count(moduleName) == 0) {
313 extSources.emplace(moduleName, program->Allocator()->Adapter());
314 }
315 extSources.at(moduleName).emplace_back(extProgram);
316 }
317
CreateETSTypeReference(checker::ETSChecker * checker,util::StringView name)318 ir::ETSTypeReference *CreateETSTypeReference(checker::ETSChecker *checker, util::StringView name)
319 {
320 auto *identRef = checker->AllocNode<ir::Identifier>(name, checker->Allocator());
321 auto *typeRefPart = checker->AllocNode<ir::ETSTypeReferencePart>(identRef);
322 return checker->AllocNode<ir::ETSTypeReference>(typeRefPart);
323 }
324
325 // Be aware of lifecycle of string and string_view
SplitRecordName(std::string_view recordName)326 std::pair<std::string_view, std::string_view> SplitRecordName(std::string_view recordName)
327 {
328 std::string_view moduleName;
329 std::string_view newRecordName;
330
331 if (auto pos = recordName.find_last_of('.'); pos != std::string_view::npos) {
332 moduleName = recordName.substr(0, pos);
333 newRecordName = recordName.substr(pos + 1, recordName.size());
334 } else {
335 newRecordName = recordName;
336 }
337
338 return std::make_pair(moduleName, newRecordName);
339 }
340
CreateClassProperty(checker::ETSChecker * checker,std::string_view name,ir::TypeNode * type,ir::ModifierFlags modifiers)341 ir::ClassProperty *CreateClassProperty(checker::ETSChecker *checker, std::string_view name, ir::TypeNode *type,
342 ir::ModifierFlags modifiers)
343 {
344 ASSERT(type);
345
346 auto *fieldIdent = checker->AllocNode<ir::Identifier>(name, checker->Allocator());
347 auto *field =
348 checker->AllocNode<ir::ClassProperty>(fieldIdent, nullptr, type, modifiers, checker->Allocator(), false);
349
350 return field;
351 }
352
GetModifierFlags(panda_file::ClassDataAccessor & da)353 ir::ModifierFlags GetModifierFlags(panda_file::ClassDataAccessor &da)
354 {
355 auto modifierFlags = ir::ModifierFlags::NONE;
356 auto accFlags = da.GetAccessFlags();
357 if ((accFlags & ACC_ABSTRACT) != 0) {
358 modifierFlags |= ir::ModifierFlags::ABSTRACT;
359 }
360 if ((accFlags & ACC_FINAL) != 0) {
361 modifierFlags |= ir::ModifierFlags::FINAL;
362 }
363 return modifierFlags;
364 }
365
366 } // namespace ark::es2panda::evaluate::helpers
367