Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds the Transform Flags concept for tree transformations #6983

Merged
merged 121 commits into from
Mar 18, 2016

Conversation

rbuckton
Copy link
Member

@rbuckton rbuckton commented Feb 9, 2016

To ensure tree transformations in the TypeScript compiler are efficient, we compute information about various required transformations for each node. Transform flags are aggregated in a bottom-up fashion, so that we already have information about a node's subtree when we compute the transform flags for the node. These transform flags are initially computed during the bind phase of the compiler, as at that point we guarantee a full walk of the tree.

transformflags

The values in the TransformFlags enum fall into several categories:

  • Facts - State something about a node or its subtree. Facts that start with "Contains" indicate that a node somewhere within the current node's subtree requires a transformation. Facts that do not start with "Contains" indicate that the current node itself requires a transformation.
    • Markers - A special class of Facts that indicate specific transformations that need to occur higher in the source tree.
  • Assertions - Sets of Facts to apply to a node. These are used primarily in the computeTransformFlagsForNode function in binder.ts.
  • Exclusions - Bitmasks that are used to prevent some Facts from being copied up the subtree.

As a result, the Node interface is extended to include two additional properties:

  • transformFlags - Contains the TransformFlags that pertain to this specific node.
  • excludeTransformFlags - Contains a bitmask of TransformFlags that should be excluded from this node's transformFlags when aggregating the transform flags of a subtree containing this node.

The binder is modified to compute the transform flags for each node as it walks the tree. As a performance optimization, we skip transform flag aggregation for ambient nodes, type nodes, and declaration files.

Each transformation phase will likely introduce new nodes. As a result, the visitNode and visitNodes functions in visitor.ts perform supplemental aggregation of transform flags to ensure successive transformation phases have the requisite information to perform additional transformations.

For example, it is possible that during the transformation from TypeScript to ES6 we may introduce an arrow function a for more idiomatic ES6 emit. This may then result in a new this capture which we would then need to know about during the next transformation. Rather than send the new tree through another pass of the checker, we will instead leverage the TransformFlags.ContainsLexicalThis and TransformFlags.ContainsCapturedLexicalThis flags to determine whether we need further transformation.

These flags will enable us to have more efficient transformers in the long term, that can make decisions about whether to continue to walk a subtree based on querying these flags. The following example builds on the one found in #6892:

function visitor(node: Node) {
  if (node.transformFlags & TransformFlags.ES7) {
    // This node is an ES7 node and needs a specific transformation
    return visitorWorker(node);
  }
  else if (node.transformFlags & TransformFlags.ContainsES7) {
    // This node contains an ES7 node somewhere in its subtree, so we must walk the subtree
    return visitEachChild(node, visitor);
  }
  else {
    // Neither this node, nor its subtree, require any transformation, so we can simply 
    // reuse the node and its subtree and avoid a costly walk
    return node;
  }
}

function visitorWorker(node: Node) {
  switch (node.kind) {
    case SyntaxKind.BinaryExpression:
      return visitBinaryExpression(<BinaryExpression>node);
    ...
    default:
      return visitEachChild(node, visitor);
  }
}

function visitBinaryExpression(node: BinaryExpression) {
  if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken) {
    return createCall(
      createPropertyAccess(createIdentifier("Math"), "pow"),
      [
        visitNode(node.left, visitor, isExpressionNode),
        visitNode(node.right, visitor, isExpressionNode)
      ]
    );
  }
  else {
    return visitEachChild(node, visitor);
  }
}

Related Pull Requests:

@rbuckton
Copy link
Member Author

rbuckton commented Feb 9, 2016

Paging for review: @mhegazy, @ahejlsberg, @yuit, @DanielRosenwasser, @RyanCavanaugh, @vladima

case SyntaxKind.AbstractKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.AsyncKeyword:
case SyntaxKind.ConstKeyword:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this trigger false positives for const x = ... variable declarations?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we don't treat const as a modifier for variable declarations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how is 'const' has flag of AssertTypeScript should it be under es6?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only ES6 const is for VariableDeclarationList, which is not stored as a modifier. The only time const is a Modifier is for a const enum, which is TypeScript only.

rbuckton and others added 26 commits March 2, 2016 15:19
Adds a simplified pretty printer for tree transformations
Adds the transformFiles API for tree transformations
rbuckton added a commit that referenced this pull request Mar 18, 2016
Adds the Transform Flags concept for tree transformations
@rbuckton rbuckton merged commit f9cb493 into transforms-visitor Mar 18, 2016
@rbuckton rbuckton deleted the transforms-flags branch March 18, 2016 23:39
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants