aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp3
-rw-r--r--src/qml/compiler/qv4codegen.cpp136
-rw-r--r--src/qml/compiler/qv4codegen_p.h26
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h11
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp4
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h2
-rw-r--r--src/qml/jit/qv4baselinejit.cpp13
-rw-r--r--src/qml/jit/qv4baselinejit_p.h1
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp11
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h1
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp6
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);