1// Copyright (C) 2016 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#ifndef QV4COMPILERCONTROLFLOW_P_H
4#define QV4COMPILERCONTROLFLOW_P_H
5
6//
7// W A R N I N G
8// -------------
9//
10// This file is not part of the Qt API. It exists purely as an
11// implementation detail. This header file may change from version to
12// version without notice, or even be removed.
13//
14// We mean it.
15//
16
17#include <private/qv4codegen_p.h>
18#include <private/qqmljsast_p.h>
19#include <private/qv4bytecodegenerator_p.h>
20
21QT_BEGIN_NAMESPACE
22
23namespace QV4 {
24
25namespace Compiler {
26
27struct ControlFlow {
28 using Reference = Codegen::Reference;
29 using BytecodeGenerator = Moth::BytecodeGenerator;
30 using Instruction = Moth::Instruction;
31
32 enum Type {
33 Loop,
34 With,
35 Block,
36 Finally,
37 Catch
38 };
39
40 enum UnwindType {
41 Break,
42 Continue,
43 Return
44 };
45
46 struct UnwindTarget {
47 BytecodeGenerator::Label linkLabel;
48 int unwindLevel;
49 };
50
51 Codegen *cg;
52 ControlFlow *parent;
53 Type type;
54
55 ControlFlow(Codegen *cg, Type type)
56 : cg(cg), parent(cg->controlFlow), type(type)
57 {
58 cg->controlFlow = this;
59 }
60
61 virtual ~ControlFlow() {
62 cg->controlFlow = parent;
63 }
64
65 UnwindTarget unwindTarget(UnwindType type, const QString &label = QString())
66 {
67 Q_ASSERT(type == Break || type == Continue || type == Return);
68 ControlFlow *flow = this;
69 int level = 0;
70 while (flow) {
71 BytecodeGenerator::Label l = flow->getUnwindTarget(type, label);
72 if (l.isValid())
73 return UnwindTarget{.linkLabel: l, .unwindLevel: level};
74 if (flow->requiresUnwind())
75 ++level;
76 flow = flow->parent;
77 }
78 if (type == Return)
79 return UnwindTarget{ .linkLabel: cg->returnLabel(), .unwindLevel: level };
80 return UnwindTarget();
81 }
82
83 virtual QString label() const { return QString(); }
84
85 bool hasLoop() const {
86 const ControlFlow *flow = this;
87 while (flow) {
88 if (flow->type == Loop)
89 return true;
90 flow = flow->parent;
91 }
92 return false;
93 }
94
95protected:
96 virtual BytecodeGenerator::Label getUnwindTarget(UnwindType, const QString & = QString()) {
97 return BytecodeGenerator::Label();
98 }
99 virtual bool requiresUnwind() {
100 return false;
101 }
102
103public:
104 BytecodeGenerator::ExceptionHandler *parentUnwindHandler() {
105 return parent ? parent->unwindHandler() : nullptr;
106 }
107
108 virtual BytecodeGenerator::ExceptionHandler *unwindHandler() {
109 return parentUnwindHandler();
110 }
111
112
113protected:
114 QString loopLabel() const {
115 QString label;
116 if (cg->_labelledStatement) {
117 label = cg->_labelledStatement->label.toString();
118 cg->_labelledStatement = nullptr;
119 }
120 return label;
121 }
122 BytecodeGenerator *generator() const {
123 return cg->bytecodeGenerator;
124 }
125};
126
127struct ControlFlowUnwind : public ControlFlow
128{
129 BytecodeGenerator::ExceptionHandler unwindLabel;
130
131 ControlFlowUnwind(Codegen *cg, Type type)
132 : ControlFlow(cg, type)
133 {
134 }
135
136 void setupUnwindHandler()
137 {
138 unwindLabel = generator()->newExceptionHandler();
139 }
140
141 void emitUnwindHandler()
142 {
143 Q_ASSERT(requiresUnwind());
144
145 Instruction::UnwindDispatch dispatch;
146 generator()->addInstruction(data: dispatch);
147 }
148
149 virtual BytecodeGenerator::ExceptionHandler *unwindHandler() override {
150 return unwindLabel.isValid() ? &unwindLabel : parentUnwindHandler();
151 }
152};
153
154struct ControlFlowUnwindCleanup : public ControlFlowUnwind
155{
156 std::function<void()> cleanup = nullptr;
157
158 ControlFlowUnwindCleanup(Codegen *cg, std::function<void()> cleanup, Type type = Block)
159 : ControlFlowUnwind(cg, type), cleanup(cleanup)
160 {
161 if (cleanup) {
162 setupUnwindHandler();
163 generator()->setUnwindHandler(&unwindLabel);
164 }
165 }
166
167 ~ControlFlowUnwindCleanup() {
168 if (cleanup) {
169 unwindLabel.link();
170 generator()->setUnwindHandler(parentUnwindHandler());
171 cleanup();
172 emitUnwindHandler();
173 }
174 }
175
176 bool requiresUnwind() override {
177 return cleanup != nullptr;
178 }
179};
180
181struct ControlFlowLoop : public ControlFlowUnwindCleanup
182{
183 QString loopLabel;
184 BytecodeGenerator::Label *breakLabel = nullptr;
185 BytecodeGenerator::Label *continueLabel = nullptr;
186
187 ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr, std::function<void()> cleanup = nullptr)
188 : ControlFlowUnwindCleanup(cg, cleanup, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel)
189 {
190 }
191
192 BytecodeGenerator::Label getUnwindTarget(UnwindType type, const QString &label) override {
193 switch (type) {
194 case Break:
195 if (breakLabel && (label.isEmpty() || label == loopLabel))
196 return *breakLabel;
197 break;
198 case Continue:
199 if (continueLabel && (label.isEmpty() || label == loopLabel))
200 return *continueLabel;
201 break;
202 default:
203 break;
204 }
205 return BytecodeGenerator::Label();
206 }
207
208 QString label() const override { return loopLabel; }
209};
210
211
212struct ControlFlowWith : public ControlFlowUnwind
213{
214 ControlFlowWith(Codegen *cg)
215 : ControlFlowUnwind(cg, With)
216 {
217 setupUnwindHandler();
218
219 // assumes the with object is in the accumulator
220 Instruction::PushWithContext pushScope;
221 generator()->addInstruction(data: pushScope);
222 generator()->setUnwindHandler(&unwindLabel);
223 }
224
225 ~ControlFlowWith() {
226 // emit code for unwinding
227 unwindLabel.link();
228
229 generator()->setUnwindHandler(parentUnwindHandler());
230 Instruction::PopContext pop;
231 generator()->addInstruction(data: pop);
232
233 emitUnwindHandler();
234 }
235
236 bool requiresUnwind() override {
237 return true;
238 }
239
240
241};
242
243struct ControlFlowBlock : public ControlFlowUnwind
244{
245 ControlFlowBlock(Codegen *cg, QQmlJS::AST::Node *ast)
246 : ControlFlowUnwind(cg, Block)
247 {
248 block = cg->enterBlock(node: ast);
249 block->emitBlockHeader(codegen: cg);
250
251 if (block->requiresExecutionContext) {
252 setupUnwindHandler();
253 generator()->setUnwindHandler(&unwindLabel);
254 }
255 }
256
257 virtual ~ControlFlowBlock() {
258 // emit code for unwinding
259 if (block->requiresExecutionContext) {
260 unwindLabel.link();
261 generator()->setUnwindHandler(parentUnwindHandler());
262 }
263
264 block->emitBlockFooter(codegen: cg);
265
266 if (block->requiresExecutionContext )
267 emitUnwindHandler();
268 cg->leaveBlock();
269 }
270
271 virtual bool requiresUnwind() override {
272 return block->requiresExecutionContext;
273 }
274
275 Context *block;
276};
277
278struct ControlFlowCatch : public ControlFlowUnwind
279{
280 QQmlJS::AST::Catch *catchExpression;
281 bool insideCatch = false;
282 BytecodeGenerator::ExceptionHandler exceptionLabel;
283
284 ControlFlowCatch(Codegen *cg, QQmlJS::AST::Catch *catchExpression)
285 : ControlFlowUnwind(cg, Catch), catchExpression(catchExpression),
286 exceptionLabel(generator()->newExceptionHandler())
287 {
288 generator()->setUnwindHandler(&exceptionLabel);
289 }
290
291 virtual bool requiresUnwind() override {
292 return true;
293 }
294
295 BytecodeGenerator::ExceptionHandler *unwindHandler() override {
296 return insideCatch ? &unwindLabel : &exceptionLabel;
297 }
298
299 ~ControlFlowCatch() {
300 // emit code for unwinding
301 insideCatch = true;
302 setupUnwindHandler();
303
304 Codegen::RegisterScope scope(cg);
305
306 // exceptions inside the try block go here
307 exceptionLabel.link();
308 BytecodeGenerator::Jump noException = generator()->jumpNoException();
309
310 Context *block = cg->enterBlock(node: catchExpression);
311
312 block->emitBlockHeader(codegen: cg);
313
314 generator()->setUnwindHandler(&unwindLabel);
315
316 if (catchExpression->patternElement->bindingIdentifier.isEmpty())
317 // destructuring pattern
318 cg->initializeAndDestructureBindingElement(e: catchExpression->patternElement, baseRef: Reference::fromName(cg, QStringLiteral("@caught")));
319 // skip the additional block
320 cg->statementList(ast: catchExpression->statement->statements);
321
322 // exceptions inside catch and break/return statements go here
323 unwindLabel.link();
324 block->emitBlockFooter(codegen: cg);
325
326 cg->leaveBlock();
327
328 noException.link();
329 generator()->setUnwindHandler(parentUnwindHandler());
330
331 emitUnwindHandler();
332 insideCatch = false;
333 }
334};
335
336struct ControlFlowFinally : public ControlFlowUnwind
337{
338 QQmlJS::AST::Finally *finally;
339 bool insideFinally = false;
340
341 ControlFlowFinally(Codegen *cg, QQmlJS::AST::Finally *finally)
342 : ControlFlowUnwind(cg, Finally), finally(finally)
343 {
344 Q_ASSERT(finally != nullptr);
345 setupUnwindHandler();
346 generator()->setUnwindHandler(&unwindLabel);
347 }
348
349 virtual bool requiresUnwind() override {
350 return !insideFinally;
351 }
352
353 BytecodeGenerator::ExceptionHandler *unwindHandler() override {
354 return insideFinally ? parentUnwindHandler() : ControlFlowUnwind::unwindHandler();
355 }
356
357 ~ControlFlowFinally() {
358 // emit code for unwinding
359 unwindLabel.link();
360
361 Codegen::RegisterScope scope(cg);
362
363 insideFinally = true;
364 int returnValueTemp = -1;
365 if (cg->requiresReturnValue) {
366 returnValueTemp = generator()->newRegister();
367 Instruction::MoveReg move;
368 move.srcReg = cg->_returnAddress;
369 move.destReg = returnValueTemp;
370 generator()->addInstruction(data: move);
371 }
372 int exceptionTemp = generator()->newRegister();
373 Instruction::GetException instr;
374 generator()->addInstruction(data: instr);
375 Reference::fromStackSlot(cg, tempIndex: exceptionTemp).storeConsumeAccumulator();
376
377 generator()->setUnwindHandler(parentUnwindHandler());
378 cg->statement(ast: finally->statement);
379 insideFinally = false;
380
381 if (cg->requiresReturnValue) {
382 Instruction::MoveReg move;
383 move.srcReg = returnValueTemp;
384 move.destReg = cg->_returnAddress;
385 generator()->addInstruction(data: move);
386 }
387 Reference::fromStackSlot(cg, tempIndex: exceptionTemp).loadInAccumulator();
388 Instruction::SetException setException;
389 generator()->addInstruction(data: setException);
390
391 emitUnwindHandler();
392 }
393};
394
395} } // QV4::Compiler namespace
396
397QT_END_NAMESPACE
398
399#endif
400

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