From 08036b7ef65dfbbe0634c4486282553c92bd6404 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 23 Feb 2016 16:26:04 -0800 Subject: [PATCH] Fixed linter warnings, added comments and minor cleanup. --- src/compiler/transformers/destructuring.ts | 2 +- src/compiler/transformers/ts.ts | 284 ++++++++++++++------- 2 files changed, 186 insertions(+), 100 deletions(-) diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 0ada8d177b2..442201a1f7d 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -197,7 +197,7 @@ namespace ts { } if (isBinaryExpression(root)) { - emitDestructuringAssignment(root.left, value, location) + emitDestructuringAssignment(root.left, value, location); } else { emitBindingElement(root, value); diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 8c76e9843e7..d791dd1eb4c 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -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 = {}; - const currentDecoratedClassAliases: Map = {}; + + // 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; + + // 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; + + // 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(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(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(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(); - } - } - } } } \ No newline at end of file