6to5-core
Advanced tools
Comparing version 3.5.2 to 3.5.3
"use strict"; | ||
/* | ||
var util = require("../../../util"); | ||
var t = require("../../../types"); | ||
function returnBlock(expr) { | ||
return t.blockStatement([t.returnStatement(expr)]); | ||
} | ||
function transformExpression(node, scope, state) { | ||
@@ -12,40 +16,102 @@ if (!node) return; | ||
case "ConditionalExpression": | ||
// any value of ternary operator can be final one | ||
subTransform(node.consequent); | ||
subTransform(node.alternate); | ||
break; | ||
var callConsequent = subTransform(node.consequent); | ||
var callAlternate = subTransform(node.alternate); | ||
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); | ||
if (callAlternate) { | ||
node.alternate = t.isIfStatement(callAlternate) ? callAlternate : t.toBlock(callAlternate); | ||
} else { | ||
node.alternate = returnBlock(node.alternate); | ||
} | ||
return node; | ||
case "LogicalExpression": | ||
// only right expression can be final and so optimized | ||
subTransform(node.right); | ||
break; | ||
// only call in right-value of can be optimized | ||
var callRight = subTransform(node.right); | ||
if (!callRight) { | ||
return; | ||
} | ||
// cache left value as it might have side-effects | ||
var leftId = state.getLeftId(); | ||
var testExpr = t.assignmentExpression( | ||
"=", | ||
leftId, | ||
node.left | ||
); | ||
if (node.operator === "&&") { | ||
testExpr = t.unaryExpression("!", testExpr); | ||
} | ||
return [t.ifStatement(testExpr, returnBlock(leftId))].concat(callRight); | ||
case "SequenceExpression": | ||
// only last element of sequence can be optimized | ||
var seq = node.expressions; | ||
subTransform(seq[seq.length - 1]); | ||
break; | ||
case "CallExpression": | ||
var callee = node.callee, thisBinding; | ||
var args = [callee]; | ||
// only last element can be optimized | ||
var lastCall = subTransform(seq[seq.length - 1]); | ||
if (!lastCall) { | ||
return; | ||
} | ||
// bind `this` to object in member expressions | ||
if (t.isMemberExpression(callee)) { | ||
var object = state.wrapSideEffect(callee.object); | ||
callee.object = object.expr; | ||
thisBinding = object.ref; | ||
// remove converted expression from sequence | ||
// and convert to regular expression if needed | ||
if (--seq.length === 1) { | ||
node = seq[0]; | ||
} | ||
if (node.arguments.length > 0 || thisBinding) { | ||
args.push(t.arrayExpression(node.arguments)); | ||
return [t.expressionStatement(node)].concat(lastCall); | ||
case "CallExpression": | ||
var callee = node.callee, prop, thisBinding, args; | ||
if (t.isMemberExpression(callee, { computed: false }) && | ||
t.isIdentifier(prop = callee.property)) { | ||
switch (prop.name) { | ||
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; | ||
} | ||
if (thisBinding) { | ||
args.push(thisBinding); | ||
// only tail recursion can be optimized as for now | ||
if (!t.isIdentifier(callee) || !scope.bindingEquals(callee.name, state.ownerId)) { | ||
return; | ||
} | ||
node.callee = state.getHelperRef(); | ||
node.arguments = args; | ||
break; | ||
state.hasTailRecursion = true; | ||
return [ | ||
t.expressionStatement(t.assignmentExpression( | ||
"=", | ||
state.getArgumentsId(), | ||
args || t.arrayExpression(node.arguments) | ||
)), | ||
t.expressionStatement(t.assignmentExpression( | ||
"=", | ||
state.getThisId(), | ||
thisBinding || t.identifier("undefined") | ||
)), | ||
t.returnStatement(t.assignmentExpression( | ||
"=", | ||
state.getShouldContinueId(), | ||
t.literal(true) | ||
)) | ||
]; | ||
} | ||
@@ -62,13 +128,15 @@ })(node); | ||
// it contains tail recursion | ||
transformExpression(node.argument, scope, state); | ||
return transformExpression(node.argument, scope, state); | ||
} else if (t.isFunction(node)) { | ||
// inner function's bodies are irrelevant | ||
this.skip(); | ||
return this.skip(); | ||
} else if (t.isTryStatement(parent)) { | ||
if (node === parent.block) { | ||
// `try`-blocks can't be optimized | ||
this.skip(); | ||
} else if (parent.finalizer && node !== parent.finalizer) { | ||
// `catch` clause followed by `finally` can't be optimized | ||
this.skip(); | ||
return this.skip(); | ||
} else if (node === parent.finalizer) { | ||
return; | ||
} else { | ||
if (parent.finalizer) { | ||
this.skip(); | ||
} | ||
return; | ||
} | ||
@@ -89,21 +157,28 @@ } | ||
exports.FunctionDeclaration = | ||
exports.FunctionExpression = function (node, parent, scope, file) { | ||
var tempId, helperRef; | ||
exports.FunctionExpression = function (node, parent, scope) { | ||
// only tail recursion can be optimized as for now, | ||
// so we can skip anonymous functions entirely | ||
var ownerId = node.id; | ||
if (!ownerId) return; | ||
var argumentsId, thisId, shouldContinueId, leftId; | ||
var state = { | ||
ownerId: node.id, | ||
hasTailRecursion: false, | ||
ownerId: ownerId, | ||
getHelperRef: function () { | ||
return helperRef = helperRef || file.addHelper("tail-call"); | ||
getArgumentsId: function () { | ||
return argumentsId = argumentsId || scope.generateUidIdentifier("arguments"); | ||
}, | ||
wrapSideEffect: function (node) { | ||
if (t.isIdentifier(node) || t.isLiteral(node)) { | ||
return {expr: node, ref: node}; | ||
} | ||
tempId = tempId || scope.generateUidIdentifier("temp"); | ||
return { | ||
expr: t.assignmentExpression("=", tempId, node), | ||
ref: tempId | ||
}; | ||
getThisId: function () { | ||
return thisId = thisId || scope.generateUidIdentifier("this"); | ||
}, | ||
getShouldContinueId: function () { | ||
return shouldContinueId = shouldContinueId || scope.generateUidIdentifier("shouldContinue"); | ||
}, | ||
getLeftId: function () { | ||
return leftId = leftId || scope.generateUidIdentifier("left"); | ||
} | ||
@@ -115,8 +190,22 @@ }; | ||
if (tempId) { | ||
t.ensureBlock(node).body.unshift(t.variableDeclaration("var", [ | ||
t.variableDeclarator(tempId) | ||
if (!state.hasTailRecursion) return; | ||
var block = t.ensureBlock(node); | ||
if (leftId) { | ||
block.body.unshift(t.variableDeclaration("var", [ | ||
t.variableDeclarator(leftId) | ||
])); | ||
} | ||
var resultId = scope.generateUidIdentifier("result"); | ||
state.getShouldContinueId(); | ||
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, | ||
}); | ||
}; | ||
*/ |
{ | ||
"name": "6to5-core", | ||
"description": "Turn ES6 code into readable vanilla ES5 with source maps", | ||
"version": "3.5.2", | ||
"version": "3.5.3", | ||
"author": "Sebastian McKenzie <sebmck@gmail.com>", | ||
@@ -6,0 +6,0 @@ "homepage": "https://6to5.org/", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
1503832
12697