traceur
Version:
ES6 to ES5 compiler
588 lines (587 loc) • 25.6 kB
JavaScript
"use strict";
function $__interopRequire(id) {
id = require(id);
return id && id.__esModule && id || {default: id};
}
Object.defineProperties(module.exports, {
__esModule: {value: true},
BlockBindingTransformer: {
enumerable: true,
get: function() {
return BlockBindingTransformer;
}
}
});
var $__spread = $__interopRequire("traceur/dist/commonjs/runtime/modules/spread.js").default;
var $__createClass = $__interopRequire("traceur/dist/commonjs/runtime/modules/createClass.js").default;
var $__superConstructor = $__interopRequire("traceur/dist/commonjs/runtime/modules/superConstructor.js").default;
var $__superGet = $__interopRequire("traceur/dist/commonjs/runtime/modules/superGet.js").default;
var $__getTemplateObject = $__interopRequire("traceur/dist/commonjs/runtime/modules/getTemplateObject.js").default;
var AlphaRenamer = require("./AlphaRenamer.js").AlphaRenamer;
var $__12 = require("../syntax/trees/ParseTreeType.js"),
ANON_BLOCK = $__12.ANON_BLOCK,
BINDING_IDENTIFIER = $__12.BINDING_IDENTIFIER,
FOR_IN_STATEMENT = $__12.FOR_IN_STATEMENT,
FOR_OF_STATEMENT = $__12.FOR_OF_STATEMENT,
VARIABLE_DECLARATION_LIST = $__12.VARIABLE_DECLARATION_LIST;
var $__13 = require("../syntax/trees/ParseTrees.js"),
AnonBlock = $__13.AnonBlock,
BindingElement = $__13.BindingElement,
BindingIdentifier = $__13.BindingIdentifier,
Block = $__13.Block,
Catch = $__13.Catch,
DoWhileStatement = $__13.DoWhileStatement,
ForInStatement = $__13.ForInStatement,
ForStatement = $__13.ForStatement,
FormalParameter = $__13.FormalParameter,
FunctionBody = $__13.FunctionBody,
FunctionExpression = $__13.FunctionExpression,
LabelledStatement = $__13.LabelledStatement,
LiteralPropertyName = $__13.LiteralPropertyName,
Module = $__13.Module,
ObjectPatternField = $__13.ObjectPatternField,
Script = $__13.Script,
VariableDeclaration = $__13.VariableDeclaration,
VariableDeclarationList = $__13.VariableDeclarationList,
VariableStatement = $__13.VariableStatement,
WhileStatement = $__13.WhileStatement;
var IdentifierToken = require("../syntax/IdentifierToken.js").IdentifierToken;
var ParseTreeTransformer = require("./ParseTreeTransformer.js").ParseTreeTransformer;
var VAR = require("../syntax/TokenType.js").VAR;
var $__17 = require("./ParseTreeFactory.js"),
createBindingIdentifier = $__17.createBindingIdentifier,
createIdentifierExpression = $__17.createIdentifierExpression,
createIdentifierToken = $__17.createIdentifierToken;
var FindIdentifiers = require("./FindIdentifiers.js").FindIdentifiers;
var FindVisitor = require("./FindVisitor.js").FindVisitor;
var FnExtractAbruptCompletions = require("./FnExtractAbruptCompletions.js").FnExtractAbruptCompletions;
var ScopeChainBuilderWithReferences = require("../semantics/ScopeChainBuilderWithReferences.js").ScopeChainBuilderWithReferences;
var parseExpression = require("./PlaceholderParser.js").parseExpression;
var prependStatements = require("./PrependStatements.js").prependStatements;
function varNeedsInitializer(tree, loopTree) {
if (loopTree === null)
return false;
var type = loopTree.type;
if (type !== FOR_IN_STATEMENT && type !== FOR_OF_STATEMENT)
return true;
return loopTree.initializer.declarations[0] !== tree;
}
var BlockBindingTransformer = function($__super) {
function BlockBindingTransformer(idGenerator, reporter, tree) {
var scopeBuilder = arguments[3];
var latestScope = arguments[4];
$__superConstructor(BlockBindingTransformer).call(this);
this.idGenerator_ = idGenerator;
this.reporter_ = reporter;
if (!scopeBuilder) {
scopeBuilder = new ScopeChainBuilderWithReferences(reporter);
scopeBuilder.visitAny(tree);
}
this.scopeBuilder_ = scopeBuilder;
this.labelledLoops_ = new Map();
this.prependStatement_ = [];
this.prependBlockStatement_ = [];
this.blockRenames_ = [];
this.rootTree_ = tree;
if (latestScope) {
this.scope_ = latestScope;
} else {
this.pushScope(tree);
}
this.usedVars_ = this.scope_.getAllBindingNames();
this.maybeRename_ = false;
this.inObjectPattern_ = false;
this.currentLoopTree_ = null;
}
return ($__createClass)(BlockBindingTransformer, {
getVariableName_: function(variable) {
var lvalue = variable.lvalue;
if (lvalue.type === BINDING_IDENTIFIER) {
return lvalue.getStringValue();
}
throw new Error('Unexpected destructuring declaration found.');
},
flushRenames: function(tree) {
tree = renameAll(this.blockRenames_, tree);
this.blockRenames_.length = 0;
return tree;
},
pushScope: function(tree) {
var scope = this.scopeBuilder_.getScopeForTree(tree);
if (!scope)
throw new Error('BlockBindingTransformer tree with no scope');
if (this.scope_)
this.scope_.blockBindingRenames = this.blockRenames_;
this.scope_ = scope;
this.blockRenames_ = [];
return scope;
},
popScope: function(scope) {
if (this.scope_ !== scope) {
throw new Error('BlockBindingTransformer scope mismatch');
}
this.scope_ = scope.parent;
this.blockRenames_ = this.scope_ && this.scope_.blockBindingRenames || [];
},
revisitTreeForScopes: function(tree) {
this.scopeBuilder_.scope = this.scope_;
this.scopeBuilder_.visitAny(tree);
this.scopeBuilder_.scope = null;
},
needsRename_: function(name) {
if (this.usedVars_.has(name))
return true;
var scope = this.scope_;
var parent = scope.parent;
if (!parent || scope.isVarScope)
return false;
var varScope = scope.getVarScope();
if (varScope && varScope.hasFreeVariable(name)) {
return true;
}
var parentBinding = parent.getBindingByName(name);
if (!parentBinding)
return false;
var currentBinding = scope.getBindingByName(name);
if (currentBinding.tree === parentBinding.tree)
return false;
return true;
},
newNameFromOrig: function(origName, renames) {
var newName;
if (this.needsRename_(origName)) {
newName = origName + this.idGenerator_.generateUniqueIdentifier();
renames.push(new Rename(origName, newName));
} else {
this.usedVars_.add(origName);
newName = origName;
}
return newName;
},
transformFunctionBody: function(tree) {
if (tree === this.rootTree_ || !this.rootTree_) {
tree = $__superGet(this, BlockBindingTransformer.prototype, "transformFunctionBody").call(this, tree);
if (this.prependStatement_.length || this.blockRenames_.length) {
var statements = prependStatements.apply((void 0), $__spread([tree.statements], this.prependStatement_));
tree = new FunctionBody(tree.location, statements);
tree = this.flushRenames(tree);
}
} else {
var functionTransform = new BlockBindingTransformer(this.idGenerator_, this.reporter_, tree, this.scopeBuilder_, this.scope_);
var functionBodyTree = functionTransform.transformAny(tree);
if (functionBodyTree === tree) {
return tree;
}
tree = new FunctionBody(tree.location, functionBodyTree.statements);
}
return tree;
},
transformScript: function(tree) {
if (tree === this.rootTree_ || !this.rootTree_) {
tree = $__superGet(this, BlockBindingTransformer.prototype, "transformScript").call(this, tree);
if (this.prependStatement_.length || this.blockRenames_.length) {
var scriptItemList = prependStatements.apply((void 0), $__spread([tree.scriptItemList], this.prependStatement_));
tree = new Script(tree.location, scriptItemList, tree.moduleName);
tree = this.flushRenames(tree);
}
} else {
var functionTransform = new BlockBindingTransformer(this.idGenerator_, this.reporter_, tree, this.scopeBuilder_);
var newTree = functionTransform.transformAny(tree);
if (newTree === tree) {
return tree;
}
tree = new Script(tree.location, newTree.scriptItemList, tree.moduleName);
}
return tree;
},
transformModule: function(tree) {
if (tree === this.rootTree_ || !this.rootTree_) {
tree = $__superGet(this, BlockBindingTransformer.prototype, "transformModule").call(this, tree);
if (this.prependStatement_.length || this.blockRenames_.length) {
var scriptItemList = prependStatements.apply((void 0), $__spread([tree.scriptItemList], this.prependStatement_));
tree = new Module(tree.location, scriptItemList, tree.moduleName);
tree = this.flushRenames(tree);
}
} else {
var functionTransform = new BlockBindingTransformer(this.idGenerator_, this.reporter_, tree, this.scopeBuilder_);
var newTree = functionTransform.transformAny(tree);
if (newTree === tree) {
return tree;
}
tree = new Module(tree.location, newTree.scriptItemList, tree.moduleName);
}
return tree;
},
transformVariableStatement: function(tree) {
var declarations = this.transformAny(tree.declarations);
if (declarations.type === ANON_BLOCK) {
return declarations;
}
if (declarations === tree.declarations) {
return tree;
}
return new VariableStatement(tree.location, declarations);
},
transformVariableDeclarationList: function(tree) {
if (tree.declarationType === VAR) {
return $__superGet(this, BlockBindingTransformer.prototype, "transformVariableDeclarationList").call(this, tree);
}
this.maybeRename_ = !this.scope_.isVarScope;
var declarations = this.transformList(tree.declarations);
this.maybeRename_ = false;
return new VariableDeclarationList(tree.location, VAR, declarations);
},
transformVariableDeclaration: function(tree) {
var maybeRename = this.maybeRename_;
var lvalue = this.transformAny(tree.lvalue);
this.maybeRename_ = false;
var initializer = null;
if (tree.initializer) {
initializer = this.transformAny(tree.initializer);
} else if (varNeedsInitializer(tree, this.currentLoopTree_)) {
initializer = parseExpression($__getTemplateObject(["void 0"]));
}
this.maybeRename_ = maybeRename;
if (tree.lvalue === lvalue && tree.initializer === initializer) {
return tree;
}
return new VariableDeclaration(tree.location, lvalue, tree.typeAnnotation, initializer);
},
transformBindingIdentifier: function(tree) {
if (this.maybeRename_) {
var origName = tree.getStringValue();
var newName = this.newNameFromOrig(origName, this.blockRenames_);
if (origName === newName) {
return tree;
}
var newToken = new IdentifierToken(tree.location, newName);
var bindingIdentifier = new BindingIdentifier(tree.location, newToken);
this.scope_.renameBinding(origName, bindingIdentifier, VAR, this.reporter_);
return bindingIdentifier;
}
return $__superGet(this, BlockBindingTransformer.prototype, "transformBindingIdentifier").call(this, tree);
},
transformBindingElement: function(tree) {
var maybeRename = this.maybeRename_;
var inObjectPattern = this.inObjectPattern_;
var binding = this.transformAny(tree.binding);
this.maybeRename_ = false;
this.inObjectPattern_ = false;
var initializer = this.transformAny(tree.initializer);
this.maybeRename_ = maybeRename;
this.inObjectPattern_ = inObjectPattern;
if (tree.binding === binding && tree.initializer === initializer) {
return tree;
}
var bindingElement = new BindingElement(tree.location, binding, initializer);
if (this.inObjectPattern_ && tree.binding !== binding && tree.binding.type === BINDING_IDENTIFIER) {
return new ObjectPatternField(tree.location, new LiteralPropertyName(tree.location, tree.binding.identifierToken), bindingElement);
}
return bindingElement;
},
transformObjectPattern: function(tree) {
var inObjectPattern = this.inObjectPattern_;
this.inObjectPattern_ = true;
var transformed = $__superGet(this, BlockBindingTransformer.prototype, "transformObjectPattern").call(this, tree);
this.inObjectPattern_ = inObjectPattern;
return transformed;
},
transformObjectPatternField: function(tree) {
var name = this.transformAny(tree.name);
this.inObjectPattern_ = false;
var element = this.transformAny(tree.element);
this.inObjectPattern_ = true;
if (tree.name === name && tree.element === element) {
return tree;
}
return new ObjectPatternField(tree.location, name, element);
},
transformBlock: function(tree) {
var scope = this.pushScope(tree);
var outerPrepends = this.prependBlockStatement_;
this.prependBlockStatement_ = [];
tree = $__superGet(this, BlockBindingTransformer.prototype, "transformBlock").call(this, tree);
if (this.prependBlockStatement_.length) {
tree = new Block(tree.location, prependStatements.apply((void 0), $__spread([tree.statements], this.prependBlockStatement_)));
}
this.prependBlockStatement_ = outerPrepends;
tree = this.flushRenames(tree);
this.popScope(scope);
return tree;
},
transformCatch: function(tree) {
var scope = this.pushScope(tree);
var binding = this.transformAny(tree.binding);
var statements = this.transformList(tree.catchBody.statements);
if (binding !== tree.binding || statements !== tree.catchBody.statements) {
tree = new Catch(tree.location, binding, new Block(tree.catchBody.location, statements));
}
tree = this.flushRenames(tree);
this.popScope(scope);
return tree;
},
transformFunctionForScope_: function(func, tree) {
var scope = this.pushScope(tree);
tree = func();
tree = this.flushRenames(tree);
this.popScope(scope);
return tree;
},
transformGetAccessor: function(tree) {
var $__3 = this;
return this.transformFunctionForScope_(function() {
return $__superGet($__3, BlockBindingTransformer.prototype, "transformGetAccessor").call($__3, tree);
}, tree);
},
transformSetAccessor: function(tree) {
var $__3 = this;
return this.transformFunctionForScope_(function() {
return $__superGet($__3, BlockBindingTransformer.prototype, "transformSetAccessor").call($__3, tree);
}, tree);
},
transformFunctionExpression: function(tree) {
var $__3 = this;
return this.transformFunctionForScope_(function() {
return $__superGet($__3, BlockBindingTransformer.prototype, "transformFunctionExpression").call($__3, tree);
}, tree);
},
transformFunctionDeclaration: function(tree) {
var $__3 = this;
if (!this.scope_.isVarScope) {
var origName = tree.name.getStringValue();
var newName = this.newNameFromOrig(origName, this.blockRenames_);
var functionExpression = new FunctionExpression(tree.location, null, tree.functionKind, tree.parameterList, tree.typeAnnotation, tree.annotations, tree.body);
this.revisitTreeForScopes(functionExpression);
functionExpression = this.transformAny(functionExpression);
var bindingIdentifier = createBindingIdentifier(newName);
var statement = new VariableStatement(tree.location, new VariableDeclarationList(tree.location, VAR, [new VariableDeclaration(tree.location, bindingIdentifier, null, functionExpression)]));
this.scope_.renameBinding(origName, bindingIdentifier, VAR, this.reporter_);
this.prependBlockStatement_.push(statement);
return new AnonBlock(null, []);
}
return this.transformFunctionForScope_(function() {
return $__superGet($__3, BlockBindingTransformer.prototype, "transformFunctionDeclaration").call($__3, tree);
}, tree);
},
transformLoop_: function(func, tree, loopFactory) {
var $__3 = this;
var scope,
initializerIsBlockBinding;
if (tree.initializer && tree.initializer.type === VARIABLE_DECLARATION_LIST && tree.initializer.declarationType !== VAR) {
initializerIsBlockBinding = true;
}
if (initializerIsBlockBinding) {
scope = this.pushScope(tree);
}
var finder = new FindBlockBindingInLoop(tree, this.scopeBuilder_);
finder.visitAny(tree);
if (!finder.found) {
var callFunc = function() {
var currentLoopTree = $__3.currentLoopTree_;
$__3.currentLoopTree_ = tree;
var rv = func(tree);
$__3.currentLoopTree_ = currentLoopTree;
return rv;
};
if (!initializerIsBlockBinding) {
return callFunc();
}
var renames = [];
var initializer = new VariableDeclarationList(null, VAR, tree.initializer.declarations.map(function(declaration) {
var origName = $__3.getVariableName_(declaration);
var newName = $__3.newNameFromOrig(origName, renames);
var bindingIdentifier = createBindingIdentifier(newName);
$__3.scope_.renameBinding(origName, bindingIdentifier, VAR, $__3.reporter_);
return new VariableDeclaration(null, bindingIdentifier, null, declaration.initializer);
}));
initializer = renameAll(renames, initializer);
tree = loopFactory(initializer, renames, renameAll(renames, tree.body));
this.revisitTreeForScopes(tree);
tree = callFunc();
} else {
var iifeParameterList = [];
var iifeArgumentList = [];
var renames$__4 = [];
var initializer$__5 = null;
if (tree.initializer) {
if (tree.initializer.type === VARIABLE_DECLARATION_LIST && tree.initializer.declarationType !== VAR) {
initializer$__5 = new VariableDeclarationList(null, VAR, tree.initializer.declarations.map(function(declaration) {
var origName = $__3.getVariableName_(declaration);
var newName = $__3.newNameFromOrig(origName, renames$__4);
iifeArgumentList.push(createIdentifierExpression(newName));
iifeParameterList.push(new FormalParameter(null, new BindingElement(null, createBindingIdentifier(origName), null), null, []));
var bindingIdentifier = createBindingIdentifier(newName);
$__3.scope_.renameBinding(origName, bindingIdentifier, VAR, $__3.reporter_);
return new VariableDeclaration(null, bindingIdentifier, null, declaration.initializer);
}));
initializer$__5 = renameAll(renames$__4, initializer$__5);
} else {
initializer$__5 = this.transformAny(tree.initializer);
}
}
var loopLabel = this.labelledLoops_.get(tree);
var iifeInfo = FnExtractAbruptCompletions.createIIFE(this.idGenerator_, tree.body, iifeParameterList, iifeArgumentList, function() {
return loopLabel = loopLabel || createIdentifierToken($__3.idGenerator_.generateUniqueIdentifier());
}, this.scope_.inGenerator);
tree = loopFactory(initializer$__5, renames$__4, iifeInfo.loopBody);
if (loopLabel) {
tree = new LabelledStatement(tree.location, loopLabel, tree);
}
tree = new AnonBlock(tree.location, [iifeInfo.variableStatements, tree]);
this.revisitTreeForScopes(tree);
tree = this.transformAny(tree);
}
if (initializerIsBlockBinding) {
tree = this.flushRenames(tree);
this.popScope(scope);
}
return tree;
},
transformForInStatement: function(tree) {
var $__3 = this;
return this.transformLoop_(function(t) {
return $__superGet($__3, BlockBindingTransformer.prototype, "transformForInStatement").call($__3, t);
}, tree, function(initializer, renames, body) {
return new ForInStatement(tree.location, initializer, renameAll(renames, tree.collection), body);
});
},
transformForStatement: function(tree) {
var $__3 = this;
return this.transformLoop_(function(t) {
return $__superGet($__3, BlockBindingTransformer.prototype, "transformForStatement").call($__3, t);
}, tree, function(initializer, renames, body) {
return new ForStatement(tree.location, initializer, renameAll(renames, tree.condition), renameAll(renames, tree.increment), body);
});
},
transformWhileStatement: function(tree) {
var $__3 = this;
return this.transformLoop_(function(t) {
return $__superGet($__3, BlockBindingTransformer.prototype, "transformWhileStatement").call($__3, t);
}, tree, function(initializer, renames, body) {
return new WhileStatement(tree.location, renameAll(renames, tree.condition), body);
});
},
transformDoWhileStatement: function(tree) {
var $__3 = this;
return this.transformLoop_(function(t) {
return $__superGet($__3, BlockBindingTransformer.prototype, "transformDoWhileStatement").call($__3, t);
}, tree, function(initializer, renames, body) {
return new DoWhileStatement(tree.location, body, renameAll(renames, tree.condition));
});
},
transformLabelledStatement: function(tree) {
if (tree.statement.isIterationStatement()) {
this.labelledLoops_.set(tree.statement, tree.name.value);
var statement = this.transformAny(tree.statement);
if (!statement.isStatement()) {
return statement;
}
if (statement === tree.statement) {
return tree;
}
return new LabelledStatement(tree.location, tree.name, statement);
}
return $__superGet(this, BlockBindingTransformer.prototype, "transformLabelledStatement").call(this, tree);
}
}, {}, $__super);
}(ParseTreeTransformer);
var Rename = function() {
function Rename(oldName, newName) {
this.oldName = oldName;
this.newName = newName;
}
return ($__createClass)(Rename, {}, {});
}();
function renameAll(renames, tree) {
renames.forEach(function(rename) {
tree = AlphaRenamer.rename(tree, rename.oldName, rename.newName);
});
return tree;
}
var FindBlockBindingInLoop = function($__super) {
function FindBlockBindingInLoop(tree, scopeBuilder) {
$__superConstructor(FindBlockBindingInLoop).call(this);
this.scopeBuilder_ = scopeBuilder;
this.topScope_ = scopeBuilder.getScopeForTree(tree) || scopeBuilder.getScopeForTree(tree.body);
this.outOfScope_ = null;
this.acceptLoop_ = tree.isIterationStatement();
}
return ($__createClass)(FindBlockBindingInLoop, {
visitForInStatement: function(tree) {
var $__3 = this;
this.visitLoop_(tree, function() {
return $__superGet($__3, FindBlockBindingInLoop.prototype, "visitForInStatement").call($__3, tree);
});
},
visitForStatement: function(tree) {
var $__3 = this;
this.visitLoop_(tree, function() {
return $__superGet($__3, FindBlockBindingInLoop.prototype, "visitForStatement").call($__3, tree);
});
},
visitWhileStatement: function(tree) {
var $__3 = this;
this.visitLoop_(tree, function() {
return $__superGet($__3, FindBlockBindingInLoop.prototype, "visitWhileStatement").call($__3, tree);
});
},
visitDoWhileStatement: function(tree) {
var $__3 = this;
this.visitLoop_(tree, function() {
return $__superGet($__3, FindBlockBindingInLoop.prototype, "visitDoWhileStatement").call($__3, tree);
});
},
visitLoop_: function(tree, func) {
if (this.acceptLoop_) {
this.acceptLoop_ = false;
} else if (!this.outOfScope_) {
this.outOfScope_ = this.scopeBuilder_.getScopeForTree(tree) || this.scopeBuilder_.getScopeForTree(tree.body);
}
func();
},
visitArrowFunction: function(tree) {
this.visitFunction_(tree);
},
visitFunctionDeclaration: function(tree) {
this.visitFunction_(tree);
},
visitFunctionExpression: function(tree) {
this.visitFunction_(tree);
},
visitGetAccessor: function(tree) {
this.visitFunction_(tree);
},
visitMethod: function(tree) {
this.visitFunction_(tree);
},
visitSetAccessor: function(tree) {
this.visitFunction_(tree);
},
visitFunction_: function(tree) {
var $__3 = this;
this.found = new FindIdentifiers(tree, function(identifierToken, identScope) {
identScope = $__3.scopeBuilder_.getScopeForTree(identScope);
var fnScope = $__3.outOfScope_ || $__3.scopeBuilder_.getScopeForTree(tree);
if (identScope.hasLexicalBindingName(identifierToken)) {
return false;
}
while (identScope !== fnScope && (identScope = identScope.parent)) {
if (identScope.hasLexicalBindingName(identifierToken)) {
return false;
}
}
while (fnScope = fnScope.parent) {
if (fnScope.hasLexicalBindingName(identifierToken)) {
return true;
}
if (fnScope.hasVariableBindingName(identifierToken)) {
return false;
}
if (fnScope === $__3.topScope_)
break;
}
return false;
}).found;
}
}, {}, $__super);
}(FindVisitor);