diff options
-rw-r--r-- | src/qml/compiler/qv4bytecodegenerator_p.h | 44 | ||||
-rw-r--r-- | src/qml/compiler/qv4bytecodehandler.cpp | 19 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 135 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 7 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontrolflow_p.h | 334 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth.cpp | 19 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 10 | ||||
-rw-r--r-- | src/qml/jit/qv4assembler.cpp | 105 | ||||
-rw-r--r-- | src/qml/jit/qv4assembler_p.h | 5 | ||||
-rw-r--r-- | src/qml/jit/qv4baselinejit.cpp | 22 | ||||
-rw-r--r-- | src/qml/jit/qv4baselinejit_p.h | 7 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 68 | ||||
-rw-r--r-- | tests/auto/qml/ecmascripttests/TestExpectations | 2 |
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 |