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 transformFiles API for tree transformations #6986

Merged
merged 109 commits into from
Mar 18, 2016

Conversation

rbuckton
Copy link
Member

@rbuckton rbuckton commented Feb 9, 2016

The transformFiles function will take on some of the responsibilities of the existing emitter, and integrates it with our new visitor pattern.

This function takes in an array of Transformer callbacks, which have the following signature (found in types.ts):

export type Transformer = (context: TransformationContext) => (node: SourceFile) => SourceFile;

The TransformationContext is allocated at the start of transformFiles, and each Transformer is called sequentially with this context to initialize each transformer. The result of each initialization is a callback that will be used to transform each SourceFile.

The TransformationContext provides the following information/capabilities to a Transformer:

  • Access to compiler options.
  • Access to the emit resolver.
  • The ability to generate unique names for nodes.
  • The ability to hoist variable and function declarations (such as temporary variables, or hoisting declarations in SystemJS).
  • The ability to explicitly enter and exit a lexical environment (used to track hoisted declarations).
  • The ability to associate additional transformation- and emit-specific information and flags to a node.
  • Hooks to perform certain just-in-time substitutions during the final print phase of the tree transforming emitter.

While TransformFlags provides us the ability to quickly make decisions about whether (and how) to transform a node, there are some transformations that would still require a full walk of the tree. This includes situations such as renaming block-scoped let/const declarations. As we do not want to do a full walk of the tree for every transformation phase, we save these final transformations until the last possible moment before we emit. We can do this by using the identifierSubstitution and expressionSubstitution hooks provided on the TransformationContext. The final print phase will then evaluate these substitutions on a just-in-time basis immediately before printing the node.

The following example builds on the examples in #6892 and #6983:

function createES7Transformer(context: TransformationContext) {
  const { 
    startLexicalEnvironment, 
    endLexicalEnvironment
  } = context;

  let currentSourceFile: SourceFile;

  // hook substitutions
  const previousIdentifierSubstitution = context.identifierSubstitution;
  context.identifierSubstitution = substituteIdentifier;

  const previousExpressionSubstitution = context.expressionSubstitution;
  context.expressionSubstitution = substituteExpression;

  function transformSourceFile(node: SourceFile) {
    const savedSourceFile = currentSourceFile;
    const visited = visitEachChild(node, visitor, /*environment*/ context);
    currentSourceFile = savedSourceFile;
    return visited;
  }

  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
      // NOTE: Added third argument to the following call to support entering and exiting
      //       lexical environments.
      return visitEachChild(node, visitor, /*environment*/ context);
    }
    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) {
    ...
  }

  ...

  function substituteIdentifier(node: Identifier) {
    // Evaluate any previous substitutions
    node = previousIdentifierSubstitution ? previousIdentifierSubstitution(node) : node;
    // Perform any other substitution we may need.
    // NOTE: You cannot rely on `parent` pointers on nodes created during transformation.
    // NOTE: You may be able to use `getOriginalNode` on a node to get to its representation in 
    //       the original source tree.
    return node;
  }

  function substituteExpression(node: Expression) {
    // Evaluate any previous substitutions
    node = previousExpressionSubstitution ? previousExpressionSubstitution(node) : node;
    // Perform any other substitution we may need.
    // NOTE: You cannot rely on `parent` pointers on nodes created during transformation.
    // NOTE: You may be able to use `getOriginalNode` on a node to get to its representation in 
    //       the original source tree.
    return node;
  }
}

Related Pull Requests:

Review on Reviewable

@rbuckton
Copy link
Member Author

rbuckton commented Feb 9, 2016

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

}
}

function coerceExpression(value: string | number | boolean | Expression): Expression {
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand the purpose of this one.

Copy link
Member Author

Choose a reason for hiding this comment

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

I will most likely remove these and inline them where necessary. All of the "coerce" functions are helpers internal to "factory.ts" that allow you to write createParameter("args") instead of createParameter(createIdentifier("args")), etc.

rbuckton and others added 26 commits March 2, 2016 12:32
Adds a simplified pretty printer for tree transformations
rbuckton added a commit that referenced this pull request Mar 18, 2016
Adds the transformFiles API for tree transformations
@rbuckton rbuckton merged commit a212e2b into transforms-flags Mar 18, 2016
@rbuckton rbuckton deleted the transforms-transformer 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.

5 participants