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
28 namespace ark::es2panda::evaluate {
29
30 namespace {
31
32 constexpr std::string_view OBJECT_NAME = "std.core.Object";
33
GetFullSuperClassName(panda_file::ClassDataAccessor & cda)34 std::string GetFullSuperClassName(panda_file::ClassDataAccessor &cda)
35 {
36 return panda_file::ClassDataAccessor::DemangledName(cda.GetPandaFile().GetStringData(cda.GetSuperClassId()));
37 }
38
GetSuperClassModuleAndClassName(panda_file::ClassDataAccessor & cda,DebugInfoStorage * debugInfoStorage)39 std::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
61 struct ChainEntryInfo final {
ChainEntryInfoark::es2panda::evaluate::ChainEntryInfo62 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
GetSuperClass(panda_file::ClassDataAccessor & cda)83 ir::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
ResolveInheritanceChain(util::StringView abcSuperName,FileDebugInfo * debugInfo)93 ir::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
ResolveInheritanceChainImpl(util::StringView abcSuperName,FileDebugInfo * debugInfo)107 ir::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
CollectChainInfo(ArenaVector<ChainEntryInfo> & chainEntryList,util::StringView abcSuperName,FileDebugInfo * debugInfo)141 util::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