traceur
Version:
ES6 to ES5 compiler
142 lines (123 loc) • 4.28 kB
JavaScript
// Copyright 2015 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 {TempVarTransformer} from './TempVarTransformer.js';
import {RewriteTailCallsTransformer} from './RewriteTailCallsTransformer.js';
import {
createFunctionBody,
createFunctionExpression,
createIdentifierExpression as id
} from './ParseTreeFactory.js';
import {
parseExpression,
parseStatement,
parseStatements
} from './PlaceholderParser.js';
import {
AnonBlock,
FunctionDeclaration,
FunctionExpression,
} from '../syntax/trees/ParseTrees.js';
import ImportRuntimeTrait from './ImportRuntimeTrait.js';
//
// Example:
//
// function f(a) {
// var b = g(a);
// f;
// return h(b);
// }
//
// Becomes:
//
// $traceurRuntime.initTailRecursiveFunction(function f(a) {
// return $traceurRuntime.call(function(a) {
// var b = g(a);
// f;
// return $traceurRuntime.continuation(h, null, [b]);
// }, this, arguments);
// })
export class ProperTailCallTransformer extends
ImportRuntimeTrait(TempVarTransformer) {
// TODO(mnieper): This transformer currently expects that classes and template
// literals have already been desugared. Otherwise they are not guaranteed
// to have proper tail calls.
constructor(identifierGenerator, reporter, options) {
super(identifierGenerator, reporter, options);
this.inBlock_ = false;
this.options = options;
}
transformFunctionDeclaration(tree) {
tree = super.transformFunctionDeclaration(tree);
if (tree.functionKind !== null) {
// do not transform async/generator functions
return tree;
}
let nameIdExpression = id(tree.name.identifierToken);
const initTailRecursiveFunction =
this.getRuntimeExpression('initTailRecursiveFunction');
let setupFlagExpression = parseExpression
`${initTailRecursiveFunction}(${nameIdExpression})`;
let funcDecl = this.transformFunction_(tree, FunctionDeclaration);
if (funcDecl === tree) {
return tree;
}
// Function declarations in blocks do not hoist. In that case we add the
// variable declaration after the function declaration.
let tmpVar = id(this.inBlock_ ?
this.getTempIdentifier() : this.addTempVar(setupFlagExpression));
if (!this.inBlock_) {
return funcDecl;
}
return new AnonBlock(null, [
funcDecl,
parseStatement `var ${tmpVar} = ${setupFlagExpression};`
]);
}
transformFunctionExpression(tree) {
tree = super.transformFunctionExpression(tree);
if (tree.functionKind) {
// do not transform async/generator functions
return tree;
}
let functionExpression =
this.transformFunction_(tree, FunctionExpression);
if (functionExpression === tree) {
return tree;
}
const initTailRecursiveFunction =
this.getRuntimeExpression('initTailRecursiveFunction');
return parseExpression `${
initTailRecursiveFunction}(${functionExpression})`;
}
transformFunction_(tree, constructor) {
let body = RewriteTailCallsTransformer.transform(this, tree.body);
if (body === tree.body) {
return tree;
}
let func = id(this.getTempIdentifier());
let innerFunction = createFunctionExpression(tree.parameterList, body);
const call = this.getRuntimeExpression('call');
let outerBody = createFunctionBody(parseStatements `
return ${call}(${innerFunction}, this, arguments);`);
return new constructor(tree.location, tree.name, tree.functionKind,
tree.parameterList, tree.typeAnnotation, tree.annotations, outerBody);
}
transformBlock(tree) {
let inBlock = this.inBlock_;
this.inBlock_ = true;
let rv = super.transformBlock(tree);
this.inBlock_ = inBlock;
return rv;
}
}