use a template in tail call transformer - @RReverser

This commit is contained in:
Sebastian McKenzie
2015-02-07 23:52:35 +11:00
parent 9f7bcf585d
commit 8a143bf957
2 changed files with 36 additions and 29 deletions

View File

@@ -0,0 +1,13 @@
{
var ARGUMENTS_ID = arguments,
THIS_ID = this,
SHOULD_CONTINUE_ID,
RESULT_ID;
do {
SHOULD_CONTINUE_ID = false;
RESULT_ID = FUNCTION.apply(THIS_ID, ARGUMENTS_ID);
} while(SHOULD_CONTINUE_ID);
return RESULT_ID;
}

View File

@@ -1,6 +1,7 @@
"use strict";
var t = require("../../../types");
var util = require("../../../util");
var t = require("../../../types");
function returnBlock(expr) {
return t.blockStatement([t.returnStatement(expr)]);
@@ -17,6 +18,7 @@ function transformExpression(node, scope, state) {
if (!callConsequent && !callAlternate) {
return;
}
// if ternary operator had tail recursion in value, convert to optimized if-statement
node.type = "IfStatement";
node.consequent = callConsequent ? t.toBlock(callConsequent) : returnBlock(node.consequent);
@@ -33,6 +35,7 @@ function transformExpression(node, scope, state) {
if (!callRight) {
return;
}
// cache left value as it might have side-effects
var leftId = state.getLeftId();
var testExpr = t.assignmentExpression(
@@ -47,16 +50,19 @@ function transformExpression(node, scope, state) {
case "SequenceExpression":
var seq = node.expressions;
// only last element can be optimized
var lastCall = subTransform(seq[seq.length - 1]);
if (!lastCall) {
return;
}
// remove converted expression from sequence
// and convert to regular expression if needed
if (--seq.length === 1) {
node = seq[0];
}
return [t.expressionStatement(node)].concat(lastCall);
case "CallExpression":
@@ -68,12 +74,15 @@ function transformExpression(node, scope, state) {
case "call":
args = t.arrayExpression(node.arguments.slice(1));
break;
case "apply":
args = node.arguments[1] || t.identifier("undefined");
break;
default:
return;
}
thisBinding = node.arguments[0];
callee = callee.object;
}
@@ -91,11 +100,13 @@ function transformExpression(node, scope, state) {
state.getArgumentsId(),
args || t.arrayExpression(node.arguments)
)),
t.expressionStatement(t.assignmentExpression(
"=",
state.getThisId(),
thisBinding || t.identifier("undefined")
)),
t.returnStatement(t.assignmentExpression(
"=",
state.getShouldContinueId(),
@@ -152,15 +163,19 @@ exports.FunctionExpression = function (node, parent, scope) {
var state = {
hasTailRecursion: false,
ownerId: ownerId,
getArgumentsId: function () {
return argumentsId = argumentsId || scope.generateUidIdentifier("arguments");
},
getThisId: function () {
return thisId = thisId || scope.generateUidIdentifier("this");
},
getShouldContinueId: function () {
return shouldContinueId = shouldContinueId || scope.generateUidIdentifier("shouldContinue");
},
getLeftId: function () {
return leftId = leftId || scope.generateUidIdentifier("left");
}
@@ -182,32 +197,11 @@ exports.FunctionExpression = function (node, parent, scope) {
var resultId = scope.generateUidIdentifier("result");
state.getShouldContinueId();
node.body = t.blockStatement([
t.variableDeclaration("var", [
t.variableDeclarator(argumentsId, t.identifier("arguments")),
t.variableDeclarator(thisId, t.thisExpression()),
t.variableDeclarator(shouldContinueId),
t.variableDeclarator(resultId)
]),
t.doWhileStatement(t.blockStatement([
t.expressionStatement(t.assignmentExpression(
"=",
shouldContinueId,
t.literal(false)
)),
t.expressionStatement(t.assignmentExpression(
"=",
resultId,
t.callExpression(
t.memberExpression(
t.functionExpression(null, node.params, block),
t.identifier("apply"),
false
),
[thisId, argumentsId]
)
))
]), shouldContinueId),
t.returnStatement(resultId)
]);
node.body = util.template("tail-call-body", {
SHOULD_CONTINUE_ID: shouldContinueId,
ARGUMENTS_ID: argumentsId,
RESULT_ID: resultId,
FUNCTION: t.functionExpression(null, node.params, block),
THIS_ID: thisId,
});
};