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