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