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

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