Merge branch 'transforms' into exportTransforms

This commit is contained in:
Daniel Rosenwasser 2016-04-14 13:31:21 -07:00
commit 0409c242af
12 changed files with 151 additions and 56 deletions

View File

@ -717,7 +717,7 @@ function runTestsAndWriteOutput(file) {
}
var args = [];
args.push("-R", "TAP");
args.push("-R", "tap");
args.push("--no-colors");
args.push("-t", testTimeout);
if (tests) {
@ -1272,4 +1272,4 @@ function environmentVariableIsEnabled(name) {
function environmentVariableIsDisabled(name) {
return /^(no?|f(alse)?|off|disabled?|0|-)$/.test(process.env[name]);
}
}

View File

@ -1910,6 +1910,17 @@ namespace ts {
// This is so that they can flow through PropertyName transforms unaffected.
// Instead, we mark the container as ES6, so that it can properly handle the transform.
transformFlags = TransformFlags.ContainsComputedPropertyName;
if (subtreeFlags & TransformFlags.ContainsLexicalThis) {
// A computed method name like `[this.getName()](x: string) { ... }` needs to
// distinguish itself from the normal case of a method body containing `this`:
// `this` inside a method doesn't need to be rewritten (the method provides `this`),
// whereas `this` inside a computed name *might* need to be rewritten if the class/object
// is inside an arrow function:
// `_this = this; () => class K { [_this.getName()]() { ... } }`
// To make this distinction, use ContainsLexicalThisInComputedPropertyName
// instead of ContainsLexicalThis for computed property names
transformFlags |= TransformFlags.ContainsLexicalThisInComputedPropertyName;
}
break;
case SyntaxKind.SpreadElementExpression:
@ -1945,6 +1956,11 @@ namespace ts {
// is an ES6 node.
transformFlags = TransformFlags.AssertES6;
}
if (subtreeFlags & TransformFlags.ContainsLexicalThisInComputedPropertyName) {
// A computed property name containing `this` might need to be rewritten,
// so propagate the ContainsLexicalThis flag upward.
transformFlags |= TransformFlags.ContainsLexicalThis;
}
break;
case SyntaxKind.CallExpression:
@ -2256,6 +2272,11 @@ namespace ts {
|| hasModifier(node, ModifierFlags.Export)) {
transformFlags |= TransformFlags.AssertTypeScript;
}
if (subtreeFlags & TransformFlags.ContainsLexicalThisInComputedPropertyName) {
// A computed property name containing `this` might need to be rewritten,
// so propagate the ContainsLexicalThis flag upward.
transformFlags |= TransformFlags.ContainsLexicalThis;
}
return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.ClassExcludes);
}
@ -2272,6 +2293,11 @@ namespace ts {
| TransformFlags.ContainsDecorators)) {
transformFlags |= TransformFlags.AssertTypeScript;
}
if (subtreeFlags & TransformFlags.ContainsLexicalThisInComputedPropertyName) {
// A computed property name containing `this` might need to be rewritten,
// so propagate the ContainsLexicalThis flag upward.
transformFlags |= TransformFlags.ContainsLexicalThis;
}
return updateTransformFlags(node, subtreeFlags, transformFlags, TransformFlags.ClassExcludes);
}

View File

@ -1252,7 +1252,18 @@ namespace ts {
return (node.expression as StringLiteral).text === "use strict";
}
/**
* Add any necessary prologue-directives into target statement-array.
* The function needs to be called during each transformation step.
* This function needs to be called whenever we transform the statement
* list of a source file, namespace, or function-like body.
*
* @param target: result statements array
* @param source: origin statements array
* @param ensureUseStrict: boolean determining whether the function need to add prologue-directives
*/
export function addPrologueDirectives(target: Statement[], source: Statement[], ensureUseStrict?: boolean): number {
Debug.assert(target.length === 0, "PrologueDirectives should be at the first statement in the target statements array");
let foundUseStrict = false;
for (let i = 0; i < source.length; i++) {
if (isPrologueDirective(source[i])) {

View File

@ -370,16 +370,19 @@ namespace ts {
);
}
else if (isLiteralExpression(propertyName)) {
return createElementAccess(
expression,
getSynthesizedClone(propertyName)
);
const clone = getSynthesizedClone(propertyName);
clone.text = unescapeIdentifier(clone.text);
return createElementAccess(expression, clone);
}
else {
return createPropertyAccess(
expression,
isGeneratedIdentifier(propertyName) ? getSynthesizedClone(propertyName) : createIdentifier(propertyName.text)
);
if (isGeneratedIdentifier(propertyName)) {
const clone = getSynthesizedClone(propertyName);
clone.text = unescapeIdentifier(clone.text);
return createPropertyAccess(expression, clone);
}
else {
return createPropertyAccess(expression, createIdentifier(unescapeIdentifier(propertyName.text)));
}
}
}
}

