• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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