traceur
Version:
ES6 to ES5 compiler
176 lines (156 loc) • 6.17 kB
JavaScript
// Copyright 2012 Traceur Authors.
//
// Licensed under the Apache License, Version 2.0 (the 'License');
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an 'AS IS' BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {ARGUMENTS, CONSTRUCTOR, THIS} from '../syntax/PredefinedName.js';
import {AlphaRenamer} from './AlphaRenamer.js';
import {FunctionExpression} from '../syntax/trees/ParseTrees.js';
import {TempVarTransformer} from './TempVarTransformer.js';
import {ParenTrait} from './ParenTrait.js';
import alphaRenameThisAndArguments from './alphaRenameThisAndArguments.js';
import {FUNCTION_BODY, LITERAL_PROPERTY_NAME} from '../syntax/trees/ParseTreeType.js';
import {FindThisOrArguments} from './FindThisOrArguments.js';
import {
createAssignmentExpression,
createCommaExpression,
createFunctionBody,
createIdentifierExpression,
createReturnStatement,
createThisExpression,
} from './ParseTreeFactory.js';
/**
* Converts a concise body to a function body.
* @param {ParseTree} tree
* @return {FunctionBody}
*/
function convertConciseBody(tree) {
if (tree.type !== FUNCTION_BODY)
return createFunctionBody([createReturnStatement(tree)]);
return tree;
}
/**
* Desugars arrow function expressions
*
* @see <a href="http://wiki.ecmascript.org/doku.php?id=strawman:arrow_function_syntax">strawman:arrow_function_syntax</a>
*/
export class ArrowFunctionTransformer extends ParenTrait(TempVarTransformer) {
constructor(identifierGenerator, reporter, options) {
super(identifierGenerator, reporter, options);
this.inDerivedClass_ = false;
this.inConstructor_ = false;
}
/**
* Transforms an arrow function expression into a function declaration.
* The main things we need to deal with are the 'this' binding, and adding a
* function body and return statement if needed.
*/
transformArrowFunction(tree) {
if (this.inDerivedClass_ && this.inConstructor_) {
return this.transformUsingCommaExpression_(tree);
}
return this.transformUsingTempVar_(tree);
}
// Transforms the arrow function using a comma expression to avoid needing to
// read this earlier in the function.
//
// ($tmp = this, function() {})
transformUsingCommaExpression_(tree) {
let finder = new FindThisOrArguments();
let argumentsTempName, thisTempName;
finder.visitAny(tree);
if (finder.foundArguments) {
argumentsTempName = this.addTempVar();
tree = AlphaRenamer.rename(tree, ARGUMENTS, argumentsTempName);
}
if (finder.foundThis) {
thisTempName = this.addTempVar();
tree = AlphaRenamer.rename(tree, THIS, thisTempName);
}
let parameterList = this.transformAny(tree.parameterList);
let body = this.transformAny(tree.body);
body = convertConciseBody(body);
let functionExpression = new FunctionExpression(tree.location, null,
tree.functionKind, parameterList, null, [], body);
let expressions = [];
if (argumentsTempName) {
expressions.push(createAssignmentExpression(
createIdentifierExpression(argumentsTempName),
createIdentifierExpression(ARGUMENTS)));
}
if (thisTempName) {
expressions.push(createAssignmentExpression(
createIdentifierExpression(thisTempName),
createThisExpression()));
}
if (expressions.length === 0) {
return functionExpression;
}
expressions.push(functionExpression);
return createCommaExpression(expressions);
}
// This transforms the arrow function into:
//
// var $tmp = this;
// ...
// (function() {})
//
// with a temp variable added to the top of the current function.
transformUsingTempVar_(tree) {
// TODO(arv): alphaRenameThisAndArguments is used in other places. It is
// possible that we run into the same issues with super() in derived
// constructors in those cases (that we hoist this before the super call).
// Come up with a better reusable way to do this.
let alphaRenamed = alphaRenameThisAndArguments(this, tree);
let parameterList = this.transformAny(alphaRenamed.parameterList);
let body = this.transformAny(alphaRenamed.body);
body = convertConciseBody(body);
let functionExpression = new FunctionExpression(tree.location, null,
tree.functionKind, parameterList, null, [], body);
return functionExpression;
}
transformClassExpression(tree) {
let inDerivedClass = this.inDerivedClass_;
this.inDerivedClass_ = tree.superClass !== null;
let result = super.transformClassExpression(tree);
this.inDerivedClass_ = inDerivedClass;
return result;
}
transformClassDeclaration(tree) {
let inDerivedClass = this.inDerivedClass_;
this.inDerivedClass_ = tree.superClass !== null;
let result = super.transformClassDeclaration(tree);
this.inDerivedClass_ = inDerivedClass;
return result;
}
transformMethod(tree) {
let inConstructor = this.inConstructor_;
this.inConstructor_ = !tree.isStatic && tree.functionKind === null &&
tree.name.type === LITERAL_PROPERTY_NAME &&
tree.name.literalToken.value === CONSTRUCTOR;
let result = super.transformMethod(tree);
this.inConstructor_ = inConstructor;
return result;
}
/**
* Shallowly transforms |tree| into a FunctionExpression and adds the needed
* temp variables to the |tempVarTransformer|.
* @param {TempVarTransformer} tempVarTransformer
* @param {ArrowFunction} tree
* @return {FunctionExpression}
*/
static transform(tempVarTransformer, tree) {
tree = alphaRenameThisAndArguments(tempVarTransformer, tree);
let body = convertConciseBody(tree.body);
return new FunctionExpression(tree.location, null, tree.functionKind,
tree.parameterList, null, [], body);
}
}