View File

@ -1211,7 +1211,15 @@ namespace ts {
let statementsLocation: TextRange;
const statements: Statement[] = [];
const body = node.body;
let statementOffset: number;
startLexicalEnvironment();
if (isBlock(body)) {
// ensureUseStrict is false because no new prologue-directive should be added.
// addPrologueDirectives will simply put already-existing directives at the beginning of the target statement-array
statementOffset = addPrologueDirectives(statements, body.statements, /*ensureUseStrict*/ false);
}
addCaptureThisForNodeIfNeeded(statements, node);
addDefaultValueAssignmentsIfNeeded(statements, node);
addRestParameterIfNeeded(statements, node, /*inConstructorWithSynthesizedSuper*/ false);
@ -1221,10 +1229,9 @@ namespace ts {
multiLine = true;
}
const body = node.body;
if (isBlock(body)) {
statementsLocation = body.statements;
addRange(statements, visitNodes(body.statements, visitor, isStatement));
addRange(statements, visitNodes(body.statements, visitor, isStatement, statementOffset));
// If the original body was a multi-line block, this must be a multi-line block.
if (!multiLine && body.multiLine) {

View File

@ -34,12 +34,18 @@ namespace ts {
return visitImportSpecifier(<ImportSpecifier>node);
case SyntaxKind.ExportAssignment:
return visitExportAssignment(<ExportAssignment>node);
case SyntaxKind.ExportDeclaration:
return visitExportDeclaration(<ExportDeclaration>node);
case SyntaxKind.NamedExports:
return visitNamedExports(<NamedExports>node);
case SyntaxKind.ExportSpecifier:
return visitExportSpecifier(<ExportSpecifier>node);
}
return node;
}
function visitExportAssignment(node: ExportAssignment): ExportDeclaration {
function visitExportAssignment(node: ExportAssignment): ExportAssignment {
if (node.isExportEquals) {
return undefined; // do not emit export equals for ES6
}
@ -47,6 +53,34 @@ namespace ts {
return nodeIsSynthesized(original) || resolver.isValueAliasDeclaration(original) ? node: undefined;
}
function visitExportDeclaration(node: ExportDeclaration): ExportDeclaration {
if (!node.exportClause) {
return resolver.moduleExportsSomeValue(node.moduleSpecifier) ? node : undefined;
}
if (!resolver.isValueAliasDeclaration(node)) {
return undefined;
}
const newExportClause = visitNode(node.exportClause, visitor, isNamedExports, /*optional*/ true);
if (node.exportClause === newExportClause) {
return node;
}
return newExportClause
? createExportDeclaration(newExportClause, node.moduleSpecifier)
: undefined;
}
function visitNamedExports(node: NamedExports): NamedExports {
const newExports = visitNodes(node.elements, visitor, isExportSpecifier);
if (node.elements === newExports) {
return node;
}
return newExports.length ? createNamedExports(newExports) : undefined;
}
function visitExportSpecifier(node: ExportSpecifier): ExportSpecifier {
return resolver.isValueAliasDeclaration(node) ? node : undefined;
}
function visitImportEqualsDeclaration(node: ImportEqualsDeclaration): ImportEqualsDeclaration {
return !isExternalModuleImportEqualsDeclaration(node) || resolver.isReferencedAliasDeclaration(node) ? node : undefined;
}

View File

@ -358,6 +358,9 @@ namespace ts {
return undefined;
}
// Set emitFlags on the name of the importEqualsDeclaration
// This is so the printer will not substitute the identifier
setNodeEmitFlags(node.name, NodeEmitFlags.NoSubstitution);
const statements: Statement[] = [];
if (moduleKind !== ModuleKind.AMD) {
if (hasModifier(node, ModifierFlags.Export)) {
@ -672,6 +675,9 @@ namespace ts {
function visitClassDeclaration(node: ClassDeclaration): VisitResult<Statement> {
const statements: Statement[] = [];
const name = node.name || getGeneratedNameForNode(node);
// Set emitFlags on the name of the classDeclaration
// This is so that when printer will not substitute the identifier
setNodeEmitFlags(name, NodeEmitFlags.NoSubstitution);
if (hasModifier(node, ModifierFlags.Export)) {
statements.push(
createClassDeclaration(
@ -804,11 +810,20 @@ namespace ts {
else if (declaration.kind === SyntaxKind.ImportSpecifier) {
const name = (<ImportSpecifier>declaration).propertyName
|| (<ImportSpecifier>declaration).name;
return createPropertyAccess(
getGeneratedNameForNode(declaration.parent.parent.parent),
getSynthesizedClone(name),
/*location*/ node
);
if (name.originalKeywordKind === SyntaxKind.DefaultKeyword && languageVersion <= ScriptTarget.ES3) {
return createElementAccess(
getGeneratedNameForNode(declaration.parent.parent.parent),
createLiteral(name.text),
/*location*/ node
);
}
else {
return createPropertyAccess(
getGeneratedNameForNode(declaration.parent.parent.parent),
getSynthesizedClone(name),
/*location*/ node
);
}
}
}
}
@ -840,7 +855,7 @@ namespace ts {
function createExportAssignment(name: Identifier, value: Expression) {
return createAssignment(
name.originalKeywordKind && languageVersion === ScriptTarget.ES3
name.originalKeywordKind === SyntaxKind.DefaultKeyword && languageVersion === ScriptTarget.ES3
? createElementAccess(
createIdentifier("exports"),
createLiteral(name.text)
@ -889,6 +904,9 @@ namespace ts {
// Find the name of the module alias, if there is one
const importAliasName = getLocalNameForExternalImport(importNode, currentSourceFile);
if (includeNonAmdDependencies && importAliasName) {
// Set emitFlags on the name of the classDeclaration
// This is so that when printer will not substitute the identifier
setNodeEmitFlags(importAliasName, NodeEmitFlags.NoSubstitution);
aliasedModuleNames.push(externalModuleName);
importAliasNames.push(createParameter(importAliasName));
}

View File

@ -193,7 +193,7 @@ namespace ts {
startLexicalEnvironment();
// Add any prologue directives.
const statementOffset = addPrologueDirectives(statements, node.statements, !compilerOptions.noImplicitUseStrict);
const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict);
// var __moduleName = context_1 && context_1.id;
addNode(statements,

View File

@ -2223,28 +2223,7 @@ namespace ts {
|| (isES6ExportedDeclaration(node) && isFirstDeclarationOfKind(node, node.kind));
}
/**
* Adds a leading VariableStatement for an enum or module declaration.
*/
function addVarForEnumDeclaration(statements: Statement[], node: EnumDeclaration) {
// Emit a variable statement for the enum.
statements.push(
setOriginalNode(
createVariableStatement(
isES6ExportedDeclaration(node)
? visitNodes(node.modifiers, visitor, isModifier)
: undefined,
[createVariableDeclaration(
getDeclarationName(node)
)],
/*location*/ node
),
/*original*/ node
)
);
}
/**
/*
* Adds a trailing VariableStatement for an enum or module declaration.
*/
function addVarForEnumExportedFromNamespace(statements: Statement[], node: EnumDeclaration | ModuleDeclaration) {
@ -2274,7 +2253,7 @@ namespace ts {
const statements: Statement[] = [];
if (shouldEmitVarForEnumDeclaration(node)) {
addVarForEnumDeclaration(statements, node);
addVarForEnumOrModuleDeclaration(statements, node);
}
const localName = getGeneratedNameForNode(node);
@ -2411,9 +2390,9 @@ namespace ts {
}
/**
* Adds a leading VariableStatement for a module declaration.
* Adds a leading VariableStatement for a enum or module declaration.
*/
function addVarForModuleDeclaration(statements: Statement[], node: ModuleDeclaration) {
function addVarForEnumOrModuleDeclaration(statements: Statement[], node: ModuleDeclaration | EnumDeclaration) {
// Emit a variable statement for the module.
statements.push(
setOriginalNode(
@ -2424,7 +2403,21 @@ namespace ts {
[createVariableDeclaration(
getDeclarationName(node)
)],
/*location*/ node
// Trailing comments for module declaration should be emitted with function closure instead of variable statement
// So do not set the end position for the variable statement node
// /** Module comment*/
// module m1 {
// function foo4Export() {
// }
// } // trailing comment module
// Should emit
// /** Module comment*/
// var m1;
// (function (m1) {
// function foo4Export() {
// }
// })(m1 || (m1 = {})); // trailing comment module
/*location*/ { pos: node.pos, end: -1 }
),
node
)
@ -2449,7 +2442,7 @@ namespace ts {
const statements: Statement[] = [];
if (shouldEmitVarForModuleDeclaration(node)) {
addVarForModuleDeclaration(statements, node);
addVarForEnumOrModuleDeclaration(statements, node);
}
const localName = getGeneratedNameForNode(node);

View File

@ -2830,11 +2830,12 @@ namespace ts {
ContainsPropertyInitializer = 1 << 10,
ContainsLexicalThis = 1 << 11,
ContainsCapturedLexicalThis = 1 << 12,
ContainsDefaultValueAssignments = 1 << 13,
ContainsParameterPropertyAssignments = 1 << 14,
ContainsSpreadElementExpression = 1 << 15,
ContainsComputedPropertyName = 1 << 16,
ContainsBlockScopedBinding = 1 << 17,
ContainsLexicalThisInComputedPropertyName = 1 << 13,
ContainsDefaultValueAssignments = 1 << 14,
ContainsParameterPropertyAssignments = 1 << 15,
ContainsSpreadElementExpression = 1 << 16,
ContainsComputedPropertyName = 1 << 17,
ContainsBlockScopedBinding = 1 << 18,
HasComputedFlags = 1 << 31, // Transform flags have been computed.
@ -2853,10 +2854,10 @@ namespace ts {
FunctionExcludes = ContainsDecorators | ContainsDefaultValueAssignments | ContainsCapturedLexicalThis | ContainsLexicalThis | ContainsParameterPropertyAssignments | ContainsBlockScopedBinding,
ConstructorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding,
MethodOrAccessorExcludes = ContainsDefaultValueAssignments | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding,
ClassExcludes = ContainsDecorators | ContainsPropertyInitializer | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsComputedPropertyName | ContainsParameterPropertyAssignments,
ClassExcludes = ContainsDecorators | ContainsPropertyInitializer | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsComputedPropertyName | ContainsParameterPropertyAssignments | ContainsLexicalThisInComputedPropertyName,
ModuleExcludes = ContainsDecorators | ContainsLexicalThis | ContainsCapturedLexicalThis | ContainsBlockScopedBinding,
TypeExcludes = ~ContainsTypeScript,
ObjectLiteralExcludes = ContainsDecorators | ContainsComputedPropertyName,
ObjectLiteralExcludes = ContainsDecorators | ContainsComputedPropertyName | ContainsLexicalThisInComputedPropertyName,
ArrayLiteralOrCallOrNewExcludes = ContainsSpreadElementExpression,
}

View File

@ -18,7 +18,8 @@ var C = (function () {
var _this = this;
(function () {
var obj = (_a = {},
_a[_this.bar()] = function () { },
_a[_this.bar()] = function () { } // needs capture
,
_a);
var _a;
});

View File

@ -38,7 +38,8 @@ var C = (function (_super) {
var _this = this;
(function () {
var obj = (_a = {},
_a[_super.prototype.bar.call(_this)] = function () { },
_a[_super.prototype.bar.call(_this)] = function () { } // needs capture
,
_a);
var _a;
});