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/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
EndsWith(std::string_view str, std::string_view suffix)38 bool EndsWith(std::string_view str, std::string_view suffix)
39 {
40 auto pos = str.rfind(suffix);
41 return pos != std::string::npos && (str.size() - pos) == suffix.size();
42 }
43
44 } // namespace
45
ImportExportTable(ArenaAllocator *allocator)46 ImportExportTable::ImportExportTable(ArenaAllocator *allocator)
47 : imports_(allocator->Adapter()), exports_(allocator->Adapter())
48 {
49 }
50
DebugInfoStorage(const CompilerOptions &options, ArenaAllocator *allocator)51 DebugInfoStorage::DebugInfoStorage(const CompilerOptions &options, ArenaAllocator *allocator)
52 : allocator_(allocator), sourceFileToDebugInfo_(allocator->Adapter()), moduleNameToDebugInfo_(allocator->Adapter())
53 {
54 for (const auto &pfPath : options.debuggerEvalPandaFiles) {
55 LoadFileDebugInfo(pfPath);
56 }
57 }
58
LoadFileDebugInfo(std::string_view pfPath)59 void DebugInfoStorage::LoadFileDebugInfo(std::string_view pfPath)
60 {
61 auto pf = panda_file::OpenPandaFile(pfPath);
62 if (!pf) {
63 LOG(FATAL, ES2PANDA) << "Failed to load a provided abc file: " << pfPath;
64 }
65
66 for (auto id : pf->GetClasses()) {
67 panda_file::File::EntityId classId(id);
68 if (pf->IsExternal(classId)) {
69 continue;
70 }
71
72 auto recordName = GetFullRecordName(*pf, classId);
73 if (!EndsWith(recordName, compiler::Signatures::ETS_GLOBAL)) {
74 continue;
75 }
76
77 std::string_view moduleName = helpers::SplitRecordName(recordName).first;
78 auto *debugInfo = allocator_->New<FileDebugInfo>(std::move(pf), classId, moduleName);
79 auto sourceFileId = debugInfo->globalClassAcc.GetSourceFileId();
80 ASSERT(sourceFileId.has_value());
81 std::string_view sourceFileName = utf::Mutf8AsCString(debugInfo->pf->GetStringData(*sourceFileId).data);
82 debugInfo->sourceFilePath = sourceFileName;
83
84 sourceFileToDebugInfo_.emplace(sourceFileName, debugInfo);
85 moduleNameToDebugInfo_.emplace(moduleName, debugInfo);
86 return;
87 }
88
89 LOG(FATAL, ES2PANDA) << "ETSGLOBAL not found in provided file: " << pfPath;
90 }
91
GetPandaFile(std::string_view filePath)92 const panda_file::File *DebugInfoStorage::GetPandaFile(std::string_view filePath)
93 {
94 auto iter = sourceFileToDebugInfo_.find(filePath);
95 if (iter == sourceFileToDebugInfo_.end()) {
96 return nullptr;
97 }
98 return iter->second->pf.get();
99 }
100
GetImportExportTable(std::string_view filePath)101 const ImportExportTable *DebugInfoStorage::GetImportExportTable(std::string_view filePath)
102 {
103 auto iter = sourceFileToDebugInfo_.find(filePath);
104 if (iter == sourceFileToDebugInfo_.end()) {
105 return nullptr;
106 }
107 return &LazyLoadImportExportTable(iter->second);
108 }
109
GetGlobalClassAccessor(std::string_view filePath)110 panda_file::ClassDataAccessor *DebugInfoStorage::GetGlobalClassAccessor(std::string_view filePath)
111 {
112 auto iter = sourceFileToDebugInfo_.find(filePath);
113 if (iter == sourceFileToDebugInfo_.end()) {
114 return nullptr;
115 }
116 return &iter->second->globalClassAcc;
117 }
118
GetModuleName(std::string_view filePath)119 std::string_view DebugInfoStorage::GetModuleName(std::string_view filePath)
120 {
121 auto iter = sourceFileToDebugInfo_.find(filePath);
122 if (iter == sourceFileToDebugInfo_.end()) {
123 return {};
124 }
125 return iter->second->moduleName;
126 }
127
FindClass(std::string_view filePath, std::string_view className)128 panda_file::File::EntityId DebugInfoStorage::FindClass(std::string_view filePath, std::string_view className)
129 {
130 auto iter = sourceFileToDebugInfo_.find(filePath);
131 if (iter == sourceFileToDebugInfo_.end()) {
132 return panda_file::File::EntityId();
133 }
134
135 const auto &records = LazyLoadRecords(iter->second);
136
137 auto classIter = records.find(className);
138 return classIter == records.end() ? panda_file::File::EntityId() : classIter->second;
139 }
140
FillEvaluateContext(EvaluateContext &context)141 bool DebugInfoStorage::FillEvaluateContext(EvaluateContext &context)
142 {
143 const auto *contextPandaFile = GetPandaFile(context.sourceFilePath.Utf8());
144 if (contextPandaFile == nullptr) {
145 LOG(WARNING, ES2PANDA) << "Could not find context file: " << context.sourceFilePath << std::endl;
146 return false;
147 }
148
149 context.file = contextPandaFile;
150 context.extractor = std::make_unique<panda_file::DebugInfoExtractor>(contextPandaFile);
151
152 for (auto methodId : context.extractor->GetMethodIdList()) {
153 for (const auto &entry : context.extractor->GetLineNumberTable(methodId)) {
154 if (context.lineNumber == entry.line) {
155 context.methodId = methodId;
156 context.bytecodeOffset = entry.offset;
157 util::UString sourceFilePath(std::string_view(context.extractor->GetSourceFile(methodId)), allocator_);
158 context.sourceFilePath = sourceFilePath.View();
159 return true;
160 }
161 }
162 }
163 LOG(WARNING, ES2PANDA) << "Could not find code at line: " << context.lineNumber << std::endl;
164 return false;
165 }
166
LazyLoadImportExportTable(FileDebugInfo *info)167 const ImportExportTable &DebugInfoStorage::LazyLoadImportExportTable(FileDebugInfo *info)
168 {
169 ASSERT(info);
170
171 if (info->importExportTable.has_value()) {
172 return *info->importExportTable;
173 }
174
175 // NOTE: load table after it is implemented in compiler.
176 info->importExportTable.emplace(allocator_);
177 return info->importExportTable.value();
178 }
179
LazyLoadRecords(FileDebugInfo *info)180 const FileDebugInfo::RecordsMap &DebugInfoStorage::LazyLoadRecords(FileDebugInfo *info)
181 {
182 ASSERT(info);
183
184 if (info->records.has_value()) {
185 return *info->records;
186 }
187
188 info->records.emplace(allocator_->Adapter());
189 auto &records = *info->records;
190
191 const auto *pf = info->pf.get();
192 for (auto id : pf->GetClasses()) {
193 panda_file::File::EntityId classId(id);
194 if (pf->IsExternal(classId)) {
195 // Сlass that marked in currect .abc file as <external> should be define in some other .abc file.
196 // Thus we will not lose information about this class.
197 continue;
198 }
199
200 auto recordName = helpers::SplitRecordName(GetFullRecordName(*pf, classId)).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