diff options
author | Erik Verbruggen <erik.verbruggen@qt.io> | 2018-08-24 15:41:53 +0200 |
---|---|---|
committer | Erik Verbruggen <erik.verbruggen@qt.io> | 2018-10-04 11:35:54 +0000 |
commit | 1c61e70a0c0f1718c538c04dcc80dd3039ccd384 (patch) | |
tree | 9be7d80be0bb8d00ee6b8f9500c7e4805ef3662e /src | |
parent | b2610d9d04c2a65d60b36ebf43e3a8d469c0397a (diff) |
ES7: Detect Tail Position Calls and pass that to the runtime
Doing the tail call in the runtime will come in a follow-up patch
Change-Id: I8224aac0edbdc765ee9b97703948edd52fd33f3e
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/compiler/qv4bytecodehandler.cpp | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 136 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 26 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontext_p.h | 11 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth.cpp | 4 | ||||
-rw-r--r-- | src/qml/compiler/qv4instr_moth_p.h | 2 | ||||
-rw-r--r-- | src/qml/jit/qv4baselinejit.cpp | 13 | ||||
-rw-r--r-- | src/qml/jit/qv4baselinejit_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 11 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4runtimeapi_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 6 |
11 files changed, 180 insertions, 34 deletions
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp index 65293becfd..9cf96d27f3 100644 --- a/src/qml/compiler/qv4bytecodehandler.cpp +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -542,6 +542,9 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint COLLECTOR_BEGIN_INSTR(LoadQmlImportedScripts) COLLECTOR_END_INSTR(LoadQmlImportedScripts) + + COLLECTOR_BEGIN_INSTR(TailCall) + COLLECTOR_END_INSTR(TailCall) } return labels; diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 8709e3fff8..4af3c99df8 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -500,6 +500,7 @@ void Codegen::statementList(StatementList *ast) void Codegen::variableDeclaration(PatternElement *ast) { + TailCallBlocker blockTailCalls(this); RegisterScope scope(this); if (!ast->initializer) { @@ -807,6 +808,7 @@ bool Codegen::visit(ExportDeclaration *ast) if (!ast->exportDefault) return true; + TailCallBlocker blockTailCalls(this); Reference exportedValue; if (auto *fdecl = AST::cast<FunctionDeclaration*>(ast->variableStatementOrDeclaration)) { @@ -903,6 +905,8 @@ bool Codegen::visit(VariableDeclarationList *) bool Codegen::visit(ClassExpression *ast) { + TailCallBlocker blockTailCalls(this); + Compiler::Class jsClass; jsClass.nameIndex = registerString(ast->name.toString()); @@ -1007,6 +1011,7 @@ bool Codegen::visit(ClassExpression *ast) bool Codegen::visit(ClassDeclaration *ast) { + TailCallBlocker blockTailCalls(this); Reference outerVar = referenceForName(ast->name.toString(), true); visit(static_cast<ClassExpression *>(ast)); (void) outerVar.storeRetainAccumulator(); @@ -1018,7 +1023,9 @@ bool Codegen::visit(Expression *ast) if (hasError) return false; + TailCallBlocker blockTailCalls(this); statement(ast->left); + blockTailCalls.unblock(); accept(ast->right); return false; } @@ -1028,6 +1035,8 @@ bool Codegen::visit(ArrayPattern *ast) if (hasError) return false; + TailCallBlocker blockTailCalls(this); + PatternElementList *it = ast->elements; int argc = 0; @@ -1189,6 +1198,7 @@ bool Codegen::visit(ArrayMemberExpression *ast) if (hasError) return false; + TailCallBlocker blockTailCalls(this); Reference base = expression(ast->base); if (hasError) return false; @@ -1240,11 +1250,14 @@ bool Codegen::visit(BinaryExpression *ast) if (hasError) return false; + TailCallBlocker blockTailCalls(this); + if (ast->op == QSOperator::And) { if (_expr.accept(cx)) { auto iftrue = bytecodeGenerator->newLabel(); condition(ast->left, &iftrue, _expr.iffalse(), true); iftrue.link(); + blockTailCalls.unblock(); condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition()); } else { auto iftrue = bytecodeGenerator->newLabel(); @@ -1259,6 +1272,7 @@ bool Codegen::visit(BinaryExpression *ast) bytecodeGenerator->jumpFalse().link(endif); iftrue.link(); + blockTailCalls.unblock(); Reference right = expression(ast->right); if (hasError) return false; @@ -1288,6 +1302,7 @@ bool Codegen::visit(BinaryExpression *ast) bytecodeGenerator->jumpTrue().link(endif); iffalse.link(); + blockTailCalls.unblock(); Reference right = expression(ast->right); if (hasError) return false; @@ -1320,6 +1335,7 @@ bool Codegen::visit(BinaryExpression *ast) left = left.asLValue(); if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation())) return false; + blockTailCalls.unblock(); Reference r = expression(ast->right); if (hasError) return false; @@ -1814,6 +1830,7 @@ bool Codegen::visit(CallExpression *ast) return false; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); Reference base = expression(ast->base); @@ -1845,7 +1862,8 @@ bool Codegen::visit(CallExpression *ast) if (hasError) return false; - if (calldata.hasSpread) { + blockTailCalls.unblock(); + if (calldata.hasSpread || _tailCallsAreAllowed) { Reference baseObject = base.baseObject(); if (!baseObject.isStackSlot()) { baseObject.storeOnStack(thisObject); @@ -1856,12 +1874,21 @@ bool Codegen::visit(CallExpression *ast) base = Reference::fromStackSlot(this, functionObject); } - Instruction::CallWithSpread call; - call.func = base.stackSlot(); - call.thisObject = baseObject.stackSlot(); - call.argc = calldata.argc; - call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + if (calldata.hasSpread) { + Instruction::CallWithSpread call; + call.func = base.stackSlot(); + call.thisObject = baseObject.stackSlot(); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } else { + Instruction::TailCall call; + call.func = base.stackSlot(); + call.thisObject = baseObject.stackSlot(); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } _expr.setResult(Reference::fromAccumulator(this)); return false; @@ -2028,11 +2055,14 @@ bool Codegen::visit(ConditionalExpression *ast) return true; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); BytecodeGenerator::Label iftrue = bytecodeGenerator->newLabel(); BytecodeGenerator::Label iffalse = bytecodeGenerator->newLabel(); condition(ast->expression, &iftrue, &iffalse, true); + blockTailCalls.unblock(); + iftrue.link(); Reference ok = expression(ast->ok); if (hasError) @@ -2058,6 +2088,7 @@ bool Codegen::visit(DeleteExpression *ast) return false; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); Reference expr = expression(ast->expression); if (hasError) return false; @@ -2145,6 +2176,7 @@ bool Codegen::visit(FieldMemberExpression *ast) if (hasError) return false; + TailCallBlocker blockTailCalls(this); if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) { if (id->name == QLatin1String("new")) { // new.target @@ -2266,6 +2298,8 @@ bool Codegen::visit(FunctionExpression *ast) if (hasError) return false; + TailCallBlocker blockTailCalls(this); + RegisterScope scope(this); int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body); @@ -2395,6 +2429,7 @@ bool Codegen::visit(NewExpression *ast) return false; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); Reference base = expression(ast->expression); if (hasError) @@ -2414,6 +2449,7 @@ bool Codegen::visit(NewMemberExpression *ast) return false; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); Reference base = expression(ast->base); if (hasError) @@ -2432,6 +2468,7 @@ bool Codegen::visit(NotExpression *ast) if (hasError) return false; + TailCallBlocker blockTailCalls(this); _expr.setResult(unop(Not, expression(ast->expression))); return false; } @@ -2463,6 +2500,8 @@ bool Codegen::visit(ObjectPattern *ast) if (hasError) return false; + TailCallBlocker blockTailCalls(this); + QVector<QPair<Reference, ObjectPropertyValue>> computedProperties; QMap<QString, ObjectPropertyValue> valueMap; @@ -2684,6 +2723,8 @@ bool Codegen::visit(TemplateLiteral *ast) if (hasError) return false; + TailCallBlocker blockTailCalls(this); + Instruction::LoadRuntimeString instr; instr.stringId = registerString(ast->value.toString()); bytecodeGenerator->addInstruction(instr); @@ -2742,6 +2783,7 @@ bool Codegen::visit(TildeExpression *ast) if (hasError) return false; + TailCallBlocker blockTailCalls(this); _expr.setResult(unop(Compl, expression(ast->expression))); return false; } @@ -2761,6 +2803,7 @@ bool Codegen::visit(TypeOfExpression *ast) return false; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); Reference expr = expression(ast->expression); if (hasError) @@ -2786,6 +2829,7 @@ bool Codegen::visit(UnaryMinusExpression *ast) if (hasError) return false; + TailCallBlocker blockTailCalls(this); _expr.setResult(unop(UMinus, expression(ast->expression))); return false; } @@ -2795,6 +2839,7 @@ bool Codegen::visit(UnaryPlusExpression *ast) if (hasError) return false; + TailCallBlocker blockTailCalls(this); _expr.setResult(unop(UPlus, expression(ast->expression))); return false; } @@ -2805,6 +2850,7 @@ bool Codegen::visit(VoidExpression *ast) return false; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); statement(ast->expression); _expr.setResult(Reference::fromConst(this, Encode::undefined())); @@ -2816,6 +2862,7 @@ bool Codegen::visit(FunctionDeclaration * ast) if (hasError) return false; + // no need to block tail calls: the function body isn't visited here. RegisterScope scope(this); if (_functionContext->contextType == ContextType::Binding) @@ -2832,6 +2879,7 @@ bool Codegen::visit(YieldExpression *ast) } RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); Reference expr = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined()); if (hasError) return false; @@ -2981,38 +3029,43 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, _context->blockIndex = _module->blocks.count() - 1; } + TailCallBlocker maybeBlockTailCalls(this, _context->canHaveTailCalls()); + RegisterScope registerScope(this); _context->emitBlockHeader(this); - inFormalParameterList = true; - int argc = 0; - while (formals) { - PatternElement *e = formals->element; - if (!e) { - if (!formals->next) - // trailing comma - break; - Q_UNREACHABLE(); - } - - Reference arg = referenceForName(e->bindingIdentifier.toString(), true); - if (e->type == PatternElement::RestElement) { - Q_ASSERT(!formals->next); - Instruction::CreateRestParameter rest; - rest.argIndex = argc; - bytecodeGenerator->addInstruction(rest); - arg.storeConsumeAccumulator(); - } else { - if (e->bindingTarget || e->initializer) { - initializeAndDestructureBindingElement(e, arg); - if (hasError) + { + QScopedValueRollback<bool> inFormals(inFormalParameterList, true); + TailCallBlocker blockTailCalls(this); // we're not in the FunctionBody or ConciseBody yet + + int argc = 0; + while (formals) { + PatternElement *e = formals->element; + if (!e) { + if (!formals->next) + // trailing comma break; + Q_UNREACHABLE(); } + + Reference arg = referenceForName(e->bindingIdentifier.toString(), true); + if (e->type == PatternElement::RestElement) { + Q_ASSERT(!formals->next); + Instruction::CreateRestParameter rest; + rest.argIndex = argc; + bytecodeGenerator->addInstruction(rest); + arg.storeConsumeAccumulator(); + } else { + if (e->bindingTarget || e->initializer) { + initializeAndDestructureBindingElement(e, arg); + if (hasError) + break; + } + } + formals = formals->next; + ++argc; } - formals = formals->next; - ++argc; } - inFormalParameterList = false; if (_context->isGenerator) { Instruction::Yield yield; @@ -3083,6 +3136,7 @@ bool Codegen::visit(BreakStatement *ast) if (hasError) return false; + // no need to block tail calls here: children aren't visited if (!controlFlow) { throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop")); return false; @@ -3107,6 +3161,7 @@ bool Codegen::visit(ContinueStatement *ast) if (hasError) return false; + // no need to block tail calls here: children aren't visited RegisterScope scope(this); if (!controlFlow) { @@ -3152,6 +3207,7 @@ bool Codegen::visit(DoWhileStatement *ast) cond.link(); + TailCallBlocker blockTailCalls(this); if (!AST::cast<FalseLiteral *>(ast->expression)) { if (AST::cast<TrueLiteral *>(ast->expression)) bytecodeGenerator->jump().link(body); @@ -3178,6 +3234,7 @@ bool Codegen::visit(ExpressionStatement *ast) return true; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); if (requiresReturnValue) { Reference e = expression(ast->expression); @@ -3196,6 +3253,7 @@ bool Codegen::visit(ForEachStatement *ast) return true; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); Reference iterator = Reference::fromStackSlot(this); Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack(); @@ -3289,6 +3347,7 @@ bool Codegen::visit(ForStatement *ast) return true; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); ControlFlowBlock controlFlow(this, ast); @@ -3307,7 +3366,9 @@ bool Codegen::visit(ForStatement *ast) condition(ast->condition, &body, &end, true); body.link(); + blockTailCalls.unblock(); statement(ast->statement); + blockTailCalls.reblock(); setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); step.link(); @@ -3329,10 +3390,12 @@ bool Codegen::visit(IfStatement *ast) return true; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); BytecodeGenerator::Label trueLabel = bytecodeGenerator->newLabel(); BytecodeGenerator::Label falseLabel = bytecodeGenerator->newLabel(); condition(ast->expression, &trueLabel, &falseLabel, true); + blockTailCalls.unblock(); trueLabel.link(); statement(ast->ok); @@ -3433,6 +3496,7 @@ bool Codegen::visit(SwitchStatement *ast) Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress); RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); if (ast->block) { BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel(); @@ -3480,6 +3544,7 @@ bool Codegen::visit(SwitchStatement *ast) ControlFlowLoop flow(this, &switchEnd); insideSwitch = true; + blockTailCalls.unblock(); for (CaseClauses *it = ast->block->clauses; it; it = it->next) { CaseClause *clause = it->clause; blockMap[clause].link(); @@ -3515,6 +3580,7 @@ bool Codegen::visit(ThrowStatement *ast) return false; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); Reference expr = expression(ast->expression); if (hasError) @@ -3534,6 +3600,7 @@ void Codegen::handleTryCatch(TryStatement *ast) { ControlFlowCatch catchFlow(this, ast->catchExpression); RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); // IMPORTANT: destruction will unblock tail calls before catch is generated statement(ast->statement); bytecodeGenerator->jump().link(noException); } @@ -3544,6 +3611,7 @@ void Codegen::handleTryFinally(TryStatement *ast) { RegisterScope scope(this); ControlFlowFinally finally(this, ast->finallyExpression); + TailCallBlocker blockTailCalls(this); // IMPORTANT: destruction will unblock tail calls before finally is generated if (ast->catchExpression) { handleTryCatch(ast); @@ -3587,6 +3655,7 @@ bool Codegen::visit(WhileStatement *ast) return false; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); BytecodeGenerator::Label start = bytecodeGenerator->newLabel(); BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); @@ -3595,6 +3664,7 @@ bool Codegen::visit(WhileStatement *ast) if (!AST::cast<TrueLiteral *>(ast->expression)) condition(ast->expression, &start, &end, true); + blockTailCalls.unblock(); start.link(); statement(ast->statement); @@ -3611,6 +3681,7 @@ bool Codegen::visit(WithStatement *ast) return true; RegisterScope scope(this); + TailCallBlocker blockTailCalls(this); Reference src = expression(ast->expression); if (hasError) @@ -3620,6 +3691,7 @@ bool Codegen::visit(WithStatement *ast) enterContext(ast); { + blockTailCalls.unblock(); ControlFlowWith flow(this); statement(ast->statement); } diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index ae3e074e07..aaf8baef34 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -733,12 +733,38 @@ protected: bool insideSwitch = false; bool inFormalParameterList = false; bool functionEndsWithReturn = false; + bool _tailCallsAreAllowed = true; + ControlFlow *controlFlow = nullptr; bool _fileNameIsUrl; bool hasError; QList<QQmlJS::DiagnosticMessage> _errors; + class TailCallBlocker + { + public: + TailCallBlocker(Codegen *cg, bool onoff = false) + : _cg(cg) + , _saved(_cg->_tailCallsAreAllowed) + , _onoff(onoff) + { _cg->_tailCallsAreAllowed = onoff; } + + ~TailCallBlocker() + { _cg->_tailCallsAreAllowed = _saved; } + + void unblock() const + { _cg->_tailCallsAreAllowed = _saved; } + + void reblock() const + { _cg->_tailCallsAreAllowed = _onoff; } + + private: + Codegen *_cg; + bool _saved; + bool _onoff; + }; + private: VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const; void handleConstruct(const Reference &base, AST::ArgumentList *args); diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 63306b3672..a29f4ea4cb 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -343,6 +343,17 @@ struct Context { void emitBlockFooter(Compiler::Codegen *codegen); void setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator); + + bool canHaveTailCalls() const + { + if (!isStrict) + return false; + if (contextType == ContextType::Function) + return !isGenerator; + if (contextType == ContextType::Block && parent) + return parent->canHaveTailCalls(); + return false; + } }; diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index a125f0ce28..eed8ffe6b8 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -719,6 +719,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadQmlImportedScripts) d << dumpRegister(result, nFormals); MOTH_END_INSTR(LoadQmlImportedScripts) + + MOTH_BEGIN_INSTR(TailCall) + d << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(TailCall) } } diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index f9bd3cdd4a..2b1660ee58 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -198,6 +198,7 @@ QT_BEGIN_NAMESPACE #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) #define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count) #define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0) +#define INSTR_TailCall(op) INSTRUCTION(op, TailCall, 4, func, thisObject, argc, argv) #define FOR_EACH_MOTH_INSTR_ALL(F) \ F(Nop) \ @@ -338,6 +339,7 @@ QT_BEGIN_NAMESPACE F(PopScriptContext) \ F(InitializeBlockDeadTemporalZone) \ F(ThrowOnNullOrUndefined) \ + F(TailCall) \ F(Debug) \ #define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::Debug_Wide) + 1) diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index 7b4f2b00e7..70d1672689 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -552,6 +552,19 @@ void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, in as->checkException(); } +void BaselineJIT::generate_TailCall(int func, int thisObject, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(5); + as->passInt32AsArg(argc, 4); + as->passJSSlotAsArg(argv, 3); + as->passJSSlotAsArg(thisObject, 2); + as->passJSSlotAsArg(func, 1); + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_tailCall, CallResultDestination::InAccumulator); + as->checkException(); +} + void BaselineJIT::generate_Construct(int func, int argc, int argv) { diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index d4c1b074f6..47ad274d23 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -131,6 +131,7 @@ 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_CallWithSpread(int func, int thisObject, int argc, int argv) override; + void generate_TailCall(int func, int thisObject, int argc, int argv) override; void generate_Construct(int func, int argc, int argv) override; void generate_ConstructWithSpread(int func, int argc, int argv) override; void generate_SetUnwindHandler(int offset) override; diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 7b1a38ff06..470629bd1f 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1061,8 +1061,6 @@ ReturnedValue Runtime::method_loadSuperConstructor(ExecutionEngine *engine, cons return c->asReturnedValue(); } - - #endif // V4_BOOTSTRAP uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) @@ -1538,6 +1536,15 @@ ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const return static_cast<const FunctionObject &>(function).callAsConstructor(arguments.argv, arguments.argc, &newTarget); } +ReturnedValue Runtime::method_tailCall(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc) +{ + //### unwinding the stack, etc, is done in a subsequent patch + if (!function.isFunctionObject()) + return engine->throwTypeError(); + + return static_cast<const FunctionObject &>(function).call(&thisObject, argv, argc); +} + void Runtime::method_throwException(ExecutionEngine *engine, const Value &value) { if (!value.isEmpty()) diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 34b04929af..826b371c1d 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -101,6 +101,7 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { F(ReturnedValue, callWithReceiver, (ExecutionEngine *engine, const Value &func, const Value *thisObject, Value *argv, int argc)) \ F(ReturnedValue, callPossiblyDirectEval, (ExecutionEngine *engine, Value *argv, int argc)) \ F(ReturnedValue, callWithSpread, (ExecutionEngine *engine, const Value &func, const Value &thisObject, Value *argv, int argc)) \ + F(ReturnedValue, tailCall, (ExecutionEngine *engine, const Value &func, const Value &thisObject, Value *argv, int argc)) \ \ /* construct */ \ F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \ diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index f81c8438ce..7fd7be8e38 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -776,6 +776,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, CHECK_EXCEPTION; MOTH_END_INSTR(CallWithSpread) + MOTH_BEGIN_INSTR(TailCall) + STORE_IP(); + acc = Runtime::method_tailCall(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(TailCall) + MOTH_BEGIN_INSTR(Construct) STORE_IP(); acc = Runtime::method_construct(engine, STACK_VALUE(func), ACC, stack + argv, argc); |