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::unordered_set<std::string> & debugRecordName)95 bool MatchWithLocation(const Callback &cb, int32_t line, int32_t column, 96 const std::string &url, const std::unordered_set<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 // Check record name in stage mode 113 if (!jsPandaFile_->IsBundlePack()) { 114 // the recordName for testcases is empty 115 if (!debugRecordName.empty()) { 116 auto iter = debugRecordName.find(std::string(recordName.c_str())); 117 if (iter == debugRecordName.end()) { 118 continue; 119 } 120 } 121 } 122 cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) { 123 methodIds.push_back(mda.GetMethodId()); 124 }); 125 126 int32_t minColumn = INT32_MAX; 127 uint32_t currentOffset = UINT32_MAX; 128 uint32_t minColumnOffset = UINT32_MAX; 129 EntityId currentMethodId; 130 EntityId minColumnMethodId; 131 for (auto &methodId : methodIds) { 132 const std::string &sourceFile = GetSourceFile(methodId); 133 // the url for testcases is empty 134 if (!url.empty() && sourceFile != url) { 135 continue; 136 } 137 const LineNumberTable &lineTable = GetLineNumberTable(methodId); 138 const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId); 139 for (uint32_t j = 0; j < lineTable.size(); j++) { 140 if (lineTable[j].line != line) { 141 continue; 142 } 143 currentMethodId = methodId; 144 currentOffset = lineTable[j].offset; 145 uint32_t nextOffset = ((j == lineTable.size() - 1) ? UINT32_MAX : lineTable[j + 1].offset); 146 for (const auto &pair : columnTable) { 147 if (pair.offset >= currentOffset && pair.offset < nextOffset) { 148 if (pair.column == column) { 149 return cb(JSPtLocation(jsPandaFile_, methodId, pair.offset, url)); 150 } else if (pair.column < minColumn) { 151 minColumn = pair.column; 152 minColumnOffset = currentOffset; 153 minColumnMethodId = currentMethodId; 154 } 155 } 156 } 157 } 158 } 159 if (minColumn != INT32_MAX) { // find the smallest column for the corresponding row 160 return cb(JSPtLocation(jsPandaFile_, minColumnMethodId, minColumnOffset, url)); 161 } 162 if (currentOffset != UINT32_MAX) { // find corresponding row, but not find corresponding column 163 return cb(JSPtLocation(jsPandaFile_, currentMethodId, currentOffset, url)); 164 } 165 } 166 return false; 167 } 168 169 template<class Callback> MatchLineWithOffset(const Callback & cb,panda_file::File::EntityId methodId,uint32_t offset)170 bool MatchLineWithOffset(const Callback &cb, panda_file::File::EntityId methodId, uint32_t offset) 171 { 172 int32_t line = 0; 173 const LineNumberTable &lineTable = GetLineNumberTable(methodId); 174 auto iter = std::upper_bound(lineTable.begin(), lineTable.end(), LineTableEntry {offset, 0}); 175 if (iter != lineTable.begin()) { 176 line = (iter - 1)->line; 177 } 178 return cb(line); 179 } 180 181 template<class Callback> MatchColumnWithOffset(const Callback & cb,panda_file::File::EntityId methodId,uint32_t offset)182 bool MatchColumnWithOffset(const Callback &cb, panda_file::File::EntityId methodId, uint32_t offset) 183 { 184 int32_t column = 0; 185 const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId); 186 auto iter = std::upper_bound(columnTable.begin(), columnTable.end(), ColumnTableEntry {offset, 0}); 187 if (iter != columnTable.begin()) { 188 column = (iter - 1)->column; 189 } 190 return cb(column); 191 } 192 GetFristLine(panda_file::File::EntityId methodId)193 int32_t GetFristLine(panda_file::File::EntityId methodId) 194 { 195 const LineNumberTable &lineTable = GetLineNumberTable(methodId); 196 auto tableSize = lineTable.size(); 197 if (tableSize == 0) { 198 return 0; 199 } 200 if (tableSize == 1) { 201 return lineTable[0].line + 1; 202 } 203 int firstLineIndex = ((lineTable[0].line == SPECIAL_LINE_MARK) ? 1 : 0); 204 return lineTable[firstLineIndex].line + 1; 205 } 206 GetFristColumn(panda_file::File::EntityId methodId)207 int32_t GetFristColumn(panda_file::File::EntityId methodId) 208 { 209 const ColumnNumberTable &columnTable = GetColumnNumberTable(methodId); 210 auto tableSize = columnTable.size(); 211 if (tableSize == 0) { 212 return 0; 213 } 214 if (tableSize == 1) { 215 return columnTable[0].column + 1; 216 } 217 int firstColumnIndex = ((columnTable[0].column == SPECIAL_LINE_MARK) ? 1 : 0); 218 return columnTable[firstColumnIndex].column + 1; 219 } 220 221 void Extract(); 222 223 constexpr static int32_t SPECIAL_LINE_MARK = -1; 224 225 private: 226 bool ExtractorMethodDebugInfo(const panda_file::File::EntityId methodId); 227 void ExtractorMethodDebugInfo(const panda_file::File &pandaFile, 228 const std::optional<panda_file::File::EntityId> sourceFileId, 229 const std::optional<panda_file::File::EntityId> debugInfoId, 230 uint32_t offset); 231 struct MethodDebugInfo { 232 std::string sourceFile; 233 std::string sourceCode; 234 LineNumberTable lineNumberTable; 235 ColumnNumberTable columnNumberTable; 236 LocalVariableTable localVariableTable; 237 }; 238 239 std::recursive_mutex mutex_; 240 CUnorderedMap<uint32_t, MethodDebugInfo> methods_; 241 const JSPandaFile *jsPandaFile_ {nullptr}; 242 }; 243 } // namespace panda::ecmascript 244 245 #endif // ECMASCRIPT_JSPANDAFILE_DEBUG_INFO_EXTRACTOR_H 246