Fixed linter warnings, added comments and minor cleanup.

This commit is contained in:
Ron Buckton 2016-02-23 16:26:04 -08:00
parent 951ce55ded
commit 08036b7ef6
2 changed files with 186 additions and 100 deletions

View file

@ -197,7 +197,7 @@ namespace ts {
}
if (isBinaryExpression(root)) {
emitDestructuringAssignment(root.left, value, location)
emitDestructuringAssignment(root.left, value, location);
}
else {
emitBindingElement(root, value);

View file

@ -8,7 +8,6 @@ namespace ts {
export function transformTypeScript(context: TransformationContext) {
const {
nodeHasGeneratedName,
getGeneratedNameForNode,
makeUniqueName,
setNodeEmitFlags,
@ -20,23 +19,46 @@ namespace ts {
const resolver = context.getEmitResolver();
const compilerOptions = context.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
const decoratedClassAliases: Map<Identifier> = {};
const currentDecoratedClassAliases: Map<Identifier> = {};
// Save the previous transformation hooks.
const previousExpressionSubstitution = context.expressionSubstitution;
const previousOnBeforeEmitNode = context.onBeforeEmitNode;
const previousOnAfterEmitNode = context.onAfterEmitNode;
context.enableExpressionSubstitution(SyntaxKind.Identifier);
// Set new transformation hooks.
context.expressionSubstitution = substituteExpression;
context.onBeforeEmitNode = onBeforeEmitNode;
context.onAfterEmitNode = onAfterEmitNode;
let hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = false;
// These variables contain state that changes as we descend into the tree.
let currentSourceFile: SourceFile;
let currentNamespace: ModuleDeclaration;
let currentNamespaceLocalName: Identifier;
let currentScope: SourceFile | Block | ModuleBlock | CaseBlock;
let currentParent: Node;
let currentNode: Node;
// These variables keep track of whether expression substitution has been enabled for
// specific edge cases. They are persisted between each SourceFile transformation and
// should not be reset.
let hasEnabledExpressionSubstitutionForDecoratedClasses = false;
let hasEnabledExpressionSubstitutionForNamespaceExports = false;
let hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = false;
// This map keeps track of aliases created for classes with decorators to avoid issues
// with the double-binding behavior of classes.
let decoratedClassAliases: Map<Identifier>;
// This map keeps track of currently active aliases defined in `decoratedClassAliases`
// when just-in-time substitution occurs while printing an expression identifier.
let currentDecoratedClassAliases: Map<Identifier>;
// This value keeps track of how deeply nested we are within any containing namespaces
// when performing just-in-time substitution while printing an expression identifier.
let namespaceNestLevel: number;
// This array keeps track of containers where `super` is valid, for use with
// just-in-time substitution for `super` expressions inside of async methods.
let superContainerStack: SuperContainer[];
return transformSourceFile;
@ -54,16 +76,23 @@ namespace ts {
* @param node The node to visit.
*/
function visitWithStack(node: Node, visitor: (node: Node) => Node): Node {
// Save state
const savedCurrentNamespace = currentNamespace;
const savedCurrentScope = currentScope;
const savedCurrentParent = currentParent;
const savedCurrentNode = currentNode;
// Handle state changes before visiting a node.
onBeforeVisitNode(node);
node = visitor(node);
// Restore state
currentNamespace = savedCurrentNamespace;
currentScope = savedCurrentScope;
currentParent = savedCurrentParent;
currentNode = savedCurrentNode;
return node;
}
@ -146,6 +175,11 @@ namespace ts {
return visitorWorker(node);
}
/**
* Branching visitor, visits a TypeScript syntax node.
*
* @param node The node to visit.
*/
function visitTypeScript(node: Node): Node {
// TypeScript ambient declarations are elided.
if (node.flags & NodeFlags.Ambient) {
@ -461,11 +495,13 @@ namespace ts {
// Record an alias to avoid class double-binding.
if (resolver.getNodeCheckFlags(getOriginalNode(node)) & NodeCheckFlags.ClassWithBodyScopedClassBinding) {
enableExpressionSubstitutionForDecoratedClasses();
decoratedClassAlias = makeUniqueName(node.name ? node.name.text : "default");
decoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAlias;
// We emit the class alias as a `let` declaration here so that it has the same
// TDZ as the class.
// let ${decoratedClassAlias};
addNode(statements,
createVariableStatement(
@ -528,7 +564,7 @@ namespace ts {
// Write any decorators of the node.
addNodes(statements, generateClassElementDecorationStatements(node, /*isStatic*/ false));
addNodes(statements, generateClassElementDecorationStatements(node, /*isStatic*/ true));
addNode(statements, generateConstructorDecorationStatement(node, decoratedClassAlias))
addNode(statements, generateConstructorDecorationStatement(node, decoratedClassAlias));
// If the class is exported as part of a TypeScript namespace, emit the namespace export.
// Otherwise, if the class was exported at the top level and was decorated, emit an export
@ -1060,7 +1096,7 @@ namespace ts {
* Transforms all of the decorators for a declaration into an array of expressions.
*
* @param node The declaration node.
* @param allDecorators The AllDecorators object for the node.
* @param allDecorators An object containing all of the decorators for the declaration.
*/
function transformAllDecoratorsOfDeclaration(node: Declaration, allDecorators: AllDecorators) {
if (!allDecorators) {
@ -1257,13 +1293,13 @@ namespace ts {
*/
function addTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) {
if (compilerOptions.emitDecoratorMetadata) {
if (shouldAppendTypeMetadata(node)) {
if (shouldAddTypeMetadata(node)) {
decoratorExpressions.push(createMetadataHelper("design:type", serializeTypeOfNode(node), /*defer*/ true));
}
if (shouldAppendParamTypesMetadata(node)) {
if (shouldAddParamTypesMetadata(node)) {
decoratorExpressions.push(createMetadataHelper("design:paramtypes", serializeParameterTypesOfNode(node), /*defer*/ true));
}
if (shouldAppendReturnTypeMetadata(node)) {
if (shouldAddReturnTypeMetadata(node)) {
decoratorExpressions.push(createMetadataHelper("design:returntype", serializeReturnTypeOfNode(node), /*defer*/ true));
}
}
@ -1271,12 +1307,12 @@ namespace ts {
/**
* Determines whether to emit the "design:type" metadata based on the node's kind.
* The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata
* compiler option is set.
* The caller should have already tested whether the node has decorators and whether the
* emitDecoratorMetadata compiler option is set.
*
* @param node The node to test.
*/
function shouldAppendTypeMetadata(node: Declaration): boolean {
function shouldAddTypeMetadata(node: Declaration): boolean {
const kind = node.kind;
return kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
@ -1286,23 +1322,23 @@ namespace ts {
/**
* Determines whether to emit the "design:returntype" metadata based on the node's kind.
* The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata
* compiler option is set.
* The caller should have already tested whether the node has decorators and whether the
* emitDecoratorMetadata compiler option is set.
*
* @param node The node to test.
*/
function shouldAppendReturnTypeMetadata(node: Declaration): boolean {
function shouldAddReturnTypeMetadata(node: Declaration): boolean {
return node.kind === SyntaxKind.MethodDeclaration;
}
/**
* Determines whether to emit the "design:paramtypes" metadata based on the node's kind.
* The caller should have already tested whether the node has decorators and whether the emitDecoratorMetadata
* compiler option is set.
* The caller should have already tested whether the node has decorators and whether the
* emitDecoratorMetadata compiler option is set.
*
* @param node The node to test.
*/
function shouldAppendParamTypesMetadata(node: Declaration): boolean {
function shouldAddParamTypesMetadata(node: Declaration): boolean {
const kind = node.kind;
return kind === SyntaxKind.ClassDeclaration
|| kind === SyntaxKind.ClassExpression
@ -1432,7 +1468,7 @@ namespace ts {
case SyntaxKind.TypePredicate:
case SyntaxKind.BooleanKeyword:
return createIdentifier("Boolean")
return createIdentifier("Boolean");
case SyntaxKind.StringKeyword:
case SyntaxKind.StringLiteral:
@ -1558,7 +1594,7 @@ namespace ts {
* qualified name at runtime.
*/
function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): Expression {
let left: Expression
let left: Expression;
if (node.left.kind === SyntaxKind.Identifier) {
left = serializeEntityNameAsExpression(node.left, useFallback);
}
@ -1990,25 +2026,6 @@ namespace ts {
}
}
function enableExpressionSubstitutionForAsyncMethodsWithSuper() {
if (!hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper) {
hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = true;
// We need to enable substitutions for call, property access, and element access
// if we need to rewrite super calls.
context.enableExpressionSubstitution(SyntaxKind.CallExpression);
context.enableExpressionSubstitution(SyntaxKind.PropertyAccessExpression);
context.enableExpressionSubstitution(SyntaxKind.ElementAccessExpression);
// We need to be notified when entering and exiting declarations that bind super.
context.enableEmitNotification(SyntaxKind.ClassDeclaration);
context.enableEmitNotification(SyntaxKind.MethodDeclaration);
context.enableEmitNotification(SyntaxKind.GetAccessor);
context.enableEmitNotification(SyntaxKind.SetAccessor);
context.enableEmitNotification(SyntaxKind.Constructor);
}
}
/**
* Visits a parameter declaration node.
*
@ -2188,7 +2205,7 @@ namespace ts {
* @param member The enum member node.
*/
function transformEnumMemberDeclarationValue(member: EnumMember): Expression {
let value = resolver.getConstantValue(member);
const value = resolver.getConstantValue(member);
if (value !== undefined) {
return createLiteral(value);
}
@ -2274,6 +2291,8 @@ namespace ts {
Debug.assert(isIdentifier(node.name));
enableExpressionSubstitutionForNamespaceExports();
const savedCurrentNamespaceLocalName = currentNamespaceLocalName;
const modifiers = visitNodes(node.modifiers, visitor, isModifier);
const statements: Statement[] = [];
@ -2471,19 +2490,6 @@ namespace ts {
return createStatement(expression, /*location*/ undefined);
}
function createExportStatement(node: ClassExpression | ClassDeclaration | FunctionDeclaration): Statement {
const name = getDeclarationName(node);
if (currentNamespace) {
return createNamespaceExport(name, name);
}
else if (node.flags & NodeFlags.Default) {
return createExportDefault(name);
}
else {
return createModuleExport(name);
}
}
function createNamespaceExport(exportName: Identifier, exportValue: Expression, location?: TextRange) {
return createStatement(
createAssignment(
@ -2506,7 +2512,7 @@ namespace ts {
name = getSynthesizedNode(name);
return currentNamespaceLocalName
? createPropertyAccess(currentNamespaceLocalName, name)
: name
: name;
}
function getDeclarationName(node: ClassExpression | ClassDeclaration | FunctionDeclaration) {
@ -2523,8 +2529,65 @@ namespace ts {
: getClassPrototype(node);
}
function onBeforeEmitNode(node: Node): void {
previousOnBeforeEmitNode(node);
const kind = node.kind;
if (hasEnabledExpressionSubstitutionForDecoratedClasses
&& kind === SyntaxKind.ClassDeclaration && node.decorators) {
currentDecoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAliases[getOriginalNodeId(node)];
}
if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper
&& (kind === SyntaxKind.ClassDeclaration
|| kind === SyntaxKind.Constructor
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor)) {
if (!superContainerStack) {
superContainerStack = [];
}
superContainerStack.push(<SuperContainer>node);
}
if (hasEnabledExpressionSubstitutionForNamespaceExports
&& kind === SyntaxKind.ModuleDeclaration) {
namespaceNestLevel++;
}
}
function onAfterEmitNode(node: Node): void {
previousOnAfterEmitNode(node);
const kind = node.kind;
if (hasEnabledExpressionSubstitutionForDecoratedClasses
&& kind === SyntaxKind.ClassDeclaration && node.decorators) {
currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined;
}
if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper
&& (kind === SyntaxKind.ClassDeclaration
|| kind === SyntaxKind.Constructor
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor)) {
if (superContainerStack) {
superContainerStack.pop();
}
}
if (hasEnabledExpressionSubstitutionForNamespaceExports
&& kind === SyntaxKind.ModuleDeclaration) {
namespaceNestLevel--;
}
}
function substituteExpression(node: Expression): Expression {
node = previousExpressionSubstitution ? previousExpressionSubstitution(node) : node;
node = previousExpressionSubstitution(node);
switch (node.kind) {
case SyntaxKind.Identifier:
return substituteExpressionIdentifier(<Identifier>node);
@ -2544,8 +2607,10 @@ namespace ts {
return node;
}
function substituteExpressionIdentifier(node: Identifier) {
if (!nodeIsSynthesized(node) && resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) {
function substituteExpressionIdentifier(node: Identifier): Expression {
if (hasEnabledExpressionSubstitutionForDecoratedClasses
&& !nodeIsSynthesized(node)
&& resolver.getNodeCheckFlags(node) & NodeCheckFlags.BodyScopedClassBinding) {
// Due to the emit for class decorators, any reference to the class from inside of the class body
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
// behavior of class names in ES6.
@ -2559,10 +2624,23 @@ namespace ts {
}
}
if (hasEnabledExpressionSubstitutionForNamespaceExports
&& namespaceNestLevel > 0) {
// If we are nested within a namespace declaration, we may need to qualifiy
// an identifier that is exported from a merged namespace.
const original = getOriginalNode(node);
if (isIdentifier(original) && original.parent) {
const container = resolver.getReferencedExportContainer(original);
if (container && container.kind === SyntaxKind.ModuleDeclaration) {
return createPropertyAccess(getGeneratedNameForNode(container), node, /*location*/ node);
}
}
}
return node;
}
function substituteCallExpression(node: CallExpression) {
function substituteCallExpression(node: CallExpression): Expression {
const expression = node.expression;
if (isSuperPropertyOrElementAccess(expression)) {
const flags = getSuperContainerAsyncMethodFlags();
@ -2612,6 +2690,54 @@ namespace ts {
return node;
}
function enableExpressionSubstitutionForAsyncMethodsWithSuper() {
if (!hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper) {
hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper = true;
// We need to enable substitutions for call, property access, and element access
// if we need to rewrite super calls.
context.enableExpressionSubstitution(SyntaxKind.CallExpression);
context.enableExpressionSubstitution(SyntaxKind.PropertyAccessExpression);
context.enableExpressionSubstitution(SyntaxKind.ElementAccessExpression);
// We need to be notified when entering and exiting declarations that bind super.
context.enableEmitNotification(SyntaxKind.ClassDeclaration);
context.enableEmitNotification(SyntaxKind.MethodDeclaration);
context.enableEmitNotification(SyntaxKind.GetAccessor);
context.enableEmitNotification(SyntaxKind.SetAccessor);
context.enableEmitNotification(SyntaxKind.Constructor);
}
}
function enableExpressionSubstitutionForDecoratedClasses() {
if (!hasEnabledExpressionSubstitutionForDecoratedClasses) {
hasEnabledExpressionSubstitutionForDecoratedClasses = true;
// We need to enable substitutions for identifiers. This allows us to
// substitute class names inside of a class declaration.
context.enableExpressionSubstitution(SyntaxKind.Identifier);
// Keep track of class aliases.
decoratedClassAliases = {};
currentDecoratedClassAliases = {};
}
}
function enableExpressionSubstitutionForNamespaceExports() {
if (!hasEnabledExpressionSubstitutionForNamespaceExports) {
hasEnabledExpressionSubstitutionForNamespaceExports = true;
// We need to enable substitutions for identifiers. This allows us to
// substitute the names of exported members of a namespace.
context.enableExpressionSubstitution(SyntaxKind.Identifier);
// We need to be notified when entering and exiting namespaces.
context.enableEmitNotification(SyntaxKind.ModuleDeclaration);
// Keep track of namespace nesting depth
namespaceNestLevel = 0;
}
}
function createSuperAccessInAsyncMethod(argumentExpression: Expression, flags: NodeCheckFlags, location: TextRange): LeftHandSideExpression {
if (flags & NodeCheckFlags.AsyncMethodWithSuperBinding) {
@ -2638,45 +2764,5 @@ namespace ts {
return container !== undefined
&& resolver.getNodeCheckFlags(getOriginalNode(container)) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding);
}
function onBeforeEmitNode(node: Node): void {
const kind = node.kind;
if (kind === SyntaxKind.ClassDeclaration && node.decorators) {
currentDecoratedClassAliases[getOriginalNodeId(node)] = decoratedClassAliases[getOriginalNodeId(node)];
}
if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper
&& (kind === SyntaxKind.ClassDeclaration
|| kind === SyntaxKind.Constructor
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor)) {
if (!superContainerStack) {
superContainerStack = [];
}
superContainerStack.push(<SuperContainer>node);
}
}
function onAfterEmitNode(node: Node): void {
const kind = node.kind;
if (kind === SyntaxKind.ClassDeclaration && node.decorators) {
currentDecoratedClassAliases[getOriginalNodeId(node)] = undefined;
}
if (hasEnabledExpressionSubstitutionForAsyncMethodsWithSuper
&& (kind === SyntaxKind.ClassDeclaration
|| kind === SyntaxKind.Constructor
|| kind === SyntaxKind.MethodDeclaration
|| kind === SyntaxKind.GetAccessor
|| kind === SyntaxKind.SetAccessor)) {
if (superContainerStack) {
superContainerStack.pop();
}
}
}
}
}