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
24QT_BEGIN_NAMESPACE
25
26namespace QQmlJS {
27class SourceLocation;
28}
29
30namespace QV4 {
31
32namespace Compiler {
33struct Context;
34}
35
36namespace Moth {
37
38class BytecodeGenerator {
39public:
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 {
142QT_WARNING_PUSH
143QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug()
144 Instruction::Jump data;
145 return addJumpInstruction(data);
146QT_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
271private:
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;
297public:
298 int currentReg = 0;
299private:
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
319QT_END_NAMESPACE
320
321#endif
322

source code of qtdeclarative/src/qml/compiler/qv4bytecodegenerator_p.h