aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@qt.io>2017-06-20 14:30:03 +0200
committerErik Verbruggen <erik.verbruggen@qt.io>2017-06-21 09:16:46 +0000
commit8a0dad67ae16a6b49e40de37ea406ee7bf3c64d2 (patch)
tree042cd85b052cc82c9e7c486330515fef579f728c /src/qml
parent7daf31f2858d6f77e7e5a3be1146c43552fcb4e8 (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.cpp98
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp5
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h13
-rw-r--r--src/qml/jsruntime/qv4function.cpp2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp9
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));