1 /*
2 * Copyright (c) 2024-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 "evaluate/debugInfoStorage.h"
17 #include "assembler/assembly-type.h"
18 #include "generated/signatures.h"
19 #include "evaluate/helpers.h"
20
21 #include "libpandafile/class_data_accessor-inl.h"
22 #include "libpandafile/file-inl.h"
23
24 namespace ark::es2panda::evaluate {
25
26 namespace {
27
GetFullRecordName(const panda_file::File & pf,const panda_file::File::EntityId & classId)28 std::string GetFullRecordName(const panda_file::File &pf, const panda_file::File::EntityId &classId)
29 {
30 std::string name = utf::Mutf8AsCString(pf.GetStringData(classId).data);
31
32 auto type = pandasm::Type::FromDescriptor(name);
33 type = pandasm::Type(type.GetComponentName(), type.GetRank());
34
35 return type.GetPandasmName();
36 }
37
38 } // namespace
39
ImportExportTable(ArenaAllocator * allocator)40 ImportExportTable::ImportExportTable(ArenaAllocator *allocator)
41 : imports_(allocator->Adapter()), exports_(allocator->Adapter())
42 {
43 }
44
DebugInfoStorage(const util::Options & options,ArenaAllocator * allocator)45 DebugInfoStorage::DebugInfoStorage(const util::Options &options, ArenaAllocator *allocator)
46 : allocator_(allocator), sourceFileToDebugInfo_(allocator->Adapter()), moduleNameToDebugInfo_(allocator->Adapter())
47 {
48 for (const auto &pfPath : options.GetDebuggerEvalPandaFiles()) {
49 LoadFileDebugInfo(pfPath);
50 }
51 }
52
LoadFileDebugInfo(std::string_view pfPath)53 void DebugInfoStorage::LoadFileDebugInfo(std::string_view pfPath)
54 {
55 auto pf = panda_file::OpenPandaFile(pfPath);
56 if (!pf) {
57 LOG(FATAL, ES2PANDA) << "Failed to load a provided abc file: " << pfPath;
58 }
59
60 for (auto id : pf->GetClasses()) {
61 panda_file::File::EntityId classId(id);
62 if (pf->IsExternal(classId)) {
63 continue;
64 }
65
66 panda_file::ClassDataAccessor cda(*pf, classId);
67 bool isModule = false;
68 cda.EnumerateAnnotation(ANNOTATION_MODULE.data(), [&isModule](auto &) { return (isModule = true); });
69 if (!isModule) {
70 continue;
71 }
72 auto recordName = GetFullRecordName(*pf, classId);
73
74 std::string_view moduleName = helpers::SplitRecordName(recordName).first;
75 auto *debugInfo = allocator_->New<FileDebugInfo>(std::move(pf), classId, moduleName);
76 auto sourceFileId = debugInfo->globalClassAcc.GetSourceFileId();
77 ES2PANDA_ASSERT(sourceFileId.has_value());
78 std::string_view sourceFileName = utf::Mutf8AsCString(debugInfo->pf->GetStringData(*sourceFileId).data);
79 debugInfo->sourceFilePath = sourceFileName;
80
81 sourceFileToDebugInfo_.emplace(sourceFileName, debugInfo);
82 moduleNameToDebugInfo_.emplace(moduleName, debugInfo);
83 return;
84 }
85
86 LOG(FATAL, ES2PANDA) << "ETSGLOBAL not found in provided file: " << pfPath;
87 }
88
GetPandaFile(std::string_view filePath)89 const panda_file::File *DebugInfoStorage::GetPandaFile(std::string_view filePath)
90 {
91 auto iter = sourceFileToDebugInfo_.find(filePath);
92 if (iter == sourceFileToDebugInfo_.end()) {
93 return nullptr;
94 }
95 return iter->second->pf.get();
96 }
97
GetImportExportTable(std::string_view filePath)98 const ImportExportTable *DebugInfoStorage::GetImportExportTable(std::string_view filePath)
99 {
100 auto iter = sourceFileToDebugInfo_.find(filePath);
101 if (iter == sourceFileToDebugInfo_.end()) {
102 return nullptr;
103 }
104 return &LazyLoadImportExportTable(iter->second);
105 }
106
GetGlobalClassAccessor(std::string_view filePath)107 panda_file::ClassDataAccessor *DebugInfoStorage::GetGlobalClassAccessor(std::string_view filePath)
108 {
109 auto iter = sourceFileToDebugInfo_.find(filePath);
110 if (iter == sourceFileToDebugInfo_.end()) {
111 return nullptr;
112 }
113 return &iter->second->globalClassAcc;
114 }
115
GetModuleName(std::string_view filePath)116 std::string_view DebugInfoStorage::GetModuleName(std::string_view filePath)
117 {
118 auto iter = sourceFileToDebugInfo_.find(filePath);
119 if (iter == sourceFileToDebugInfo_.end()) {
120 return {};
121 }
122 return iter->second->moduleName;
123 }
124
FindClass(std::string_view filePath,std::string_view className)125 panda_file::File::EntityId DebugInfoStorage::FindClass(std::string_view filePath, std::string_view className)
126 {
127 auto iter = sourceFileToDebugInfo_.find(filePath);
128 if (iter == sourceFileToDebugInfo_.end()) {
129 return panda_file::File::EntityId();
130 }
131
132 const auto &records = LazyLoadRecords(iter->second);
133
134 auto classIter = records.find(className);
135 return classIter == records.end() ? panda_file::File::EntityId() : classIter->second;
136 }
137
FillEvaluateContext(EvaluateContext & context)138 bool DebugInfoStorage::FillEvaluateContext(EvaluateContext &context)
139 {
140 const auto *contextPandaFile = GetPandaFile(context.sourceFilePath.Utf8());
141 if (contextPandaFile == nullptr) {
142 LOG(WARNING, ES2PANDA) << "Could not find context file: " << context.sourceFilePath << std::endl;
143 return false;
144 }
145
146 context.file = contextPandaFile;
147 context.extractor = std::make_unique<panda_file::DebugInfoExtractor>(contextPandaFile);
148
149 for (auto methodId : context.extractor->GetMethodIdList()) {
150 for (const auto &entry : context.extractor->GetLineNumberTable(methodId)) {
151 if (context.lineNumber == entry.line) {
152 context.methodId = methodId;
153 context.bytecodeOffset = entry.offset;
154 util::UString sourceFilePath(std::string_view(context.extractor->GetSourceFile(methodId)), allocator_);
155 context.sourceFilePath = sourceFilePath.View();
156 return true;
157 }
158 }
159 }
160 LOG(WARNING, ES2PANDA) << "Could not find code at line: " << context.lineNumber << std::endl;
161 return false;
162 }
163
LazyLoadImportExportTable(FileDebugInfo * info)164 const ImportExportTable &DebugInfoStorage::LazyLoadImportExportTable(FileDebugInfo *info)
165 {
166 ES2PANDA_ASSERT(info);
167
168 if (info->importExportTable.has_value()) {
169 return *info->importExportTable;
170 }
171
172 // NOTE: load table after it is implemented in compiler.
173 info->importExportTable.emplace(allocator_);
174 return info->importExportTable.value();
175 }
176
LazyLoadRecords(FileDebugInfo * info)177 const FileDebugInfo::RecordsMap &DebugInfoStorage::LazyLoadRecords(FileDebugInfo *info)
178 {
179 ES2PANDA_ASSERT(info);
180
181 if (info->records.has_value()) {
182 return *info->records;
183 }
184
185 info->records.emplace(allocator_->Adapter());
186 auto &records = *info->records;
187
188 const auto *pf = info->pf.get();
189 for (auto id : pf->GetClasses()) {
190 panda_file::File::EntityId classId(id);
191 if (pf->IsExternal(classId)) {
192 // Сlass that marked in currect .abc file as <external> should be define in some other .abc file.
193 // Thus we will not lose information about this class.
194 continue;
195 }
196
197 std::string fullRecordName = GetFullRecordName(*pf, classId);
198 // Be aware of lifecycle of string and string_view
199 auto splitedPair = helpers::SplitRecordName(fullRecordName);
200 auto recordName = splitedPair.second;
201 auto recordNameView = util::UString(recordName, allocator_).View();
202 if (!records.emplace(recordNameView, classId).second) {
203 LOG(FATAL, ES2PANDA) << "Failed to emplace class '" << recordNameView << "' in records."
204 << "There should be only one declaration of the same class.";
205 }
206 }
207
208 return records;
209 }
210
GetDebugInfoByModuleName(std::string_view moduleName) const211 FileDebugInfo *DebugInfoStorage::GetDebugInfoByModuleName(std::string_view moduleName) const
212 {
213 auto find = moduleNameToDebugInfo_.find(moduleName);
214 if (find != moduleNameToDebugInfo_.end()) {
215 return find->second;
216 }
217 return nullptr;
218 }
219
220 } // namespace ark::es2panda::evaluate
221