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