1 /* 2 * Copyright (c) 2021 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 #ifndef ECMASCRIPT_JSPANDAFILE_DEBUG_INFO_EXTRACTOR_H 17 #define ECMASCRIPT_JSPANDAFILE_DEBUG_INFO_EXTRACTOR_H 18 19 #include <mutex> 20 21 #include "ecmascript/common.h" 22 #include "ecmascript/debugger/js_pt_location.h" 23 #include "ecmascript/jspandafile/js_pandafile.h" 24 #include "ecmascript/mem/c_containers.h" 25 #include "ecmascript/mem/c_string.h" 26 27 #include "libpandafile/class_data_accessor-inl.h" 28 #include "libpandafile/file.h" 29 #include "libpandabase/utils/utf.h" 30 31 namespace panda::ecmascript { 32 class JSPandaFile; 33 34 struct LineTableEntry { 35 uint32_t offset; 36 int32_t line; 37 38 bool operator<(const LineTableEntry &other) const 39 { 40 return offset < other.offset; 41 } 42 }; 43 44 struct ColumnTableEntry { 45 uint32_t offset; 46 int32_t column; 47 48 bool operator<(const ColumnTableEntry &other) const 49 { 50 return offset < other.offset; 51 } 52 }; 53 54 using LineNumberTable = CVector<LineTableEntry>; 55 using ColumnNumberTable = CVector<ColumnTableEntry>; 56 using JSPtLocation = tooling::JSPtLocation; 57 58 /* 59 * Full version of LocalVariableInfo is defined in frontend, 60 * here only using name, reg_number, start_offset, and end_offset: 61 * std::string name 62 * std::string type 63 * std::string typeSignature 64 * int32_t regNumber 65 * uint32_t startOffset 66 * uint32_t endOffset 67 */ 68 struct LocalVariableInfo { 69 std::string name; 70 int32_t regNumber; 71 uint32_t startOffset; 72 uint32_t endOffset; 73 }; 74 using LocalVariableTable = CVector<LocalVariableInfo>; 75 76 // public for debugger 77 class PUBLIC_API DebugInfoExtractor { 78 public: DebugInfoExtractor(const JSPandaFile * jsPandaFile)79 explicit DebugInfoExtractor(const JSPandaFile *jsPandaFile) : jsPandaFile_(jsPandaFile) 80 {} 81 82 ~DebugInfoExtractor() = default; 83 84 const LineNumberTable &GetLineNumberTable(const panda_file::File::EntityId methodId); 85 86 const ColumnNumberTable &GetColumnNumberTable(const panda_file::File::EntityId methodId); 87 88 const LocalVariableTable &GetLocalVariableTable(const panda_file::File::EntityId methodId); 89 90 const std::string &GetSourceFile(const panda_file::File::EntityId methodId); 91 92 const std::string &GetSourceCode(const panda_file::File::EntityId methodId); 93 94 template<class Callback> MatchWithLocation(const Callback & cb,int32_t line,int32_t column,const std::string & url,const std::string & debugRecordName)95 bool MatchWithLocation(const Callback &cb, int32_t line, int32_t column, 96 const std::string &url, const std::string &debugRecordName) 97 { 98 if (line == SPECIAL_LINE_MARK) { 99 return false; 100 } 101 auto &pandaFile = *jsPandaFile_->GetPandaFile(); 102 auto classes = jsPandaFile_->GetClasses(); 103 for (size_t i = 0; i < classes.Size(); i++) { 104 panda_file::File::EntityId id(classes[i]); 105 if (jsPandaFile_->IsExternal(id)) { 106 continue; 107 } 108 109 CVector<panda_file::File::EntityId> methodIds; 110 panda_file::ClassDataAccessor cda(pandaFile, id); 111 CString recordName = JSPandaFile::ParseEntryPoint(utf::Mutf8AsCString(cda.GetDescriptor())); 112 // the recordName for testcases is empty 113 if (!debugRecordName.empty() && recordName != debugRecordName.c_str() 114 && debugRecordName != JSPandaFile::ENTRY_MAIN_FUNCTION) { 115 continue; 116 } 117 cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) { 118 methodIds.push_back(mda.GetMethodId()); 119 }); 120 121 int32_t minColumn = INT32_MAX; 122 uint32_t currentOffset = UINT32_MAX; 123 uint32_t minColumnOffset = UINT32_MAX; 124 EntityId currentMethodId; 125 EntityId minColumnMethodId; 126 for (auto &methodId : methodIds) { 127 const std::string &sourceFile = GetSourceFile(methodId); 128 // the url for testcases is empty 129 if (!url.empty() && sourceFile != url) { 130 continue; 131 } 132 const LineNumberTable &lineTable = GetLineNumberTable(methodId); 133 const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId); 134 for (uint32_t j = 0; j < lineTable.size(); j++) { 135 if (lineTable[j].line != line) { 136 continue; 137 } 138 currentMethodId = methodId; 139 currentOffset = lineTable[j].offset; 140 uint32_t nextOffset = ((j == lineTable.size() - 1) ? UINT32_MAX : lineTable[j + 1].offset); 141 for (const auto &pair : columnTable) { 142 if (pair.offset >= currentOffset && pair.offset < nextOffset) { 143 if (pair.column == column) { 144 return cb(JSPtLocation(jsPandaFile_, methodId, pair.offset, url)); 145 } else if (pair.column < minColumn) { 146 minColumn = pair.column; 147 minColumnOffset = currentOffset; 148 minColumnMethodId = currentMethodId; 149 } 150 } 151 } 152 } 153 } 154 if (minColumn != INT32_MAX) { // find the smallest column for the corresponding row 155 return cb(JSPtLocation(jsPandaFile_, minColumnMethodId, minColumnOffset, url)); 156 } 157 if (currentOffset != UINT32_MAX) { // find corresponding row, but not find corresponding column 158 return cb(JSPtLocation(jsPandaFile_, currentMethodId, currentOffset, url)); 159 } 160 } 161 return false; 162 } 163 164 template<class Callback> MatchLineWithOffset(const Callback & cb,panda_file::File::EntityId methodId,uint32_t offset)165 bool MatchLineWithOffset(const Callback &cb, panda_file::File::EntityId methodId, uint32_t offset) 166 { 167 int32_t line = 0; 168 const LineNumberTable &lineTable = GetLineNumberTable(methodId); 169 auto iter = std::upper_bound(lineTable.begin(), lineTable.end(), LineTableEntry {offset, 0}); 170 if (iter != lineTable.begin()) { 171 line = (iter - 1)->line; 172 } 173 return cb(line); 174 } 175 176 template<class Callback> MatchColumnWithOffset(const Callback & cb,panda_file::File::EntityId methodId,uint32_t offset)177 bool MatchColumnWithOffset(const Callback &cb, panda_file::File::EntityId methodId, uint32_t offset) 178 { 179 int32_t column = 0; 180 const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId); 181 auto iter = std::upper_bound(columnTable.begin(), columnTable.end(), ColumnTableEntry {offset, 0}); 182 if (iter != columnTable.begin()) { 183 column = (iter - 1)->column; 184 } 185 return cb(column); 186 } 187 GetFristLine(panda_file::File::EntityId methodId)188 int32_t GetFristLine(panda_file::File::EntityId methodId) 189 { 190 const LineNumberTable &lineTable = GetLineNumberTable(methodId); 191 auto tableSize = lineTable.size(); 192 if (tableSize == 0) { 193 return 0; 194 } 195 if (tableSize == 1) { 196 return lineTable[0].line + 1; 197 } 198 int firstLineIndex = ((lineTable[0].line == SPECIAL_LINE_MARK) ? 1 : 0); 199 return lineTable[firstLineIndex].line + 1; 200 } 201 GetFristColumn(panda_file::File::EntityId methodId)202 int32_t GetFristColumn(panda_file::File::EntityId methodId) 203 { 204 const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId); 205 auto tableSize = columnTable.size(); 206 if (tableSize == 0) { 207 return 0; 208 } 209 if (tableSize == 1) { 210 return columnTable[0].column + 1; 211 } 212 int firstColumnIndex = ((columnTable[0].column == SPECIAL_LINE_MARK) ? 1 : 0); 213 return columnTable[firstColumnIndex].column + 1; 214 } 215 216 void Extract(); 217 218 constexpr static int32_t SPECIAL_LINE_MARK = -1; 219 220 private: 221 bool ExtractorMethodDebugInfo(const panda_file::File::EntityId methodId); 222 void ExtractorMethodDebugInfo(const panda_file::File &pandaFile, 223 const std::optional<panda_file::File::EntityId> sourceFileId, 224 const std::optional<panda_file::File::EntityId> debugInfoId, 225 uint32_t offset); 226 struct MethodDebugInfo { 227 std::string sourceFile; 228 std::string sourceCode; 229 LineNumberTable lineNumberTable; 230 ColumnNumberTable columnNumberTable; 231 LocalVariableTable localVariableTable; 232 }; 233 234 std::recursive_mutex mutex_; 235 CUnorderedMap<uint32_t, MethodDebugInfo> methods_; 236 const JSPandaFile *jsPandaFile_ {nullptr}; 237 }; 238 } // namespace panda::ecmascript 239 240 #endif // ECMASCRIPT_JSPANDAFILE_DEBUG_INFO_EXTRACTOR_H 241