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 | |
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')
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 98 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth.cpp | 5 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 13 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 9 |
5 files changed, 97 insertions, 30 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; } diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index 16680809bb..3f234f1394 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -263,11 +263,12 @@ void dumpBytecode(const char *code, int len) d << instr.arg; MOTH_END_INSTR(CallBuiltinThrow) - MOTH_BEGIN_INSTR(HasException) + MOTH_BEGIN_INSTR(GetException) d << instr.result; MOTH_END_INSTR(HasException) - MOTH_BEGIN_INSTR(SetExceptionFlag) + MOTH_BEGIN_INSTR(SetException) + d << instr.exception; MOTH_END_INSTR(SetExceptionFlag) MOTH_BEGIN_INSTR(CallBuiltinUnwindException) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 270950eeb9..6625018443 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -108,8 +108,8 @@ QT_BEGIN_NAMESPACE F(CallGlobalLookup, callGlobalLookup) \ F(SetExceptionHandler, setExceptionHandler) \ F(CallBuiltinThrow, callBuiltinThrow) \ - F(HasException, hasException) \ - F(SetExceptionFlag, setExceptionFlag) \ + F(GetException, getException) \ + F(SetException, setException) \ F(CallBuiltinUnwindException, callBuiltinUnwindException) \ F(CallBuiltinPushCatchScope, callBuiltinPushCatchScope) \ F(CallBuiltinPushScope, callBuiltinPushScope) \ @@ -500,12 +500,13 @@ union Instr MOTH_INSTR_HEADER Param arg; }; - struct instr_hasException { + struct instr_getException { MOTH_INSTR_HEADER Param result; }; - struct instr_setExceptionFlag { + struct instr_setException { MOTH_INSTR_HEADER + Param exception; }; struct instr_callBuiltinUnwindException { MOTH_INSTR_HEADER @@ -861,8 +862,8 @@ union Instr instr_callActivationProperty callActivationProperty; instr_callGlobalLookup callGlobalLookup; instr_callBuiltinThrow callBuiltinThrow; - instr_hasException hasException; - instr_setExceptionFlag setExceptionFlag; + instr_getException getException; + instr_setException setException; instr_setExceptionHandler setExceptionHandler; instr_callBuiltinUnwindException callBuiltinUnwindException; instr_callBuiltinPushCatchScope callBuiltinPushCatchScope; diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 4c8117527c..e4402d4aa0 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -83,7 +83,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, for (quint32 i = 0; i < compiledFunction->nLocals; ++i) internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); - canUseSimpleCall = compiledFunction->flags & CompiledData::Function::CanUseSimpleCall; + canUseSimpleCall = false;//compiledFunction->flags & CompiledData::Function::CanUseSimpleCall; } Function::~Function() diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 328c395b52..1f1e202a2d 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -660,14 +660,15 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) CHECK_EXCEPTION; MOTH_END_INSTR(CallBuiltinThrow) - MOTH_BEGIN_INSTR(HasException) - *VALUEPTR(instr.result) = QV4::Encode(engine->hasException); + MOTH_BEGIN_INSTR(GetException) + *VALUEPTR(instr.result) = engine->hasException ? *engine->exceptionValue : Primitive::emptyValue(); engine->hasException = false; MOTH_END_INSTR(HasException) - MOTH_BEGIN_INSTR(SetExceptionFlag) + MOTH_BEGIN_INSTR(SetException) + *engine->exceptionValue = VALUE(instr.exception); engine->hasException = true; - MOTH_END_INSTR(SetExceptionFlag) + MOTH_END_INSTR(SetException) MOTH_BEGIN_INSTR(CallBuiltinUnwindException) STOREVALUE(instr.result, Runtime::method_unwindException(engine)); |