/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QV4COMPILERCONTROLFLOW_P_H #define QV4COMPILERCONTROLFLOW_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include QT_BEGIN_NAMESPACE namespace QV4 { namespace Compiler { struct ControlFlow { using Reference = Codegen::Reference; using BytecodeGenerator = Moth::BytecodeGenerator; using Instruction = Moth::Instruction; enum Type { Loop, With, Block, Finally, Catch }; enum UnwindType { Break, Continue, Return }; struct UnwindTarget { BytecodeGenerator::Label linkLabel; int unwindLevel; }; Codegen *cg; ControlFlow *parent; Type type; ControlFlow(Codegen *cg, Type type) : cg(cg), parent(cg->controlFlow), type(type) { cg->controlFlow = this; } virtual ~ControlFlow() { cg->controlFlow = parent; } UnwindTarget unwindTarget(UnwindType type, const QString &label = QString()) { Q_ASSERT(type == Break || type == Continue || type == Return); ControlFlow *flow = this; int level = 0; while (flow) { BytecodeGenerator::Label l = flow->getUnwindTarget(type, label); if (l.isValid()) return UnwindTarget{l, level}; if (flow->requiresUnwind()) ++level; flow = flow->parent; } if (type == Return) return UnwindTarget{ cg->returnLabel(), level }; return UnwindTarget(); } virtual QString label() const { return QString(); } bool hasLoop() const { const ControlFlow *flow = this; while (flow) { if (flow->type == Loop) return true; flow = flow->parent; } return false; } protected: virtual BytecodeGenerator::Label getUnwindTarget(UnwindType, const QString & = QString()) { return BytecodeGenerator::Label(); } virtual bool requiresUnwind() { return false; } public: BytecodeGenerator::ExceptionHandler *parentUnwindHandler() { return parent ? parent->unwindHandler() : nullptr; } virtual BytecodeGenerator::ExceptionHandler *unwindHandler() { return parentUnwindHandler(); } protected: QString loopLabel() const { QString label; if (cg->_labelledStatement) { label = cg->_labelledStatement->label.toString(); cg->_labelledStatement = nullptr; } return label; } BytecodeGenerator *generator() const { return cg->bytecodeGenerator; } }; struct ControlFlowUnwind : public ControlFlow { BytecodeGenerator::ExceptionHandler unwindLabel; ControlFlowUnwind(Codegen *cg, Type type) : ControlFlow(cg, type) { } void setupUnwindHandler() { unwindLabel = generator()->newExceptionHandler(); } void emitUnwindHandler() { Q_ASSERT(requiresUnwind()); Instruction::UnwindDispatch dispatch; generator()->addInstruction(dispatch); } virtual BytecodeGenerator::ExceptionHandler *unwindHandler() override { return unwindLabel.isValid() ? &unwindLabel : parentUnwindHandler(); } }; struct ControlFlowUnwindCleanup : public ControlFlowUnwind { std::function cleanup = nullptr; ControlFlowUnwindCleanup(Codegen *cg, std::function cleanup, Type type = Block) : ControlFlowUnwind(cg, type), cleanup(cleanup) { if (cleanup) { setupUnwindHandler(); generator()->setUnwindHandler(&unwindLabel); } } ~ControlFlowUnwindCleanup() { if (cleanup) { unwindLabel.link(); generator()->setUnwindHandler(parentUnwindHandler()); cleanup(); emitUnwindHandler(); } } bool requiresUnwind() override { return cleanup != nullptr; } }; struct ControlFlowLoop : public ControlFlowUnwindCleanup { QString loopLabel; BytecodeGenerator::Label *breakLabel = nullptr; BytecodeGenerator::Label *continueLabel = nullptr; ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr, std::function cleanup = nullptr) : ControlFlowUnwindCleanup(cg, cleanup, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel) { } BytecodeGenerator::Label getUnwindTarget(UnwindType type, const QString &label) override { switch (type) { case Break: if (breakLabel && (label.isEmpty() || label == loopLabel)) return *breakLabel; break; case Continue: if (continueLabel && (label.isEmpty() || label == loopLabel)) return *continueLabel; break; default: break; } return BytecodeGenerator::Label(); } QString label() const override { return loopLabel; } }; struct ControlFlowWith : public ControlFlowUnwind { ControlFlowWith(Codegen *cg) : ControlFlowUnwind(cg, With) { setupUnwindHandler(); // assumes the with object is in the accumulator Instruction::PushWithContext pushScope; generator()->addInstruction(pushScope); generator()->setUnwindHandler(&unwindLabel); } ~ControlFlowWith() { // emit code for unwinding unwindLabel.link(); generator()->setUnwindHandler(parentUnwindHandler()); Instruction::PopContext pop; generator()->addInstruction(pop); emitUnwindHandler(); } bool requiresUnwind() override { return true; } }; struct ControlFlowBlock : public ControlFlowUnwind { ControlFlowBlock(Codegen *cg, QQmlJS::AST::Node *ast) : ControlFlowUnwind(cg, Block) { block = cg->enterBlock(ast); block->emitBlockHeader(cg); if (block->requiresExecutionContext) { setupUnwindHandler(); generator()->setUnwindHandler(&unwindLabel); } } virtual ~ControlFlowBlock() { // emit code for unwinding if (block->requiresExecutionContext) { unwindLabel.link(); generator()->setUnwindHandler(parentUnwindHandler()); } block->emitBlockFooter(cg); if (block->requiresExecutionContext ) emitUnwindHandler(); cg->leaveBlock(); } virtual bool requiresUnwind() override { return block->requiresExecutionContext; } Context *block; }; struct ControlFlowCatch : public ControlFlowUnwind { QQmlJS::AST::Catch *catchExpression; bool insideCatch = false; BytecodeGenerator::ExceptionHandler exceptionLabel; ControlFlowCatch(Codegen *cg, QQmlJS::AST::Catch *catchExpression) : ControlFlowUnwind(cg, Catch), catchExpression(catchExpression), exceptionLabel(generator()->newExceptionHandler()) { generator()->setUnwindHandler(&exceptionLabel); } virtual bool requiresUnwind() override { return true; } BytecodeGenerator::ExceptionHandler *unwindHandler() override { return insideCatch ? &unwindLabel : &exceptionLabel; } ~ControlFlowCatch() { // emit code for unwinding insideCatch = true; setupUnwindHandler(); Codegen::RegisterScope scope(cg); // exceptions inside the try block go here exceptionLabel.link(); BytecodeGenerator::Jump noException = generator()->jumpNoException(); Context *block = cg->enterBlock(catchExpression); block->emitBlockHeader(cg); generator()->setUnwindHandler(&unwindLabel); if (catchExpression->patternElement->bindingIdentifier.isEmpty()) // destructuring pattern cg->initializeAndDestructureBindingElement(catchExpression->patternElement, Reference::fromName(cg, QStringLiteral("@caught"))); // skip the additional block cg->statementList(catchExpression->statement->statements); // exceptions inside catch and break/return statements go here unwindLabel.link(); block->emitBlockFooter(cg); cg->leaveBlock(); noException.link(); generator()->setUnwindHandler(parentUnwindHandler()); emitUnwindHandler(); insideCatch = false; } }; struct ControlFlowFinally : public ControlFlowUnwind { QQmlJS::AST::Finally *finally; bool insideFinally = false; ControlFlowFinally(Codegen *cg, QQmlJS::AST::Finally *finally) : ControlFlowUnwind(cg, Finally), finally(finally) { Q_ASSERT(finally != nullptr); setupUnwindHandler(); generator()->setUnwindHandler(&unwindLabel); } virtual bool requiresUnwind() override { return !insideFinally; } BytecodeGenerator::ExceptionHandler *unwindHandler() override { return insideFinally ? parentUnwindHandler() : ControlFlowUnwind::unwindHandler(); } ~ControlFlowFinally() { // emit code for unwinding unwindLabel.link(); Codegen::RegisterScope scope(cg); insideFinally = true; int returnValueTemp = -1; if (cg->requiresReturnValue) { returnValueTemp = generator()->newRegister(); Instruction::MoveReg move; move.srcReg = cg->_returnAddress; move.destReg = returnValueTemp; generator()->addInstruction(move); } int exceptionTemp = generator()->newRegister(); Instruction::GetException instr; generator()->addInstruction(instr); Reference::fromStackSlot(cg, exceptionTemp).storeConsumeAccumulator(); generator()->setUnwindHandler(parentUnwindHandler()); cg->statement(finally->statement); insideFinally = false; if (cg->requiresReturnValue) { Instruction::MoveReg move; move.srcReg = returnValueTemp; move.destReg = cg->_returnAddress; generator()->addInstruction(move); } Reference::fromStackSlot(cg, exceptionTemp).loadInAccumulator(); Instruction::SetException setException; generator()->addInstruction(setException); emitUnwindHandler(); } }; } } // QV4::Compiler namespace QT_END_NAMESPACE #endif