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
33namespace ark::es2panda::evaluate::helpers {
34
35namespace {
36
37ir::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
75ir::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
81ir::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
119ir::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
133ir::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
145ir::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
156static 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
190static 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
234std::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
250panda_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
262ir::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
276SafeStateScope::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
288SafeStateScope::~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
306void 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
318ir::ETSTypeReference *CreateETSTypeReference(checker::ETSChecker *checker, util::StringView name)
319{
320    auto *identRef = checker->AllocNode<ir::Identifier>(name, checker->Allocator());
321    identRef->AsIdentifier()->SetReference();
322
323    auto *typeRefPart = checker->AllocNode<ir::ETSTypeReferencePart>(identRef);
324    return checker->AllocNode<ir::ETSTypeReference>(typeRefPart);
325}
326
327std::pair<std::string_view, std::string_view> SplitRecordName(std::string_view recordName)
328{
329    std::string_view moduleName;
330    std::string_view newRecordName;
331
332    if (auto pos = recordName.find_last_of('.'); pos != std::string_view::npos) {
333        moduleName = recordName.substr(0, pos);
334        newRecordName = recordName.substr(pos + 1, recordName.size());
335    } else {
336        newRecordName = recordName;
337    }
338
339    return std::make_pair(moduleName, newRecordName);
340}
341
342ir::ClassProperty *CreateClassProperty(checker::ETSChecker *checker, std::string_view name, ir::TypeNode *type,
343                                       ir::ModifierFlags modifiers)
344{
345    ASSERT(type);
346
347    auto *fieldIdent = checker->AllocNode<ir::Identifier>(name, checker->Allocator());
348    auto *field =
349        checker->AllocNode<ir::ClassProperty>(fieldIdent, nullptr, type, modifiers, checker->Allocator(), false);
350
351    return field;
352}
353
354ir::ModifierFlags GetModifierFlags(panda_file::ClassDataAccessor &da)
355{
356    auto modifierFlags = ir::ModifierFlags::NONE;
357    auto accFlags = da.GetAccessFlags();
358    if ((accFlags & ACC_ABSTRACT) != 0) {
359        modifierFlags |= ir::ModifierFlags::ABSTRACT;
360    }
361    if ((accFlags & ACC_FINAL) != 0) {
362        modifierFlags |= ir::ModifierFlags::FINAL;
363    }
364    return modifierFlags;
365}
366
367}  // namespace ark::es2panda::evaluate::helpers
368