1/**
2 * Copyright (c) 2021-2022 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 LIBPANDAFILE_LINE_NUMBER_PROGRAM_H
17#define LIBPANDAFILE_LINE_NUMBER_PROGRAM_H
18
19#include "file-inl.h"
20#include "file_items.h"
21
22namespace panda::panda_file {
23
24class LineProgramState {
25public:
26    LineProgramState(const File &pf, File::EntityId file, int32_t line, Span<const uint8_t> constant_pool)
27        : pf_(pf), file_(file), line_(line), constant_pool_(constant_pool)
28    {
29    }
30
31    void AdvanceLine(int32_t v)
32    {
33        line_ += v;
34    }
35
36    void AdvancePc(uint32_t v)
37    {
38        address_ += v;
39    }
40
41    void SetFile(uint32_t offset)
42    {
43        file_ = File::EntityId(offset);
44    }
45
46    const uint8_t *GetFile() const
47    {
48        return pf_.GetStringData(file_).data;
49    }
50
51    bool HasFile() const
52    {
53        return file_.IsValid();
54    }
55
56    void SetSourceCode(uint32_t offset)
57    {
58        source_code_ = File::EntityId(offset);
59    }
60
61    const uint8_t *GetSourceCode() const
62    {
63        if (UNLIKELY(!HasSourceCode())) {
64            return nullptr;
65        }
66        return pf_.GetStringData(source_code_).data;
67    }
68
69    bool HasSourceCode() const
70    {
71        return source_code_.IsValid();
72    }
73
74    int32_t GetLine() const
75    {
76        return line_;
77    }
78
79    void SetColumn(int32_t c)
80    {
81        column_ = static_cast<size_t>(c);
82    }
83
84    size_t GetColumn() const
85    {
86        return column_;
87    }
88
89    uint32_t GetAddress() const
90    {
91        return address_;
92    }
93
94    uint32_t ReadULeb128()
95    {
96        return panda_file::helpers::ReadULeb128(&constant_pool_);
97    }
98
99    int32_t ReadSLeb128()
100    {
101        return panda_file::helpers::ReadLeb128(&constant_pool_);
102    }
103
104    const File &GetPandaFile() const
105    {
106        return pf_;
107    }
108
109private:
110    const File &pf_;
111
112    File::EntityId file_;
113    File::EntityId source_code_;
114    size_t line_;
115    size_t column_ {0};
116    Span<const uint8_t> constant_pool_;
117
118    uint32_t address_ {0};
119};
120
121template <class Handler>
122class LineNumberProgramProcessor {
123public:
124    LineNumberProgramProcessor(const uint8_t *program, Handler *handler)
125        : state_(handler->GetState()), program_(program), handler_(handler)
126    {
127    }
128
129    ~LineNumberProgramProcessor() = default;
130
131    NO_COPY_SEMANTIC(LineNumberProgramProcessor);
132    NO_MOVE_SEMANTIC(LineNumberProgramProcessor);
133
134    void Process()
135    {
136        handler_->ProcessBegin();
137
138        auto opcode = ReadOpcode();
139        bool res = false;
140        while (opcode != Opcode::END_SEQUENCE) {
141            switch (opcode) {
142                case Opcode::ADVANCE_LINE: {
143                    res = HandleAdvanceLine();
144                    break;
145                }
146                case Opcode::ADVANCE_PC: {
147                    res = HandleAdvancePc();
148                    break;
149                }
150                case Opcode::SET_FILE: {
151                    res = HandleSetFile();
152                    break;
153                }
154                case Opcode::SET_SOURCE_CODE: {
155                    res = HandleSetSourceCode();
156                    break;
157                }
158                case Opcode::SET_PROLOGUE_END:
159                case Opcode::SET_EPILOGUE_BEGIN:
160                    break;
161                case Opcode::START_LOCAL: {
162                    res = HandleStartLocal();
163                    break;
164                }
165                case Opcode::START_LOCAL_EXTENDED: {
166                    res = HandleStartLocalExtended();
167                    break;
168                }
169                case Opcode::RESTART_LOCAL: {
170                    LOG(FATAL, PANDAFILE) << "Opcode RESTART_LOCAL is not supported";
171                    break;
172                }
173                case Opcode::END_LOCAL: {
174                    res = HandleEndLocal();
175                    break;
176                }
177                case Opcode::SET_COLUMN: {
178                    res = HandleSetColumn();
179                    break;
180                }
181                default: {
182                    res = HandleSpecialOpcode(opcode);
183                    break;
184                }
185            }
186
187            if (!res) {
188                break;
189            }
190
191            opcode = ReadOpcode();
192        }
193
194        handler_->ProcessEnd();
195    }
196
197private:
198    using Opcode = LineNumberProgramItem::Opcode;
199
200    Opcode ReadOpcode()
201    {
202        auto opcode = static_cast<Opcode>(*program_);
203        ++program_;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
204        return opcode;
205    }
206
207    int32_t ReadRegisterNumber()
208    {
209        auto [regiser_number, n, is_full] = leb128::DecodeSigned<int32_t>(program_);
210        LOG_IF(!is_full, FATAL, COMMON) << "Cannot read a register number";
211        program_ += n;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
212        return regiser_number;
213    }
214
215    bool HandleAdvanceLine() const
216    {
217        auto line_diff = state_->ReadSLeb128();
218        return handler_->HandleAdvanceLine(line_diff);
219    }
220
221    bool HandleAdvancePc() const
222    {
223        auto pc_diff = state_->ReadULeb128();
224        return handler_->HandleAdvancePc(pc_diff);
225    }
226
227    bool HandleSetFile() const
228    {
229        return handler_->HandleSetFile(state_->ReadULeb128());
230    }
231
232    bool HandleSetSourceCode() const
233    {
234        return handler_->HandleSetSourceCode(state_->ReadULeb128());
235    }
236
237    bool HandleSetPrologueEnd() const
238    {
239        return handler_->HandleSetPrologueEnd();
240    }
241
242    bool HandleSetEpilogueBegin() const
243    {
244        return handler_->HandleSetEpilogueBegin();
245    }
246
247    bool HandleStartLocal()
248    {
249        auto reg_number = ReadRegisterNumber();
250        auto name_index = state_->ReadULeb128();
251        auto type_index = state_->ReadULeb128();
252        return handler_->HandleStartLocal(reg_number, name_index, type_index);
253    }
254
255    bool HandleStartLocalExtended()
256    {
257        auto reg_number = ReadRegisterNumber();
258        auto name_index = state_->ReadULeb128();
259        auto type_index = state_->ReadULeb128();
260        auto type_signature_index = state_->ReadULeb128();
261        return handler_->HandleStartLocalExtended(reg_number, name_index, type_index, type_signature_index);
262    }
263
264    bool HandleEndLocal()
265    {
266        auto reg_number = ReadRegisterNumber();
267        return handler_->HandleEndLocal(reg_number);
268    }
269
270    bool HandleSetColumn()
271    {
272        auto cn = state_->ReadULeb128();
273        return handler_->HandleSetColumn(cn);
274    }
275
276    bool HandleSpecialOpcode(LineNumberProgramItem::Opcode opcode)
277    {
278        ASSERT(static_cast<uint8_t>(opcode) >= LineNumberProgramItem::OPCODE_BASE);
279
280        auto adjust_opcode = static_cast<int32_t>(static_cast<uint8_t>(opcode) - LineNumberProgramItem::OPCODE_BASE);
281        auto pc_offset = static_cast<uint32_t>(adjust_opcode / LineNumberProgramItem::LINE_RANGE);
282        int32_t line_offset = adjust_opcode % LineNumberProgramItem::LINE_RANGE + LineNumberProgramItem::LINE_BASE;
283        return handler_->HandleSpecialOpcode(pc_offset, line_offset);
284    }
285
286    LineProgramState *state_;
287    const uint8_t *program_;
288    Handler *handler_;
289};
290
291}  // namespace panda::panda_file
292
293#endif  // LIBPANDAFILE_LINE_NUMBER_PROGRAM_H
294