diff options
-rw-r--r-- | src/qml/compiler/qv4bytecodegenerator_p.h | 24 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 135 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontrolflow_p.h | 74 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth.cpp | 9 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 17 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 13 |
7 files changed, 210 insertions, 65 deletions
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index 81b64f96da..f53a720d2c 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -77,6 +77,14 @@ public: index(generator->labels.size()) { generator->labels.append(mode == LinkNow ? generator->instructions.size() : -1); } + static Label returnLabel() { + Label l; + l.index = INT_MAX; + return l; + } + bool isReturn() const { + return index == INT_MAX; + } void link() { Q_ASSERT(index >= 0); @@ -176,6 +184,22 @@ public: return addJumpInstruction(data); } + 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 setExceptionHandler(ExceptionHandler *handler) { currentExceptionHandler = handler; diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 4fb34a677f..c795841f00 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -214,7 +214,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) return Reference::fromAccumulator(this); } case PostIncrement: - if (!_expr.accept(nx)) { + if (!_expr.accept(nx) || requiresReturnValue) { Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::UPlus uplus; @@ -239,7 +239,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) return e.storeRetainAccumulator(); } case PostDecrement: - if (!_expr.accept(nx)) { + if (!_expr.accept(nx) || requiresReturnValue) { Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::UPlus uplus; @@ -361,13 +361,33 @@ void Codegen::program(Program *ast) void Codegen::sourceElements(SourceElements *ast) { + bool _requiresReturnValue = false; + qSwap(_requiresReturnValue, requiresReturnValue); for (SourceElements *it = ast; it; it = it->next) { + if (!it->next) + qSwap(_requiresReturnValue, requiresReturnValue); sourceElement(it->element); if (hasError) return; } } +void Codegen::statementList(StatementList *ast) +{ + bool _requiresReturnValue = requiresReturnValue; + requiresReturnValue = false; + for (StatementList *it = ast; it; it = it->next) { + if (!it->next || + it->next->statement->kind == Statement::Kind_BreakStatement || + it->next->statement->kind == Statement::Kind_ContinueStatement || + it->next->statement->kind == Statement::Kind_ReturnStatement) + requiresReturnValue = _requiresReturnValue; + statement(it->statement); + requiresReturnValue = false; + } + requiresReturnValue = _requiresReturnValue; +} + void Codegen::variableDeclaration(VariableDeclaration *ast) { RegisterScope scope(this); @@ -1761,14 +1781,39 @@ bool Codegen::visit(FunctionDeclaration * ast) RegisterScope scope(this); - if (_context->compilationMode == QmlBinding) { + if (_context->compilationMode == QmlBinding) Reference::fromName(this, ast->name.toString()).loadInAccumulator(); - Reference::fromStackSlot(this, _returnAddress).storeConsumeAccumulator(); - } _expr.accept(nx); return false; } +static bool endsWithReturn(Node *node) +{ + if (!node) + return false; + if (AST::cast<ReturnStatement *>(node)) + return true; + if (Program *p = AST::cast<Program *>(node)) + return endsWithReturn(p->elements); + if (SourceElements *se = AST::cast<SourceElements *>(node)) { + while (se->next) + se = se->next; + return endsWithReturn(se->element); + } + if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(node)) + return endsWithReturn(sse->statement); + if (StatementList *sl = AST::cast<StatementList *>(node)) { + while (sl->next) + sl = sl->next; + return endsWithReturn(sl->statement); + } + if (Block *b = AST::cast<Block *>(node)) + return endsWithReturn(b->statements); + if (IfStatement *is = AST::cast<IfStatement *>(node)) + return is->ko && endsWithReturn(is->ok) && endsWithReturn(is->ko); + return false; +} + int Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::SourceElements *body) @@ -1797,8 +1842,11 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, // allocate the js stack frame (Context & js Function & accumulator) bytecodeGenerator->newRegisterArray(sizeof(JSStackFrame)/sizeof(Value)); - int returnAddress = bytecodeGenerator->newRegister(); - + int returnAddress = -1; + bool _requiresReturnValue = (_context->compilationMode == QmlBinding || _context->compilationMode == EvalCode); + qSwap(requiresReturnValue, _requiresReturnValue); + if (requiresReturnValue) + returnAddress = bytecodeGenerator->newRegister(); if (!_context->parent || _context->usesArgumentsObject == Context::ArgumentsObjectUnknown) _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) @@ -1830,11 +1878,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, } } - auto exitBlock = bytecodeGenerator->newLabel(); - - qSwap(_exitBlock, exitBlock); qSwap(_returnAddress, returnAddress); - for (const Context::Member &member : qAsConst(_context->members)) { if (member.function) { const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals, @@ -1870,13 +1914,17 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, sourceElements(body); - _exitBlock.link(); - bytecodeGenerator->setLocation(ast->lastSourceLocation()); - - { - Instruction::Ret ret; - ret.result = Reference::fromStackSlot(this, _returnAddress).theStackSlot; - bytecodeGenerator->addInstruction(ret); + if (hasError || !endsWithReturn(body)) { + if (requiresReturnValue) { + if (_returnAddress >= 0) { + Instruction::LoadReg load; + load.reg = Moth::StackSlot::createRegister(_returnAddress); + bytecodeGenerator->addInstruction(load); + } + } else { + Reference::fromConst(this, Encode::undefined()).loadInAccumulator(); + } + bytecodeGenerator->addInstruction(Instruction::Ret()); } _context->code = bytecodeGenerator->finalize(); @@ -1889,9 +1937,8 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, qDebug(); } - qSwap(_exitBlock, exitBlock); qSwap(_returnAddress, returnAddress); - + qSwap(requiresReturnValue, _requiresReturnValue); bytecodeGenerator = savedBytecodeGenerator; return leaveContext(); @@ -1922,9 +1969,7 @@ bool Codegen::visit(Block *ast) RegisterScope scope(this); - for (StatementList *it = ast->statements; it; it = it->next) { - statement(it->statement); - } + statementList(ast->statements); return false; } @@ -2022,7 +2067,7 @@ bool Codegen::visit(ExpressionStatement *ast) RegisterScope scope(this); - if (_context->compilationMode == EvalCode || _context->compilationMode == QmlBinding) { + if (requiresReturnValue) { Reference e = expression(ast->expression); if (hasError) return false; @@ -2122,10 +2167,15 @@ bool Codegen::visit(IfStatement *ast) trueLabel.link(); statement(ast->ok); if (ast->ko) { - BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump(); - falseLabel.link(); - statement(ast->ko); - jump_endif.link(); + if (endsWithReturn(ast)) { + falseLabel.link(); + statement(ast->ko); + } else { + BytecodeGenerator::Jump jump_endif = bytecodeGenerator->jump(); + falseLabel.link(); + statement(ast->ko); + jump_endif.link(); + } } else { falseLabel.link(); } @@ -2253,19 +2303,25 @@ bool Codegen::visit(ReturnStatement *ast) throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function")); return false; } + Reference expr; if (ast->expression) { - Reference expr = expression(ast->expression); + expr = expression(ast->expression); if (hasError) return false; - expr.loadInAccumulator(); - Reference::fromStackSlot(this, _returnAddress).storeConsumeAccumulator(); + } else { + expr = Reference::fromConst(this, Encode::undefined()); } - if (_context->controlFlow) { + if (_context->controlFlow && _context->controlFlow->returnRequiresUnwind()) { + if (_returnAddress >= 0) + (void) expr.storeOnStack(_returnAddress); + else + expr.loadInAccumulator(); ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Return); _context->controlFlow->jumpToHandler(h); } else { - bytecodeGenerator->jump().link(_exitBlock); + expr.loadInAccumulator(); + bytecodeGenerator->addInstruction(Instruction::Ret()); } return false; } @@ -2324,24 +2380,21 @@ bool Codegen::visit(SwitchStatement *ast) CaseClause *clause = it->clause; blockMap[clause].link(); - for (StatementList *it2 = clause->statements; it2; it2 = it2->next) - statement(it2->statement); + statementList(clause->statements); } if (ast->block->defaultClause) { DefaultClause *clause = ast->block->defaultClause; blockMap[clause].link(); - for (StatementList *it2 = clause->statements; it2; it2 = it2->next) - statement(it2->statement); + statementList(clause->statements); } for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { CaseClause *clause = it->clause; blockMap[clause].link(); - for (StatementList *it2 = clause->statements; it2; it2 = it2->next) - statement(it2->statement); + statementList(clause->statements); } switchEnd.link(); @@ -2437,6 +2490,8 @@ bool Codegen::visit(WhileStatement *ast) if (hasError) return true; + RegisterScope scope(this); + BytecodeGenerator::Label start = bytecodeGenerator->newLabel(); BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); BytecodeGenerator::Label cond = bytecodeGenerator->label(); @@ -2449,7 +2504,6 @@ bool Codegen::visit(WhileStatement *ast) bytecodeGenerator->jump().link(cond); end.link(); - return false; } @@ -3037,4 +3091,3 @@ void Codegen::Reference::loadInAccumulator() const Q_ASSERT(false); Q_UNREACHABLE(); } - diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index f55ef026a8..0d63854ed1 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -484,6 +484,7 @@ protected: void functionBody(AST::FunctionBody *ast); void program(AST::Program *ast); void sourceElements(AST::SourceElements *ast); + void statementList(AST::StatementList *ast); void variableDeclaration(AST::VariableDeclaration *ast); void variableDeclarationList(AST::VariableDeclarationList *ast); @@ -623,7 +624,6 @@ protected: friend struct ControlFlowFinally; Result _expr; Module *_module; - BytecodeGenerator::Label _exitBlock; int _returnAddress; Context *_context; AST::LabelledStatement *_labelledStatement; @@ -631,6 +631,7 @@ protected: BytecodeGenerator *bytecodeGenerator = 0; bool _strictMode; bool useFastLookups = true; + bool requiresReturnValue = false; bool _fileNameIsUrl; bool hasError; diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h index f8efd010a3..354015a542 100644 --- a/src/qml/compiler/qv4compilercontrolflow_p.h +++ b/src/qml/compiler/qv4compilercontrolflow_p.h @@ -103,10 +103,34 @@ struct ControlFlow { cg->_context->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.tempIndex >= 0) - Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex); - cg->bytecodeGenerator->jump().link(h.linkLabel); + 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; + } + return false; } virtual QString label() const { return QString(); } @@ -124,7 +148,7 @@ struct ControlFlow { return { Invalid, QString(), {}, -1, 0 }; case Return: case Throw: - return { type, QString(), cg->_exitBlock, -1, 0 }; + return { type, QString(), BytecodeGenerator::Label::returnLabel(), -1, 0 }; case Invalid: break; } @@ -132,17 +156,17 @@ struct ControlFlow { Q_UNREACHABLE(); } - virtual Handler getHandler(HandlerType type, const QString &label = QString()) { - return getParentHandler(type, label); - } + virtual Handler getHandler(HandlerType type, const QString &label = QString()) = 0; - virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { - return parent ? parent->exceptionHandler() : 0; - } BytecodeGenerator::ExceptionHandler *parentExceptionHandler() { return parent ? parent->exceptionHandler() : 0; } + virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() { + return parentExceptionHandler(); + } + + virtual void handleThrow(const Reference &expr) { Reference e = expr; Handler h = getHandler(ControlFlow::Throw); @@ -199,7 +223,7 @@ struct ControlFlowLoop : public ControlFlow Q_ASSERT(false); Q_UNREACHABLE(); } - return ControlFlow::getHandler(type, label); + return getParentHandler(type, label); } }; @@ -226,21 +250,24 @@ struct ControlFlowUnwind : public ControlFlow Reference temp = Reference::fromStackSlot(cg, controlFlowTemp); for (const auto &h : qAsConst(handlers)) { - Codegen::RegisterScope tempScope(cg); Handler parentHandler = getParentHandler(h.type, h.label); if (h.type == Throw || parentHandler.tempIndex >= 0) { BytecodeGenerator::Label skip = generator()->newLabel(); - Reference::fromConst(cg, QV4::Encode(h.value)).loadInAccumulator(); - generator()->jumpStrictNotEqual(temp.stackSlot()).link(skip); + generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip); if (h.type == Throw) emitForThrowHandling(); - Reference::storeConstOnStack(cg, QV4::Encode(parentHandler.value), parentHandler.tempIndex); - generator()->jump().link(parentHandler.linkLabel); + jumpToHandler(parentHandler); skip.link(); } else { - Reference::fromConst(cg, QV4::Encode(h.value)).loadInAccumulator(); - generator()->jumpStrictEqual(temp.stackSlot()).link(parentHandler.linkLabel); + 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); + } } } } @@ -389,7 +416,7 @@ struct ControlFlowFinally : public ControlFlowUnwind // if we're inside the finally block, any exceptions etc. should // go directly to the parent handler if (insideFinally) - return ControlFlow::getHandler(type, label); + return getParentHandler(type, label); return ControlFlowUnwind::getHandler(type, label); } @@ -403,6 +430,11 @@ 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; @@ -413,6 +445,10 @@ struct ControlFlowFinally : public ControlFlowUnwind cg->statement(finally->statement); insideFinally = false; + Instruction::LoadReg loadRetVal; + loadRetVal.reg = retVal; + generator()->addInstruction(loadRetVal); + emitUnwindHandler(); } diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index b3034e8b81..ed2f556e01 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -405,6 +405,14 @@ void dumpBytecode(const char *code, int len, int nFormals) d << instr.lhs.dump(nFormals) << " " << absoluteInstructionOffset(start, instr); MOTH_END_INSTR(JumpStrictNotEqual) + MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt) + d << instr.lhs.dump(nFormals) << ", " << instr.rhs << " " << absoluteInstructionOffset(start, instr); + MOTH_END_INSTR(JumpStrictEqualStackSlotInt) + + MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt) + d << instr.lhs.dump(nFormals) << ", " << instr.rhs << " " << absoluteInstructionOffset(start, instr); + MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) + MOTH_BEGIN_INSTR(UNot) MOTH_END_INSTR(UNot) @@ -484,7 +492,6 @@ void dumpBytecode(const char *code, int len, int nFormals) MOTH_END_INSTR(BinopContext) MOTH_BEGIN_INSTR(Ret) - d << instr.result.dump(nFormals); MOTH_END_INSTR(Ret) #ifndef QT_NO_QML_DEBUGGER diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 63b117dc0e..d07e44c41b 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -144,6 +144,8 @@ QT_BEGIN_NAMESPACE F(CmpJmpLe, cmpJmpLe) \ F(JumpStrictEqual, jumpStrictEqual) \ F(JumpStrictNotEqual, jumpStrictNotEqual) \ + F(JumpStrictEqualStackSlotInt, jumpStrictEqualStackSlotInt) \ + F(JumpStrictNotEqualStackSlotInt, jumpStrictNotEqualStackSlotInt) \ F(UNot, unot) \ F(UPlus, uplus) \ F(UMinus, uminus) \ @@ -247,7 +249,6 @@ union Instr }; struct instr_ret { MOTH_INSTR_HEADER - StackSlot result; }; #ifndef QT_NO_QML_DEBUGGING @@ -597,6 +598,18 @@ union Instr ptrdiff_t offset; StackSlot lhs; }; + struct instr_jumpStrictEqualStackSlotInt { + MOTH_INSTR_HEADER + ptrdiff_t offset; + StackSlot lhs; + int rhs; + }; + struct instr_jumpStrictNotEqualStackSlotInt { + MOTH_INSTR_HEADER + ptrdiff_t offset; + StackSlot lhs; + int rhs; + }; struct instr_unot { MOTH_INSTR_HEADER }; @@ -770,6 +783,8 @@ union Instr instr_cmpJmpLe cmpJmpLe; instr_jumpStrictEqual jumpStrictEqual; instr_jumpStrictNotEqual jumpStrictNotEqual; + instr_jumpStrictEqualStackSlotInt jumpStrictEqualStackSlotInt; + instr_jumpStrictNotEqualStackSlotInt jumpStrictNotEqualStackSlotInt; instr_unot unot; instr_uplus uplus; instr_uminus uminus; diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index efab04b32d..b17341e5e5 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -640,7 +640,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *jsFunction, CallData *callDat MOTH_BEGIN_INSTR(ThrowException) Runtime::method_throwException(engine, accumulator); - CHECK_EXCEPTION; + goto catchException; MOTH_END_INSTR(ThrowException) MOTH_BEGIN_INSTR(GetException) @@ -905,6 +905,16 @@ QV4::ReturnedValue VME::exec(const FunctionObject *jsFunction, CallData *callDat } MOTH_END_INSTR(JumpStrictNotEqual) + MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt) + if (STACK_VALUE(instr.lhs).int_32() != instr.rhs || STACK_VALUE(instr.lhs).isUndefined()) + code = reinterpret_cast<const uchar *>(&instr.offset) + instr.offset; + MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) + + MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt) + if (STACK_VALUE(instr.lhs).int_32() == instr.rhs && !STACK_VALUE(instr.lhs).isUndefined()) + code = reinterpret_cast<const uchar *>(&instr.offset) + instr.offset; + MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) + MOTH_BEGIN_INSTR(UNot) if (accumulator.integerCompatible()) { STORE_ACCUMULATOR(Encode(!static_cast<bool>(accumulator.int_32()))) @@ -1039,7 +1049,6 @@ QV4::ReturnedValue VME::exec(const FunctionObject *jsFunction, CallData *callDat MOTH_END_INSTR(BinopContext) MOTH_BEGIN_INSTR(Ret) - accumulator = STACK_VALUE(instr.result).asReturnedValue(); goto functionExit; MOTH_END_INSTR(Ret) |