1 | // Copyright (C) 2017 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QV4BYTECODEGENERATOR_P_H |
5 | #define QV4BYTECODEGENERATOR_P_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists purely as an |
12 | // implementation detail. This header file may change from version to |
13 | // version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | // |
17 | #include <private/qv4instr_moth_p.h> |
18 | #include <private/qv4compileddata_p.h> |
19 | #include <private/qv4compilercontext_p.h> |
20 | #include <private/qqmljssourcelocation_p.h> |
21 | |
22 | #include <memory> |
23 | |
24 | QT_BEGIN_NAMESPACE |
25 | |
26 | namespace QQmlJS { |
27 | class SourceLocation; |
28 | } |
29 | |
30 | namespace QV4 { |
31 | |
32 | namespace Compiler { |
33 | struct Context; |
34 | } |
35 | |
36 | namespace Moth { |
37 | |
38 | class BytecodeGenerator { |
39 | public: |
40 | BytecodeGenerator(int line, bool debug, bool storeSourceLocation = false) |
41 | : startLine(line), debugMode(debug) |
42 | { |
43 | if (storeSourceLocation) |
44 | m_sourceLocationTable.reset(p: new QV4::Compiler::Context::SourceLocationTable {}); |
45 | } |
46 | |
47 | struct Label { |
48 | enum LinkMode { |
49 | LinkNow, |
50 | LinkLater |
51 | }; |
52 | Label() = default; |
53 | Label(BytecodeGenerator *generator, LinkMode mode = LinkNow) |
54 | : generator(generator), |
55 | index(generator->labels.size()) { |
56 | generator->labels.append(t: -1); |
57 | if (mode == LinkNow) |
58 | link(); |
59 | } |
60 | |
61 | void link() { |
62 | Q_ASSERT(index >= 0); |
63 | Q_ASSERT(generator->labels[index] == -1); |
64 | generator->labels[index] = generator->instructions.size(); |
65 | generator->clearLastInstruction(); |
66 | } |
67 | bool isValid() const { return generator != nullptr; } |
68 | |
69 | BytecodeGenerator *generator = nullptr; |
70 | int index = -1; |
71 | }; |
72 | |
73 | struct Jump { |
74 | Jump(BytecodeGenerator *generator, int instruction) |
75 | : generator(generator), |
76 | index(instruction) |
77 | { Q_ASSERT(generator && index != -1); } |
78 | |
79 | ~Jump() { |
80 | Q_ASSERT(index == -1 || generator->instructions[index].linkedLabel != -1); // make sure link() got called |
81 | } |
82 | |
83 | Jump(Jump &&j) { |
84 | std::swap(a&: generator, b&: j.generator); |
85 | std::swap(a&: index, b&: j.index); |
86 | } |
87 | |
88 | BytecodeGenerator *generator = nullptr; |
89 | int index = -1; |
90 | |
91 | void link() { |
92 | link(l: generator->label()); |
93 | } |
94 | void link(Label l) { |
95 | Q_ASSERT(l.index >= 0); |
96 | Q_ASSERT(generator->instructions[index].linkedLabel == -1); |
97 | generator->instructions[index].linkedLabel = l.index; |
98 | } |
99 | |
100 | private: |
101 | // make this type move-only: |
102 | Q_DISABLE_COPY(Jump) |
103 | // we never move-assign this type anywhere, so disable it: |
104 | Jump &operator=(Jump &&) = delete; |
105 | }; |
106 | |
107 | struct ExceptionHandler : public Label { |
108 | ExceptionHandler() = default; |
109 | ExceptionHandler(BytecodeGenerator *generator) |
110 | : Label(generator, LinkLater) |
111 | { |
112 | } |
113 | ~ExceptionHandler() |
114 | { |
115 | Q_ASSERT(!generator || generator->currentExceptionHandler != this); |
116 | } |
117 | bool isValid() const { return generator != nullptr; } |
118 | }; |
119 | |
120 | Label label() { |
121 | return Label(this, Label::LinkNow); |
122 | } |
123 | |
124 | Label newLabel() { |
125 | return Label(this, Label::LinkLater); |
126 | } |
127 | |
128 | ExceptionHandler newExceptionHandler() { |
129 | return ExceptionHandler(this); |
130 | } |
131 | |
132 | template<int InstrT> |
133 | void addInstruction(const InstrData<InstrT> &data) |
134 | { |
135 | Instr genericInstr; |
136 | InstrMeta<InstrT>::setData(genericInstr, data); |
137 | addInstructionHelper(type: Moth::Instr::Type(InstrT), i: genericInstr); |
138 | } |
139 | |
140 | Q_REQUIRED_RESULT Jump jump() |
141 | { |
142 | QT_WARNING_PUSH |
143 | QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized" ) // broken gcc warns about Instruction::Debug() |
144 | Instruction::Jump data; |
145 | return addJumpInstruction(data); |
146 | QT_WARNING_POP |
147 | } |
148 | |
149 | Q_REQUIRED_RESULT Jump jumpTrue() |
150 | { |
151 | return addJumpInstruction(data: Instruction::JumpTrue()); |
152 | } |
153 | |
154 | Q_REQUIRED_RESULT Jump jumpFalse() |
155 | { |
156 | return addJumpInstruction(data: Instruction::JumpFalse()); |
157 | } |
158 | |
159 | Q_REQUIRED_RESULT Jump jumpNotUndefined() |
160 | { |
161 | Instruction::JumpNotUndefined data{}; |
162 | return addJumpInstruction(data); |
163 | } |
164 | |
165 | Q_REQUIRED_RESULT Jump jumpNoException() |
166 | { |
167 | Instruction::JumpNoException data{}; |
168 | return addJumpInstruction(data); |
169 | } |
170 | |
171 | Q_REQUIRED_RESULT Jump jumpOptionalLookup(int index) |
172 | { |
173 | Instruction::GetOptionalLookup data{}; |
174 | data.index = index; |
175 | return addJumpInstruction(data); |
176 | } |
177 | |
178 | Q_REQUIRED_RESULT Jump jumpOptionalProperty(int name) |
179 | { |
180 | Instruction::LoadOptionalProperty data{}; |
181 | data.name = name; |
182 | return addJumpInstruction(data); |
183 | } |
184 | |
185 | void jumpStrictEqual(const StackSlot &lhs, const Label &target) |
186 | { |
187 | Instruction::CmpStrictEqual cmp; |
188 | cmp.lhs = lhs; |
189 | addInstruction(data: std::move(cmp)); |
190 | addJumpInstruction(data: Instruction::JumpTrue()).link(l: target); |
191 | } |
192 | |
193 | void jumpStrictNotEqual(const StackSlot &lhs, const Label &target) |
194 | { |
195 | Instruction::CmpStrictNotEqual cmp; |
196 | cmp.lhs = lhs; |
197 | addInstruction(data: std::move(cmp)); |
198 | addJumpInstruction(data: Instruction::JumpTrue()).link(l: target); |
199 | } |
200 | |
201 | void checkException() |
202 | { |
203 | Instruction::CheckException chk; |
204 | addInstruction(data: chk); |
205 | } |
206 | |
207 | void setUnwindHandler(ExceptionHandler *handler) |
208 | { |
209 | currentExceptionHandler = handler; |
210 | Instruction::SetUnwindHandler data; |
211 | data.offset = 0; |
212 | if (!handler) |
213 | addInstruction(data); |
214 | else |
215 | addJumpInstruction(data).link(l: *handler); |
216 | } |
217 | |
218 | void unwindToLabel(int level, const Label &target) |
219 | { |
220 | if (level) { |
221 | Instruction::UnwindToLabel unwind; |
222 | unwind.level = level; |
223 | addJumpInstruction(data: unwind).link(l: target); |
224 | } else { |
225 | jump().link(l: target); |
226 | } |
227 | } |
228 | |
229 | |
230 | |
231 | void setLocation(const QQmlJS::SourceLocation &loc); |
232 | void incrementStatement(); |
233 | |
234 | ExceptionHandler *exceptionHandler() const { |
235 | return currentExceptionHandler; |
236 | } |
237 | |
238 | int newRegister(); |
239 | int newRegisterArray(int n); |
240 | int registerCount() const { return regCount; } |
241 | int currentRegister() const { return currentReg; } |
242 | |
243 | void finalize(Compiler::Context *context); |
244 | |
245 | template<int InstrT> |
246 | Jump addJumpInstruction(const InstrData<InstrT> &data) |
247 | { |
248 | Instr genericInstr; |
249 | InstrMeta<InstrT>::setData(genericInstr, data); |
250 | return Jump(this, addInstructionHelper(type: Moth::Instr::Type(InstrT), i: genericInstr, offsetof(InstrData<InstrT>, offset))); |
251 | } |
252 | |
253 | void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel) |
254 | { |
255 | if (jumpOnFalse) |
256 | addJumpInstruction(data: Instruction::JumpFalse()).link(l: *falseLabel); |
257 | else |
258 | addJumpInstruction(data: Instruction::JumpTrue()).link(l: *trueLabel); |
259 | } |
260 | |
261 | void clearLastInstruction() |
262 | { |
263 | lastInstrType = -1; |
264 | } |
265 | |
266 | void addLoopStart(const Label &start) |
267 | { |
268 | _labelInfos.push_back(x: { .labelIndex: start.index }); |
269 | } |
270 | |
271 | private: |
272 | friend struct Jump; |
273 | friend struct Label; |
274 | friend struct ExceptionHandler; |
275 | |
276 | int addInstructionHelper(Moth::Instr::Type type, const Instr &i, int offsetOfOffset = -1); |
277 | |
278 | struct I { |
279 | Moth::Instr::Type type; |
280 | short size; |
281 | uint position; |
282 | int line; |
283 | int statement; |
284 | int offsetForJump; |
285 | int linkedLabel; |
286 | unsigned char packed[sizeof(Instr) + 2]; // 2 for instruction type |
287 | }; |
288 | |
289 | void compressInstructions(); |
290 | void packInstruction(I &i); |
291 | void adjustJumpOffsets(); |
292 | |
293 | QVector<I> instructions; |
294 | QVector<int> labels; |
295 | ExceptionHandler *currentExceptionHandler = nullptr; |
296 | int regCount = 0; |
297 | public: |
298 | int currentReg = 0; |
299 | private: |
300 | int startLine = 0; |
301 | int currentLine = 0; |
302 | int currentStatement = 0; |
303 | QQmlJS::SourceLocation currentSourceLocation; |
304 | std::unique_ptr<QV4::Compiler::Context::SourceLocationTable> m_sourceLocationTable; |
305 | bool debugMode = false; |
306 | |
307 | int lastInstrType = -1; |
308 | Moth::Instr lastInstr; |
309 | |
310 | struct LabelInfo { |
311 | int labelIndex; |
312 | }; |
313 | std::vector<LabelInfo> _labelInfos; |
314 | }; |
315 | |
316 | } |
317 | } |
318 | |
319 | QT_END_NAMESPACE |
320 | |
321 | #endif |
322 | |