aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h44
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp19
-rw-r--r--src/qml/compiler/qv4codegen.cpp135
-rw-r--r--src/qml/compiler/qv4codegen_p.h7
-rw-r--r--src/qml/compiler/qv4compilercontrolflow_p.h334
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp19
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h10
-rw-r--r--src/qml/jit/qv4assembler.cpp105
-rw-r--r--src/qml/jit/qv4assembler_p.h5
-rw-r--r--src/qml/jit/qv4baselinejit.cpp22
-rw-r--r--src/qml/jit/qv4baselinejit_p.h7
-rw-r--r--src/qml/jsruntime/qv4engine_p.h4
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp68
-rw-r--r--tests/auto/qml/ecmascripttests/TestExpectations2
14 files changed, 350 insertions, 431 deletions
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h
index b12584d41f..78ce3624ea 100644
--- a/src/qml/compiler/qv4bytecodegenerator_p.h
+++ b/src/qml/compiler/qv4bytecodegenerator_p.h
@@ -81,14 +81,6 @@ public:
if (mode == LinkNow)
link();
}
- static Label returnLabel() {
- Label l;
- l.index = INT_MAX;
- return l;
- }
- bool isReturn() const {
- return index == INT_MAX;
- }
void link() {
Q_ASSERT(index >= 0);
@@ -96,6 +88,7 @@ public:
generator->labels[index] = generator->instructions.size();
generator->clearLastInstruction();
}
+ bool isValid() const { return generator != nullptr; }
BytecodeGenerator *generator = nullptr;
int index = -1;
@@ -192,6 +185,12 @@ public:
return addJumpInstruction(data);
}
+ Q_REQUIRED_RESULT Jump jumpNoException()
+ {
+ Instruction::JumpNoException data;
+ return addJumpInstruction(data);
+ }
+
void jumpStrictEqual(const StackSlot &lhs, const Label &target)
{
Instruction::CmpStrictEqual cmp;
@@ -208,22 +207,6 @@ public:
addJumpInstruction(Instruction::JumpTrue()).link(target);
}
- Q_REQUIRED_RESULT Jump jumpStrictEqualStackSlotInt(const StackSlot &lhs, int rhs)
- {
- Instruction::JumpStrictEqualStackSlotInt data;
- data.lhs = lhs;
- data.rhs = rhs;
- return addJumpInstruction(data);
- }
-
- Q_REQUIRED_RESULT Jump jumpStrictNotEqualStackSlotInt(const StackSlot &lhs, int rhs)
- {
- Instruction::JumpStrictNotEqualStackSlotInt data;
- data.lhs = lhs;
- data.rhs = rhs;
- return addJumpInstruction(data);
- }
-
void setUnwindHandler(ExceptionHandler *handler)
{
currentExceptionHandler = handler;
@@ -235,6 +218,19 @@ public:
addJumpInstruction(data).link(*handler);
}
+ void unwindToLabel(int level, const Label &target)
+ {
+ if (level) {
+ Instruction::UnwindToLabel unwind;
+ unwind.level = level;
+ addJumpInstruction(unwind).link(target);
+ } else {
+ jump().link(target);
+ }
+ }
+
+
+
void setLocation(const QQmlJS::AST::SourceLocation &loc);
ExceptionHandler *exceptionHandler() const {
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp
index 27182dcc72..ccd54c7444 100644
--- a/src/qml/compiler/qv4bytecodehandler.cpp
+++ b/src/qml/compiler/qv4bytecodehandler.cpp
@@ -273,6 +273,13 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint
addLabel(code - start + offset);
COLLECTOR_END_INSTR(SetUnwindHandler)
+ COLLECTOR_BEGIN_INSTR(UnwindDispatch)
+ COLLECTOR_END_INSTR(UnwindDispatch)
+
+ COLLECTOR_BEGIN_INSTR(UnwindToLabel)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(UnwindToLabel)
+
COLLECTOR_BEGIN_INSTR(ThrowException)
COLLECTOR_END_INSTR(ThrowException)
@@ -372,6 +379,10 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint
addLabel(code - start + offset);
COLLECTOR_END_INSTR(JumpFalse)
+ COLLECTOR_BEGIN_INSTR(JumpNoException)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpNoException)
+
COLLECTOR_BEGIN_INSTR(JumpNotUndefined)
addLabel(code - start + offset);
COLLECTOR_END_INSTR(JumpNotUndefined)
@@ -422,14 +433,6 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint
COLLECTOR_BEGIN_INSTR(CmpInstanceOf)
COLLECTOR_END_INSTR(CmpInstanceOf)
- COLLECTOR_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- addLabel(code - start + offset);
- COLLECTOR_END_INSTR(JumpStrictEqualStackSlotInt)
-
- COLLECTOR_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- addLabel(code - start + offset);
- COLLECTOR_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
COLLECTOR_BEGIN_INSTR(UNot)
COLLECTOR_END_INSTR(UNot)
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index ec2c0dad3e..9337f6c625 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -90,7 +90,7 @@ static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGene
Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
: _module(nullptr)
- , _returnAddress(0)
+ , _returnAddress(-1)
, _context(nullptr)
, _labelledStatement(nullptr)
, jsUnitGenerator(jsUnitGenerator)
@@ -2376,6 +2376,8 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
savedBytecodeGenerator = bytecodeGenerator;
bytecodeGenerator = &bytecode;
bytecodeGenerator->setLocation(ast->firstSourceLocation());
+ BytecodeGenerator::Label *savedReturnLabel = _returnLabel;
+ _returnLabel = nullptr;
// reserve the js stack frame (Context & js Function & accumulator)
bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size());
@@ -2386,8 +2388,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
int returnAddress = -1;
bool _requiresReturnValue = _context->requiresImplicitReturnValue();
qSwap(requiresReturnValue, _requiresReturnValue);
- if (requiresReturnValue)
- returnAddress = bytecodeGenerator->newRegister();
+ returnAddress = bytecodeGenerator->newRegister();
qSwap(_returnAddress, returnAddress);
// register the lexical scope for global code
@@ -2438,19 +2439,21 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
statementList(body);
+ bytecodeGenerator->setLocation(ast->lastSourceLocation());
_context->emitBlockFooter(this);
- if (hasError || !endsWithReturn(_module, body)) {
- bytecodeGenerator->setLocation(ast->lastSourceLocation());
- if (requiresReturnValue) {
- if (_returnAddress >= 0) {
- Instruction::LoadReg load;
- load.reg = Moth::StackSlot::createRegister(_returnAddress);
- bytecodeGenerator->addInstruction(load);
- }
+ if (_returnLabel || hasError || !endsWithReturn(_module, body)) {
+ if (_returnLabel)
+ _returnLabel->link();
+
+ if (_returnLabel || requiresReturnValue) {
+ Instruction::LoadReg load;
+ load.reg = Moth::StackSlot::createRegister(_returnAddress);
+ bytecodeGenerator->addInstruction(load);
} else {
Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
}
+
bytecodeGenerator->addInstruction(Instruction::Ret());
}
@@ -2470,6 +2473,8 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
qSwap(requiresReturnValue, _requiresReturnValue);
qSwap(_inFormalParameterList, inFormalParameterList);
bytecodeGenerator = savedBytecodeGenerator;
+ delete _returnLabel;
+ _returnLabel = savedReturnLabel;
controlFlow = savedControlFlow;
_functionContext = savedFunctionContext;
@@ -2498,8 +2503,8 @@ bool Codegen::visit(BreakStatement *ast)
return false;
}
- ControlFlow::Handler h = controlFlow->getHandler(ControlFlow::Break, ast->label.toString());
- if (h.type == ControlFlow::Invalid) {
+ ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Break, ast->label.toString());
+ if (!target.linkLabel.isValid()) {
if (ast->label.isEmpty())
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
else
@@ -2507,7 +2512,7 @@ bool Codegen::visit(BreakStatement *ast)
return false;
}
- controlFlow->jumpToHandler(h);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
return false;
}
@@ -2524,8 +2529,8 @@ bool Codegen::visit(ContinueStatement *ast)
return false;
}
- ControlFlow::Handler h = controlFlow->getHandler(ControlFlow::Continue, ast->label.toString());
- if (h.type == ControlFlow::Invalid) {
+ ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Continue, ast->label.toString());
+ if (!target.linkLabel.isValid()) {
if (ast->label.isEmpty())
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
else
@@ -2533,7 +2538,7 @@ bool Codegen::visit(ContinueStatement *ast)
return false;
}
- controlFlow->jumpToHandler(h);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
return false;
}
@@ -2628,51 +2633,53 @@ bool Codegen::visit(ForEachStatement *ast)
BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label done = bytecodeGenerator->newLabel();
- bytecodeGenerator->jump().link(in);
-
- ControlFlowLoop flow(this, &end, &in);
-
- BytecodeGenerator::Label body = bytecodeGenerator->label();
-
- // each iteration gets it's own context, as per spec
{
- RegisterScope innerScope(this);
- ControlFlowBlock controlFlow(this, ast);
+ ControlFlowLoop flow(this, &end, &in, /*requiresUnwind*/ true);
+ bytecodeGenerator->jump().link(in);
- if (ExpressionNode *e = ast->lhs->expressionCast()) {
- if (AST::Pattern *p = e->patternCast()) {
- RegisterScope scope(this);
- destructurePattern(p, lhsValue);
- } else {
- Reference lhs = expression(e);
+ BytecodeGenerator::Label body = bytecodeGenerator->label();
+
+ // each iteration gets it's own context, as per spec
+ {
+ RegisterScope innerScope(this);
+ ControlFlowBlock controlFlow(this, ast);
+
+ if (ExpressionNode *e = ast->lhs->expressionCast()) {
+ if (AST::Pattern *p = e->patternCast()) {
+ RegisterScope scope(this);
+ destructurePattern(p, lhsValue);
+ } else {
+ Reference lhs = expression(e);
+ if (hasError)
+ goto error;
+ lhs = lhs.asLValue();
+ lhsValue.loadInAccumulator();
+ lhs.storeConsumeAccumulator();
+ }
+ } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) {
+ initializeAndDestructureBindingElement(p, lhsValue);
if (hasError)
goto error;
- lhs = lhs.asLValue();
- lhsValue.loadInAccumulator();
- lhs.storeConsumeAccumulator();
+ } else {
+ Q_UNREACHABLE();
}
- } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) {
- initializeAndDestructureBindingElement(p, lhsValue);
- if (hasError)
- goto error;
- } else {
- Q_UNREACHABLE();
- }
- statement(ast->statement);
- setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
+ statement(ast->statement);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
- }
+ }
- error:
- in.link();
- iterator.loadInAccumulator();
- Instruction::IteratorNext next;
- next.value = lhsValue.stackSlot();
- bytecodeGenerator->addInstruction(next);
- bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body);
- BytecodeGenerator::Jump done = bytecodeGenerator->jump();
+ error:
+ in.link();
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = lhsValue.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body);
+ bytecodeGenerator->jump().link(done);
+ }
end.link();
@@ -2796,13 +2803,11 @@ bool Codegen::visit(LabelledStatement *ast)
void Codegen::emitReturn(const Reference &expr)
{
- if (controlFlow && controlFlow->returnRequiresUnwind()) {
- if (_returnAddress >= 0)
- (void) expr.storeOnStack(_returnAddress);
- else
- expr.loadInAccumulator();
- ControlFlow::Handler h = controlFlow->getHandler(ControlFlow::Return);
- controlFlow->jumpToHandler(h);
+ ControlFlow::UnwindTarget target = controlFlow ? controlFlow->unwindTarget(ControlFlow::Return) : ControlFlow::UnwindTarget();
+ if (target.linkLabel.isValid() && target.unwindLevel) {
+ Q_ASSERT(_returnAddress >= 0);
+ (void) expr.storeOnStack(_returnAddress);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
} else {
expr.loadInAccumulator();
bytecodeGenerator->addInstruction(Instruction::Ret());
@@ -2923,13 +2928,9 @@ bool Codegen::visit(ThrowStatement *ast)
if (hasError)
return false;
- if (controlFlow) {
- controlFlow->handleThrow(expr);
- } else {
- expr.loadInAccumulator();
- Instruction::ThrowException instr;
- bytecodeGenerator->addInstruction(instr);
- }
+ expr.loadInAccumulator();
+ Instruction::ThrowException instr;
+ bytecodeGenerator->addInstruction(instr);
return false;
}
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 80ef8edfcc..697da71480 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -660,6 +660,12 @@ public:
Module *module() const { return _module; }
+ BytecodeGenerator::Label returnLabel() {
+ if (!_returnLabel)
+ _returnLabel = new BytecodeGenerator::Label(bytecodeGenerator->newLabel());
+ return *_returnLabel;
+ }
+
protected:
friend class ScanFunctions;
friend struct ControlFlow;
@@ -674,6 +680,7 @@ protected:
AST::LabelledStatement *_labelledStatement;
QV4::Compiler::JSUnitGenerator *jsUnitGenerator;
BytecodeGenerator *bytecodeGenerator = nullptr;
+ Moth::BytecodeGenerator::Label *_returnLabel = nullptr;
bool _strictMode;
bool useFastLookups = true;
bool requiresReturnValue = false;
diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h
index b6c207e68c..9ab581f015 100644
--- a/src/qml/compiler/qv4compilercontrolflow_p.h
+++ b/src/qml/compiler/qv4compilercontrolflow_p.h
@@ -53,6 +53,7 @@
#include <private/qv4global_p.h>
#include <private/qv4codegen_p.h>
#include <private/qqmljsast_p.h>
+#include <private/qv4bytecodegenerator_p.h>
QT_BEGIN_NAMESPACE
@@ -73,20 +74,15 @@ struct ControlFlow {
Catch
};
- enum HandlerType {
- Invalid,
+ enum UnwindType {
Break,
Continue,
- Return,
- Throw
+ Return
};
- struct Handler {
- HandlerType type;
- QString label;
+ struct UnwindTarget {
BytecodeGenerator::Label linkLabel;
- int tempIndex;
- int value;
+ int unwindLevel;
};
Codegen *cg;
@@ -103,82 +99,44 @@ struct ControlFlow {
cg->controlFlow = parent;
}
- void emitReturnStatement() const {
- if (cg->_returnAddress >= 0) {
- Instruction::LoadReg load;
- load.reg = Moth::StackSlot::createRegister(cg->_returnAddress);
- generator()->addInstruction(load);
- }
- Instruction::Ret ret;
- cg->bytecodeGenerator->addInstruction(ret);
- }
-
- void jumpToHandler(const Handler &h) {
- if (h.linkLabel.isReturn()) {
- emitReturnStatement();
- } else {
- if (h.tempIndex >= 0)
- Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex);
- cg->bytecodeGenerator->jump().link(h.linkLabel);
- }
- }
-
- bool returnRequiresUnwind() const {
- const ControlFlow *f = this;
- while (f) {
- if (f->type == Finally)
- return true;
- f = f->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;
}
- return false;
+ if (type == Return)
+ return UnwindTarget{ cg->returnLabel(), level };
+ return UnwindTarget();
}
virtual QString label() const { return QString(); }
- bool isSimple() const {
- return type == Loop;
+protected:
+ virtual BytecodeGenerator::Label getUnwindTarget(UnwindType, const QString & = QString()) {
+ return BytecodeGenerator::Label();
}
-
- Handler getParentHandler(HandlerType type, const QString &label = QString()) {
- if (parent)
- return parent->getHandler(type, label);
- switch (type) {
- case Break:
- case Continue:
- return { Invalid, QString(), {}, -1, 0 };
- case Return:
- case Throw:
- return { type, QString(), BytecodeGenerator::Label::returnLabel(), -1, 0 };
- case Invalid:
- break;
- }
- Q_ASSERT(false);
- Q_UNREACHABLE();
+ virtual bool requiresUnwind() {
+ return false;
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) = 0;
-
- BytecodeGenerator::ExceptionHandler *parentExceptionHandler() {
- return parent ? parent->exceptionHandler() : nullptr;
+public:
+ BytecodeGenerator::ExceptionHandler *parentUnwindHandler() {
+ return parent ? parent->unwindHandler() : nullptr;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return parentExceptionHandler();
+ virtual BytecodeGenerator::ExceptionHandler *unwindHandler() {
+ return parentUnwindHandler();
}
- virtual void handleThrow(const Reference &expr) {
- Reference e = expr;
- Handler h = getHandler(ControlFlow::Throw);
- if (h.tempIndex >= 0) {
- e = e.storeOnStack();
- Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex);
- }
- e.loadInAccumulator();
- Instruction::ThrowException instr;
- generator()->addInstruction(instr);
- }
-
protected:
QString loopLabel() const {
QString label;
@@ -193,119 +151,87 @@ protected:
}
};
-struct ControlFlowLoop : public ControlFlow
-{
- QString loopLabel;
- BytecodeGenerator::Label *breakLabel = nullptr;
- BytecodeGenerator::Label *continueLabel = nullptr;
-
- ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr)
- : ControlFlow(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel)
- {
- }
-
- virtual QString label() const { return loopLabel; }
-
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- switch (type) {
- case Break:
- if (breakLabel && (label.isEmpty() || label == loopLabel))
- return { type, loopLabel, *breakLabel, -1, 0 };
- break;
- case Continue:
- if (continueLabel && (label.isEmpty() || label == loopLabel))
- return { type, loopLabel, *continueLabel, -1, 0 };
- break;
- case Return:
- case Throw:
- break;
- case Invalid:
- Q_ASSERT(false);
- Q_UNREACHABLE();
- }
- return getParentHandler(type, label);
- }
-
-};
-
struct ControlFlowUnwind : public ControlFlow
{
BytecodeGenerator::ExceptionHandler unwindLabel;
- int controlFlowTemp;
- QVector<Handler> handlers;
ControlFlowUnwind(Codegen *cg, Type type)
: ControlFlow(cg, type)
{
- Q_ASSERT(type != Loop);
}
- void setupExceptionHandler()
+ void setupUnwindHandler()
{
unwindLabel = generator()->newExceptionHandler();
- controlFlowTemp = static_cast<int>(generator()->newRegister());
- Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
- // we'll need at least a handler for throw
- getHandler(Throw);
}
void emitUnwindHandler()
{
- Q_ASSERT(!isSimple());
-
- Reference temp = Reference::fromStackSlot(cg, controlFlowTemp);
- for (const auto &h : qAsConst(handlers)) {
- Handler parentHandler = getParentHandler(h.type, h.label);
-
- if (h.type == Throw || parentHandler.tempIndex >= 0) {
- BytecodeGenerator::Label skip = generator()->newLabel();
- generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip);
- if (h.type == Throw)
- emitForThrowHandling();
- jumpToHandler(parentHandler);
- skip.link();
- } else {
- if (parentHandler.linkLabel.isReturn()) {
- BytecodeGenerator::Label skip = generator()->newLabel();
- generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip);
- emitReturnStatement();
- skip.link();
- } else {
- generator()->jumpStrictEqualStackSlotInt(temp.stackSlot(), h.value).link(parentHandler.linkLabel);
- }
- }
+ Q_ASSERT(requiresUnwind());
+
+ Instruction::UnwindDispatch dispatch;
+ generator()->addInstruction(dispatch);
+ }
+
+ virtual BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return unwindLabel.isValid() ? &unwindLabel : parentUnwindHandler();
+ }
+};
+
+struct ControlFlowLoop : public ControlFlowUnwind
+{
+ QString loopLabel;
+ BytecodeGenerator::Label *breakLabel = nullptr;
+ BytecodeGenerator::Label *continueLabel = nullptr;
+ bool _requiresUnwind;
+
+ ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr, bool requiresUnwind = false)
+ : ControlFlowUnwind(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel), _requiresUnwind(requiresUnwind)
+ {
+ if (_requiresUnwind) {
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
}
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- for (const auto &h : qAsConst(handlers)) {
- if (h.type == type && h.label == label)
- return h;
+ ~ControlFlowLoop() {
+ if (_requiresUnwind) {
+ unwindLabel.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
+ emitUnwindHandler();
}
- Handler h = {
- type,
- label,
- unwindLabel,
- controlFlowTemp,
- handlers.size()
- };
- handlers.append(h);
- return h;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return unwindLabel.isValid() ? &unwindLabel : parentExceptionHandler();
+ bool requiresUnwind() override {
+ return _requiresUnwind;
}
- virtual void emitForThrowHandling() { }
+ 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)
{
- setupExceptionHandler();
+ setupUnwindHandler();
// assumes the with object is in the accumulator
Instruction::PushWithContext pushScope;
@@ -313,16 +239,22 @@ struct ControlFlowWith : public ControlFlowUnwind
generator()->setUnwindHandler(&unwindLabel);
}
- virtual ~ControlFlowWith() {
+ ~ControlFlowWith() {
// emit code for unwinding
unwindLabel.link();
- generator()->setUnwindHandler(parentExceptionHandler());
+ generator()->setUnwindHandler(parentUnwindHandler());
Instruction::PopContext pop;
generator()->addInstruction(pop);
emitUnwindHandler();
}
+
+ bool requiresUnwind() override {
+ return true;
+ }
+
+
};
struct ControlFlowBlock : public ControlFlowUnwind
@@ -334,16 +266,16 @@ struct ControlFlowBlock : public ControlFlowUnwind
block->emitBlockHeader(cg);
if (block->requiresExecutionContext) {
- setupExceptionHandler();
+ setupUnwindHandler();
generator()->setUnwindHandler(&unwindLabel);
}
}
virtual ~ControlFlowBlock() {
// emit code for unwinding
- if (block->requiresExecutionContext ) {
+ if (block->requiresExecutionContext) {
unwindLabel.link();
- generator()->setUnwindHandler(parentExceptionHandler());
+ generator()->setUnwindHandler(parentUnwindHandler());
}
block->emitBlockFooter(cg);
@@ -352,10 +284,9 @@ struct ControlFlowBlock : public ControlFlowUnwind
emitUnwindHandler();
cg->leaveBlock();
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- if (!block->requiresExecutionContext )
- return getParentHandler(type, label);
- return ControlFlowUnwind::getHandler(type, label);
+
+ virtual bool requiresUnwind() override {
+ return block->requiresExecutionContext;
}
Context *block;
@@ -366,54 +297,39 @@ struct ControlFlowCatch : public ControlFlowUnwind
AST::Catch *catchExpression;
bool insideCatch = false;
BytecodeGenerator::ExceptionHandler exceptionLabel;
- BytecodeGenerator::ExceptionHandler catchUnwindLabel;
bool oldLookupByName;
ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression)
: ControlFlowUnwind(cg, Catch), catchExpression(catchExpression),
- exceptionLabel(generator()->newExceptionHandler()),
- catchUnwindLabel(generator()->newExceptionHandler())
+ exceptionLabel(generator()->newExceptionHandler())
{
- setupExceptionHandler();
generator()->setUnwindHandler(&exceptionLabel);
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- Handler h = getParentHandler(type, label);
- if (h.type == Invalid)
- return h;
- 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 bool requiresUnwind() override {
+ return true;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return insideCatch ? &catchUnwindLabel : &exceptionLabel;
+ 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);
- // clear the unwind temp for exceptions, we want to resume normal code flow afterwards
- Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
- generator()->setUnwindHandler(&catchUnwindLabel);
+ generator()->setUnwindHandler(&unwindLabel);
if (catchExpression->patternElement->bindingIdentifier.isEmpty())
// destructuring pattern
@@ -421,19 +337,17 @@ struct ControlFlowCatch : public ControlFlowUnwind
// skip the additional block
cg->statementList(catchExpression->statement->statements);
- insideCatch = false;
-
// exceptions inside catch and break/return statements go here
- catchUnwindLabel.link();
+ unwindLabel.link();
block->emitBlockFooter(cg);
cg->leaveBlock();
- // break/continue/return statements in try go here
- unwindLabel.link();
- generator()->setUnwindHandler(parentExceptionHandler());
+ noException.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
emitUnwindHandler();
+ insideCatch = false;
}
};
@@ -447,20 +361,16 @@ struct ControlFlowFinally : public ControlFlowUnwind
: ControlFlowUnwind(cg, Finally), finally(finally)
{
Q_ASSERT(finally != nullptr);
- setupExceptionHandler();
+ setupUnwindHandler();
generator()->setUnwindHandler(&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 getParentHandler(type, label);
- return ControlFlowUnwind::getHandler(type, label);
+ virtual bool requiresUnwind() override {
+ return !insideFinally;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return insideFinally ? parentExceptionHandler() : ControlFlowUnwind::exceptionHandler();
+ BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return insideFinally ? parentUnwindHandler() : ControlFlowUnwind::unwindHandler();
}
~ControlFlowFinally() {
@@ -469,34 +379,22 @@ struct ControlFlowFinally : public ControlFlowUnwind
Codegen::RegisterScope scope(cg);
- Moth::StackSlot retVal = Moth::StackSlot::createRegister(generator()->newRegister());
- Instruction::StoreReg storeRetVal;
- storeRetVal.reg = retVal;
- generator()->addInstruction(storeRetVal);
-
insideFinally = true;
exceptionTemp = generator()->newRegister();
Instruction::GetException instr;
generator()->addInstruction(instr);
Reference::fromStackSlot(cg, exceptionTemp).storeConsumeAccumulator();
- generator()->setUnwindHandler(parentExceptionHandler());
+ generator()->setUnwindHandler(parentUnwindHandler());
cg->statement(finally->statement);
insideFinally = false;
- Instruction::LoadReg loadRetVal;
- loadRetVal.reg = retVal;
- generator()->addInstruction(loadRetVal);
-
- emitUnwindHandler();
- }
-
- virtual void emitForThrowHandling() {
- // reset the exception flag, that got cleared before executing the statements in finally
Reference::fromStackSlot(cg, exceptionTemp).loadInAccumulator();
+
Instruction::SetException setException;
- Q_ASSERT(exceptionTemp != -1);
generator()->addInstruction(setException);
+
+ emitUnwindHandler();
}
};
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index ec9f3fc972..4a11f46dc5 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -391,6 +391,13 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << "<null>";
MOTH_END_INSTR(SetUnwindHandler)
+ MOTH_BEGIN_INSTR(UnwindDispatch)
+ MOTH_END_INSTR(UnwindDispatch)
+
+ MOTH_BEGIN_INSTR(UnwindToLabel)
+ d << "(" << level << ") " << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(UnwindToLabel)
+
MOTH_BEGIN_INSTR(ThrowException)
MOTH_END_INSTR(ThrowException)
@@ -511,6 +518,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpNotUndefined)
+ MOTH_BEGIN_INSTR(JumpNoException)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpNoException)
+
MOTH_BEGIN_INSTR(JumpEmpty)
d << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpEmpty)
@@ -561,14 +572,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpStrictNotEqual)
- MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET();
- MOTH_END_INSTR(JumpStrictEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET();
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
MOTH_BEGIN_INSTR(UNot)
MOTH_END_INSTR(UNot)
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index ea72be12fe..3adb9c2e0f 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -112,6 +112,8 @@ QT_BEGIN_NAMESPACE
#define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 4, name, base, argc, argv)
#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 4, name, base, argc, argv)
#define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset)
+#define INSTR_UnwindDispatch(op) INSTRUCTION(op, UnwindDispatch, 0)
+#define INSTR_UnwindToLabel(op) INSTRUCTION(op, UnwindToLabel, 2, level, offset)
#define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0)
#define INSTR_GetException(op) INSTRUCTION(op, GetException, 0)
#define INSTR_SetException(op) INSTRUCTION(op, SetException, 0)
@@ -145,6 +147,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset)
#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset)
#define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset)
+#define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset)
#define INSTR_JumpEmpty(op) INSTRUCTION(op, JumpEmpty, 1, offset)
#define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0)
#define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0)
@@ -160,8 +163,6 @@ QT_BEGIN_NAMESPACE
#define INSTR_CmpStrictNotEqual(op) INSTRUCTION(op, CmpStrictNotEqual, 1, lhs)
#define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs)
#define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs)
-#define INSTR_JumpStrictEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictEqualStackSlotInt, 3, lhs, rhs, offset)
-#define INSTR_JumpStrictNotEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictNotEqualStackSlotInt, 3, lhs, rhs, offset)
#define INSTR_UNot(op) INSTRUCTION(op, UNot, 0)
#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0)
#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0)
@@ -241,6 +242,8 @@ QT_BEGIN_NAMESPACE
F(CallScopeObjectProperty) \
F(CallContextObjectProperty) \
F(SetUnwindHandler) \
+ F(UnwindDispatch) \
+ F(UnwindToLabel) \
F(ThrowException) \
F(GetException) \
F(SetException) \
@@ -273,6 +276,7 @@ QT_BEGIN_NAMESPACE
F(Jump) \
F(JumpTrue) \
F(JumpFalse) \
+ F(JumpNoException) \
F(JumpNotUndefined) \
F(JumpEmpty) \
F(CmpEqNull) \
@@ -289,8 +293,6 @@ QT_BEGIN_NAMESPACE
F(CmpStrictNotEqual) \
F(CmpIn) \
F(CmpInstanceOf) \
- F(JumpStrictEqualStackSlotInt) \
- F(JumpStrictNotEqualStackSlotInt) \
F(UNot) \
F(UPlus) \
F(UMinus) \
diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp
index a32039dc96..5702774cd7 100644
--- a/src/qml/jit/qv4assembler.cpp
+++ b/src/qml/jit/qv4assembler.cpp
@@ -712,6 +712,11 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
patches.push_back({ jump, offset });
}
+ Jump jumpEmpty()
+ {
+ return branch64(Equal, AccumulatorRegister, TrustedImm64(Primitive::emptyValue().asReturnedValue()));
+ }
+
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister);
@@ -815,26 +820,6 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
return branch32(Equal, TrustedImm32(3), ScratchRegister);
}
- void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
- {
- Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
- load64(lhsAddr, ScratchRegister);
- Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
- Jump equal = branch32(Equal, TrustedImm32(rhs), ScratchRegister);
- patches.push_back({ equal, offset });
- isUndef.link(this);
- }
-
- void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
- {
- Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
- load64(lhsAddr, ScratchRegister);
- Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
- patches.push_back({ isUndef, offset });
- Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
- patches.push_back({ notEqual, offset });
- }
-
void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
{
if (sourceReg == NoRegister)
@@ -1163,6 +1148,11 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
patches.push_back({ jump, offset });
}
+ Jump jumpEmpty()
+ {
+ return branch32(Equal, AccumulatorRegisterTag, TrustedImm32(Primitive::emptyValue().asReturnedValue() >> 32));
+ }
+
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerConvertible_Shift - 32),
@@ -1202,34 +1192,6 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
done.link(this);
}
- void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
- {
- Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
- load32(lhsAddr, ScratchRegister);
- Jump notEqInt = branch32(NotEqual, ScratchRegister, TrustedImm32(rhs));
- Jump notEqUndefVal = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
- patches.push_back({ notEqUndefVal, offset });
- lhsAddr.offset += 4;
- load32(lhsAddr, ScratchRegister);
- Jump notEqUndefTag = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
- patches.push_back({ notEqUndefTag, offset });
- notEqInt.link(this);
- }
-
- void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
- {
- Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
- load32(lhsAddr, ScratchRegister);
- Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
- patches.push_back({ notEqual, offset });
- Jump notUndefValue = branch32(NotEqual, TrustedImm32(0), ScratchRegister);
- lhsAddr.offset += 4;
- load32(lhsAddr, ScratchRegister);
- Jump equalUndef = branch32(Equal, TrustedImm32(0), ScratchRegister);
- patches.push_back({ equalUndef, offset });
- notUndefValue.link(this);
- }
-
void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
{
if (sourceReg != NoRegister)
@@ -2009,6 +1971,16 @@ void Assembler::jumpFalse(int offset)
});
}
+void Assembler::jumpNoException(int offset)
+{
+ auto jump = pasm()->branch32(
+ PlatformAssembler::Equal,
+ PlatformAssembler::Address(PlatformAssembler::EngineRegister,
+ offsetof(EngineBase, hasException)),
+ TrustedImm32(0));
+ pasm()->patches.push_back({ jump, offset });
+}
+
void Assembler::jumpNotUndefined(int offset)
{
pasm()->jumpNotUndefined(offset);
@@ -2019,16 +1991,6 @@ void JIT::Assembler::jumpEmpty(int offset)
pasm()->jumpEmpty(offset);
}
-void Assembler::jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
-{
- pasm()->jumpStrictEqualStackSlotInt(lhs, rhs, offset);
-}
-
-void Assembler::jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
-{
- pasm()->jumpStrictNotEqualStackSlotInt(lhs, rhs, offset);
-}
-
void Assembler::prepareCallWithArgCount(int argc)
{
#ifndef QT_NO_DEBUG
@@ -2220,12 +2182,14 @@ void Assembler::getException()
void Assembler::setException()
{
+ auto noException = pasm()->jumpEmpty();
Address addr(PlatformAssembler::EngineRegister, offsetof(EngineBase, exceptionValue));
pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
pasm()->storeAccumulator(Address(PlatformAssembler::ScratchRegister));
addr.offset = offsetof(EngineBase, hasException);
Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1);
pasm()->store8(TrustedImm32(1), addr);
+ noException.link(pasm());
}
void Assembler::setUnwindHandler(int offset)
@@ -2240,6 +2204,31 @@ void Assembler::clearUnwindHandler()
pasm()->storePtr(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
}
+void JIT::Assembler::unwindDispatch()
+{
+ checkException();
+ pasm()->load32(Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)), PlatformAssembler::ScratchRegister);
+ auto noUnwind = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0));
+ pasm()->sub32(TrustedImm32(1), PlatformAssembler::ScratchRegister);
+ pasm()->store32(PlatformAssembler::ScratchRegister, Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)));
+ auto jump = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0));
+ gotoCatchException();
+ jump.link(pasm());
+
+ pasm()->loadPtr(Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)), PlatformAssembler::ScratchRegister);
+ pasm()->jump(PlatformAssembler::ScratchRegister);
+
+ noUnwind.link(pasm());
+}
+
+void JIT::Assembler::unwindToLabel(int level, int offset)
+{
+ auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)));
+ pasm()->ehTargets.push_back({ l, offset });
+ pasm()->store32(TrustedImm32(level), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)));
+ gotoCatchException();
+}
+
void Assembler::pushCatchContext(int index, int name)
{
prepareCallWithArgCount(3);
diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h
index 73faf7b69f..d41b79f562 100644
--- a/src/qml/jit/qv4assembler_p.h
+++ b/src/qml/jit/qv4assembler_p.h
@@ -140,10 +140,9 @@ public:
void jump(int offset);
void jumpTrue(int offset);
void jumpFalse(int offset);
+ void jumpNoException(int offset);
void jumpNotUndefined(int offset);
void jumpEmpty(int offset);
- void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset);
- void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset);
// stuff for runtime calls
void prepareCallWithArgCount(int argc);
@@ -164,6 +163,8 @@ public:
void setException();
void setUnwindHandler(int offset);
void clearUnwindHandler();
+ void unwindDispatch();
+ void unwindToLabel(int level, int offset);
void pushCatchContext(int index, int name);
void popContext();
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
index 031c6e620c..6c298d9331 100644
--- a/src/qml/jit/qv4baselinejit.cpp
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -561,6 +561,17 @@ void BaselineJIT::generate_SetUnwindHandler(int offset)
as->clearUnwindHandler();
}
+void BaselineJIT::generate_UnwindDispatch()
+{
+ as->unwindDispatch();
+}
+
+void BaselineJIT::generate_UnwindToLabel(int level, int offset)
+{
+ as->unwindToLabel(level, instructionOffset() + offset);
+}
+
+
void BaselineJIT::generate_ThrowException()
{
STORE_IP();
@@ -891,6 +902,7 @@ void BaselineJIT::generate_Construct(int func, int argc, int argv)
void BaselineJIT::generate_Jump(int offset) { as->jump(instructionOffset() + offset); }
void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(instructionOffset() + offset); }
void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(instructionOffset() + offset); }
+void BaselineJIT::generate_JumpNoException(int offset) { as->jumpNoException(instructionOffset() + offset); }
void BaselineJIT::generate_JumpNotUndefined(int offset) { as->jumpNotUndefined(instructionOffset() + offset); }
void BaselineJIT::generate_JumpEmpty(int offset) { as->jumpEmpty(instructionOffset() + offset); }
@@ -929,16 +941,6 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs)
as->checkException();
}
-void BaselineJIT::generate_JumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
-{
- as->jumpStrictEqualStackSlotInt(lhs, rhs, instructionOffset() + offset);
-}
-
-void BaselineJIT::generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
-{
- as->jumpStrictNotEqualStackSlotInt(lhs, rhs, instructionOffset() + offset);
-}
-
void BaselineJIT::generate_UNot() { as->unot(); }
void BaselineJIT::generate_UPlus() { as->toNumber(); }
void BaselineJIT::generate_UMinus() { as->uminus(); }
diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 96fd8348b8..5441ea974b 100644
--- a/src/qml/jit/qv4baselinejit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -129,6 +129,8 @@ public:
void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) override;
void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv) override;
void generate_SetUnwindHandler(int offset) override;
+ void generate_UnwindDispatch() override;
+ void generate_UnwindToLabel(int level, int offset) override;
void generate_ThrowException() override;
void generate_GetException() override;
void generate_SetException() override;
@@ -161,6 +163,7 @@ public:
void generate_Jump(int offset) override;
void generate_JumpTrue(int offset) override;
void generate_JumpFalse(int offset) override;
+ void generate_JumpNoException(int offset) override;
void generate_JumpNotUndefined(int offset) override;
void generate_JumpEmpty(int offset) override;
void generate_CmpEqNull() override;
@@ -177,10 +180,6 @@ public:
void generate_CmpStrictNotEqual(int lhs) override;
void generate_CmpIn(int lhs) override;
void generate_CmpInstanceOf(int lhs) override;
- void generate_JumpStrictEqualStackSlotInt(int lhs, int rhs,
- int offset) override;
- void generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs,
- int offset) override;
void generate_UNot() override;
void generate_UPlus() override;
void generate_UMinus() override;
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index ecc110a16f..b007e65c4b 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -97,7 +97,9 @@ struct Q_QML_EXPORT CppStackFrame {
int originalArgumentsCount;
int instructionPointer;
const char *yield;
- const char *exceptionHandler;
+ const char *unwindHandler;
+ const char *unwindLabel;
+ int unwindLevel;
QString source() const;
QString function() const;
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index e180298789..165902ed36 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -344,7 +344,7 @@ static struct InstrCount {
#endif
#define CHECK_EXCEPTION \
if (engine->hasException) \
- goto catchException
+ goto handleUnwind
static inline Heap::CallContext *getScope(QV4::Value *stack, int level)
{
@@ -510,7 +510,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
frame.originalArguments = argv;
frame.originalArgumentsCount = argc;
frame.yield = nullptr;
- frame.exceptionHandler = nullptr;
+ frame.unwindHandler = nullptr;
+ frame.unwindLabel = nullptr;
+ frame.unwindLevel = 0;
+
Function *function;
{
@@ -810,7 +813,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
if (engine->hasException) {
// an empty value indicates that the generator was called with return()
if (engine->exceptionValue->asReturnedValue() != Primitive::emptyValue().asReturnedValue())
- goto catchException;
+ goto handleUnwind;
engine->hasException = false;
*engine->exceptionValue = Primitive::undefinedValue();
} else {
@@ -823,7 +826,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
Value func = STACK_VALUE(name);
if (Q_UNLIKELY(!func.isFunctionObject())) {
acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
- goto catchException;
+ goto handleUnwind;
}
acc = static_cast<const FunctionObject &>(func).call(nullptr, stack + argv, argc);
CHECK_EXCEPTION;
@@ -843,7 +846,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
if (Q_UNLIKELY(!f.isFunctionObject())) {
acc = engine->throwTypeError();
- goto catchException;
+ goto handleUnwind;
}
acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc);
@@ -887,14 +890,30 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
MOTH_END_INSTR(CallContextObjectProperty)
MOTH_BEGIN_INSTR(SetUnwindHandler)
- frame.exceptionHandler = offset ? code + offset : nullptr;
+ frame.unwindHandler = offset ? code + offset : nullptr;
MOTH_END_INSTR(SetUnwindHandler)
+ MOTH_BEGIN_INSTR(UnwindDispatch)
+ CHECK_EXCEPTION;
+ if (frame.unwindLevel) {
+ --frame.unwindLevel;
+ if (frame.unwindLevel)
+ goto handleUnwind;
+ code = frame.unwindLabel;
+ }
+ MOTH_END_INSTR(UnwindDispatch)
+
+ MOTH_BEGIN_INSTR(UnwindToLabel)
+ frame.unwindLevel = level;
+ frame.unwindLabel = code + offset;
+ goto handleUnwind;
+ MOTH_END_INSTR(UnwindToLabel)
+
MOTH_BEGIN_INSTR(ThrowException)
STORE_IP();
STORE_ACC();
Runtime::method_throwException(engine, accumulator);
- goto catchException;
+ goto handleUnwind;
MOTH_END_INSTR(ThrowException)
MOTH_BEGIN_INSTR(GetException)
@@ -904,8 +923,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
MOTH_END_INSTR(HasException)
MOTH_BEGIN_INSTR(SetException)
- *engine->exceptionValue = acc;
- engine->hasException = true;
+ if (acc != Primitive::emptyValue().asReturnedValue()) {
+ *engine->exceptionValue = acc;
+ engine->hasException = true;
+ }
MOTH_END_INSTR(SetException)
MOTH_BEGIN_INSTR(PushCatchContext)
@@ -979,7 +1000,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
if (function->isStrict()) {
STORE_IP();
engine->throwTypeError();
- goto catchException;
+ goto handleUnwind;
}
acc = Encode(false);
} else {
@@ -992,7 +1013,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
if (function->isStrict()) {
STORE_IP();
engine->throwTypeError();
- goto catchException;
+ goto handleUnwind;
}
acc = Encode(false);
} else {
@@ -1006,7 +1027,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
STORE_IP();
QString n = function->compilationUnit->runtimeStrings[name]->toQString();
engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n));
- goto catchException;
+ goto handleUnwind;
}
acc = Encode(false);
} else {
@@ -1096,6 +1117,11 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
}
MOTH_END_INSTR(JumpFalse)
+ MOTH_BEGIN_INSTR(JumpNoException)
+ if (!engine->hasException)
+ code += offset;
+ MOTH_END_INSTR(JumpNoException)
+
MOTH_BEGIN_INSTR(JumpNotUndefined)
if (Q_LIKELY(acc != QV4::Encode::undefined()))
code += offset;
@@ -1242,16 +1268,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
CHECK_EXCEPTION;
MOTH_END_INSTR(CmpInstanceOf)
- MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- if (STACK_VALUE(lhs).int_32() != rhs || STACK_VALUE(lhs).isUndefined())
- code += offset;
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- if (STACK_VALUE(lhs).int_32() == rhs && !STACK_VALUE(lhs).isUndefined())
- code += offset;
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
MOTH_BEGIN_INSTR(UNot)
if (ACC.integerCompatible()) {
acc = Encode(!static_cast<bool>(ACC.int_32()));
@@ -1456,12 +1472,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
STACK_VALUE(result) = Runtime::method_loadQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine));
MOTH_END_INSTR(LoadQmlImportedScripts)
- catchException:
- Q_ASSERT(engine->hasException);
- if (!frame.exceptionHandler) {
+ handleUnwind:
+ Q_ASSERT(engine->hasException || frame.unwindLevel);
+ if (!frame.unwindHandler) {
acc = Encode::undefined();
return acc;
}
- code = frame.exceptionHandler;
+ code = frame.unwindHandler;
}
}
diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations
index 89efd5e876..7ab2dcd9aa 100644
--- a/tests/auto/qml/ecmascripttests/TestExpectations
+++ b/tests/auto/qml/ecmascripttests/TestExpectations
@@ -496,7 +496,6 @@ built-ins/Function/prototype/toString/setter-object.js fails
built-ins/Function/prototype/toString/symbol-named-builtins.js fails
built-ins/Function/prototype/toString/unicode.js fails
built-ins/GeneratorFunction/proto-from-ctor-realm.js fails
-built-ins/GeneratorPrototype/throw/try-finally-nested-try-catch-within-catch.js fails
built-ins/JSON/parse/revived-proxy-revoked.js fails
built-ins/JSON/parse/revived-proxy.js fails
built-ins/JSON/parse/reviver-array-define-prop-err.js fails
@@ -1575,6 +1574,7 @@ built-ins/String/prototype/includes/return-abrupt-from-searchstring-regexp-test.
built-ins/String/prototype/indexOf/position-tointeger-toprimitive.js fails
built-ins/String/prototype/indexOf/position-tointeger.js fails
built-ins/String/prototype/indexOf/searchstring-tostring-toprimitive.js fails
+built-ins/String/prototype/match/invoke-builtin-match.js fails
built-ins/String/prototype/match/cstm-matcher-invocation.js fails
built-ins/String/prototype/replace/cstm-replace-invocation.js fails
built-ins/String/prototype/replace/this-value-not-obj-coercible.js fails