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 #include "ecmascript/jspandafile/debug_info_extractor.h"
17
18 #include "ecmascript/jspandafile/js_pandafile.h"
19
20 #include "libpandabase/utils/utf.h"
21 #include "libpandafile/class_data_accessor-inl.h"
22 #include "libpandafile/debug_data_accessor-inl.h"
23 #include "libpandafile/line_number_program.h"
24
25 namespace panda::ecmascript {
26 using panda::panda_file::ClassDataAccessor;
27 using panda::panda_file::DebugInfoDataAccessor;
28 using panda::panda_file::LineNumberProgramItem;
29 using panda::panda_file::LineProgramState;
30 using panda::panda_file::LineNumberProgramProcessor;
31 using panda::panda_file::MethodDataAccessor;
32 using panda::panda_file::ProtoDataAccessor;
33
GetStringFromConstantPool(const panda_file::File & pf,uint32_t offset)34 static const char *GetStringFromConstantPool(const panda_file::File &pf, uint32_t offset)
35 {
36 return reinterpret_cast<const char *>(pf.GetStringData(panda_file::File::EntityId(offset)).data);
37 }
38
DebugInfoExtractor(const JSPandaFile * jsPandaFile)39 DebugInfoExtractor::DebugInfoExtractor(const JSPandaFile *jsPandaFile)
40 {
41 jsPandaFile_ = jsPandaFile;
42 Extract();
43 }
44
45 class LineNumberProgramHandler {
46 public:
LineNumberProgramHandler(LineProgramState * state)47 explicit LineNumberProgramHandler(LineProgramState *state) : state_(state) {}
48 ~LineNumberProgramHandler() = default;
49
50 NO_COPY_SEMANTIC(LineNumberProgramHandler);
51 NO_MOVE_SEMANTIC(LineNumberProgramHandler);
52
GetState() const53 LineProgramState *GetState() const
54 {
55 return state_;
56 }
57
ProcessBegin()58 void ProcessBegin()
59 {
60 lnt_.push_back({state_->GetAddress(), static_cast<int32_t>(state_->GetLine())});
61 }
62
ProcessEnd()63 void ProcessEnd()
64 {
65 }
66
HandleAdvanceLine(int32_t lineDiff) const67 bool HandleAdvanceLine(int32_t lineDiff) const
68 {
69 state_->AdvanceLine(lineDiff);
70 return true;
71 }
72
HandleAdvancePc(uint32_t pcDiff) const73 bool HandleAdvancePc(uint32_t pcDiff) const
74 {
75 state_->AdvancePc(pcDiff);
76 return true;
77 }
78
HandleSetFile(uint32_t sourceFileId) const79 bool HandleSetFile(uint32_t sourceFileId) const
80 {
81 state_->SetFile(sourceFileId);
82 return true;
83 }
84
HandleSetSourceCode(uint32_t sourceCodeId) const85 bool HandleSetSourceCode(uint32_t sourceCodeId) const
86 {
87 state_->SetSourceCode(sourceCodeId);
88 return true;
89 }
90
HandleSetPrologueEnd() const91 bool HandleSetPrologueEnd() const
92 {
93 return true;
94 }
95
HandleSetEpilogueBegin() const96 bool HandleSetEpilogueBegin() const
97 {
98 return true;
99 }
100
HandleStartLocal(int32_t regNumber,uint32_t nameId,uint32_t typeId)101 bool HandleStartLocal(int32_t regNumber, uint32_t nameId, [[maybe_unused]] uint32_t typeId)
102 {
103 const char *name = GetStringFromConstantPool(state_->GetPandaFile(), nameId);
104 lvt_.emplace(name, regNumber);
105 return true;
106 }
107
HandleStartLocalExtended(int32_t regNumber,uint32_t nameId,uint32_t typeId,uint32_t typeSignatureId)108 bool HandleStartLocalExtended(int32_t regNumber, uint32_t nameId, [[maybe_unused]] uint32_t typeId,
109 [[maybe_unused]] uint32_t typeSignatureId)
110 {
111 const char *name = GetStringFromConstantPool(state_->GetPandaFile(), nameId);
112 lvt_.emplace(name, regNumber);
113 return true;
114 }
115
HandleEndLocal(int32_t regNumber)116 bool HandleEndLocal([[maybe_unused]] int32_t regNumber)
117 {
118 return true;
119 }
120
HandleSetColumn(int32_t columnNumber)121 bool HandleSetColumn(int32_t columnNumber)
122 {
123 state_->SetColumn(columnNumber);
124 cnt_.push_back({state_->GetAddress(), static_cast<int32_t>(state_->GetColumn())});
125 return true;
126 }
127
HandleSpecialOpcode(uint32_t pcOffset,int32_t lineOffset)128 bool HandleSpecialOpcode(uint32_t pcOffset, int32_t lineOffset)
129 {
130 state_->AdvancePc(pcOffset);
131 state_->AdvanceLine(lineOffset);
132 lnt_.push_back({state_->GetAddress(), static_cast<int32_t>(state_->GetLine())});
133 return true;
134 }
135
GetLineNumberTable() const136 LineNumberTable GetLineNumberTable() const
137 {
138 return lnt_;
139 }
140
GetLocalVariableTable() const141 LocalVariableTable GetLocalVariableTable() const
142 {
143 return lvt_;
144 }
145
GetColumnNumberTable() const146 ColumnNumberTable GetColumnNumberTable() const
147 {
148 return cnt_;
149 }
150
GetFile() const151 const uint8_t *GetFile() const
152 {
153 return state_->GetFile();
154 }
155
GetSourceCode() const156 const uint8_t *GetSourceCode() const
157 {
158 return state_->GetSourceCode();
159 }
160
161 private:
162 using Opcode = LineNumberProgramItem::Opcode;
163
164 LineProgramState *state_;
165 LineNumberTable lnt_;
166 LocalVariableTable lvt_;
167 ColumnNumberTable cnt_;
168 };
169
Extract()170 void DebugInfoExtractor::Extract()
171 {
172 const panda_file::File *pf = jsPandaFile_->GetPandaFile();
173 ASSERT(pf != nullptr);
174 const auto &pandaFile = *pf;
175 auto classes = pf->GetClasses();
176 for (size_t i = 0; i < classes.Size(); i++) {
177 panda_file::File::EntityId id(classes[i]);
178 if (pandaFile.IsExternal(id)) {
179 continue;
180 }
181
182 ClassDataAccessor cda(pandaFile, id);
183
184 auto sourceFileId = cda.GetSourceFileId();
185
186 cda.EnumerateMethods([&](MethodDataAccessor &mda) {
187 auto debugInfoId = mda.GetDebugInfoId();
188 if (!debugInfoId) {
189 return;
190 }
191
192 DebugInfoDataAccessor dda(pandaFile, debugInfoId.value());
193 const uint8_t *program = dda.GetLineNumberProgram();
194 LineProgramState state(pandaFile, sourceFileId.value_or(panda_file::File::EntityId(0)), dda.GetLineStart(),
195 dda.GetConstantPool());
196
197 LineNumberProgramHandler handler(&state);
198 LineNumberProgramProcessor<LineNumberProgramHandler> programProcessor(program, &handler);
199 programProcessor.Process();
200
201 const char *sourceFile = "";
202 if (state.HasFile()) {
203 sourceFile = reinterpret_cast<const char *>(handler.GetFile());
204 }
205 const char *sourceCode = "";
206 if (state.HasSourceCode()) {
207 sourceCode = reinterpret_cast<const char *>(handler.GetSourceCode());
208 }
209 panda_file::File::EntityId methodId = mda.GetMethodId();
210 methods_.emplace(methodId.GetOffset(), MethodDebugInfo {sourceFile, sourceCode,
211 handler.GetLineNumberTable(),
212 handler.GetColumnNumberTable(),
213 handler.GetLocalVariableTable()});
214 });
215 }
216 }
217
GetLineNumberTable(panda_file::File::EntityId methodId) const218 const LineNumberTable &DebugInfoExtractor::GetLineNumberTable(panda_file::File::EntityId methodId) const
219 {
220 static const LineNumberTable EMPTY_LINE_TABLE {};
221
222 auto iter = methods_.find(methodId.GetOffset());
223 if (iter == methods_.end()) {
224 return EMPTY_LINE_TABLE;
225 }
226 return iter->second.lineNumberTable;
227 }
228
GetColumnNumberTable(panda_file::File::EntityId methodId) const229 const ColumnNumberTable &DebugInfoExtractor::GetColumnNumberTable(panda_file::File::EntityId methodId) const
230 {
231 static const ColumnNumberTable EMPTY_COLUMN_TABLE {};
232
233 auto iter = methods_.find(methodId.GetOffset());
234 if (iter == methods_.end()) {
235 return EMPTY_COLUMN_TABLE;
236 }
237 return iter->second.columnNumberTable;
238 }
239
GetLocalVariableTable(panda_file::File::EntityId methodId) const240 const LocalVariableTable &DebugInfoExtractor::GetLocalVariableTable(panda_file::File::EntityId methodId) const
241 {
242 static const LocalVariableTable EMPTY_VARIABLE_TABLE {};
243
244 auto iter = methods_.find(methodId.GetOffset());
245 if (iter == methods_.end()) {
246 return EMPTY_VARIABLE_TABLE;
247 }
248 return iter->second.localVariableTable;
249 }
250
GetSourceFile(panda_file::File::EntityId methodId) const251 const std::string &DebugInfoExtractor::GetSourceFile(panda_file::File::EntityId methodId) const
252 {
253 auto iter = methods_.find(methodId.GetOffset());
254 if (iter == methods_.end()) {
255 LOG_DEBUGGER(FATAL) << "Get source file of unknown method id: " << methodId.GetOffset();
256 }
257 return iter->second.sourceFile;
258 }
259
GetSourceCode(panda_file::File::EntityId methodId) const260 const std::string &DebugInfoExtractor::GetSourceCode(panda_file::File::EntityId methodId) const
261 {
262 auto iter = methods_.find(methodId.GetOffset());
263 if (iter == methods_.end()) {
264 LOG_DEBUGGER(FATAL) << "Get source code of unknown method id: " << methodId.GetOffset();
265 }
266 return iter->second.sourceCode;
267 }
268
GetMethodIdList() const269 CVector<panda_file::File::EntityId> DebugInfoExtractor::GetMethodIdList() const
270 {
271 CVector<panda_file::File::EntityId> list;
272
273 for (const auto &method : methods_) {
274 list.push_back(panda_file::File::EntityId(method.first));
275 }
276 return list;
277 }
278
ContainsMethod(panda_file::File::EntityId methodId) const279 bool DebugInfoExtractor::ContainsMethod(panda_file::File::EntityId methodId) const
280 {
281 return methods_.find(methodId.GetOffset()) != methods_.end();
282 }
283 } // namespace panda::ecmascript
284