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 "checker/ETSchecker.h"
17#include "evaluate/helpers.h"
18
19#include "evaluate/debugInfoDeserialization/debugInfoDeserializer.h"
20#include "evaluate/scopedDebugInfoPlugin.h"
21#include "evaluate/debugInfoStorage.h"
22#include "evaluate/proxyProgramsCache.h"
23#include "evaluate/entityDeclarator-inl.h"
24
25#include "libpandafile/class_data_accessor.h"
26#include "libpandafile/file-inl.h"
27
28namespace ark::es2panda::evaluate {
29
30namespace {
31
32constexpr std::string_view OBJECT_NAME = "std.core.Object";
33
34std::string GetFullSuperClassName(panda_file::ClassDataAccessor &cda)
35{
36    return panda_file::ClassDataAccessor::DemangledName(cda.GetPandaFile().GetStringData(cda.GetSuperClassId()));
37}
38
39std::optional<std::pair<util::StringView, FileDebugInfo *>> GetSuperClassModuleAndClassName(
40    panda_file::ClassDataAccessor &cda, DebugInfoStorage *debugInfoStorage)
41{
42    ASSERT(debugInfoStorage);
43
44    auto fullSuperClassName = GetFullSuperClassName(cda);
45    if (fullSuperClassName == OBJECT_NAME) {
46        // Must stop iterating upon reaching std.core.Object.
47        return {};
48    }
49
50    auto [moduleName, recordName] = helpers::SplitRecordName(fullSuperClassName);
51    auto *debugInfo = debugInfoStorage->GetDebugInfoByModuleName(moduleName);
52    if (UNLIKELY(debugInfo == nullptr)) {
53        LOG(FATAL, ES2PANDA) << "Failed to find debug info for module '" << moduleName << "'";
54    }
55    // Allocate record name for later usage in compilation.
56    return std::make_pair(util::UString(recordName, debugInfoStorage->Allocator()).View(), debugInfo);
57}
58
59}  // namespace
60
61struct ChainEntryInfo final {
62    explicit ChainEntryInfo(std::string_view filePath, std::string_view declName,
63                            panda_file::ClassDataAccessor *accessor, parser::Program *prog)
64        : sourceFilePath(filePath), entityDeclarationName(declName), cda(accessor), program(prog)
65    {
66        ASSERT(cda != nullptr);
67    }
68
69    DEFAULT_MOVE_SEMANTIC(ChainEntryInfo);
70    DEFAULT_COPY_SEMANTIC(ChainEntryInfo);
71
72    ~ChainEntryInfo() = default;
73
74    // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
75    std::string_view sourceFilePath;
76    std::string_view entityDeclarationName;
77    // Pointer is required for move semantics.
78    panda_file::ClassDataAccessor *cda {nullptr};
79    parser::Program *program {nullptr};
80    // NOLINTEND(misc-non-private-member-variables-in-classes)
81};
82
83ir::ETSTypeReference *DebugInfoDeserializer::GetSuperClass(panda_file::ClassDataAccessor &cda)
84{
85    auto optClassInfo = GetSuperClassModuleAndClassName(cda, debugInfoPlugin_.GetDebugInfoStorage());
86    if (!optClassInfo) {
87        return nullptr;
88    }
89    auto [superClassName, debugInfo] = *optClassInfo;
90    return ResolveInheritanceChain(superClassName, debugInfo);
91}
92
93ir::ETSTypeReference *DebugInfoDeserializer::ResolveInheritanceChain(util::StringView abcSuperName,
94                                                                     FileDebugInfo *debugInfo)
95{
96    ASSERT(debugInfo);
97
98    auto *program = debugInfoPlugin_.GetProxyProgramsCache()->GetProgram(debugInfo->sourceFilePath);
99    if (debugInfoPlugin_.GetEntityDeclarator()->IsEntityDeclared(program, abcSuperName)) {
100        // If a superclass has already been declared, then we just need to create the TypeReference of it.
101        // Note that a TypeReference is created for each of usage of the super class.
102        return helpers::CreateETSTypeReference(debugInfoPlugin_.GetIrCheckHelper()->GetChecker(), abcSuperName);
103    }
104    return ResolveInheritanceChainImpl(abcSuperName, debugInfo);
105}
106
107ir::ETSTypeReference *DebugInfoDeserializer::ResolveInheritanceChainImpl(util::StringView abcSuperName,
108                                                                         FileDebugInfo *debugInfo)
109{
110    auto *checker = debugInfoPlugin_.GetIrCheckHelper()->GetChecker();
111
112    // List starting from the most derived to the base class.
113    ArenaVector<ChainEntryInfo> chainEntryList(checker->Allocator()->Adapter());
114    auto alreadyCreatedSuperClassName = CollectChainInfo(chainEntryList, abcSuperName, debugInfo);
115
116    ir::ETSTypeReference *superClass = nullptr;
117    if (!alreadyCreatedSuperClassName.Empty()) {
118        superClass = helpers::CreateETSTypeReference(checker, util::StringView(alreadyCreatedSuperClassName));
119    }
120
121    auto *entityDeclarator = debugInfoPlugin_.GetEntityDeclarator();
122    for (auto it = chainEntryList.rbegin(); it != chainEntryList.rend(); ++it) {
123        // Create entity in the same file in that it destinded to be declared, so import declaration should not be
124        // inserted in AST.
125        std::string_view declarationName = it->entityDeclarationName;
126        auto *cda = it->cda;
127        ASSERT(cda != nullptr);
128        // clang-format off
129        entityDeclarator->ImportGlobalEntity(it->sourceFilePath, declarationName, it->program, declarationName,
130            [this, superClass, cda](auto, auto *program, auto, auto name) {
131                auto *classDecl = CreateClassDeclaration(name, *cda, superClass, program);
132                return classDecl->Definition()->Ident()->Variable();
133            });
134        // clang-format on
135        superClass = helpers::CreateETSTypeReference(checker, declarationName);
136    }
137
138    return superClass;
139}
140
141util::StringView DebugInfoDeserializer::CollectChainInfo(ArenaVector<ChainEntryInfo> &chainEntryList,
142                                                         util::StringView abcSuperName, FileDebugInfo *debugInfo)
143{
144    ASSERT(debugInfo != nullptr);
145
146    auto *proxyProgramsCache = debugInfoPlugin_.GetProxyProgramsCache();
147    auto *debugInfoStorage = debugInfoPlugin_.GetDebugInfoStorage();
148    auto *entityDeclarator = debugInfoPlugin_.GetEntityDeclarator();
149    auto *allocator = debugInfoPlugin_.GetIrCheckHelper()->GetChecker()->Allocator();
150
151    // CC-OFFNXT(G.CTL.03) false positive
152    while (true) {
153        auto *program = proxyProgramsCache->GetProgram(debugInfo->sourceFilePath);
154        ASSERT(program != nullptr);
155        if (entityDeclarator->IsEntityDeclared(program, abcSuperName)) {
156            // Go until reach A_i, which has already been declared.
157            // Object <-- A_1 <-- ... <-- A_i <-- ... <-- A_n
158            //    |                        |
159            //    +------------------------+
160            //                |
161            //             Declared
162            return abcSuperName;
163        }
164
165        // Class must be created - save necessary information about it.
166        auto classId = debugInfoStorage->FindClass(debugInfo->sourceFilePath, abcSuperName.Utf8());
167        if (!classId.IsValid()) {
168            LOG(FATAL, ES2PANDA) << "Can't find classId for " << abcSuperName;
169        }
170        auto *cda = allocator->New<panda_file::ClassDataAccessor>(*debugInfo->pf, classId);
171        chainEntryList.emplace_back(debugInfo->sourceFilePath, abcSuperName.Utf8(), cda, program);
172
173        // Update information for the next iteration.
174        auto optClassInfo = GetSuperClassModuleAndClassName(*cda, debugInfoStorage);
175        if (!optClassInfo) {
176            // Go until reach Object:
177            // std.core.Object <-- A1 <-- A2 <-- ... <-- An
178            return "";
179        }
180        std::tie(abcSuperName, debugInfo) = *optClassInfo;
181    }
182}
183
184}  // namespace ark::es2panda::evaluate
185