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 24namespace ark::es2panda::evaluate { 25 26namespace { 27 28std::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 38bool 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 46ImportExportTable::ImportExportTable(ArenaAllocator *allocator) 47 : imports_(allocator->Adapter()), exports_(allocator->Adapter()) 48{ 49} 50 51DebugInfoStorage::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 59void 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 92const 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 101const 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 110panda_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 119std::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 128panda_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 141bool 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 167const 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 180const 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 211FileDebugInfo *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