diff options
| author | Lars Knoll <lars.knoll@qt.io> | 2017-06-20 14:30:03 +0200 |
|---|---|---|
| committer | Erik Verbruggen <erik.verbruggen@qt.io> | 2017-06-21 09:16:46 +0000 |
| commit | 8a0dad67ae16a6b49e40de37ea406ee7bf3c64d2 (patch) | |
| tree | 042cd85b052cc82c9e7c486330515fef579f728c /src/qml/compiler/qv4codegen.cpp | |
| parent | 7daf31f2858d6f77e7e5a3be1146c43552fcb4e8 (diff) | |
Fix exception handling
Fix all exception handling related test failures
in test262.
Change-Id: Iba50238627c31705a4878b43abbb8f20f0ecee88
Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
Diffstat (limited to 'src/qml/compiler/qv4codegen.cpp')
| -rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 98 |
1 files changed, 81 insertions, 17 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 93cee2f9ae..da2e8ea224 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -157,6 +157,18 @@ struct ControlFlow { return parent ? parent->exceptionHandler() : 0; } + virtual void handleThrow(const Reference &expr) { + Handler h = getHandler(ControlFlow::Throw); + if (h.tempIndex >= 0) { + Reference val = Reference::fromConst(cg, QV4::Encode(h.value)); + Reference temp = Reference::fromTemp(cg, h.tempIndex); + temp.store(val); + } + Instruction::CallBuiltinThrow instr; + instr.arg = expr.asRValue(); + generator()->addInstruction(instr); + } + protected: QString loopLabel() const { QString label; @@ -220,7 +232,6 @@ struct ControlFlowUnwind : public ControlFlow Reference::fromTemp(cg, controlFlowTemp).store(Reference::fromConst(cg, QV4::Encode::undefined())); // we'll need at least a handler for throw getHandler(Throw); - generator()->setExceptionHandler(&unwindLabel); } void emitUnwindHandler() @@ -278,6 +289,7 @@ struct ControlFlowWith : public ControlFlowUnwind ControlFlowWith(Codegen *cg) : ControlFlowUnwind(cg, With) { + generator()->setExceptionHandler(&unwindLabel); } virtual ~ControlFlowWith() { @@ -295,37 +307,65 @@ struct ControlFlowWith : public ControlFlowUnwind struct ControlFlowCatch : public ControlFlowUnwind { AST::Catch *catchExpression; + bool insideCatch = false; + BytecodeGenerator::ExceptionHandler exceptionLabel; + BytecodeGenerator::ExceptionHandler catchUnwindLabel; ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression) - : ControlFlowUnwind(cg, Catch), catchExpression(catchExpression) + : ControlFlowUnwind(cg, Catch), catchExpression(catchExpression), + exceptionLabel(generator()->newExceptionHandler()), + catchUnwindLabel(generator()->newExceptionHandler()) { + generator()->setExceptionHandler(&exceptionLabel); } virtual Handler getHandler(HandlerType type, const QString &label = QString()) { - // if it's no throw, ignore the catch handler and go directly to the parent - // handler - if (type == Throw) - return ControlFlowUnwind::getHandler(type, label); - return ControlFlow::getHandler(type, label); + Handler h = ControlFlowUnwind::getHandler(type, label); + if (insideCatch) + // if we're inside the catch block, we need to jump to the pop scope + // instruction at the end of the catch block, not the unwind handler + h.linkLabel = catchUnwindLabel; + else if (type == Throw) + // if we're inside the try block, we need to jump to the catch block, + // not the unwind handler + h.linkLabel = exceptionLabel; + return h; + } + + virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { + return insideCatch ? &catchUnwindLabel : &exceptionLabel; } ~ControlFlowCatch() { // emit code for unwinding - unwindLabel.link(); ++cg->_function->insideWithOrCatch; + insideCatch = true; + + // exceptions inside the try block go here + exceptionLabel.link(); Reference name = Reference::fromName(cg, catchExpression->name.toString()); Instruction::CallBuiltinPushCatchScope pushCatchScope; pushCatchScope.name = name.nameIndex; generator()->addInstruction(pushCatchScope); - generator()->setExceptionHandler(parentExceptionHandler()); + // clear the unwind temp for exceptions, we want to resume normal code flow afterwards + Reference::fromTemp(cg, controlFlowTemp).store(Reference::fromConst(cg, QV4::Encode::undefined())); + generator()->setExceptionHandler(&catchUnwindLabel); cg->statement(catchExpression->statement); + --cg->_function->insideWithOrCatch; + insideCatch = false; + // exceptions inside catch and break/return statements go here + catchUnwindLabel.link(); Instruction::CallBuiltinPopScope pop; generator()->addInstruction(pop); + // break/continue/return statements in try go here + unwindLabel.link(); + generator()->setExceptionHandler(parentExceptionHandler()); + emitUnwindHandler(); } }; @@ -333,11 +373,26 @@ struct ControlFlowCatch : public ControlFlowUnwind struct ControlFlowFinally : public ControlFlowUnwind { AST::Finally *finally; + bool insideFinally = false; + int exceptionTemp = -1; ControlFlowFinally(Codegen *cg, AST::Finally *finally) : ControlFlowUnwind(cg, Finally), finally(finally) { Q_ASSERT(finally != 0); + generator()->setExceptionHandler(&unwindLabel); + } + + virtual Handler getHandler(HandlerType type, const QString &label = QString()) { + // if we're inside the finally block, any exceptions etc. should + // go directly to the parent handler + if (insideFinally) + return ControlFlow::getHandler(type, label); + return ControlFlowUnwind::getHandler(type, label); + } + + virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { + return insideFinally ? parentExceptionHandler() : ControlFlowUnwind::exceptionHandler(); } ~ControlFlowFinally() { @@ -346,21 +401,26 @@ struct ControlFlowFinally : public ControlFlowUnwind Codegen::TempScope scope(cg); - Reference hasException = Reference::fromTemp(cg); - Instruction::HasException instr; - instr.result = hasException.asLValue(); + insideFinally = true; + exceptionTemp = generator()->newTemp(); + Reference exception = Reference::fromTemp(cg, exceptionTemp); + Instruction::GetException instr; + instr.result = exception.asLValue(); generator()->addInstruction(instr); generator()->setExceptionHandler(parentExceptionHandler()); cg->statement(finally->statement); + insideFinally = false; emitUnwindHandler(); } virtual void emitForThrowHandling() { // reset the exception flag, that got cleared before executing the statements in finally - Instruction::SetExceptionFlag setFlag; - generator()->addInstruction(setFlag); + Instruction::SetException setException; + Q_ASSERT(exceptionTemp != -1); + setException.exception = Reference::fromTemp(cg, exceptionTemp).asRValue(); + generator()->addInstruction(setException); } }; @@ -2932,9 +2992,13 @@ bool Codegen::visit(ThrowStatement *ast) Reference expr = expression(ast->expression); - Instruction::CallBuiltinThrow instr; - instr.arg = expr.asRValue(); - bytecodeGenerator->addInstruction(instr); + if (_controlFlow) { + _controlFlow->handleThrow(expr); + } else { + Instruction::CallBuiltinThrow instr; + instr.arg = expr.asRValue(); + bytecodeGenerator->addInstruction(instr); + } return false; } |
