vendor tsgo

This commit is contained in:
2026-07-09 16:50:43 -04:00
parent c06ea2e5a4
commit 98978e4930
5804 changed files with 1556156 additions and 101 deletions

View File

@@ -0,0 +1,995 @@
package estransforms
import (
"slices"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
type asyncContextFlags int
const (
asyncContextNonTopLevel asyncContextFlags = 1 << iota
asyncContextHasLexicalThis
)
type lexicalArgumentsInfo struct {
binding *ast.IdentifierNode
used bool
}
type asyncTransformer struct {
transformers.Transformer
superAccessState
contextFlags asyncContextFlags
enclosingFunctionParameterNames *collections.Set[string]
lexicalArguments lexicalArgumentsInfo
asyncBodyVisitor *ast.NodeVisitor
fallbackNodeVisitor *ast.NodeVisitor
}
func newAsyncTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &asyncTransformer{}
result := tx.NewTransformer(tx.visit, opts.Context)
tx.initSuperAccessVisitor(tx.EmitContext(), tx.Factory())
tx.asyncBodyVisitor = tx.EmitContext().NewNodeVisitor(tx.visitAsyncBodyNode)
tx.fallbackNodeVisitor = tx.EmitContext().NewNodeVisitor(tx.visitFallback)
return result
}
func (tx *asyncTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
if node.IsDeclarationFile {
return node.AsNode()
}
tx.setContextFlag(asyncContextNonTopLevel, false)
tx.setContextFlag(asyncContextHasLexicalThis, false)
visited := tx.Visitor().VisitEachChild(node.AsNode())
tx.EmitContext().AddEmitHelper(visited, tx.EmitContext().ReadEmitHelpers()...)
return visited
}
func (tx *asyncTransformer) setContextFlag(flag asyncContextFlags, val bool) {
if val {
tx.contextFlags |= flag
} else {
tx.contextFlags &^= flag
}
}
func (tx *asyncTransformer) inContext(flags asyncContextFlags) bool {
return tx.contextFlags&flags != 0
}
func (tx *asyncTransformer) inTopLevelContext() bool {
return !tx.inContext(asyncContextNonTopLevel)
}
func (tx *asyncTransformer) inHasLexicalThisContext() bool {
return tx.inContext(asyncContextHasLexicalThis)
}
func (tx *asyncTransformer) doWithContext(flags asyncContextFlags, cb func(*asyncTransformer, *ast.Node) *ast.Node, node *ast.Node) *ast.Node {
flagsToSet := flags & ^tx.contextFlags
if flagsToSet != 0 {
tx.setContextFlag(flagsToSet, true)
result := cb(tx, node)
tx.setContextFlag(flagsToSet, false)
return result
}
return cb(tx, node)
}
func (tx *asyncTransformer) visitDefault(node *ast.Node) *ast.Node {
return tx.Visitor().VisitEachChild(node)
}
func (tx *asyncTransformer) fallbackVisitor(node *ast.Node) *ast.Node {
if tx.capturedSuperProperties == nil && tx.lexicalArguments.binding == nil {
return node
}
tx.trackSuperAccess(node)
switch node.Kind {
case ast.KindFunctionExpression,
ast.KindFunctionDeclaration,
ast.KindMethodDeclaration,
ast.KindGetAccessor,
ast.KindSetAccessor,
ast.KindConstructor:
return node
case ast.KindParameter,
ast.KindBindingElement,
ast.KindVariableDeclaration:
// fall through to visitEachChild
case ast.KindIdentifier:
if tx.lexicalArguments.binding != nil &&
node.Text() == "arguments" &&
!ast.IsIdentifierName(node) &&
!ast.IsLabelName(node) {
tx.lexicalArguments.used = true
return tx.lexicalArguments.binding
}
}
return tx.fallbackNodeVisitor.VisitEachChild(node)
}
func (tx *asyncTransformer) visitFallback(node *ast.Node) *ast.Node {
return tx.fallbackVisitor(node)
}
func (tx *asyncTransformer) visit(node *ast.Node) *ast.Node {
if tx.EmitContext().EmitFlags(node)&printer.EFNoLexicalThis != 0 && tx.inHasLexicalThisContext() {
tx.setContextFlag(asyncContextHasLexicalThis, false)
defer tx.setContextFlag(asyncContextHasLexicalThis, true)
}
if node.SubtreeFacts()&(ast.SubtreeContainsAnyAwait|ast.SubtreeContainsAwait) == 0 {
return tx.fallbackVisitor(node)
}
tx.trackSuperAccess(node)
switch node.Kind {
case ast.KindAsyncKeyword:
// ES2017 async modifier should be elided for targets < ES2017
return nil
case ast.KindSourceFile:
return tx.visitSourceFile(node.AsSourceFile())
case ast.KindAwaitExpression:
return tx.visitAwaitExpression(node.AsAwaitExpression())
case ast.KindMethodDeclaration:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitMethodDeclaration, node)
case ast.KindFunctionDeclaration:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitFunctionDeclaration, node)
case ast.KindFunctionExpression:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitFunctionExpression, node)
case ast.KindArrowFunction:
return tx.doWithContext(asyncContextNonTopLevel, (*asyncTransformer).visitArrowFunction, node)
case ast.KindGetAccessor:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitGetAccessorDeclaration, node)
case ast.KindSetAccessor:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitSetAccessorDeclaration, node)
case ast.KindConstructor:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitConstructorDeclaration, node)
case ast.KindClassDeclaration, ast.KindClassExpression:
return tx.doWithContext(asyncContextNonTopLevel|asyncContextHasLexicalThis, (*asyncTransformer).visitDefault, node)
default:
return tx.Visitor().VisitEachChild(node)
}
}
func (tx *asyncTransformer) visitAsyncBodyNode(node *ast.Node) *ast.Node {
if isNodeWithPossibleHoistedDeclaration(node) {
switch node.Kind {
case ast.KindVariableStatement:
return tx.visitVariableStatementInAsyncBody(node)
case ast.KindForStatement:
return tx.visitForStatementInAsyncBody(node.AsForStatement())
case ast.KindForInStatement:
return tx.visitForInStatementInAsyncBody(node.AsForInOrOfStatement())
case ast.KindForOfStatement:
return tx.visitForOfStatementInAsyncBody(node.AsForInOrOfStatement())
case ast.KindCatchClause:
return tx.visitCatchClauseInAsyncBody(node.AsCatchClause())
case ast.KindBlock,
ast.KindSwitchStatement,
ast.KindCaseBlock,
ast.KindCaseClause,
ast.KindDefaultClause,
ast.KindTryStatement,
ast.KindDoStatement,
ast.KindWhileStatement,
ast.KindIfStatement,
ast.KindWithStatement,
ast.KindLabeledStatement:
return tx.asyncBodyVisitor.VisitEachChild(node)
}
}
return tx.visit(node)
}
func (tx *asyncTransformer) visitCatchClauseInAsyncBody(node *ast.CatchClause) *ast.Node {
catchClauseNames := &collections.Set[string]{}
if node.VariableDeclaration != nil {
tx.recordDeclarationName(node.VariableDeclaration, catchClauseNames)
}
// names declared in a catch variable are block scoped
var catchClauseUnshadowedNames *collections.Set[string]
for escapedName := range catchClauseNames.Keys() {
if tx.enclosingFunctionParameterNames != nil && tx.enclosingFunctionParameterNames.Has(escapedName) {
if catchClauseUnshadowedNames == nil {
catchClauseUnshadowedNames = tx.enclosingFunctionParameterNames.Clone()
}
catchClauseUnshadowedNames.Delete(escapedName)
}
}
if catchClauseUnshadowedNames != nil {
savedEnclosingFunctionParameterNames := tx.enclosingFunctionParameterNames
tx.enclosingFunctionParameterNames = catchClauseUnshadowedNames
result := tx.asyncBodyVisitor.VisitEachChild(node.AsNode())
tx.enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames
return result
}
return tx.asyncBodyVisitor.VisitEachChild(node.AsNode())
}
func (tx *asyncTransformer) visitVariableStatementInAsyncBody(node *ast.Node) *ast.Node {
declList := node.AsVariableStatement().DeclarationList
if tx.isVariableDeclarationListWithCollidingName(declList) {
expression := tx.visitVariableDeclarationListWithCollidingNames(declList.AsVariableDeclarationList(), false)
if expression != nil {
return tx.Factory().NewExpressionStatement(expression)
}
return nil
}
return tx.Visitor().VisitEachChild(node)
}
func (tx *asyncTransformer) visitForInStatementInAsyncBody(node *ast.ForInOrOfStatement) *ast.Node {
var visitedInitializer *ast.Node
if tx.isVariableDeclarationListWithCollidingName(node.Initializer) {
visitedInitializer = tx.visitVariableDeclarationListWithCollidingNames(node.Initializer.AsVariableDeclarationList(), true)
} else {
visitedInitializer = tx.Visitor().VisitNode(node.Initializer)
}
return tx.Factory().UpdateForInOrOfStatement(
node,
nil, /*awaitModifier*/
visitedInitializer,
tx.Visitor().VisitNode(node.Expression),
tx.asyncBodyVisitor.VisitEmbeddedStatement(node.Statement),
)
}
func (tx *asyncTransformer) visitForOfStatementInAsyncBody(node *ast.ForInOrOfStatement) *ast.Node {
var visitedInitializer *ast.Node
if tx.isVariableDeclarationListWithCollidingName(node.Initializer) {
visitedInitializer = tx.visitVariableDeclarationListWithCollidingNames(node.Initializer.AsVariableDeclarationList(), true)
} else {
visitedInitializer = tx.Visitor().VisitNode(node.Initializer)
}
return tx.Factory().UpdateForInOrOfStatement(
node,
tx.Visitor().VisitNode(node.AwaitModifier),
visitedInitializer,
tx.Visitor().VisitNode(node.Expression),
tx.asyncBodyVisitor.VisitEmbeddedStatement(node.Statement),
)
}
func (tx *asyncTransformer) visitForStatementInAsyncBody(node *ast.ForStatement) *ast.Node {
initializer := node.Initializer
var visitedInitializer *ast.Node
if initializer != nil && tx.isVariableDeclarationListWithCollidingName(initializer) {
visitedInitializer = tx.visitVariableDeclarationListWithCollidingNames(initializer.AsVariableDeclarationList(), false)
} else {
visitedInitializer = tx.Visitor().VisitNode(node.Initializer)
}
return tx.Factory().UpdateForStatement(
node,
visitedInitializer,
tx.Visitor().VisitNode(node.Condition),
tx.Visitor().VisitNode(node.Incrementor),
tx.asyncBodyVisitor.VisitEmbeddedStatement(node.Statement),
)
}
// visitAwaitExpression visits an AwaitExpression node.
//
// This function will be called any time a ES2017 await expression is encountered.
func (tx *asyncTransformer) visitAwaitExpression(node *ast.AwaitExpression) *ast.Node {
// do not downlevel a top-level await as it is module syntax...
if tx.inTopLevelContext() {
return tx.Visitor().VisitEachChild(node.AsNode())
}
yieldExpr := tx.Factory().NewYieldExpression(
nil, /*asteriskToken*/
tx.Visitor().VisitNode(node.Expression),
)
yieldExpr.Loc = node.Loc
tx.EmitContext().SetOriginal(yieldExpr, node.AsNode())
return yieldExpr
}
func (tx *asyncTransformer) visitConstructorDeclaration(node *ast.Node) *ast.Node {
decl := node.AsConstructorDeclaration()
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
updated := tx.Factory().UpdateConstructorDeclaration(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
tx.transformMethodBody(node),
)
tx.lexicalArguments = savedLexicalArguments
return updated
}
// visitMethodDeclaration visits a MethodDeclaration node.
//
// This function will be called when one of the following conditions are met:
// - The node is marked as async
func (tx *asyncTransformer) visitMethodDeclaration(node *ast.Node) *ast.Node {
decl := node.AsMethodDeclaration()
functionFlags := ast.GetFunctionFlags(node)
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
var parameters *ast.NodeList
var body *ast.Node
if functionFlags&ast.FunctionFlagsAsync != 0 {
parameters = tx.transformAsyncFunctionParameterList(node)
body = tx.transformAsyncFunctionBody(node, parameters)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.transformMethodBody(node)
}
updated := tx.Factory().UpdateMethodDeclaration(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
decl.AsteriskToken,
decl.Name(),
nil, /*postfixToken*/
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)
tx.lexicalArguments = savedLexicalArguments
return updated
}
func (tx *asyncTransformer) visitGetAccessorDeclaration(node *ast.Node) *ast.Node {
decl := node.AsGetAccessorDeclaration()
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
updated := tx.Factory().UpdateGetAccessorDeclaration(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
decl.Name(),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
tx.transformMethodBody(node),
)
tx.lexicalArguments = savedLexicalArguments
return updated
}
func (tx *asyncTransformer) visitSetAccessorDeclaration(node *ast.Node) *ast.Node {
decl := node.AsSetAccessorDeclaration()
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
updated := tx.Factory().UpdateSetAccessorDeclaration(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
decl.Name(),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
tx.transformMethodBody(node),
)
tx.lexicalArguments = savedLexicalArguments
return updated
}
// visitFunctionDeclaration visits a FunctionDeclaration node.
//
// This function will be called when one of the following conditions are met:
// - The node is marked async
func (tx *asyncTransformer) visitFunctionDeclaration(node *ast.Node) *ast.Node {
decl := node.AsFunctionDeclaration()
functionFlags := ast.GetFunctionFlags(node)
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
var parameters *ast.NodeList
var body *ast.Node
if functionFlags&ast.FunctionFlagsAsync != 0 {
parameters = tx.transformAsyncFunctionParameterList(node)
body = tx.transformAsyncFunctionBody(node, parameters)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.EmitContext().VisitFunctionBody(decl.Body, tx.Visitor())
}
updated := tx.Factory().UpdateFunctionDeclaration(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
decl.AsteriskToken,
tx.Visitor().VisitNode(decl.Name()),
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)
tx.lexicalArguments = savedLexicalArguments
return updated
}
// visitFunctionExpression visits a FunctionExpression node.
//
// This function will be called when one of the following conditions are met:
// - The node is marked async
func (tx *asyncTransformer) visitFunctionExpression(node *ast.Node) *ast.Node {
decl := node.AsFunctionExpression()
functionFlags := ast.GetFunctionFlags(node)
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
var parameters *ast.NodeList
var body *ast.Node
if functionFlags&ast.FunctionFlagsAsync != 0 {
parameters = tx.transformAsyncFunctionParameterList(node)
body = tx.transformAsyncFunctionBody(node, parameters)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.EmitContext().VisitFunctionBody(decl.Body, tx.Visitor())
}
updated := tx.Factory().UpdateFunctionExpression(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
decl.AsteriskToken,
tx.Visitor().VisitNode(decl.Name()),
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)
tx.lexicalArguments = savedLexicalArguments
return updated
}
// visitArrowFunction visits an ArrowFunction.
//
// This function will be called when one of the following conditions are met:
// - The node is marked async
func (tx *asyncTransformer) visitArrowFunction(node *ast.Node) *ast.Node {
// `arguments` in class static blocks is always an error, but we preserve Strada's emit
// behavior for baseline compatibility. In Strada, checker-based `isArgumentsLocalBinding`
// returns false for `arguments` in static blocks (since the binding doesn't exist due to
// the error), so the async transform leaves them untouched.
if tx.EmitContext().EmitFlags(node)&printer.EFNoLexicalArguments != 0 {
savedLexicalArguments := tx.lexicalArguments
tx.lexicalArguments = lexicalArgumentsInfo{}
defer func() { tx.lexicalArguments = savedLexicalArguments }()
}
decl := node.AsArrowFunction()
functionFlags := ast.GetFunctionFlags(node)
var parameters *ast.NodeList
var body *ast.Node
if functionFlags&ast.FunctionFlagsAsync != 0 {
parameters = tx.transformAsyncFunctionParameterList(node)
body = tx.transformAsyncFunctionBody(node, parameters)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.EmitContext().VisitFunctionBody(decl.Body, tx.Visitor())
}
return tx.Factory().UpdateArrowFunction(
decl,
tx.Visitor().VisitModifiers(decl.Modifiers()),
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
decl.EqualsGreaterThanToken,
body,
)
}
func (tx *asyncTransformer) recordDeclarationName(node *ast.Node, names *collections.Set[string]) {
name := node.Name()
if name == nil {
return
}
if ast.IsIdentifier(name) {
names.Add(name.Text())
} else if ast.IsBindingPattern(name) {
for _, element := range name.AsBindingPattern().Elements.Nodes {
if !ast.IsOmittedExpression(element) {
tx.recordDeclarationName(element, names)
}
}
}
}
func (tx *asyncTransformer) isVariableDeclarationListWithCollidingName(node *ast.Node) bool {
return node != nil &&
ast.IsVariableDeclarationList(node) &&
node.Flags&ast.NodeFlagsBlockScoped == 0 &&
slices.ContainsFunc(node.AsVariableDeclarationList().Declarations.Nodes, tx.collidesWithParameterName)
}
func (tx *asyncTransformer) visitVariableDeclarationListWithCollidingNames(node *ast.VariableDeclarationList, hasReceiver bool) *ast.Node {
tx.hoistVariableDeclarationList(node)
var variables []*ast.Node
for _, decl := range node.Declarations.Nodes {
if decl.AsVariableDeclaration().Initializer != nil {
variables = append(variables, decl)
}
}
if len(variables) == 0 {
if hasReceiver {
name := node.Declarations.Nodes[0].Name()
var target *ast.Node
if ast.IsBindingPattern(name) {
target = transformers.ConvertBindingPatternToAssignmentPattern(tx.EmitContext(), name.AsBindingPattern())
} else {
target = name
}
return tx.Visitor().VisitNode(target)
}
return nil
}
var expressions []*ast.Node
for _, variable := range variables {
expressions = append(expressions, tx.transformInitializedVariable(variable.AsVariableDeclaration()))
}
return tx.Factory().InlineExpressions(expressions)
}
func (tx *asyncTransformer) hoistVariableDeclarationList(node *ast.VariableDeclarationList) {
for _, decl := range node.Declarations.Nodes {
tx.hoistVariable(decl)
}
}
func (tx *asyncTransformer) hoistVariable(node *ast.Node) {
name := node.Name()
if name == nil {
return
}
if ast.IsIdentifier(name) {
tx.EmitContext().AddVariableDeclaration(name)
} else if ast.IsBindingPattern(name) {
for _, element := range name.AsBindingPattern().Elements.Nodes {
if !ast.IsOmittedExpression(element) {
tx.hoistVariable(element)
}
}
}
}
func (tx *asyncTransformer) transformInitializedVariable(node *ast.VariableDeclaration) *ast.Node {
var target *ast.Node
if ast.IsBindingPattern(node.Name()) {
target = transformers.ConvertBindingPatternToAssignmentPattern(tx.EmitContext(), node.Name().AsBindingPattern())
} else {
target = node.Name()
}
converted := tx.Factory().NewAssignmentExpression(target, node.Initializer)
tx.EmitContext().SetSourceMapRange(converted, node.Loc)
return tx.Visitor().VisitNode(converted)
}
func (tx *asyncTransformer) collidesWithParameterName(node *ast.Node) bool {
name := node.Name()
if name == nil {
return false
}
if ast.IsIdentifier(name) {
return tx.enclosingFunctionParameterNames != nil && tx.enclosingFunctionParameterNames.Has(name.Text())
}
if ast.IsBindingPattern(name) {
for _, element := range name.AsBindingPattern().Elements.Nodes {
if !ast.IsOmittedExpression(element) && tx.collidesWithParameterName(element) {
return true
}
}
}
return false
}
func (tx *asyncTransformer) transformMethodBody(node *ast.Node) *ast.Node {
savedCapturedSuperProperties := tx.capturedSuperProperties
savedHasSuperElementAccess := tx.hasSuperElementAccess
savedHasSuperPropertyAssignment := tx.hasSuperPropertyAssignment
savedSuperBinding := tx.superBinding
savedSuperIndexBinding := tx.superIndexBinding
tx.capturedSuperProperties = &collections.OrderedSet[string]{}
tx.hasSuperElementAccess = false
tx.hasSuperPropertyAssignment = false
tx.superBinding = tx.Factory().NewUniqueNameEx("_super", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
tx.superIndexBinding = tx.Factory().NewUniqueNameEx("_superIndex", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
tx.EmitContext().StartVariableEnvironment()
updated := tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor())
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
emitSuperHelpers := (tx.capturedSuperProperties.Size() > 0 || tx.hasSuperElementAccess) &&
(ast.GetFunctionFlags(tx.getOriginalIfFunctionLike(node))&ast.FunctionFlagsAsyncGenerator) != ast.FunctionFlagsAsyncGenerator
if emitSuperHelpers {
if tx.capturedSuperProperties.Size() > 0 {
tx.EmitContext().AddInitializationStatement(tx.createSuperAccessVariableStatement())
}
}
mergedStatements := tx.EmitContext().EndAndMergeVariableEnvironmentList(updated.StatementList())
if emitSuperHelpers && tx.hasSuperElementAccess && !updated.AsBlock().MultiLine {
newBlock := tx.Factory().NewBlock(mergedStatements, true)
newBlock.Loc = updated.Loc
updated = newBlock
} else {
updated = tx.Factory().UpdateBlock(updated.AsBlock(), mergedStatements, updated.AsBlock().MultiLine)
}
if emitSuperHelpers && tx.hasSuperElementAccess {
if tx.hasSuperPropertyAssignment {
tx.EmitContext().AddEmitHelper(updated, printer.AdvancedAsyncSuperHelper)
} else {
tx.EmitContext().AddEmitHelper(updated, printer.AsyncSuperHelper)
}
}
tx.capturedSuperProperties = savedCapturedSuperProperties
tx.hasSuperElementAccess = savedHasSuperElementAccess
tx.hasSuperPropertyAssignment = savedHasSuperPropertyAssignment
tx.superBinding = savedSuperBinding
tx.superIndexBinding = savedSuperIndexBinding
return updated
}
func (tx *asyncTransformer) createCaptureArgumentsStatement() *ast.Node {
variable := tx.Factory().NewVariableDeclaration(
tx.lexicalArguments.binding,
nil,
nil,
tx.Factory().NewIdentifier("arguments"),
)
declList := tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.Node{variable}), ast.NodeFlagsNone)
statement := tx.Factory().NewVariableStatement(nil, declList)
tx.EmitContext().AddEmitFlags(statement, printer.EFStartOnNewLine|printer.EFCustomPrologue)
return statement
}
func (tx *asyncTransformer) transformAsyncFunctionParameterList(node *ast.Node) *ast.NodeList {
if isSimpleParameterList(node.Parameters()) {
return tx.EmitContext().VisitParameters(node.ParameterList(), tx.Visitor())
}
var newParameters []*ast.Node
for _, parameter := range node.Parameters() {
param := parameter.AsParameterDeclaration()
if param.Initializer != nil || param.DotDotDotToken != nil {
// for an arrow function, capture the remaining arguments in a rest parameter.
// for any other function/method this isn't necessary as we can just use `arguments`.
if node.Kind == ast.KindArrowFunction {
restParameter := tx.Factory().NewParameterDeclaration(
nil,
tx.Factory().NewToken(ast.KindDotDotDotToken),
tx.Factory().NewUniqueNameEx("args", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes}),
nil,
nil,
nil,
)
newParameters = append(newParameters, restParameter)
}
break
}
// for arrow functions we capture fixed parameters to forward to `__awaiter`. For all other functions
// we add fixed parameters to preserve the function's `length` property.
newParameter := tx.Factory().NewParameterDeclaration(
nil,
nil,
tx.Factory().NewGeneratedNameForNodeEx(param.Name(), printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes}),
nil,
nil,
nil,
)
newParameters = append(newParameters, newParameter)
}
newParametersArray := tx.Factory().NewNodeList(newParameters)
newParametersArray.Loc = node.ParameterList().Loc
return newParametersArray
}
func (tx *asyncTransformer) transformAsyncFunctionBody(node *ast.Node, outerParameters *ast.NodeList) *ast.Node {
isArrow := node.Kind == ast.KindArrowFunction
savedCapturedSuperProperties := tx.capturedSuperProperties
savedHasSuperElementAccess := tx.hasSuperElementAccess
savedHasSuperPropertyAssignment := tx.hasSuperPropertyAssignment
savedSuperBinding := tx.superBinding
savedSuperIndexBinding := tx.superIndexBinding
if !isArrow {
tx.capturedSuperProperties = &collections.OrderedSet[string]{}
tx.hasSuperElementAccess = false
tx.hasSuperPropertyAssignment = false
tx.superBinding = tx.Factory().NewUniqueNameEx("_super", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
tx.superIndexBinding = tx.Factory().NewUniqueNameEx("_superIndex", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
}
innerParameters := (*ast.NodeList)(nil)
if !isSimpleParameterList(node.Parameters()) {
innerParameters = tx.EmitContext().VisitParameters(node.ParameterList(), tx.Visitor())
}
savedLexicalArguments := tx.lexicalArguments
captureLexicalArguments := tx.lexicalArguments.binding == nil
if captureLexicalArguments {
tx.lexicalArguments = lexicalArgumentsInfo{
binding: tx.Factory().NewUniqueName("arguments"),
}
}
var argumentsExpression *ast.Expression
if innerParameters != nil {
if isArrow {
// `node` does not have a simple parameter list, so `outerParameters` refers to placeholders that are
// forwarded to `innerParameters`, matching how they are introduced in `transformAsyncFunctionParameterList`.
var parameterBindings []*ast.Node
outerLen := len(outerParameters.Nodes)
for i, param := range node.Parameters() {
if i >= outerLen {
break
}
originalParameter := param.AsParameterDeclaration()
outerParameter := outerParameters.Nodes[i].AsParameterDeclaration()
if originalParameter.Initializer != nil || originalParameter.DotDotDotToken != nil {
parameterBindings = append(parameterBindings, tx.Factory().NewSpreadElement(outerParameter.Name()))
break
}
parameterBindings = append(parameterBindings, outerParameter.Name())
}
argumentsExpression = tx.Factory().NewArrayLiteralExpression(tx.Factory().NewNodeList(parameterBindings), false)
} else {
argumentsExpression = tx.Factory().NewIdentifier("arguments")
}
}
// An async function is emit as an outer function that calls an inner
// generator function. To preserve lexical bindings, we pass the current
// `this` and `arguments` objects to `__awaiter`. The generator function
// passed to `__awaiter` is executed inside of the callback to the
// promise constructor.
savedEnclosingFunctionParameterNames := tx.enclosingFunctionParameterNames
tx.enclosingFunctionParameterNames = &collections.Set[string]{}
for _, parameter := range node.Parameters() {
tx.recordDeclarationName(parameter, tx.enclosingFunctionParameterNames)
}
hasLexicalThis := tx.inHasLexicalThisContext()
asyncBody := tx.transformAsyncFunctionBodyWorker(node.Body())
asyncBody = tx.Factory().UpdateBlock(
asyncBody.AsBlock(),
tx.EmitContext().EndAndMergeVariableEnvironmentList(asyncBody.StatementList()),
asyncBody.AsBlock().MultiLine,
)
// Substitute super property accesses with _super/_superIndex helpers
emitSuperHelpers := tx.capturedSuperProperties != nil &&
(tx.capturedSuperProperties.Size() > 0 || tx.hasSuperElementAccess)
if emitSuperHelpers {
innerParameters = tx.superAccessVisitor.VisitNodes(innerParameters)
asyncBody = tx.substituteSuperAccessesInBody(asyncBody)
}
var result *ast.Node
if !isArrow {
tx.EmitContext().StartVariableEnvironment()
// Minor optimization, emit `_super` helper to capture `super` access in an arrow.
if emitSuperHelpers {
if tx.capturedSuperProperties.Size() > 0 {
tx.EmitContext().AddInitializationStatement(tx.createSuperAccessVariableStatement())
}
}
if captureLexicalArguments && tx.lexicalArguments.used {
tx.EmitContext().AddInitializationStatement(tx.createCaptureArgumentsStatement())
}
statements := []*ast.Node{
tx.Factory().NewReturnStatement(
tx.Factory().NewAwaiterHelper(
hasLexicalThis,
argumentsExpression,
innerParameters,
asyncBody,
),
),
}
block := tx.Factory().NewBlock(
tx.EmitContext().EndAndMergeVariableEnvironmentList(tx.Factory().NewNodeList(statements)),
true,
)
block.Loc = node.Body().Loc
if emitSuperHelpers && tx.hasSuperElementAccess {
if tx.hasSuperPropertyAssignment {
tx.EmitContext().AddEmitHelper(block, printer.AdvancedAsyncSuperHelper)
} else {
tx.EmitContext().AddEmitHelper(block, printer.AsyncSuperHelper)
}
}
result = block
} else {
result = tx.Factory().NewAwaiterHelper(
hasLexicalThis,
argumentsExpression,
innerParameters,
asyncBody,
)
if captureLexicalArguments && tx.lexicalArguments.used {
block := tx.convertToFunctionBlock(result)
result = tx.Factory().UpdateBlock(
block.AsBlock(),
tx.EmitContext().MergeEnvironmentList(block.StatementList(), []*ast.Node{tx.createCaptureArgumentsStatement()}),
block.AsBlock().MultiLine,
)
}
}
tx.enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames
if !isArrow {
tx.capturedSuperProperties = savedCapturedSuperProperties
tx.hasSuperElementAccess = savedHasSuperElementAccess
tx.hasSuperPropertyAssignment = savedHasSuperPropertyAssignment
tx.superBinding = savedSuperBinding
tx.superIndexBinding = savedSuperIndexBinding
tx.lexicalArguments = savedLexicalArguments
} else if captureLexicalArguments && !tx.lexicalArguments.used {
// If we created a new binding but it wasn't used, restore the previous state.
// If it was used, keep the binding alive so sibling arrows can reuse it
// (the `var` declaration hoists to the enclosing function scope).
tx.lexicalArguments = savedLexicalArguments
} else if captureLexicalArguments {
// Keep the binding but clear the used flag so siblings don't re-emit the capture statement.
tx.lexicalArguments.used = false
}
return result
}
func (tx *asyncTransformer) transformAsyncFunctionBodyWorker(body *ast.Node) *ast.Node {
if ast.IsBlock(body) {
return tx.Factory().UpdateBlock(
body.AsBlock(),
tx.asyncBodyVisitor.VisitNodes(body.StatementList()),
body.AsBlock().MultiLine,
)
}
// Convert expression body to block body with return statement
visited := tx.asyncBodyVisitor.VisitNode(body)
ret := tx.Factory().NewReturnStatement(visited)
ret.Loc = body.Loc
list := tx.Factory().NewNodeList([]*ast.Node{ret})
list.Loc = body.Loc
block := tx.Factory().NewBlock(list, false /*multiLine*/)
block.Loc = body.Loc
return block
}
func (tx *asyncTransformer) convertToFunctionBlock(node *ast.Node) *ast.Node {
if ast.IsBlock(node) {
return node
}
ret := tx.Factory().NewReturnStatement(node)
ret.Loc = node.Loc
tx.EmitContext().SetOriginal(ret, node)
list := tx.Factory().NewNodeList([]*ast.Node{ret})
list.Loc = node.Loc
block := tx.Factory().NewBlock(list, true)
block.Loc = node.Loc
return block
}
// assignmentTargetContainsSuperProperty checks top-down whether an assignment target
// expression contains a super property or element access (super.x or super[x]).
// This avoids relying on parent pointers (IsAssignmentTarget) which may not be set
// on synthesized AST nodes from prior transforms.
func assignmentTargetContainsSuperProperty(node *ast.Node) bool {
switch node.Kind {
case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression:
return node.Expression().Kind == ast.KindSuperKeyword
case ast.KindParenthesizedExpression:
return assignmentTargetContainsSuperProperty(node.AsParenthesizedExpression().Expression)
case ast.KindArrayLiteralExpression:
return slices.ContainsFunc(node.AsArrayLiteralExpression().Elements.Nodes, assignmentTargetContainsSuperProperty)
case ast.KindObjectLiteralExpression:
for _, prop := range node.AsObjectLiteralExpression().Properties.Nodes {
switch prop.Kind {
case ast.KindPropertyAssignment:
if assignmentTargetContainsSuperProperty(prop.AsPropertyAssignment().Initializer) {
return true
}
case ast.KindShorthandPropertyAssignment:
if assignmentTargetContainsSuperProperty(prop.AsShorthandPropertyAssignment().Name()) {
return true
}
case ast.KindSpreadAssignment:
if assignmentTargetContainsSuperProperty(prop.AsSpreadAssignment().Expression) {
return true
}
}
}
case ast.KindSpreadElement:
return assignmentTargetContainsSuperProperty(node.AsSpreadElement().Expression)
}
return false
}
// isUpdateExpression checks if a prefix/postfix unary expression is ++ or --.
func isUpdateExpression(node *ast.Node) bool {
if ast.IsPrefixUnaryExpression(node) {
op := node.AsPrefixUnaryExpression().Operator
return op == ast.KindPlusPlusToken || op == ast.KindMinusMinusToken
}
if ast.IsPostfixUnaryExpression(node) {
op := node.AsPostfixUnaryExpression().Operator
return op == ast.KindPlusPlusToken || op == ast.KindMinusMinusToken
}
return false
}
func (tx *asyncTransformer) getOriginalIfFunctionLike(node *ast.Node) *ast.Node {
original := tx.EmitContext().MostOriginal(node)
if original != nil && ast.IsFunctionLikeDeclaration(original) {
return original
}
return node
}
// isSimpleParameterList checks if every parameter has no initializer and an Identifier name.
func isSimpleParameterList(params []*ast.Node) bool {
for _, param := range params {
p := param.AsParameterDeclaration()
if p.Initializer != nil || !ast.IsIdentifier(p.Name()) {
return false
}
}
return true
}
// isNodeWithPossibleHoistedDeclaration checks if a node could contain hoisted declarations.
func isNodeWithPossibleHoistedDeclaration(node *ast.Node) bool {
switch node.Kind {
case ast.KindBlock,
ast.KindVariableStatement,
ast.KindWithStatement,
ast.KindIfStatement,
ast.KindSwitchStatement,
ast.KindCaseBlock,
ast.KindCaseClause,
ast.KindDefaultClause,
ast.KindLabeledStatement,
ast.KindForStatement,
ast.KindForInStatement,
ast.KindForOfStatement,
ast.KindDoStatement,
ast.KindWhileStatement,
ast.KindTryStatement,
ast.KindCatchClause:
return true
}
return false
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/printer"
)
// Gets whether a node is a `static {}` block containing only a single assignment of the static `this` to the `_classThis`
// (or similar) variable stored in the `classthis` property of the block's `EmitNode`.
func isClassThisAssignmentBlock(emitContext *printer.EmitContext, node *ast.Node) bool {
if ast.IsClassStaticBlockDeclaration(node) {
n := node.AsClassStaticBlockDeclaration()
body := n.Body.AsBlock()
if len(body.Statements.Nodes) == 1 {
statement := body.Statements.Nodes[0]
if ast.IsExpressionStatement(statement) {
expression := statement.Expression()
if ast.IsAssignmentExpression(expression, true /*excludeCompoundAssignment*/) {
binary := expression.AsBinaryExpression()
return ast.IsIdentifier(binary.Left) &&
emitContext.ClassThis(node) == binary.Left &&
binary.Right.Kind == ast.KindThisKeyword
}
}
}
}
return false
}

View File

@@ -0,0 +1,43 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/transformers"
)
var (
esDecoratorAndClassFields = transformers.Chain(newESDecoratorTransformer, newClassFieldsTransformer)
NewESNextTransformer = transformers.Chain(newUsingDeclarationTransformer, esDecoratorAndClassFields)
// 2025: only module system syntax (import attributes, json modules), untransformed regex modifiers
// 2024: no new downlevel syntax
// 2023: no new downlevel syntax
// 2022: class static blocks and class fields are handled by newClassFieldsTransformer
NewES2021Transformer = transformers.Chain(NewESNextTransformer, newLogicalAssignmentTransformer)
NewES2020Transformer = transformers.Chain(NewES2021Transformer, newNullishCoalescingTransformer, newOptionalChainTransformer)
NewES2019Transformer = transformers.Chain(NewES2020Transformer, newOptionalCatchTransformer)
NewES2018Transformer = transformers.Chain(NewES2019Transformer, newObjectRestSpreadTransformer, newforawaitTransformer, newTaggedTemplateLiftRestrictionTransformer)
NewES2017Transformer = transformers.Chain(NewES2018Transformer, newAsyncTransformer)
NewES2016Transformer = transformers.Chain(NewES2017Transformer, newExponentiationTransformer)
)
func GetESTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
options := opts.CompilerOptions
switch options.GetEmitScriptTarget() {
case core.ScriptTargetESNext:
return esDecoratorAndClassFields(opts)
case core.ScriptTargetES2025, core.ScriptTargetES2024, core.ScriptTargetES2023, core.ScriptTargetES2022, core.ScriptTargetES2021:
return NewESNextTransformer(opts)
case core.ScriptTargetES2020:
return NewES2021Transformer(opts)
case core.ScriptTargetES2019:
return NewES2020Transformer(opts)
case core.ScriptTargetES2018:
return NewES2019Transformer(opts)
case core.ScriptTargetES2017:
return NewES2018Transformer(opts)
case core.ScriptTargetES2016:
return NewES2017Transformer(opts)
default: // other, older, option, transform maximally
return NewES2016Transformer(opts)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,90 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/transformers"
)
type exponentiationTransformer struct {
transformers.Transformer
}
func (ch *exponentiationTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsExponentiationOperator == 0 {
return node
}
switch node.Kind {
case ast.KindBinaryExpression:
return ch.visitBinaryExpression(node.AsBinaryExpression())
default:
return ch.Visitor().VisitEachChild(node)
}
}
func (ch *exponentiationTransformer) visitBinaryExpression(node *ast.BinaryExpression) *ast.Node {
switch node.OperatorToken.Kind {
case ast.KindAsteriskAsteriskEqualsToken:
return ch.visitExponentiationAssignmentExpression(node)
case ast.KindAsteriskAsteriskToken:
return ch.visitExponentiationExpression(node)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *exponentiationTransformer) visitExponentiationAssignmentExpression(node *ast.BinaryExpression) *ast.Node {
var target *ast.Node
var value *ast.Node
left := ch.Visitor().VisitNode(node.Left)
right := ch.Visitor().VisitNode(node.Right)
if ast.IsElementAccessExpression(left) {
// Transforms `a[x] **= b` into `(_a = a)[_x = x] = Math.pow(_a[_x], b)`
expressionTemp := ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(expressionTemp)
argumentExpressionTemp := ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(argumentExpressionTemp)
objExpr := ch.Factory().NewAssignmentExpression(expressionTemp, left.Expression())
objExpr.Loc = left.Expression().Loc
accessExpr := ch.Factory().NewAssignmentExpression(argumentExpressionTemp, left.AsElementAccessExpression().ArgumentExpression)
accessExpr.Loc = left.AsElementAccessExpression().ArgumentExpression.Loc
target = ch.Factory().NewElementAccessExpression(objExpr, nil, accessExpr, ast.NodeFlagsNone)
value = ch.Factory().NewElementAccessExpression(expressionTemp, nil, argumentExpressionTemp, ast.NodeFlagsNone)
value.Loc = left.Loc
} else if ast.IsPropertyAccessExpression(left) {
// Transforms `a.x **= b` into `(_a = a).x = Math.pow(_a.x, b)`
expressionTemp := ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(expressionTemp)
assignment := ch.Factory().NewAssignmentExpression(expressionTemp, left.Expression())
assignment.Loc = left.Expression().Loc
target = ch.Factory().NewPropertyAccessExpression(assignment, nil, left.Name(), ast.NodeFlagsNone)
target.Loc = left.Loc
value = ch.Factory().NewPropertyAccessExpression(expressionTemp, nil, left.Name(), ast.NodeFlagsNone)
value.Loc = left.Loc
} else {
// Transforms `a **= b` into `a = Math.pow(a, b)`
target = left
value = left
}
rhs := ch.Factory().NewGlobalMethodCall("Math", "pow", []*ast.Node{value, right})
rhs.Loc = node.Loc
result := ch.Factory().NewAssignmentExpression(target, rhs)
result.Loc = node.Loc
return result
}
func (ch *exponentiationTransformer) visitExponentiationExpression(node *ast.BinaryExpression) *ast.Node {
left := ch.Visitor().VisitNode(node.Left)
right := ch.Visitor().VisitNode(node.Right)
result := ch.Factory().NewGlobalMethodCall("Math", "pow", []*ast.Node{left, right})
result.Loc = node.Loc
return result
}
func newExponentiationTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &exponentiationTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}

View File

@@ -0,0 +1,856 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
// Facts we track as we traverse the tree
type forAwaitHierarchyFacts int
const forAwaitHierarchyFactsNone forAwaitHierarchyFacts = 0
const (
//
// Ancestor facts
//
forAwaitHierarchyFactsHasLexicalThis forAwaitHierarchyFacts = 1 << iota
forAwaitHierarchyFactsIterationContainer
//
// Ancestor masks
//
forAwaitHierarchyFactsAncestorFactsMask = 1<<iota - 1
forAwaitHierarchyFactsSourceFileExcludes = forAwaitHierarchyFactsIterationContainer
forAwaitHierarchyFactsStrictModeSourceFileIncludes = forAwaitHierarchyFactsNone
forAwaitHierarchyFactsClassOrFunctionIncludes = forAwaitHierarchyFactsHasLexicalThis
forAwaitHierarchyFactsClassOrFunctionExcludes = forAwaitHierarchyFactsIterationContainer
forAwaitHierarchyFactsArrowFunctionIncludes = forAwaitHierarchyFactsNone
forAwaitHierarchyFactsArrowFunctionExcludes = forAwaitHierarchyFactsClassOrFunctionExcludes
forAwaitHierarchyFactsIterationStatementIncludes = forAwaitHierarchyFactsIterationContainer
forAwaitHierarchyFactsIterationStatementExcludes = forAwaitHierarchyFactsNone
)
type forawaitTransformer struct {
transformers.Transformer
superAccessState
compilerOptions *core.CompilerOptions
enclosingFunctionFlags ast.FunctionFlags
forAwaitHierarchyFacts forAwaitHierarchyFacts
exportedVariableStatement bool
fallbackNodeVisitor *ast.NodeVisitor
noAsyncModifierVisitor *ast.NodeVisitor
}
func newforawaitTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &forawaitTransformer{
compilerOptions: opts.CompilerOptions,
}
result := tx.NewTransformer(tx.visit, opts.Context)
tx.initSuperAccessVisitor(tx.EmitContext(), tx.Factory())
tx.fallbackNodeVisitor = tx.EmitContext().NewNodeVisitor(tx.visitFallback)
tx.noAsyncModifierVisitor = tx.EmitContext().NewNodeVisitor(func(node *ast.Node) *ast.Node {
if node.Kind == ast.KindAsyncKeyword {
return nil
}
return node
})
return result
}
func (tx *forawaitTransformer) affectsSubtree(excludeFacts forAwaitHierarchyFacts, includeFacts forAwaitHierarchyFacts) bool {
return tx.forAwaitHierarchyFacts != (tx.forAwaitHierarchyFacts&^excludeFacts | includeFacts)
}
// enterSubtree sets the HierarchyFacts for this node prior to visiting this node's subtree,
// returning the facts set prior to modification.
func (tx *forawaitTransformer) enterSubtree(excludeFacts forAwaitHierarchyFacts, includeFacts forAwaitHierarchyFacts) forAwaitHierarchyFacts {
ancestorFacts := tx.forAwaitHierarchyFacts
tx.forAwaitHierarchyFacts = (tx.forAwaitHierarchyFacts&^excludeFacts | includeFacts) & forAwaitHierarchyFactsAncestorFactsMask
return ancestorFacts
}
// exitSubtree restores the HierarchyFacts for this node's ancestor after visiting this node's
// subtree.
func (tx *forawaitTransformer) exitSubtree(ancestorFacts forAwaitHierarchyFacts) {
tx.forAwaitHierarchyFacts = ancestorFacts
}
func (tx *forawaitTransformer) visitModifiersNoAsync(modifiers *ast.ModifierList) *ast.ModifierList {
return tx.noAsyncModifierVisitor.VisitModifiers(modifiers)
}
func (tx *forawaitTransformer) doWithHierarchyFacts(cb func(*forawaitTransformer, *ast.Node) *ast.Node, node *ast.Node, excludeFacts forAwaitHierarchyFacts, includeFacts forAwaitHierarchyFacts) *ast.Node {
if tx.affectsSubtree(excludeFacts, includeFacts) {
ancestorFacts := tx.enterSubtree(excludeFacts, includeFacts)
result := cb(tx, node)
tx.exitSubtree(ancestorFacts)
return result
}
return cb(tx, node)
}
func (tx *forawaitTransformer) visitDefault(node *ast.Node) *ast.Node {
return tx.Visitor().VisitEachChild(node)
}
func (tx *forawaitTransformer) fallbackVisitor(node *ast.Node) *ast.Node {
if tx.capturedSuperProperties == nil {
return node
}
switch node.Kind {
case ast.KindFunctionExpression, ast.KindFunctionDeclaration,
ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor,
ast.KindConstructor:
return node
}
tx.trackSuperAccess(node)
return tx.fallbackNodeVisitor.VisitEachChild(node)
}
func (tx *forawaitTransformer) visitFallback(node *ast.Node) *ast.Node {
return tx.fallbackVisitor(node)
}
func (tx *forawaitTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsForAwaitOrAsyncGenerator == 0 {
return tx.fallbackVisitor(node)
}
tx.trackSuperAccess(node)
switch node.Kind {
case ast.KindSourceFile:
return tx.visitSourceFile(node.AsSourceFile())
case ast.KindAwaitExpression:
return tx.visitAwaitExpression(node.AsAwaitExpression())
case ast.KindYieldExpression:
return tx.visitYieldExpression(node.AsYieldExpression())
case ast.KindReturnStatement:
return tx.visitReturnStatement(node.AsReturnStatement())
case ast.KindLabeledStatement:
return tx.visitLabeledStatement(node.AsLabeledStatement())
case ast.KindDoStatement, ast.KindWhileStatement, ast.KindForInStatement:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitDefault,
node,
forAwaitHierarchyFactsIterationStatementExcludes,
forAwaitHierarchyFactsIterationStatementIncludes,
)
case ast.KindForOfStatement:
return tx.visitForOfStatement(node.AsForInOrOfStatement(), nil)
case ast.KindForStatement:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitDefault,
node,
forAwaitHierarchyFactsIterationStatementExcludes,
forAwaitHierarchyFactsIterationStatementIncludes,
)
case ast.KindConstructor:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitConstructorDeclaration,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
case ast.KindMethodDeclaration:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitMethodDeclaration,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
case ast.KindGetAccessor:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitGetAccessorDeclaration,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
case ast.KindSetAccessor:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitSetAccessorDeclaration,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
case ast.KindFunctionDeclaration:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitFunctionDeclaration,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
case ast.KindFunctionExpression:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitFunctionExpression,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
case ast.KindArrowFunction:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitArrowFunction,
node,
forAwaitHierarchyFactsArrowFunctionExcludes,
forAwaitHierarchyFactsArrowFunctionIncludes,
)
case ast.KindClassDeclaration, ast.KindClassExpression:
return tx.doWithHierarchyFacts(
(*forawaitTransformer).visitDefault,
node,
forAwaitHierarchyFactsClassOrFunctionExcludes,
forAwaitHierarchyFactsClassOrFunctionIncludes,
)
default:
return tx.Visitor().VisitEachChild(node)
}
}
func (tx *forawaitTransformer) visitAwaitExpression(node *ast.AwaitExpression) *ast.Node {
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 && tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
result := tx.Factory().NewYieldExpression(
nil, /*asteriskToken*/
tx.Factory().NewAwaitHelper(tx.Visitor().VisitNode(node.Expression)),
)
result.Loc = node.Loc
tx.EmitContext().SetOriginal(result, node.AsNode())
return result
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *forawaitTransformer) visitYieldExpression(node *ast.YieldExpression) *ast.Node {
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 && tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
if node.AsteriskToken != nil {
expression := tx.Visitor().VisitNode(node.Expression)
asyncValuesResult := tx.Factory().NewAsyncValuesHelper(expression)
asyncValuesResult.Loc = expression.Loc
asyncDelegatorResult := tx.Factory().NewAsyncDelegatorHelper(asyncValuesResult)
asyncDelegatorResult.Loc = expression.Loc
innerYield := tx.Factory().UpdateYieldExpression(
node,
node.AsteriskToken,
asyncDelegatorResult,
)
awaitedYield := tx.Factory().NewAwaitHelper(innerYield)
result := tx.Factory().NewYieldExpression(
nil, /*asteriskToken*/
awaitedYield,
)
result.Loc = node.Loc
tx.EmitContext().SetOriginal(result, node.AsNode())
return result
}
var innerExpression *ast.Node
if node.Expression != nil {
innerExpression = tx.Visitor().VisitNode(node.Expression)
} else {
innerExpression = tx.Factory().NewVoidZeroExpression()
}
result := tx.Factory().NewYieldExpression(
nil, /*asteriskToken*/
tx.createDownlevelAwait(innerExpression),
)
result.Loc = node.Loc
tx.EmitContext().SetOriginal(result, node.AsNode())
return result
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *forawaitTransformer) visitReturnStatement(node *ast.ReturnStatement) *ast.Node {
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 && tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
var expression *ast.Node
if node.Expression != nil {
expression = tx.Visitor().VisitNode(node.Expression)
} else {
expression = tx.Factory().NewVoidZeroExpression()
}
return tx.Factory().UpdateReturnStatement(
node,
tx.createDownlevelAwait(expression),
)
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *forawaitTransformer) visitLabeledStatement(node *ast.LabeledStatement) *ast.Node {
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 {
statement := unwrapInnermostStatementOfLabel(node)
if statement.Kind == ast.KindForOfStatement && statement.AsForInOrOfStatement().AwaitModifier != nil {
return tx.visitForOfStatement(statement.AsForInOrOfStatement(), node)
}
return tx.Factory().RestoreEnclosingLabel(tx.Visitor().VisitNode(statement), node)
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
// unwrapInnermostStatementOfLabel follows LabeledStatement chains to find the innermost statement.
func unwrapInnermostStatementOfLabel(node *ast.LabeledStatement) *ast.Node {
for {
if node.Statement.Kind != ast.KindLabeledStatement {
return node.Statement
}
node = node.Statement.AsLabeledStatement()
}
}
func (tx *forawaitTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
ancestorFacts := tx.enterSubtree(
forAwaitHierarchyFactsSourceFileExcludes,
forAwaitHierarchyFactsStrictModeSourceFileIncludes,
)
tx.exportedVariableStatement = false
visited := tx.Visitor().VisitEachChild(node.AsNode())
tx.EmitContext().AddEmitHelper(visited, tx.EmitContext().ReadEmitHelpers()...)
tx.exitSubtree(ancestorFacts)
return visited
}
// visitForOfStatement visits a ForOfStatement and converts it into a ES2015-compatible ForOfStatement.
func (tx *forawaitTransformer) visitForOfStatement(node *ast.ForInOrOfStatement, outermostLabeledStatement *ast.LabeledStatement) *ast.Node {
ancestorFacts := tx.enterSubtree(forAwaitHierarchyFactsIterationStatementExcludes, forAwaitHierarchyFactsIterationStatementIncludes)
var result *ast.Node
if node.AwaitModifier != nil {
result = tx.transformForAwaitOfStatement(node, outermostLabeledStatement, ancestorFacts)
} else {
result = tx.Factory().RestoreEnclosingLabel(tx.Visitor().VisitEachChild(node.AsNode()), outermostLabeledStatement)
}
tx.exitSubtree(ancestorFacts)
return result
}
func (tx *forawaitTransformer) convertForOfStatementHead(node *ast.ForInOrOfStatement, boundValue *ast.Node, nonUserCode *ast.Node) *ast.Node {
f := tx.Factory()
value := f.NewTempVariable()
tx.EmitContext().AddVariableDeclaration(value)
iteratorValueExpression := f.NewAssignmentExpression(value, boundValue)
iteratorValueStatement := f.NewExpressionStatement(iteratorValueExpression)
tx.EmitContext().SetSourceMapRange(iteratorValueStatement, node.Expression.Loc)
exitNonUserCodeExpression := f.NewAssignmentExpression(nonUserCode, f.NewKeywordExpression(ast.KindFalseKeyword))
exitNonUserCodeStatement := f.NewExpressionStatement(exitNonUserCodeExpression)
tx.EmitContext().SetSourceMapRange(exitNonUserCodeStatement, node.Expression.Loc)
statements := []*ast.Node{iteratorValueStatement, exitNonUserCodeStatement}
binding := tx.Factory().CreateForOfBindingStatement(node.Initializer, value)
statements = append(statements, tx.Visitor().VisitNode(binding))
var bodyLocation core.TextRange
var statementsLocation core.TextRange
statement := tx.Visitor().VisitEmbeddedStatement(node.Statement)
if ast.IsBlock(statement) {
statements = append(statements, statement.Statements()...)
bodyLocation = statement.Loc
statementsLocation = statement.StatementList().Loc
} else {
statements = append(statements, statement)
}
stmtList := f.NewNodeList(statements)
stmtList.Loc = statementsLocation
block := f.NewBlock(stmtList, true)
block.Loc = bodyLocation
return block
}
func (tx *forawaitTransformer) createDownlevelAwait(expression *ast.Node) *ast.Node {
if tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
return tx.Factory().NewYieldExpression(
nil, /*asteriskToken*/
tx.Factory().NewAwaitHelper(expression),
)
}
return tx.Factory().NewAwaitExpression(expression)
}
func (tx *forawaitTransformer) transformForAwaitOfStatement(node *ast.ForInOrOfStatement, outermostLabeledStatement *ast.LabeledStatement, ancestorFacts forAwaitHierarchyFacts) *ast.Node {
f := tx.Factory()
expression := tx.Visitor().VisitNode(node.Expression)
var iterator *ast.Node
if ast.IsIdentifier(expression) {
iterator = f.NewGeneratedNameForNode(expression)
} else {
iterator = f.NewTempVariable()
}
var result *ast.Node
if ast.IsIdentifier(expression) {
result = f.NewGeneratedNameForNode(iterator)
} else {
result = f.NewTempVariable()
}
nonUserCode := f.NewTempVariable()
done := f.NewTempVariable()
tx.EmitContext().AddVariableDeclaration(done)
errorRecord := f.NewUniqueName("e")
catchVariable := f.NewGeneratedNameForNode(errorRecord)
returnMethod := f.NewTempVariable()
callValues := f.NewAsyncValuesHelper(expression)
callValues.Loc = node.Expression.Loc
callNext := f.NewCallExpression(
f.NewPropertyAccessExpression(iterator, nil, f.NewIdentifier("next"), ast.NodeFlagsNone),
nil, nil,
f.NewNodeList([]*ast.Node{}),
ast.NodeFlagsNone,
)
getDone := f.NewPropertyAccessExpression(result, nil, f.NewIdentifier("done"), ast.NodeFlagsNone)
getValue := f.NewPropertyAccessExpression(result, nil, f.NewIdentifier("value"), ast.NodeFlagsNone)
callReturn := f.NewFunctionCallCall(returnMethod, iterator, []*ast.Node{})
tx.EmitContext().AddVariableDeclaration(errorRecord)
tx.EmitContext().AddVariableDeclaration(returnMethod)
// if we are enclosed in an outer loop ensure we reset 'errorRecord' per each iteration
var initializer *ast.Node
if ancestorFacts&forAwaitHierarchyFactsIterationContainer != 0 {
initializer = f.InlineExpressions([]*ast.Node{
f.NewAssignmentExpression(errorRecord, f.NewVoidZeroExpression()),
callValues,
})
} else {
initializer = callValues
}
// Build the for statement
iteratorDecl := f.NewVariableDeclaration(iterator, nil, nil, initializer)
iteratorDecl.Loc = node.Expression.Loc
varDeclList := f.NewVariableDeclarationList(f.NewNodeList([]*ast.Node{
f.NewVariableDeclaration(nonUserCode, nil, nil, f.NewKeywordExpression(ast.KindTrueKeyword)),
iteratorDecl,
f.NewVariableDeclaration(result, nil, nil, nil),
}), ast.NodeFlagsNone)
varDeclList.Loc = node.Expression.Loc
condition := f.InlineExpressions([]*ast.Node{
f.NewAssignmentExpression(result, tx.createDownlevelAwait(callNext)),
f.NewAssignmentExpression(done, getDone),
f.NewPrefixUnaryExpression(ast.KindExclamationToken, done),
})
incrementor := f.NewAssignmentExpression(nonUserCode, f.NewKeywordExpression(ast.KindTrueKeyword))
forStatement := f.NewForStatement(
varDeclList,
condition,
incrementor,
tx.convertForOfStatementHead(node, getValue, nonUserCode),
)
forStatement.Loc = node.Loc
tx.EmitContext().AddEmitFlags(forStatement, printer.EFNoTokenTrailingSourceMaps)
tx.EmitContext().SetOriginal(forStatement, node.AsNode())
// Build the try/catch/finally
tryBlock := f.NewBlock(f.NewNodeList([]*ast.Node{
f.RestoreEnclosingLabel(forStatement, outermostLabeledStatement),
}), true)
// catch clause: { e_1 = { error: e_2 }; }
catchBody := f.NewBlock(f.NewNodeList([]*ast.Node{
f.NewExpressionStatement(
f.NewAssignmentExpression(
errorRecord,
f.NewObjectLiteralExpression(f.NewNodeList([]*ast.Node{
f.NewPropertyAssignment(nil, f.NewIdentifier("error"), nil, nil, catchVariable),
}), false),
),
),
}), false)
tx.EmitContext().AddEmitFlags(catchBody, printer.EFSingleLine)
catchClause := f.NewCatchClause(
f.NewVariableDeclaration(catchVariable, nil, nil, nil),
catchBody,
)
// finally block
// inner try: if (!nonUserCode && !done && (returnMethod = iterator.return)) await returnMethod.call(iterator);
innerIfCondition := f.NewBinaryExpression(
nil,
f.NewBinaryExpression(
nil,
f.NewPrefixUnaryExpression(ast.KindExclamationToken, nonUserCode),
nil,
f.NewToken(ast.KindAmpersandAmpersandToken),
f.NewPrefixUnaryExpression(ast.KindExclamationToken, done),
),
nil,
f.NewToken(ast.KindAmpersandAmpersandToken),
f.NewAssignmentExpression(
returnMethod,
f.NewPropertyAccessExpression(iterator, nil, f.NewIdentifier("return"), ast.NodeFlagsNone),
),
)
innerIfStatement := f.NewIfStatement(
innerIfCondition,
f.NewExpressionStatement(tx.createDownlevelAwait(callReturn)),
nil,
)
tx.EmitContext().AddEmitFlags(innerIfStatement, printer.EFSingleLine)
innerTryBlock := f.NewBlock(f.NewNodeList([]*ast.Node{innerIfStatement}), false)
// inner finally: if (errorRecord) throw errorRecord.error;
innerFinallyIf := f.NewIfStatement(
errorRecord,
f.NewThrowStatement(
f.NewPropertyAccessExpression(errorRecord, nil, f.NewIdentifier("error"), ast.NodeFlagsNone),
),
nil,
)
tx.EmitContext().AddEmitFlags(innerFinallyIf, printer.EFSingleLine)
innerFinallyBlock := f.NewBlock(f.NewNodeList([]*ast.Node{innerFinallyIf}), false)
tx.EmitContext().AddEmitFlags(innerFinallyBlock, printer.EFSingleLine)
innerTryStatement := f.NewTryStatement(innerTryBlock, nil, innerFinallyBlock)
finallyBlock := f.NewBlock(f.NewNodeList([]*ast.Node{innerTryStatement}), true)
return f.NewTryStatement(tryBlock, catchClause, finallyBlock)
}
func (tx *forawaitTransformer) visitConstructorDeclaration(node *ast.Node) *ast.Node {
decl := node.AsConstructorDeclaration()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
updated := tx.Factory().UpdateConstructorDeclaration(
decl,
decl.Modifiers(),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor()),
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) visitGetAccessorDeclaration(node *ast.Node) *ast.Node {
decl := node.AsGetAccessorDeclaration()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
updated := tx.Factory().UpdateGetAccessorDeclaration(
decl,
decl.Modifiers(),
tx.Visitor().VisitNode(decl.Name()),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor()),
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) visitSetAccessorDeclaration(node *ast.Node) *ast.Node {
decl := node.AsSetAccessorDeclaration()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
updated := tx.Factory().UpdateSetAccessorDeclaration(
decl,
decl.Modifiers(),
tx.Visitor().VisitNode(decl.Name()),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor()),
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) visitMethodDeclaration(node *ast.Node) *ast.Node {
decl := node.AsMethodDeclaration()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
var modifiers *ast.ModifierList
if tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
modifiers = tx.visitModifiersNoAsync(decl.Modifiers())
} else {
modifiers = decl.Modifiers()
}
var asteriskToken *ast.TokenNode
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 {
asteriskToken = nil
} else {
asteriskToken = decl.AsteriskToken
}
var parameters *ast.NodeList
var body *ast.Node
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 && tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
parameters = tx.transformAsyncGeneratorFunctionParameterList(node)
body = tx.transformAsyncGeneratorFunctionBody(node)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor())
}
updated := tx.Factory().UpdateMethodDeclaration(
decl,
modifiers,
asteriskToken,
tx.Visitor().VisitNode(decl.Name()),
nil, /*postfixToken*/
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) visitFunctionDeclaration(node *ast.Node) *ast.Node {
decl := node.AsFunctionDeclaration()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
var modifiers *ast.ModifierList
if tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
modifiers = tx.visitModifiersNoAsync(decl.Modifiers())
} else {
modifiers = decl.Modifiers()
}
var asteriskToken *ast.TokenNode
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 {
asteriskToken = nil
} else {
asteriskToken = decl.AsteriskToken
}
var parameters *ast.NodeList
var body *ast.Node
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 && tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
parameters = tx.transformAsyncGeneratorFunctionParameterList(node)
body = tx.transformAsyncGeneratorFunctionBody(node)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor())
}
updated := tx.Factory().UpdateFunctionDeclaration(
decl,
modifiers,
asteriskToken,
decl.Name(),
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) visitArrowFunction(node *ast.Node) *ast.Node {
decl := node.AsArrowFunction()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
updated := tx.Factory().UpdateArrowFunction(
decl,
decl.Modifiers(),
nil, /*typeParameters*/
tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor()),
nil, /*returnType*/
nil, /*fullSignature*/
decl.EqualsGreaterThanToken,
tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor()),
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) visitFunctionExpression(node *ast.Node) *ast.Node {
decl := node.AsFunctionExpression()
savedEnclosingFunctionFlags := tx.enclosingFunctionFlags
tx.enclosingFunctionFlags = ast.GetFunctionFlags(node)
var modifiers *ast.ModifierList
if tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
modifiers = tx.visitModifiersNoAsync(decl.Modifiers())
} else {
modifiers = decl.Modifiers()
}
var asteriskToken *ast.TokenNode
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 {
asteriskToken = nil
} else {
asteriskToken = decl.AsteriskToken
}
var parameters *ast.NodeList
var body *ast.Node
if tx.enclosingFunctionFlags&ast.FunctionFlagsAsync != 0 && tx.enclosingFunctionFlags&ast.FunctionFlagsGenerator != 0 {
parameters = tx.transformAsyncGeneratorFunctionParameterList(node)
body = tx.transformAsyncGeneratorFunctionBody(node)
} else {
parameters = tx.EmitContext().VisitParameters(decl.Parameters, tx.Visitor())
body = tx.EmitContext().VisitFunctionBody(node.Body(), tx.Visitor())
}
updated := tx.Factory().UpdateFunctionExpression(
decl,
modifiers,
asteriskToken,
decl.Name(),
nil, /*typeParameters*/
parameters,
nil, /*returnType*/
nil, /*fullSignature*/
body,
)
tx.enclosingFunctionFlags = savedEnclosingFunctionFlags
return updated
}
func (tx *forawaitTransformer) transformAsyncGeneratorFunctionParameterList(node *ast.Node) *ast.NodeList {
if isSimpleParameterList(node.Parameters()) {
return tx.EmitContext().VisitParameters(node.ParameterList(), tx.Visitor())
}
// Add fixed parameters to preserve the function's `length` property.
var newParameters []*ast.Node
for _, parameter := range node.Parameters() {
param := parameter.AsParameterDeclaration()
if param.Initializer != nil || param.DotDotDotToken != nil {
break
}
newParameter := tx.Factory().NewParameterDeclaration(
nil,
nil,
tx.Factory().NewGeneratedNameForNodeEx(param.Name(), printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes}),
nil,
nil,
nil,
)
newParameters = append(newParameters, newParameter)
}
newParametersArray := tx.Factory().NewNodeList(newParameters)
newParametersArray.Loc = node.ParameterList().Loc
return newParametersArray
}
func (tx *forawaitTransformer) transformAsyncGeneratorFunctionBody(node *ast.Node) *ast.Node {
f := tx.Factory()
var innerParameters *ast.NodeList
if !isSimpleParameterList(node.Parameters()) {
innerParameters = tx.EmitContext().VisitParameters(node.ParameterList(), tx.Visitor())
}
savedCapturedSuperProperties := tx.capturedSuperProperties
savedHasSuperElementAccess := tx.hasSuperElementAccess
savedHasSuperPropertyAssignment := tx.hasSuperPropertyAssignment
savedSuperBinding := tx.superBinding
savedSuperIndexBinding := tx.superIndexBinding
tx.capturedSuperProperties = &collections.OrderedSet[string]{}
tx.hasSuperElementAccess = false
tx.hasSuperPropertyAssignment = false
tx.superBinding = f.NewUniqueNameEx("_super", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
tx.superIndexBinding = f.NewUniqueNameEx("_superIndex", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
asyncBody := f.UpdateBlock(
node.Body().AsBlock(),
tx.Visitor().VisitNodes(node.Body().StatementList()),
node.Body().AsBlock().MultiLine,
)
asyncBody = f.UpdateBlock(
asyncBody.AsBlock(),
tx.EmitContext().EndAndMergeVariableEnvironmentList(asyncBody.StatementList()),
asyncBody.AsBlock().MultiLine,
)
// Substitute super property accesses with _super/_superIndex helpers
emitSuperHelpers := tx.capturedSuperProperties.Size() > 0 || tx.hasSuperElementAccess
if emitSuperHelpers {
asyncBody = tx.substituteSuperAccessesInBody(asyncBody)
}
var innerParams *ast.NodeList
if innerParameters != nil {
innerParams = innerParameters
} else {
innerParams = f.NewNodeList([]*ast.Node{})
}
var name *ast.Node
if node.Name() != nil {
name = f.NewGeneratedNameForNode(node.Name())
}
generatorFunc := f.NewFunctionExpression(
nil, /*modifiers*/
f.NewToken(ast.KindAsteriskToken),
name,
nil, /*typeParameters*/
innerParams,
nil, /*returnType*/
nil, /*fullSignature*/
asyncBody,
)
returnStatement := f.NewReturnStatement(
f.NewAsyncGeneratorHelper(
generatorFunc,
tx.forAwaitHierarchyFacts&forAwaitHierarchyFactsHasLexicalThis != 0,
),
)
tx.EmitContext().StartVariableEnvironment()
if emitSuperHelpers {
if tx.capturedSuperProperties.Size() > 0 {
tx.EmitContext().AddInitializationStatement(tx.createSuperAccessVariableStatement())
}
}
outerStatements := []*ast.Node{returnStatement}
block := f.UpdateBlock(
node.Body().AsBlock(),
tx.EmitContext().EndAndMergeVariableEnvironmentList(f.NewNodeList(outerStatements)),
node.Body().AsBlock().MultiLine,
)
if emitSuperHelpers && tx.hasSuperElementAccess {
if tx.hasSuperPropertyAssignment {
tx.EmitContext().AddEmitHelper(block, printer.AdvancedAsyncSuperHelper)
} else {
tx.EmitContext().AddEmitHelper(block, printer.AsyncSuperHelper)
}
}
tx.capturedSuperProperties = savedCapturedSuperProperties
tx.hasSuperElementAccess = savedHasSuperElementAccess
tx.hasSuperPropertyAssignment = savedHasSuperPropertyAssignment
tx.superBinding = savedSuperBinding
tx.superIndexBinding = savedSuperIndexBinding
return block
}

View File

@@ -0,0 +1,113 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/transformers"
)
type logicalAssignmentTransformer struct {
transformers.Transformer
}
func (ch *logicalAssignmentTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsLogicalAssignments == 0 {
return node
}
switch node.Kind {
case ast.KindBinaryExpression:
return ch.visitBinaryExpression(node.AsBinaryExpression())
default:
return ch.Visitor().VisitEachChild(node)
}
}
func (ch *logicalAssignmentTransformer) visitBinaryExpression(node *ast.BinaryExpression) *ast.Node {
var nonAssignmentOperator ast.Kind
switch node.OperatorToken.Kind {
case ast.KindBarBarEqualsToken:
nonAssignmentOperator = ast.KindBarBarToken
case ast.KindAmpersandAmpersandEqualsToken:
nonAssignmentOperator = ast.KindAmpersandAmpersandToken
case ast.KindQuestionQuestionEqualsToken:
nonAssignmentOperator = ast.KindQuestionQuestionToken
default:
return ch.Visitor().VisitEachChild(node.AsNode())
}
left := ast.SkipParentheses(ch.Visitor().VisitNode(node.Left))
assignmentTarget := left
right := ast.SkipParentheses(ch.Visitor().VisitNode(node.Right))
if ast.IsAccessExpression(left) {
propertyAccessTargetSimpleCopiable := transformers.IsSimpleCopiableExpression(left.Expression())
propertyAccessTarget := left.Expression()
propertyAccessTargetAssignment := left.Expression()
if !propertyAccessTargetSimpleCopiable {
propertyAccessTarget = ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(propertyAccessTarget)
propertyAccessTargetAssignment = ch.Factory().NewAssignmentExpression(
propertyAccessTarget,
left.Expression(),
)
}
if ast.IsPropertyAccessExpression(left) {
assignmentTarget = ch.Factory().NewPropertyAccessExpression(
propertyAccessTarget,
nil,
left.Name(),
ast.NodeFlagsNone,
)
left = ch.Factory().NewPropertyAccessExpression(
propertyAccessTargetAssignment,
nil,
left.Name(),
ast.NodeFlagsNone,
)
} else {
elementAccessArgumentSimpleCopiable := transformers.IsSimpleCopiableExpression(left.AsElementAccessExpression().ArgumentExpression)
elementAccessArgument := left.AsElementAccessExpression().ArgumentExpression
argumentExpr := elementAccessArgument
if !elementAccessArgumentSimpleCopiable {
elementAccessArgument = ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(elementAccessArgument)
argumentExpr = ch.Factory().NewAssignmentExpression(
elementAccessArgument,
left.AsElementAccessExpression().ArgumentExpression,
)
}
assignmentTarget = ch.Factory().NewElementAccessExpression(
propertyAccessTarget,
nil,
elementAccessArgument,
ast.NodeFlagsNone,
)
left = ch.Factory().NewElementAccessExpression(
propertyAccessTargetAssignment,
nil,
argumentExpr,
ast.NodeFlagsNone,
)
}
}
return ch.Factory().NewBinaryExpression(
nil,
left,
nil,
ch.Factory().NewToken(nonAssignmentOperator),
ch.Factory().NewParenthesizedExpression(
ch.Factory().NewAssignmentExpression(
assignmentTarget,
right,
),
),
)
}
func newLogicalAssignmentTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &logicalAssignmentTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}

View File

@@ -0,0 +1,537 @@
package estransforms
import (
"slices"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/debug"
"github.com/microsoft/typescript-go/internal/printer"
)
/**
* Gets whether a node is a `static {}` block containing only a single call to the `__setFunctionName` helper where that
* call's second argument is the value stored in the `assignedName` property of the block's `EmitNode`.
* @internal
*/
func isClassNamedEvaluationHelperBlock(emitContext *printer.EmitContext, node *ast.Node) bool {
if !ast.IsClassStaticBlockDeclaration(node) || len(node.AsClassStaticBlockDeclaration().Body.Statements()) != 1 {
return false
}
statement := node.AsClassStaticBlockDeclaration().Body.Statements()[0]
if ast.IsExpressionStatement(statement) {
expression := statement.Expression()
if emitContext.IsCallToHelper(expression, "__setFunctionName") {
arguments := expression.AsCallExpression().Arguments
return len(arguments.Nodes) >= 2 &&
arguments.Nodes[1] == emitContext.AssignedName(node.AsNode())
}
}
return false
}
/**
* Gets whether a `ClassLikeDeclaration` has a `static {}` block containing only a single call to the
* `__setFunctionName` helper.
* @internal
*/
func classHasExplicitlyAssignedName(emitContext *printer.EmitContext, node *ast.ClassLikeDeclaration) bool {
if assignedName := emitContext.AssignedName(node); assignedName != nil {
for _, member := range node.Members() {
if isClassNamedEvaluationHelperBlock(emitContext, member) {
return true
}
}
}
return false
}
/**
* Gets whether a `ClassLikeDeclaration` has a declared name or contains a `static {}` block containing only a single
* call to the `__setFunctionName` helper.
* @internal
*/
func classHasDeclaredOrExplicitlyAssignedName(emitContext *printer.EmitContext, node *ast.ClassLikeDeclaration) bool {
return node.Name() != nil || classHasExplicitlyAssignedName(emitContext, node)
}
type anonymousFunctionDefinition = ast.Node // ClassExpression | FunctionExpression | ArrowFunction
// Indicates whether an expression is an anonymous function definition.
//
// See https://tc39.es/ecma262/#sec-isanonymousfunctiondefinition
func isAnonymousFunctionDefinition(emitContext *printer.EmitContext, node *ast.Expression, cb func(*anonymousFunctionDefinition) bool) bool {
node = ast.SkipOuterExpressions(node, ast.OEKAll)
switch node.Kind {
case ast.KindClassExpression:
if classHasDeclaredOrExplicitlyAssignedName(emitContext, node) {
return false
}
break
case ast.KindFunctionExpression:
if node.AsFunctionExpression().Name() != nil {
return false
}
break
case ast.KindArrowFunction:
break
default:
return false
}
if cb != nil {
return cb(node)
}
return true
}
func isNamedEvaluation(emitContext *printer.EmitContext, node *ast.Node) bool {
return isNamedEvaluationAnd(emitContext, node, nil)
}
func isNamedEvaluationAnd(emitContext *printer.EmitContext, node *ast.Node, cb func(*anonymousFunctionDefinition) bool) bool {
if !ast.IsNamedEvaluationSource(node) {
return false
}
switch node.Kind {
case ast.KindShorthandPropertyAssignment:
return isAnonymousFunctionDefinition(emitContext, node.AsShorthandPropertyAssignment().ObjectAssignmentInitializer, cb)
case ast.KindPropertyAssignment, ast.KindVariableDeclaration, ast.KindParameter, ast.KindBindingElement, ast.KindPropertyDeclaration:
return isAnonymousFunctionDefinition(emitContext, node.Initializer(), cb)
case ast.KindBinaryExpression:
return isAnonymousFunctionDefinition(emitContext, node.AsBinaryExpression().Right, cb)
case ast.KindExportAssignment:
return isAnonymousFunctionDefinition(emitContext, node.Expression(), cb)
default:
debug.Fail("Unhandled case in isNamedEvaluation")
return false
}
}
// Gets a string literal to use as the assigned name of an anonymous class or function declaration.
func getAssignedNameOfIdentifier(emitContext *printer.EmitContext, name *ast.IdentifierNode, expression *ast.Node /*WrappedExpression<AnonymousFunctionDefinition>*/) *ast.StringLiteralNode {
original := emitContext.MostOriginal(ast.SkipOuterExpressions(expression, ast.OEKAll))
if (ast.IsClassDeclaration(original) || ast.IsFunctionDeclaration(original)) &&
original.Name() == nil && ast.HasSyntacticModifier(original, ast.ModifierFlagsDefault) {
return emitContext.Factory.NewStringLiteral("default", ast.TokenFlagsNone)
}
return emitContext.Factory.NewStringLiteralFromNode(name)
}
func getAssignedNameOfPropertyName(emitContext *printer.EmitContext, name *ast.PropertyName, assignedNameText string) (assignedName *ast.Expression, updatedName *ast.PropertyName) {
factory := emitContext.Factory
if len(assignedNameText) > 0 {
assignedName := factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
return assignedName, name
}
if ast.IsPropertyNameLiteral(name) || ast.IsPrivateIdentifier(name) {
assignedName := factory.NewStringLiteralFromNode(name)
return assignedName, name
}
expression := name.Expression()
if ast.IsPropertyNameLiteral(expression) && !ast.IsIdentifier(expression) {
assignedName := factory.NewStringLiteralFromNode(expression)
return assignedName, name
}
debug.Assert(ast.IsComputedPropertyName(name), "Expected computed property name")
assignedName = factory.NewGeneratedNameForNode(name)
emitContext.AddVariableDeclaration(assignedName)
key := factory.NewPropKeyHelper(expression)
assignment := factory.NewAssignmentExpression(assignedName, key)
updatedName = factory.UpdateComputedPropertyName(name.AsComputedPropertyName(), assignment)
return assignedName, updatedName
}
// Creates a class `static {}` block used to dynamically set the name of a class.
//
// The assignedName parameter is the expression used to resolve the assigned name at runtime. This expression should not produce
// side effects.
// The thisExpression parameter overrides the expression to use for the actual `this` reference. This can be used to provide an
// expression that has already had its `EmitFlags` set or may have been tracked to prevent substitution.
func createClassNamedEvaluationHelperBlock(emitContext *printer.EmitContext, assignedName *ast.Expression, thisExpression *ast.Expression) *ast.Node {
// produces:
//
// static { __setFunctionName(this, "C"); }
//
if thisExpression == nil {
thisExpression = emitContext.Factory.NewThisExpression()
}
factory := emitContext.Factory
expression := factory.NewSetFunctionNameHelper(thisExpression, assignedName, "" /*prefix*/)
statement := factory.NewExpressionStatement(expression)
body := factory.NewBlock(factory.NewNodeList([]*ast.Statement{statement}), false /*multiLine*/)
block := factory.NewClassStaticBlockDeclaration(nil /*modifiers*/, body)
// We use `emitNode.assignedName` to indicate this is a NamedEvaluation helper block
// and to stash the expression used to resolve the assigned name.
emitContext.SetAssignedName(block, assignedName)
return block.AsNode()
}
// Injects a class `static {}` block used to dynamically set the name of a class, if one does not already exist.
func injectClassNamedEvaluationHelperBlockIfMissing(
emitContext *printer.EmitContext,
node *ast.ClassLikeDeclaration,
assignedName *ast.Expression,
thisExpression *ast.Expression,
) *ast.ClassLikeDeclaration {
// given:
//
// let C = class {
// };
//
// produces:
//
// let C = class {
// static { __setFunctionName(this, "C"); }
// };
// NOTE: If the class has a `_classThis` assignment block, this helper will be injected after that block.
if classHasExplicitlyAssignedName(emitContext, node) {
return node
}
factory := emitContext.Factory
namedEvaluationBlock := createClassNamedEvaluationHelperBlock(emitContext, assignedName, thisExpression)
if node.Name() != nil {
emitContext.SetSourceMapRange(namedEvaluationBlock.Body().Statements()[0], node.Name().Loc)
}
insertionIndex := slices.IndexFunc(node.Members(), func(n *ast.Node) bool {
return isClassThisAssignmentBlock(emitContext, n)
}) + 1
leading := slices.Clone(node.Members()[:insertionIndex])
trailing := slices.Clone(node.Members()[insertionIndex:])
var members []*ast.ClassElement
members = append(members, leading...)
members = append(members, namedEvaluationBlock)
members = append(members, trailing...)
membersList := factory.NewNodeList(members)
membersList.Loc = node.MemberList().Loc
oldNode := node
if ast.IsClassDeclaration(node) {
node = factory.UpdateClassDeclaration(
node.AsClassDeclaration(),
node.Modifiers(),
node.Name(),
node.TypeParameterList(),
node.AsClassDeclaration().HeritageClauses,
membersList,
)
} else {
node = factory.UpdateClassExpression(
node.AsClassExpression(),
node.Modifiers(),
node.Name(),
node.TypeParameterList(),
node.AsClassExpression().HeritageClauses,
membersList,
)
}
emitContext.SetAssignedName(node, assignedName)
// Transfer ClassThis from old to new node, since UpdateClassExpression creates
// a new node that won't have ClassThis set on it.
if ct := emitContext.ClassThis(oldNode); ct != nil {
emitContext.SetClassThis(node, ct)
}
return node
}
func finishTransformNamedEvaluation(
emitContext *printer.EmitContext,
expression *ast.Node, // WrappedExpression<AnonymousFunctionDefinition>,
assignedName *ast.Expression,
ignoreEmptyStringLiteral bool,
) *ast.Expression {
if ignoreEmptyStringLiteral && ast.IsStringLiteral(assignedName) && len(assignedName.Text()) == 0 {
return expression
}
factory := emitContext.Factory
innerExpression := ast.SkipOuterExpressions(expression, ast.OEKAll)
var updatedExpression *ast.Expression
if ast.IsClassExpression(innerExpression) {
updatedExpression = injectClassNamedEvaluationHelperBlockIfMissing(emitContext, innerExpression, assignedName, nil /*thisExpression*/)
} else {
updatedExpression = factory.NewSetFunctionNameHelper(innerExpression, assignedName, "" /*prefix*/)
}
return factory.RestoreOuterExpressions(expression, updatedExpression, ast.OEKAll)
}
func transformNamedEvaluationOfPropertyAssignment(context *printer.EmitContext, node *ast.PropertyAssignment /*NamedEvaluation & PropertyAssignment*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 13.2.5.5 RS: PropertyDefinitionEvaluation
// PropertyAssignment : PropertyName `:` AssignmentExpression
// ...
// 5. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true* and _isProtoSetter_ is *false*, then
// a. Let _popValue_ be ? NamedEvaluation of |AssignmentExpression| with argument _propKey_.
// ...
factory := context.Factory
assignedName, name := getAssignedNameOfPropertyName(context, node.Name(), assignedNameText)
initializer := finishTransformNamedEvaluation(context, node.Initializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdatePropertyAssignment(node, nil /*modifiers*/, name, nil /*postfixToken*/, nil /*typeNode*/, initializer)
}
func transformNamedEvaluationOfShorthandAssignmentProperty(emitContext *printer.EmitContext, node *ast.ShorthandPropertyAssignment /*NamedEvaluation & ShorthandPropertyAssignment*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 13.15.5.3 RS: PropertyDestructuringAssignmentEvaluation
// AssignmentProperty : IdentifierReference Initializer?
// ...
// 4. If |Initializer?| is present and _v_ is *undefined*, then
// a. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// i. Set _v_ to ? NamedEvaluation of |Initializer| with argument _P_.
// ...
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
} else {
assignedName = getAssignedNameOfIdentifier(emitContext, node.Name(), node.ObjectAssignmentInitializer)
}
objectAssignmentInitializer := finishTransformNamedEvaluation(emitContext, node.ObjectAssignmentInitializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdateShorthandPropertyAssignment(
node,
nil, /*modifiers*/
node.Name(),
nil, /*postfixToken*/
nil, /*typeNode*/
node.EqualsToken,
objectAssignmentInitializer,
)
}
func transformNamedEvaluationOfVariableDeclaration(emitContext *printer.EmitContext, node *ast.VariableDeclaration /*NamedEvaluation & VariableDeclaration*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 14.3.1.2 RS: Evaluation
// LexicalBinding : BindingIdentifier Initializer
// ...
// 3. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// a. Let _value_ be ? NamedEvaluation of |Initializer| with argument _bindingId_.
// ...
//
// 14.3.2.1 RS: Evaluation
// VariableDeclaration : BindingIdentifier Initializer
// ...
// 3. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// a. Let _value_ be ? NamedEvaluation of |Initializer| with argument _bindingId_.
// ...
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
} else {
assignedName = getAssignedNameOfIdentifier(emitContext, node.Name(), node.Initializer)
}
initializer := finishTransformNamedEvaluation(emitContext, node.Initializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdateVariableDeclaration(
node,
node.Name(),
nil, /*exclamationToken*/
nil, /*typeNode*/
initializer,
)
}
func transformNamedEvaluationOfParameterDeclaration(emitContext *printer.EmitContext, node *ast.ParameterDeclaration /*NamedEvaluation & ParameterDeclaration*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 8.6.3 RS: IteratorBindingInitialization
// SingleNameBinding : BindingIdentifier Initializer?
// ...
// 5. If |Initializer| is present and _v_ is *undefined*, then
// a. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// i. Set _v_ to ? NamedEvaluation of |Initializer| with argument _bindingId_.
// ...
//
// 14.3.3.3 RS: KeyedBindingInitialization
// SingleNameBinding : BindingIdentifier Initializer?
// ...
// 4. If |Initializer| is present and _v_ is *undefined*, then
// a. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// i. Set _v_ to ? NamedEvaluation of |Initializer| with argument _bindingId_.
// ...
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
} else {
assignedName = getAssignedNameOfIdentifier(emitContext, node.Name(), node.Initializer)
}
initializer := finishTransformNamedEvaluation(emitContext, node.Initializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdateParameterDeclaration(
node,
nil, /*modifiers*/
node.DotDotDotToken,
node.Name(),
nil, /*questionToken*/
nil, /*typeNode*/
initializer,
)
}
func transformNamedEvaluationOfBindingElement(emitContext *printer.EmitContext, node *ast.BindingElement /*NamedEvaluation & BindingElement*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 8.6.3 RS: IteratorBindingInitialization
// SingleNameBinding : BindingIdentifier Initializer?
// ...
// 5. If |Initializer| is present and _v_ is *undefined*, then
// a. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// i. Set _v_ to ? NamedEvaluation of |Initializer| with argument _bindingId_.
// ...
//
// 14.3.3.3 RS: KeyedBindingInitialization
// SingleNameBinding : BindingIdentifier Initializer?
// ...
// 4. If |Initializer| is present and _v_ is *undefined*, then
// a. If IsAnonymousFunctionDefinition(|Initializer|) is *true*, then
// i. Set _v_ to ? NamedEvaluation of |Initializer| with argument _bindingId_.
// ...
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
} else {
assignedName = getAssignedNameOfIdentifier(emitContext, node.Name(), node.Initializer)
}
initializer := finishTransformNamedEvaluation(emitContext, node.Initializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdateBindingElement(
node,
node.DotDotDotToken,
node.PropertyName,
node.Name(),
initializer,
)
}
func transformNamedEvaluationOfPropertyDeclaration(emitContext *printer.EmitContext, node *ast.PropertyDeclaration /*NamedEvaluation & PropertyDeclaration*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 10.2.1.3 RS: EvaluateBody
// Initializer : `=` AssignmentExpression
// ...
// 3. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true*, then
// a. Let _value_ be ? NamedEvaluation of |Initializer| with argument _functionObject_.[[ClassFieldInitializerName]].
// ...
factory := emitContext.Factory
assignedName, name := getAssignedNameOfPropertyName(emitContext, node.Name(), assignedNameText)
initializer := finishTransformNamedEvaluation(emitContext, node.Initializer, assignedName, ignoreEmptyStringLiteral)
return factory.UpdatePropertyDeclaration(
node,
node.Modifiers(),
name,
nil, /*postfixToken*/
nil, /*typeNode*/
initializer,
)
}
func transformNamedEvaluationOfAssignmentExpression(emitContext *printer.EmitContext, node *ast.BinaryExpression /*NamedEvaluation & BinaryExpression*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 13.15.2 RS: Evaluation
// AssignmentExpression : LeftHandSideExpression `=` AssignmentExpression
// 1. If |LeftHandSideExpression| is neither an |ObjectLiteral| nor an |ArrayLiteral|, then
// a. Let _lref_ be ? Evaluation of |LeftHandSideExpression|.
// b. If IsAnonymousFunctionDefinition(|AssignmentExpression|) and IsIdentifierRef of |LeftHandSideExpression| are both *true*, then
// i. Let _rval_ be ? NamedEvaluation of |AssignmentExpression| with argument _lref_.[[ReferencedName]].
// ...
//
// AssignmentExpression : LeftHandSideExpression `&&=` AssignmentExpression
// ...
// 5. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true* and IsIdentifierRef of |LeftHandSideExpression| is *true*, then
// a. Let _rval_ be ? NamedEvaluation of |AssignmentExpression| with argument _lref_.[[ReferencedName]].
// ...
//
// AssignmentExpression : LeftHandSideExpression `||=` AssignmentExpression
// ...
// 5. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true* and IsIdentifierRef of |LeftHandSideExpression| is *true*, then
// a. Let _rval_ be ? NamedEvaluation of |AssignmentExpression| with argument _lref_.[[ReferencedName]].
// ...
//
// AssignmentExpression : LeftHandSideExpression `??=` AssignmentExpression
// ...
// 4. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true* and IsIdentifierRef of |LeftHandSideExpression| is *true*, then
// a. Let _rval_ be ? NamedEvaluation of |AssignmentExpression| with argument _lref_.[[ReferencedName]].
// ...
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
} else {
assignedName = getAssignedNameOfIdentifier(emitContext, node.Left, node.Right)
}
right := finishTransformNamedEvaluation(emitContext, node.Right, assignedName, ignoreEmptyStringLiteral)
return factory.UpdateBinaryExpression(
node,
nil, /*modifiers*/
node.Left,
nil, /*typeNode*/
node.OperatorToken,
right,
)
}
func transformNamedEvaluationOfExportAssignment(emitContext *printer.EmitContext, node *ast.ExportAssignment /*NamedEvaluation & ExportAssignment*/, ignoreEmptyStringLiteral bool, assignedNameText string) *ast.Expression {
// 16.2.3.7 RS: Evaluation
// ExportDeclaration : `export` `default` AssignmentExpression `;`
// 1. If IsAnonymousFunctionDefinition(|AssignmentExpression|) is *true*, then
// a. Let _value_ be ? NamedEvaluation of |AssignmentExpression| with argument `"default"`.
// ...
// NOTE: Since emit for `export =` translates to `module.exports = ...`, the assigned name of the class or function
// is `""`.
factory := emitContext.Factory
var assignedName *ast.Expression
if len(assignedNameText) > 0 {
assignedName = factory.NewStringLiteral(assignedNameText, ast.TokenFlagsNone)
} else if node.IsExportEquals {
assignedName = factory.NewStringLiteral("", ast.TokenFlagsNone)
} else {
assignedName = factory.NewStringLiteral("default", ast.TokenFlagsNone)
}
expression := finishTransformNamedEvaluation(emitContext, node.Expression, assignedName, ignoreEmptyStringLiteral)
return factory.UpdateExportAssignment(
node,
nil, /*modifiers*/
node.IsExportEquals,
nil, /*typeNode*/
expression,
)
}
// Performs a shallow transformation of a `NamedEvaluation` node, such that a valid name will be assigned.
func transformNamedEvaluation(context *printer.EmitContext, node *ast.Node /*NamedEvaluation*/, ignoreEmptyStringLiteral bool, assignedName string) *ast.Expression {
switch node.Kind {
case ast.KindPropertyAssignment:
return transformNamedEvaluationOfPropertyAssignment(context, node.AsPropertyAssignment(), ignoreEmptyStringLiteral, assignedName)
case ast.KindShorthandPropertyAssignment:
return transformNamedEvaluationOfShorthandAssignmentProperty(context, node.AsShorthandPropertyAssignment(), ignoreEmptyStringLiteral, assignedName)
case ast.KindVariableDeclaration:
return transformNamedEvaluationOfVariableDeclaration(context, node.AsVariableDeclaration(), ignoreEmptyStringLiteral, assignedName)
case ast.KindParameter:
return transformNamedEvaluationOfParameterDeclaration(context, node.AsParameterDeclaration(), ignoreEmptyStringLiteral, assignedName)
case ast.KindBindingElement:
return transformNamedEvaluationOfBindingElement(context, node.AsBindingElement(), ignoreEmptyStringLiteral, assignedName)
case ast.KindPropertyDeclaration:
return transformNamedEvaluationOfPropertyDeclaration(context, node.AsPropertyDeclaration(), ignoreEmptyStringLiteral, assignedName)
case ast.KindBinaryExpression:
return transformNamedEvaluationOfAssignmentExpression(context, node.AsBinaryExpression(), ignoreEmptyStringLiteral, assignedName)
case ast.KindExportAssignment:
return transformNamedEvaluationOfExportAssignment(context, node.AsExportAssignment(), ignoreEmptyStringLiteral, assignedName)
default:
debug.Fail("Unhandled case in transformNamedEvaluation")
return node
}
}

View File

@@ -0,0 +1,49 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/transformers"
)
type nullishCoalescingTransformer struct {
transformers.Transformer
}
func (ch *nullishCoalescingTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsNullishCoalescing == 0 {
return node
}
switch node.Kind {
case ast.KindBinaryExpression:
return ch.visitBinaryExpression(node.AsBinaryExpression())
default:
return ch.Visitor().VisitEachChild(node)
}
}
func (ch *nullishCoalescingTransformer) visitBinaryExpression(node *ast.BinaryExpression) *ast.Node {
switch node.OperatorToken.Kind {
case ast.KindQuestionQuestionToken:
left := ch.Visitor().VisitNode(node.Left)
right := left
if !transformers.IsSimpleCopiableExpression(left) {
right = ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(right)
left = ch.Factory().NewAssignmentExpression(right, left)
}
return ch.Factory().NewConditionalExpression(
createNotNullCondition(ch.EmitContext(), left, right, false),
ch.Factory().NewToken(ast.KindQuestionToken),
right,
ch.Factory().NewToken(ast.KindColonToken),
ch.Visitor().VisitNode(node.Right),
)
default:
return ch.Visitor().VisitEachChild(node.AsNode())
}
}
func newNullishCoalescingTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &nullishCoalescingTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}

View File

@@ -0,0 +1,593 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
type objectRestSpreadTransformer struct {
transformers.Transformer
compilerOptions *core.CompilerOptions
inExportedVariableStatement bool
expressionResultIsUnused bool
parametersWithPrecedingObjectRestOrSpread map[*ast.Node]struct{}
}
func (ch *objectRestSpreadTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsESObjectRestOrSpread == 0 && ch.parametersWithPrecedingObjectRestOrSpread == nil {
return node
}
// Save the expressionResultIsUnused flag set by the parent for this node,
// then reset to false for children (the default). Specific cases below override as needed.
expressionResultIsUnused := ch.expressionResultIsUnused
ch.expressionResultIsUnused = false
defer func() { ch.expressionResultIsUnused = expressionResultIsUnused }()
switch node.Kind {
case ast.KindSourceFile:
return ch.visitSourceFile(node.AsSourceFile())
case ast.KindObjectLiteralExpression:
return ch.visitObjectLiteralExpression(node.AsObjectLiteralExpression())
case ast.KindBinaryExpression:
return ch.visitBinaryExpression(node.AsBinaryExpression(), expressionResultIsUnused)
case ast.KindExpressionStatement:
ch.expressionResultIsUnused = true
return ch.Visitor().VisitEachChild(node)
case ast.KindParenthesizedExpression:
ch.expressionResultIsUnused = expressionResultIsUnused
return ch.Visitor().VisitEachChild(node)
case ast.KindForOfStatement:
return ch.visitForOftatement(node.AsForInOrOfStatement())
case ast.KindVariableStatement:
return ch.visitVariableStatement(node.AsVariableStatement())
case ast.KindVariableDeclaration:
return ch.visitVariableDeclaration(node.AsVariableDeclaration())
case ast.KindCatchClause:
return ch.visitCatchClause(node.AsCatchClause())
case ast.KindParameter:
return ch.visitParameter(node.AsParameterDeclaration())
case ast.KindConstructor:
return ch.visitContructorDeclaration(node.AsConstructorDeclaration())
case ast.KindGetAccessor:
return ch.visitGetAccessorDeclaration(node.AsGetAccessorDeclaration())
case ast.KindSetAccessor:
return ch.visitSetAccessorDeclaration(node.AsSetAccessorDeclaration())
case ast.KindMethodDeclaration:
return ch.visitMethodDeclaration(node.AsMethodDeclaration())
case ast.KindFunctionDeclaration:
return ch.visitFunctionDeclaration(node.AsFunctionDeclaration())
case ast.KindArrowFunction:
return ch.visitArrowFunction(node.AsArrowFunction())
case ast.KindFunctionExpression:
return ch.visitFunctionExpression(node.AsFunctionExpression())
default:
return ch.Visitor().VisitEachChild(node)
}
}
func (ch *objectRestSpreadTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
visited := ch.Visitor().VisitEachChild(node.AsNode())
ch.EmitContext().AddEmitHelper(visited.AsNode(), ch.EmitContext().ReadEmitHelpers()...)
return visited
}
func (ch *objectRestSpreadTransformer) visitParameter(node *ast.ParameterDeclaration) *ast.Node {
if ch.parametersWithPrecedingObjectRestOrSpread != nil {
if _, ok := ch.parametersWithPrecedingObjectRestOrSpread[node.AsNode()]; ok {
name := node.Name()
if ast.IsBindingPattern(name) {
name = ch.Factory().NewGeneratedNameForNode(node.AsNode())
}
return ch.Factory().UpdateParameterDeclaration(
node,
nil,
node.DotDotDotToken,
name,
nil,
nil,
nil,
)
}
}
if node.SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 {
// Binding patterns are converted into a generated name and are
// evaluated inside the function body.
return ch.Factory().UpdateParameterDeclaration(
node,
nil,
node.DotDotDotToken,
ch.Factory().NewGeneratedNameForNode(node.AsNode()),
nil,
nil,
ch.Visitor().VisitNode(node.Initializer),
)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *objectRestSpreadTransformer) collectParametersWithPrecedingObjectRestOrSpread(node *ast.Node) map[*ast.Node]struct{} {
var result map[*ast.Node]struct{}
for _, parameter := range node.Parameters() {
if result != nil {
result[parameter] = struct{}{}
} else if parameter.SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 {
result = make(map[*ast.Node]struct{})
}
}
return result
}
type oldParamScope map[*ast.Node]struct{}
func (ch *objectRestSpreadTransformer) enterParameterListContext(node *ast.Node) oldParamScope {
old := ch.parametersWithPrecedingObjectRestOrSpread
ch.parametersWithPrecedingObjectRestOrSpread = ch.collectParametersWithPrecedingObjectRestOrSpread(node)
return oldParamScope(old)
}
func (ch *objectRestSpreadTransformer) exitParameterListContext(scope oldParamScope) {
ch.parametersWithPrecedingObjectRestOrSpread = map[*ast.Node]struct{}(scope)
}
func (ch *objectRestSpreadTransformer) visitContructorDeclaration(node *ast.ConstructorDeclaration) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateConstructorDeclaration(
node,
node.Modifiers(),
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) visitGetAccessorDeclaration(node *ast.GetAccessorDeclaration) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateGetAccessorDeclaration(
node,
node.Modifiers(),
ch.Visitor().VisitNode(node.Name()),
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) visitSetAccessorDeclaration(node *ast.SetAccessorDeclaration) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateSetAccessorDeclaration(
node,
node.Modifiers(),
ch.Visitor().VisitNode(node.Name()),
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) visitMethodDeclaration(node *ast.MethodDeclaration) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateMethodDeclaration(
node,
node.Modifiers(),
node.AsteriskToken,
ch.Visitor().VisitNode(node.Name()),
node.PostfixToken,
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) visitFunctionDeclaration(node *ast.FunctionDeclaration) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateFunctionDeclaration(
node,
node.Modifiers(),
node.AsteriskToken,
ch.Visitor().VisitNode(node.Name()),
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) visitArrowFunction(node *ast.ArrowFunction) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateArrowFunction(
node,
node.Modifiers(),
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
node.EqualsGreaterThanToken,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) visitFunctionExpression(node *ast.FunctionExpression) *ast.Node {
old := ch.enterParameterListContext(node.AsNode())
defer ch.exitParameterListContext(old)
return ch.Factory().UpdateFunctionExpression(
node,
node.Modifiers(),
node.AsteriskToken,
ch.Visitor().VisitNode(node.Name()),
nil,
ch.Visitor().VisitNodes(node.Parameters),
nil,
nil,
ch.transformFunctionBody(node.AsNode()),
)
}
func (ch *objectRestSpreadTransformer) transformFunctionBody(node *ast.Node) *ast.Node {
// EmitContext().VisitFunctionBody is not used here because this transformer needs to inject
// object rest assignments between visiting the body and merging the variable environment.
ch.EmitContext().StartVariableEnvironment()
body := ch.Visitor().VisitNode(node.Body())
extras := ch.EmitContext().EndVariableEnvironment()
ch.EmitContext().StartVariableEnvironment()
newStatements := ch.collectObjectRestAssignments(node)
extras = ch.EmitContext().EndAndMergeVariableEnvironment(extras)
if len(newStatements) == 0 && len(extras) == 0 {
return body
}
if body == nil {
body = ch.Factory().NewBlock(ch.Factory().NewNodeList([]*ast.Node{}), true)
}
var prefix []*ast.Node
var suffix []*ast.Node
if ast.IsBlock(body) {
custom := false
for i, statement := range body.Statements() {
if !custom && ast.IsPrologueDirective(statement) {
prefix = append(prefix, statement)
} else if ch.EmitContext().EmitFlags(statement)&printer.EFCustomPrologue != 0 {
custom = true
prefix = append(prefix, statement)
} else {
suffix = body.Statements()[i:]
break
}
}
} else {
ret := ch.Factory().NewReturnStatement(body)
ret.Loc = body.Loc
list := ch.Factory().NewNodeList([]*ast.Node{})
list.Loc = body.Loc
body = ch.Factory().NewBlock(list, true)
suffix = append(suffix, ret)
}
newStatementList := ch.Factory().NewNodeList(append(append(append(prefix, extras...), newStatements...), suffix...))
newStatementList.Loc = body.StatementList().Loc
return ch.Factory().UpdateBlock(body.AsBlock(), newStatementList, body.AsBlock().MultiLine)
}
func (ch *objectRestSpreadTransformer) collectObjectRestAssignments(node *ast.Node) []*ast.Node {
containsPrecedingObjectRestOrSpread := false
var results []*ast.Node
for _, parameter := range node.Parameters() {
if containsPrecedingObjectRestOrSpread {
if ast.IsBindingPattern(parameter.Name()) {
// In cases where a binding pattern is simply '[]' or '{}',
// we usually don't want to emit a var declaration; however, in the presence
// of an initializer, we must emit that expression to preserve side effects.
if len(parameter.Name().Elements()) > 0 {
declarations := transformers.FlattenDestructuringBinding(
&ch.Transformer,
parameter, ch.Factory().NewGeneratedNameForNode(parameter),
transformers.FlattenLevelAll, false, false,
)
if declarations != nil {
declarationList := ch.Factory().NewVariableDeclarationList(ch.Factory().NewNodeList([]*ast.Node{}), ast.NodeFlagsNone)
decls := []*ast.Node{declarations}
if declarations.Kind == ast.KindSyntaxList {
decls = declarations.AsSyntaxList().Children
}
declarationList.AsVariableDeclarationList().Declarations.Nodes = append(declarationList.AsVariableDeclarationList().Declarations.Nodes, decls...)
statement := ch.Factory().NewVariableStatement(nil, declarationList)
ch.EmitContext().AddEmitFlags(statement, printer.EFCustomPrologue)
results = append(results, statement)
}
} else if parameter.Initializer() != nil {
name := ch.Factory().NewGeneratedNameForNode(parameter)
initializer := ch.Visitor().VisitNode(parameter.Initializer())
assignment := ch.Factory().NewAssignmentExpression(name, initializer)
statement := ch.Factory().NewExpressionStatement(assignment)
ch.EmitContext().AddEmitFlags(statement, printer.EFCustomPrologue)
results = append(results, statement)
}
} else if parameter.Initializer() != nil {
// Converts a parameter initializer into a function body statement, i.e.:
//
// function f(x = 1) { }
//
// becomes
//
// function f(x) {
// if (typeof x === "undefined") { x = 1; }
// }
name := parameter.Name().Clone(ch.Factory())
name.Loc = parameter.Name().Loc
ch.EmitContext().AddEmitFlags(name, printer.EFNoSourceMap)
initializer := ch.Visitor().VisitNode(parameter.Initializer())
ch.EmitContext().AddEmitFlags(initializer, printer.EFNoSourceMap|printer.EFNoComments)
assignment := ch.Factory().NewAssignmentExpression(name, initializer)
assignment.Loc = parameter.Loc
ch.EmitContext().AddEmitFlags(assignment, printer.EFNoComments)
block := ch.Factory().NewBlock(ch.Factory().NewNodeList([]*ast.Node{ch.Factory().NewExpressionStatement(assignment)}), false)
block.Loc = parameter.Loc
ch.EmitContext().AddEmitFlags(block, printer.EFSingleLine|printer.EFNoTrailingSourceMap|printer.EFNoTokenSourceMaps|printer.EFNoComments)
typeCheck := ch.Factory().NewTypeCheck(name.Clone(ch.Factory()), "undefined")
statement := ch.Factory().NewIfStatement(typeCheck, block, nil)
statement.Loc = parameter.Loc
ch.EmitContext().AddEmitFlags(statement, printer.EFNoTokenSourceMaps|printer.EFNoTrailingSourceMap|printer.EFCustomPrologue|printer.EFNoComments|printer.EFStartOnNewLine)
results = append(results, statement)
}
} else if parameter.SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 {
containsPrecedingObjectRestOrSpread = true
declarations := transformers.FlattenDestructuringBinding(
&ch.Transformer,
parameter, ch.Factory().NewGeneratedNameForNode(parameter),
transformers.FlattenLevelObjectRest, false, true,
)
if declarations != nil {
declarationList := ch.Factory().NewVariableDeclarationList(ch.Factory().NewNodeList([]*ast.Node{}), ast.NodeFlagsNone)
decls := []*ast.Node{declarations}
if declarations.Kind == ast.KindSyntaxList {
decls = declarations.AsSyntaxList().Children
}
declarationList.AsVariableDeclarationList().Declarations.Nodes = append(declarationList.AsVariableDeclarationList().Declarations.Nodes, decls...)
statement := ch.Factory().NewVariableStatement(nil, declarationList)
ch.EmitContext().AddEmitFlags(statement, printer.EFCustomPrologue)
results = append(results, statement)
}
}
}
return results
}
func (ch *objectRestSpreadTransformer) visitCatchClause(node *ast.CatchClause) *ast.Node {
if node.VariableDeclaration != nil && ast.IsBindingPattern(node.VariableDeclaration.Name()) && node.VariableDeclaration.Name().SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 {
name := ch.Factory().NewGeneratedNameForNode(node.VariableDeclaration.Name())
updatedDecl := ch.Factory().UpdateVariableDeclaration(node.VariableDeclaration.AsVariableDeclaration(), node.VariableDeclaration.Name(), nil, nil, name)
visitedBindings := transformers.FlattenDestructuringBinding(
&ch.Transformer,
updatedDecl, nil,
transformers.FlattenLevelObjectRest, false, false,
)
block := ch.Visitor().VisitNode(node.Block)
if visitedBindings != nil {
var decls []*ast.Node
if visitedBindings.Kind == ast.KindSyntaxList {
decls = visitedBindings.AsSyntaxList().Children
} else {
decls = []*ast.Node{visitedBindings}
}
newStatement := ch.Factory().NewVariableStatement(nil, ch.Factory().NewVariableDeclarationList(ch.Factory().NewNodeList(decls), ast.NodeFlagsNone))
statements := []*ast.Node{newStatement}
statements = append(statements, block.Statements()...)
statementList := ch.Factory().NewNodeList(statements)
statementList.Loc = block.StatementList().Loc
block = ch.Factory().UpdateBlock(block.AsBlock(), statementList, block.AsBlock().MultiLine)
}
return ch.Factory().UpdateCatchClause(
node,
ch.Factory().UpdateVariableDeclaration(node.VariableDeclaration.AsVariableDeclaration(), name, nil, nil, nil),
block,
)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *objectRestSpreadTransformer) visitVariableStatement(node *ast.VariableStatement) *ast.Node {
if ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport) {
oldInExportedVariableStatement := ch.inExportedVariableStatement
ch.inExportedVariableStatement = true
result := ch.Visitor().VisitEachChild(node.AsNode())
ch.inExportedVariableStatement = oldInExportedVariableStatement
return result
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *objectRestSpreadTransformer) visitVariableDeclaration(node *ast.VariableDeclaration) *ast.Node {
if ch.inExportedVariableStatement {
ch.inExportedVariableStatement = false
result := ch.visitVariableDeclarationWorker(node, true)
ch.inExportedVariableStatement = true
return result
}
return ch.visitVariableDeclarationWorker(node, false)
}
func (ch *objectRestSpreadTransformer) visitVariableDeclarationWorker(node *ast.VariableDeclaration, exported bool) *ast.Node {
// If we are here it is because the name contains a binding pattern with a rest somewhere in it.
if ast.IsBindingPattern(node.Name()) && node.SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 {
return transformers.FlattenDestructuringBinding(
&ch.Transformer,
node.AsNode(), nil,
transformers.FlattenLevelObjectRest, exported, false,
)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *objectRestSpreadTransformer) visitForOftatement(node *ast.ForInOrOfStatement) *ast.Node {
if node.Initializer.SubtreeFacts()&ast.SubtreeContainsObjectRestOrSpread != 0 || (ast.IsAssignmentPattern(node.Initializer) && ast.ContainsObjectRestOrSpread(node.Initializer)) {
initializerWithoutParens := ast.SkipParentheses(node.Initializer)
if ast.IsVariableDeclarationList(initializerWithoutParens) || ast.IsAssignmentPattern(initializerWithoutParens) {
var bodyLocation core.TextRange
var statementsLocation core.TextRange
temp := ch.Factory().NewTempVariable()
res := ch.Visitor().VisitNode(ch.Factory().CreateForOfBindingStatement(initializerWithoutParens, temp))
statements := make([]*ast.Node, 0, 1)
if res != nil {
statements = append(statements, res)
}
if ast.IsBlock(node.Statement) {
for _, statement := range node.Statement.Statements() {
visited := ch.Visitor().VisitEachChild(statement)
if visited != nil {
statements = append(statements, visited)
}
}
bodyLocation = node.Statement.Loc
statementsLocation = node.Statement.StatementList().Loc
} else if node.Statement != nil {
statements = append(statements, ch.Visitor().VisitEachChild(node.Statement))
bodyLocation = node.Statement.Loc
statementsLocation = node.Statement.Loc
}
list := ch.Factory().NewVariableDeclarationList(
ch.Factory().NewNodeList([]*ast.Node{ch.Factory().NewVariableDeclaration(temp, nil, nil, nil)}),
ast.NodeFlagsLet,
)
list.Loc = node.Initializer.Loc
expr := ch.Visitor().VisitEachChild(node.Expression)
statementsList := ch.Factory().NewNodeList(statements)
statementsList.Loc = statementsLocation
block := ch.Factory().NewBlock(statementsList, true)
block.Loc = bodyLocation
return ch.Factory().UpdateForInOrOfStatement(
node,
node.AwaitModifier,
list,
expr,
block,
)
}
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *objectRestSpreadTransformer) visitBinaryExpression(node *ast.BinaryExpression, expressionResultIsUnused bool) *ast.Node {
if ast.IsDestructuringAssignment(node.AsNode()) && ast.ContainsObjectRestOrSpread(node.Left) {
return transformers.FlattenDestructuringAssignment(
&ch.Transformer,
node.AsNode(), !expressionResultIsUnused,
transformers.FlattenLevelObjectRest, nil,
)
}
if node.OperatorToken.Kind == ast.KindCommaToken {
ch.expressionResultIsUnused = true
left := ch.Visitor().VisitNode(node.Left)
ch.expressionResultIsUnused = expressionResultIsUnused
right := ch.Visitor().VisitNode(node.Right)
return ch.Factory().UpdateBinaryExpression(node, nil, left, nil, node.OperatorToken, right)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *objectRestSpreadTransformer) visitObjectLiteralExpression(node *ast.ObjectLiteralExpression) *ast.Node {
if (node.SubtreeFacts() & ast.SubtreeContainsObjectRestOrSpread) == 0 {
return ch.Visitor().VisitEachChild(node.AsNode())
}
// spread elements emit like so:
// non-spread elements are chunked together into object literals, and then all are passed to __assign:
// { a, ...o, b } => __assign(__assign({a}, o), {b});
// If the first element is a spread element, then the first argument to __assign is {}:
// { ...o, a, b, ...o2 } => __assign(__assign(__assign({}, o), {a, b}), o2)
//
// We cannot call __assign with more than two elements, since any element could cause side effects. For
// example:
// var k = { a: 1, b: 2 };
// var o = { a: 3, ...k, b: k.a++ };
// // expected: { a: 1, b: 1 }
// If we translate the above to `__assign({ a: 3 }, k, { b: k.a++ })`, the `k.a++` will evaluate before
// `k` is spread and we end up with `{ a: 2, b: 1 }`.
//
// This also occurs for spread elements, not just property assignments:
// var k = { a: 1, get b() { l = { z: 9 }; return 2; } };
// var l = { c: 3 };
// var o = { ...k, ...l };
// // expected: { a: 1, b: 2, z: 9 }
// If we translate the above to `__assign({}, k, l)`, the `l` will evaluate before `k` is spread and we
// end up with `{ a: 1, b: 2, c: 3 }`
objects := ch.chunkObjectLiteralElements(node.Properties)
if len(objects) > 0 && objects[0].Kind != ast.KindObjectLiteralExpression {
objects = append([]*ast.Node{ch.Factory().NewObjectLiteralExpression(ch.Factory().NewNodeList(nil), false)}, objects...)
}
expression := objects[0]
if len(objects) > 1 {
for i, obj := range objects {
if i == 0 {
continue
}
expression = ch.Factory().NewAssignHelper([]*ast.Node{expression, obj}, ch.compilerOptions.GetEmitScriptTarget())
}
return expression
}
return ch.Factory().NewAssignHelper(objects, ch.compilerOptions.GetEmitScriptTarget())
}
func (ch *objectRestSpreadTransformer) chunkObjectLiteralElements(list *ast.NodeList) []*ast.Node {
if list == nil || len(list.Nodes) == 0 {
return nil
}
elements := list.Nodes
var chunkObject []*ast.Node
objects := make([]*ast.Node, 0, 1)
for _, e := range elements {
if e.Kind == ast.KindSpreadAssignment {
if len(chunkObject) > 0 {
objects = append(objects, ch.Factory().NewObjectLiteralExpression(ch.Factory().NewNodeList(chunkObject), false))
chunkObject = nil
}
target := e.Expression()
objects = append(objects, ch.Visitor().VisitNode(target))
} else {
var elem *ast.Node
if e.Kind == ast.KindPropertyAssignment {
elem = ch.Factory().NewPropertyAssignment(nil, e.Name(), nil, nil, ch.Visitor().VisitNode(e.Initializer()))
} else {
elem = ch.Visitor().VisitNode(e)
}
chunkObject = append(chunkObject, elem)
}
}
if len(chunkObject) > 0 {
objects = append(objects, ch.Factory().NewObjectLiteralExpression(ch.Factory().NewNodeList(chunkObject), false))
}
return objects
}
func newObjectRestSpreadTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &objectRestSpreadTransformer{compilerOptions: opts.CompilerOptions}
return tx.NewTransformer(tx.visit, opts.Context)
}

View File

@@ -0,0 +1,37 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/transformers"
)
type optionalCatchTransformer struct {
transformers.Transformer
}
func (ch *optionalCatchTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsMissingCatchClauseVariable == 0 {
return node
}
switch node.Kind {
case ast.KindCatchClause:
return ch.visitCatchClause(node.AsCatchClause())
default:
return ch.Visitor().VisitEachChild(node)
}
}
func (ch *optionalCatchTransformer) visitCatchClause(node *ast.CatchClause) *ast.Node {
if node.VariableDeclaration == nil {
return ch.Factory().NewCatchClause(
ch.Factory().NewVariableDeclaration(ch.Factory().NewTempVariable(), nil, nil, nil),
ch.Visitor().Visit(node.Block),
)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func newOptionalCatchTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &optionalCatchTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}

View File

@@ -0,0 +1,240 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/debug"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
type optionalChainTransformer struct {
transformers.Transformer
}
func (ch *optionalChainTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsOptionalChaining == 0 {
return node
}
switch node.Kind {
case ast.KindCallExpression:
return ch.visitCallExpression(node.AsCallExpression(), false)
case ast.KindPropertyAccessExpression,
ast.KindElementAccessExpression:
if node.Flags&ast.NodeFlagsOptionalChain != 0 {
return ch.visitOptionalExpression(node, false, false)
}
return ch.Visitor().VisitEachChild(node)
case ast.KindDeleteExpression:
return ch.visitDeleteExpression(node.AsDeleteExpression())
default:
return ch.Visitor().VisitEachChild(node)
}
}
func (ch *optionalChainTransformer) visitCallExpression(node *ast.CallExpression, captureThisArg bool) *ast.Node {
if node.Flags&ast.NodeFlagsOptionalChain != 0 {
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
return ch.visitOptionalExpression(node.AsNode(), captureThisArg, false)
}
if ast.IsParenthesizedExpression(node.Expression) {
unwrapped := ast.SkipParentheses(node.Expression)
if unwrapped.Flags&ast.NodeFlagsOptionalChain != 0 {
// capture thisArg for calls of parenthesized optional chains like `(foo?.bar)()`
expression := ch.visitParenthesizedExpression(node.Expression.AsParenthesizedExpression(), true, false)
args := ch.Visitor().VisitNodes(node.Arguments)
if ast.IsSyntheticReferenceExpression(expression) {
res := ch.Factory().NewFunctionCallCall(expression.AsSyntheticReferenceExpression().Expression, expression.AsSyntheticReferenceExpression().ThisArg, args.Nodes)
res.Loc = node.Loc
ch.EmitContext().SetOriginal(res, node.AsNode())
return res
}
return ch.Factory().UpdateCallExpression(node, expression, nil /*questionDotToken*/, nil /*typeArguments*/, args, node.Flags)
}
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *optionalChainTransformer) visitParenthesizedExpression(node *ast.ParenthesizedExpression, captureThisArg bool, isDelete bool) *ast.Node {
expr := ch.visitNonOptionalExpression(node.Expression, captureThisArg, isDelete)
if ast.IsSyntheticReferenceExpression(expr) {
// `(a.b)` -> { expression `((_a = a).b)`, thisArg: `_a` }
// `(a[b])` -> { expression `((_a = a)[b])`, thisArg: `_a` }
synth := expr.AsSyntheticReferenceExpression()
res := ch.Factory().NewSyntheticReferenceExpression(ch.Factory().UpdateParenthesizedExpression(node, synth.Expression), synth.ThisArg)
ch.EmitContext().SetOriginal(res, node.AsNode())
return res
}
return ch.Factory().UpdateParenthesizedExpression(node, expr)
}
func (ch *optionalChainTransformer) visitPropertyOrElementAccessExpression(node *ast.Expression, captureThisArg bool, isDelete bool) *ast.Expression {
if node.Flags&ast.NodeFlagsOptionalChain != 0 {
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
return ch.visitOptionalExpression(node.AsNode(), captureThisArg, isDelete)
}
expression := ch.Visitor().VisitNode(node.Expression())
debug.Assert(expression == nil || !ast.IsSyntheticReferenceExpression(expression))
var thisArg *ast.Expression
if captureThisArg {
if !transformers.IsSimpleCopiableExpression(expression) {
thisArg = ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(thisArg)
expression = ch.Factory().NewAssignmentExpression(thisArg, expression)
} else {
thisArg = expression
}
}
if node.Kind == ast.KindPropertyAccessExpression {
p := node.AsPropertyAccessExpression()
expression = ch.Factory().UpdatePropertyAccessExpression(p, expression, nil /*questionDotToken*/, ch.Visitor().VisitNode(p.Name()), p.Flags)
} else {
p := node.AsElementAccessExpression()
expression = ch.Factory().UpdateElementAccessExpression(p, expression, nil, ch.Visitor().VisitNode(p.AsElementAccessExpression().ArgumentExpression), p.Flags)
}
if thisArg != nil {
res := ch.Factory().NewSyntheticReferenceExpression(expression, thisArg)
ch.EmitContext().SetOriginal(res, node.AsNode())
return res
}
return expression
}
func (ch *optionalChainTransformer) visitDeleteExpression(node *ast.DeleteExpression) *ast.Node {
unwrapped := ast.SkipParentheses(node.Expression)
if unwrapped.Flags&ast.NodeFlagsOptionalChain != 0 {
return ch.visitNonOptionalExpression(node.Expression, false, true)
}
return ch.Visitor().VisitEachChild(node.AsNode())
}
func (ch *optionalChainTransformer) visitNonOptionalExpression(node *ast.Expression, captureThisArg bool, isDelete bool) *ast.Expression {
switch node.Kind {
case ast.KindParenthesizedExpression:
return ch.visitParenthesizedExpression(node.AsParenthesizedExpression(), captureThisArg, isDelete)
case ast.KindElementAccessExpression, ast.KindPropertyAccessExpression:
return ch.visitPropertyOrElementAccessExpression(node, captureThisArg, isDelete)
case ast.KindCallExpression:
return ch.visitCallExpression(node.AsCallExpression(), captureThisArg)
default:
return ch.Visitor().VisitNode(node.AsNode())
}
}
type flattenResult struct {
expression *ast.Expression
chain []*ast.Node
}
func isNonNullChain(node *ast.Node) bool {
return ast.IsNonNullExpression(node) && node.Flags&ast.NodeFlagsOptionalChain != 0
}
func flattenChain(chain *ast.Node) flattenResult {
debug.Assert(!isNonNullChain(chain))
links := []*ast.Node{chain}
for !ast.IsTaggedTemplateExpression(chain) && chain.QuestionDotToken() == nil {
chain = ast.SkipPartiallyEmittedExpressions(chain.Expression())
debug.Assert(!isNonNullChain(chain))
links = append([]*ast.Node{chain}, links...)
}
return flattenResult{chain.Expression(), links}
}
func isCallChain(node *ast.Node) bool {
return ast.IsCallExpression(node) && node.Flags&ast.NodeFlagsOptionalChain != 0
}
func (ch *optionalChainTransformer) visitOptionalExpression(node *ast.Node, captureThisArg bool, isDelete bool) *ast.Node {
r := flattenChain(node)
expression := r.expression
chain := r.chain
left := ch.visitNonOptionalExpression(ast.SkipPartiallyEmittedExpressions(expression), isCallChain(chain[0]), false)
var leftThisArg *ast.Expression
capturedLeft := left
if ast.IsSyntheticReferenceExpression(left) {
leftThisArg = left.AsSyntheticReferenceExpression().ThisArg
capturedLeft = left.AsSyntheticReferenceExpression().Expression
}
leftExpression := ch.Factory().RestoreOuterExpressions(expression, capturedLeft, ast.OEKPartiallyEmittedExpressions)
if !transformers.IsSimpleCopiableExpression(capturedLeft) {
capturedLeft = ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(capturedLeft)
leftExpression = ch.Factory().NewAssignmentExpression(capturedLeft, leftExpression)
}
rightExpression := capturedLeft
var thisArg *ast.Expression
for i, segment := range chain {
switch segment.Kind {
case ast.KindElementAccessExpression, ast.KindPropertyAccessExpression:
if i == len(chain)-1 && captureThisArg {
if !transformers.IsSimpleCopiableExpression(rightExpression) {
thisArg = ch.Factory().NewTempVariable()
ch.EmitContext().AddVariableDeclaration(thisArg)
rightExpression = ch.Factory().NewAssignmentExpression(thisArg, rightExpression)
} else {
thisArg = rightExpression
}
}
if segment.Kind == ast.KindElementAccessExpression {
rightExpression = ch.Factory().NewElementAccessExpression(rightExpression, nil, ch.Visitor().VisitNode(segment.AsElementAccessExpression().ArgumentExpression), ast.NodeFlagsNone)
} else {
rightExpression = ch.Factory().NewPropertyAccessExpression(rightExpression, nil, ch.Visitor().VisitNode(segment.AsPropertyAccessExpression().Name()), ast.NodeFlagsNone)
}
case ast.KindCallExpression:
if i == 0 && leftThisArg != nil {
if !ch.EmitContext().HasAutoGenerateInfo(leftThisArg) {
leftThisArg = leftThisArg.Clone(ch.Factory())
ch.EmitContext().AddEmitFlags(leftThisArg, printer.EFNoComments)
}
callThisArg := leftThisArg
if leftThisArg.Kind == ast.KindSuperKeyword {
callThisArg = ch.Factory().NewThisExpression()
}
rightExpression = ch.Factory().NewFunctionCallCall(rightExpression, callThisArg, ch.Visitor().VisitNodes(segment.ArgumentList()).Nodes)
} else {
rightExpression = ch.Factory().NewCallExpression(
rightExpression,
nil,
nil,
ch.Visitor().VisitNodes(segment.ArgumentList()),
ast.NodeFlagsNone,
)
}
}
ch.EmitContext().SetOriginal(rightExpression, segment)
}
var target *ast.Node
if isDelete {
target = ch.Factory().NewConditionalExpression(
createNotNullCondition(ch.EmitContext(), leftExpression, capturedLeft, true),
ch.Factory().NewToken(ast.KindQuestionToken),
ch.Factory().NewTrueExpression(),
ch.Factory().NewToken(ast.KindColonToken),
ch.Factory().NewDeleteExpression(rightExpression),
)
} else {
target = ch.Factory().NewConditionalExpression(
createNotNullCondition(ch.EmitContext(), leftExpression, capturedLeft, true),
ch.Factory().NewToken(ast.KindQuestionToken),
ch.Factory().NewVoidZeroExpression(),
ch.Factory().NewToken(ast.KindColonToken),
rightExpression,
)
}
target.Loc = node.Loc
if thisArg != nil {
target = ch.Factory().NewSyntheticReferenceExpression(target, thisArg)
}
ch.EmitContext().SetOriginal(target, node.AsNode())
return target
}
func newOptionalChainTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &optionalChainTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}

View File

@@ -0,0 +1,174 @@
package estransforms
import (
"strings"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/scanner"
"github.com/microsoft/typescript-go/internal/transformers"
)
var newlineNormalizer = strings.NewReplacer("\r\n", "\n", "\r", "\n")
type taggedTemplateTransformer struct {
transformers.Transformer
currentSourceFile *ast.SourceFile
taggedTemplateStringDeclarations []*ast.Node
}
func newTaggedTemplateLiftRestrictionTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &taggedTemplateTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}
func (tx *taggedTemplateTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsInvalidTemplateEscape == 0 {
return node
}
switch node.Kind {
case ast.KindSourceFile:
return tx.visitSourceFile(node.AsSourceFile())
case ast.KindTaggedTemplateExpression:
return tx.visitTaggedTemplateExpression(node.AsTaggedTemplateExpression())
default:
return tx.Visitor().VisitEachChild(node)
}
}
func (tx *taggedTemplateTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
tx.currentSourceFile = node
tx.taggedTemplateStringDeclarations = nil
visited := tx.Visitor().VisitEachChild(node.AsNode())
if len(tx.taggedTemplateStringDeclarations) > 0 {
visitedSourceFile := visited.AsSourceFile()
statements := append(
visitedSourceFile.Statements.Nodes[:len(visitedSourceFile.Statements.Nodes):len(visitedSourceFile.Statements.Nodes)],
tx.Factory().NewVariableStatement(
nil, /*modifiers*/
tx.Factory().NewVariableDeclarationList(
tx.Factory().NewNodeList(tx.taggedTemplateStringDeclarations),
ast.NodeFlagsNone,
),
),
)
stmtList := tx.Factory().NewNodeList(statements)
stmtList.Loc = node.Statements.Loc
visited = tx.Factory().UpdateSourceFile(visitedSourceFile, stmtList, visitedSourceFile.EndOfFileToken)
}
tx.EmitContext().AddEmitHelper(visited, tx.EmitContext().ReadEmitHelpers()...)
return visited
}
func (tx *taggedTemplateTransformer) visitTaggedTemplateExpression(node *ast.TaggedTemplateExpression) *ast.Node {
return tx.processTaggedTemplateExpression(node)
}
func (tx *taggedTemplateTransformer) processTaggedTemplateExpression(node *ast.TaggedTemplateExpression) *ast.Node {
tag := tx.Visitor().VisitNode(node.Tag)
template := node.Template
if !hasInvalidEscape(template) {
return tx.Visitor().VisitEachChild(node.AsNode())
}
f := tx.Factory()
// Build up the template arguments and the raw and cooked strings for the template.
templateArguments := []*ast.Node{nil} // placeholder for the template object
var cookedStrings []*ast.Node
var rawStrings []*ast.Node
if ast.IsNoSubstitutionTemplateLiteral(template) {
cookedStrings = append(cookedStrings, createTemplateCooked(f, template.TemplateLiteralLikeData()))
rawStrings = append(rawStrings, getRawLiteral(f, template))
} else {
te := template.AsTemplateExpression()
cookedStrings = append(cookedStrings, createTemplateCooked(f, te.Head.TemplateLiteralLikeData()))
rawStrings = append(rawStrings, getRawLiteral(f, te.Head))
for _, span := range te.TemplateSpans.Nodes {
ts := span.AsTemplateSpan()
cookedStrings = append(cookedStrings, createTemplateCooked(f, ts.Literal.TemplateLiteralLikeData()))
rawStrings = append(rawStrings, getRawLiteral(f, ts.Literal))
templateArguments = append(templateArguments, tx.Visitor().VisitNode(ts.Expression))
}
}
helperCall := f.NewTemplateObjectHelper(
f.NewArrayLiteralExpression(f.NewNodeList(cookedStrings), false),
f.NewArrayLiteralExpression(f.NewNodeList(rawStrings), false),
)
// Create a variable to cache the template object if we're in a module.
// Do not do this in the global scope, as any variable we currently generate could conflict with
// variables from outside of the current compilation. In the future, we can revisit this behavior.
if ast.IsExternalModule(tx.currentSourceFile) {
tempVar := f.NewUniqueName("templateObject")
tx.taggedTemplateStringDeclarations = append(
tx.taggedTemplateStringDeclarations,
f.NewVariableDeclaration(tempVar, nil, nil, nil),
)
templateArguments[0] = f.NewLogicalORExpression(
tempVar,
f.NewAssignmentExpression(tempVar, helperCall),
)
} else {
templateArguments[0] = helperCall
}
call := f.NewCallExpression(tag, nil /*questionDotToken*/, nil /*typeArguments*/, f.NewNodeList(templateArguments), ast.NodeFlagsNone)
call.Loc = node.Loc
return call
}
func createTemplateCooked(f *printer.NodeFactory, template *ast.TemplateLiteralLikeNodeBase) *ast.Node {
if template.TemplateFlags&ast.TokenFlagsIsInvalid != 0 {
return f.NewVoidZeroExpression()
}
return f.NewStringLiteral(template.Text, ast.TokenFlagsNone)
}
func getRawLiteral(f *printer.NodeFactory, node *ast.Node) *ast.Node {
text := node.TemplateLiteralLikeData().RawText
if text == "" {
text = scanner.GetSourceTextOfNodeFromSourceFile(ast.GetSourceFileOfNode(node), node, false /*includeTrivia*/)
// text contains the original source, it will also contain quotes ("`"), dollar signs and braces ("${" and "}"),
// thus we need to remove those characters.
// First template piece starts with "`", others with "}"
// Last template piece ends with "`", others with "${"
isLast := node.Kind == ast.KindNoSubstitutionTemplateLiteral || node.Kind == ast.KindTemplateTail
endLen := 2
if isLast {
endLen = 1
}
text = text[1 : len(text)-endLen]
}
// Newline normalization:
// ES6 Spec 11.8.6.1 - Static Semantics of TV's and TRV's
// <CR><LF> and <CR> LineTerminatorSequences are normalized to <LF> for both TV and TRV.
text = newlineNormalizer.Replace(text)
result := f.NewStringLiteral(text, ast.TokenFlagsNone)
result.Loc = node.Loc
return result
}
func hasInvalidEscape(template *ast.Node) bool {
if ast.IsNoSubstitutionTemplateLiteral(template) {
return template.TemplateLiteralLikeData().TemplateFlags&ast.TokenFlagsContainsInvalidEscape != 0
}
te := template.AsTemplateExpression()
if te.Head.TemplateLiteralLikeData().TemplateFlags&ast.TokenFlagsContainsInvalidEscape != 0 {
return true
}
for _, span := range te.TemplateSpans.Nodes {
if span.AsTemplateSpan().Literal.TemplateLiteralLikeData().TemplateFlags&ast.TokenFlagsContainsInvalidEscape != 0 {
return true
}
}
return false
}

View File

@@ -0,0 +1,50 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/transformers"
)
func NewUseStrictTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &useStrictTransformer{
compilerOptions: opts.CompilerOptions,
getEmitModuleFormatOfFile: opts.GetEmitModuleFormatOfFile,
}
return tx.NewTransformer(tx.visit, opts.Context)
}
type useStrictTransformer struct {
transformers.Transformer
compilerOptions *core.CompilerOptions
getEmitModuleFormatOfFile func(file ast.HasFileName) core.ModuleKind
}
func (tx *useStrictTransformer) visit(node *ast.Node) *ast.Node {
if node.Kind != ast.KindSourceFile {
return node
}
return tx.visitSourceFile(node.AsSourceFile())
}
func (tx *useStrictTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
if node.ScriptKind == core.ScriptKindJSON {
return node.AsNode()
}
isExternalModule := ast.IsExternalModule(node)
moduleKind := tx.compilerOptions.GetEmitModuleKind()
format := tx.getEmitModuleFormatOfFile(node)
// ESM is always strict. If the file is ESM, and CJS emit
// has not been requested, then skip adding "use strict".
if isExternalModule && moduleKind >= core.ModuleKindES2015 &&
(moduleKind == core.ModuleKindPreserve || format >= core.ModuleKindES2015) {
return node.AsNode()
}
statements := tx.Factory().EnsureUseStrict(node.Statements.Nodes)
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = node.Statements.Loc
return tx.Factory().UpdateSourceFile(node, statementList, node.EndOfFileToken).AsSourceFile().AsNode()
}

View File

@@ -0,0 +1,799 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/debug"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
type usingDeclarationTransformer struct {
transformers.Transformer
exportBindings map[string]*ast.ExportSpecifierNode
exportBindingNames []string
exportVars []*ast.VariableDeclarationNode
defaultExportBinding *ast.IdentifierNode
exportEqualsBinding *ast.IdentifierNode
}
func newUsingDeclarationTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &usingDeclarationTransformer{}
return tx.NewTransformer(tx.visit, opts.Context)
}
type usingKind uint
const (
usingKindNone usingKind = iota
usingKindSync
usingKindAsync
)
func (tx *usingDeclarationTransformer) visit(node *ast.Node) *ast.Node {
if node.SubtreeFacts()&ast.SubtreeContainsUsing == 0 {
return node
}
switch node.Kind {
case ast.KindSourceFile:
node = tx.visitSourceFile(node.AsSourceFile())
case ast.KindBlock:
node = tx.visitBlock(node.AsBlock())
case ast.KindForStatement:
node = tx.visitForStatement(node.AsForStatement())
case ast.KindForOfStatement:
node = tx.visitForOfStatement(node.AsForInOrOfStatement())
default:
node = tx.Visitor().VisitEachChild(node)
}
return node
}
func (tx *usingDeclarationTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
if node.IsDeclarationFile {
return node.AsNode()
}
var visited *ast.SourceFileNode
usingKind := getUsingKindOfStatements(node.Statements.Nodes)
if usingKind != usingKindNone {
// Imports and exports must stay at the top level. This means we must hoist all imports, exports, and
// top-level function declarations and bindings out of the `try` statements we generate. For example:
//
// given:
//
// import { w } from "mod";
// const x = expr1;
// using y = expr2;
// const z = expr3;
// export function f() {
// console.log(z);
// }
//
// produces:
//
// import { x } from "mod"; // <-- preserved
// const x = expr1; // <-- preserved
// var y, z; // <-- hoisted
// export function f() { // <-- hoisted
// console.log(z);
// }
// const env_1 = { stack: [], error: void 0, hasError: false };
// try {
// y = __addDisposableResource(env_1, expr2, false);
// z = expr3;
// }
// catch (e_1) {
// env_1.error = e_1;
// env_1.hasError = true;
// }
// finally {
// __disposeResource(env_1);
// }
//
// In this transformation, we hoist `y`, `z`, and `f` to a new outer statement list while moving all other
// statements in the source file into the `try` block, which is the same approach we use for System module
// emit. Unlike System module emit, we attempt to preserve all statements prior to the first top-level
// `using` to isolate the complexity of the transformed output to only where it is necessary.
tx.EmitContext().StartVariableEnvironment()
tx.exportBindings = make(map[string]*ast.ExportSpecifierNode)
tx.exportVars = nil
prologue, rest := tx.Factory().SplitStandardPrologue(node.Statements.Nodes)
var topLevelStatements []*ast.Statement
topLevelStatements = append(topLevelStatements, core.FirstResult(tx.Visitor().VisitSlice(prologue))...)
// Collect and transform any leading statements up to the first `using` or `await using`. This preserves
// the original statement order much as is possible.
pos := 0
for pos < len(rest) {
statement := rest[pos]
if getUsingKind(statement) != usingKindNone {
if pos > 0 {
topLevelStatements = append(topLevelStatements, core.FirstResult(tx.Visitor().VisitSlice(rest[:pos]))...)
}
break
}
pos++
}
if pos >= len(rest) {
panic("Should have encountered at least one 'using' statement.")
}
// transform the rest of the body
envBinding := tx.createEnvBinding()
bodyStatements := tx.transformUsingDeclarations(rest[pos:], envBinding, &topLevelStatements)
// add `export {}` declarations for any hoisted bindings.
if len(tx.exportBindings) > 0 {
exportSpecifiers := make([]*ast.ExportSpecifierNode, 0, len(tx.exportBindingNames))
for _, name := range tx.exportBindingNames {
specifier := tx.exportBindings[name]
debug.Assert(specifier != nil, "Missing export binding for hoisted export name")
exportSpecifiers = append(exportSpecifiers, specifier)
}
topLevelStatements = append(
topLevelStatements,
tx.Factory().NewExportDeclaration(
nil, /*modifiers*/
false, /*isTypeOnly*/
tx.Factory().NewNamedExports(
tx.Factory().NewNodeList(
exportSpecifiers,
),
),
nil, /*moduleSpecifier*/
nil, /*attributes*/
),
)
}
topLevelStatements = append(topLevelStatements, tx.EmitContext().EndVariableEnvironment()...)
if len(tx.exportVars) > 0 {
topLevelStatements = append(topLevelStatements, tx.Factory().NewVariableStatement(
tx.Factory().NewModifierList([]*ast.Node{
tx.Factory().NewModifier(ast.KindExportKeyword),
}),
tx.Factory().NewVariableDeclarationList(
tx.Factory().NewNodeList(tx.exportVars),
ast.NodeFlagsLet,
),
))
}
topLevelStatements = append(topLevelStatements, tx.createDownlevelUsingStatements(bodyStatements, envBinding, usingKind == usingKindAsync)...)
if tx.exportEqualsBinding != nil {
topLevelStatements = append(topLevelStatements, tx.Factory().NewExportAssignment(
nil, /*modifiers*/
true, /*isExportEquals*/
nil, /*typeNode*/
tx.exportEqualsBinding,
))
}
visited = tx.Factory().UpdateSourceFile(node, tx.Factory().NewNodeList(topLevelStatements), node.EndOfFileToken)
} else {
visited = tx.Visitor().VisitEachChild(node.AsNode())
}
tx.EmitContext().AddEmitHelper(visited, tx.EmitContext().ReadEmitHelpers()...)
tx.exportVars = nil
tx.exportBindings = nil
tx.exportBindingNames = nil
tx.defaultExportBinding = nil
tx.exportEqualsBinding = nil
return visited
}
func (tx *usingDeclarationTransformer) visitBlock(node *ast.Block) *ast.Node {
usingKind := getUsingKindOfStatements(node.Statements.Nodes)
if usingKind != usingKindNone {
prologue, rest := tx.Factory().SplitStandardPrologue(node.Statements.Nodes)
envBinding := tx.createEnvBinding()
statements := make([]*ast.Statement, 0, len(prologue)+2)
statements = append(statements, core.FirstResult(tx.Visitor().VisitSlice(prologue))...)
statements = append(statements, tx.createDownlevelUsingStatements(
tx.transformUsingDeclarations(rest, envBinding, nil /*topLevelStatements*/),
envBinding,
usingKind == usingKindAsync,
)...)
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = node.Statements.Loc
return tx.Factory().UpdateBlock(node, statementList, node.MultiLine)
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *usingDeclarationTransformer) visitForStatement(node *ast.ForStatement) *ast.Node {
if node.Initializer != nil && isUsingVariableDeclarationList(node.Initializer) {
// given:
//
// for (using x = expr; cond; incr) { ... }
//
// produces a shallow transformation to:
//
// {
// using x = expr;
// for (; cond; incr) { ... }
// }
//
// before handing the shallow transformation back to the visitor for an in-depth transformation.
return tx.Visitor().VisitNode(
tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
tx.Factory().NewVariableStatement(nil /*modifiers*/, node.Initializer),
tx.Factory().UpdateForStatement(
node,
nil, /*initializer*/
node.Condition,
node.Incrementor,
node.Statement,
),
}), false /*multiLine*/),
)
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *usingDeclarationTransformer) visitForOfStatement(node *ast.ForInOrOfStatement) *ast.Node {
if isUsingVariableDeclarationList(node.Initializer) {
// given:
//
// for (using x of y) { ... }
//
// produces a shallow transformation to:
//
// for (const x_1 of y) {
// using x = x;
// ...
// }
//
// before handing the shallow transformation back to the visitor for an in-depth transformation.
forInitializer := node.Initializer.AsVariableDeclarationList()
forDecl := core.FirstOrNil(forInitializer.Declarations.Nodes)
if forDecl == nil {
forDecl = tx.Factory().NewVariableDeclaration(tx.Factory().NewTempVariable(), nil, nil, nil)
}
isAwaitUsing := getUsingKindOfVariableDeclarationList(forInitializer) == usingKindAsync
temp := tx.Factory().NewGeneratedNameForNode(forDecl.Name())
usingVar := tx.Factory().UpdateVariableDeclaration(forDecl.AsVariableDeclaration(), forDecl.Name(), nil /*exclamationToken*/, nil /*type*/, temp)
usingVarList := tx.Factory().NewVariableDeclarationList(
tx.Factory().NewNodeList([]*ast.Node{usingVar}),
core.IfElse(isAwaitUsing, ast.NodeFlagsAwaitUsing, ast.NodeFlagsUsing),
)
usingVarStatement := tx.Factory().NewVariableStatement(nil /*modifiers*/, usingVarList)
var statement *ast.Statement
if ast.IsBlock(node.Statement) {
statements := make([]*ast.Statement, 0, len(node.Statement.Statements())+1)
statements = append(statements, usingVarStatement)
statements = append(statements, node.Statement.Statements()...)
statement = tx.Factory().UpdateBlock(
node.Statement.AsBlock(),
tx.Factory().NewNodeList(statements),
node.Statement.AsBlock().MultiLine,
)
} else {
statement = tx.Factory().NewBlock(
tx.Factory().NewNodeList([]*ast.Statement{
usingVarStatement,
node.Statement,
}),
true, /*multiLine*/
)
}
return tx.Visitor().VisitNode(
tx.Factory().UpdateForInOrOfStatement(
node,
node.AwaitModifier,
tx.Factory().NewVariableDeclarationList(
tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{
tx.Factory().NewVariableDeclaration(temp, nil /*exclamationToken*/, nil /*type*/, nil),
}),
ast.NodeFlagsConst,
),
node.Expression,
statement,
),
)
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *usingDeclarationTransformer) transformUsingDeclarations(statementsIn []*ast.Statement, envBinding *ast.IdentifierNode, topLevelStatements *[]*ast.Statement) []*ast.Node {
var statements []*ast.Statement
hoist := func(node *ast.Statement) *ast.Statement {
if topLevelStatements == nil {
return node
}
switch node.Kind {
case ast.KindImportDeclaration,
ast.KindImportEqualsDeclaration,
ast.KindExportDeclaration,
ast.KindFunctionDeclaration:
tx.hoistImportOrExportOrHoistedDeclaration(node, topLevelStatements)
return nil
case ast.KindExportAssignment:
return tx.hoistExportAssignment(node.AsExportAssignment())
case ast.KindClassDeclaration:
return tx.hoistClassDeclaration(node.AsClassDeclaration())
case ast.KindVariableStatement:
return tx.hoistVariableStatement(node.AsVariableStatement())
}
return node
}
hoistOrAppendNode := func(node *ast.Node) {
node = hoist(node)
if node != nil {
statements = append(statements, node)
}
}
for _, statement := range statementsIn {
usingKind := getUsingKind(statement)
if usingKind != usingKindNone {
varStatement := statement.AsVariableStatement()
declarationList := varStatement.DeclarationList
var declarations []*ast.VariableDeclarationNode
for _, declaration := range declarationList.AsVariableDeclarationList().Declarations.Nodes {
if !ast.IsIdentifier(declaration.Name()) {
// Since binding patterns are a grammar error, we reset `declarations` so we don't process this as a `using`.
declarations = nil
break
}
// perform a shallow transform for any named evaluation
if isNamedEvaluation(tx.EmitContext(), declaration) {
declaration = transformNamedEvaluation(tx.EmitContext(), declaration, false /*ignoreEmptyStringLiteral*/, "" /*assignedName*/)
}
initializer := tx.Visitor().VisitNode(declaration.Initializer())
if initializer == nil {
initializer = tx.Factory().NewVoidZeroExpression()
}
declarations = append(declarations, tx.Factory().UpdateVariableDeclaration(
declaration.AsVariableDeclaration(),
declaration.Name(),
nil, /*exclamationToken*/
nil, /*type*/
tx.Factory().NewAddDisposableResourceHelper(
envBinding,
initializer,
usingKind == usingKindAsync,
),
))
}
// Only replace the statement if it was valid.
if len(declarations) > 0 {
varList := tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList(declarations), ast.NodeFlagsConst)
tx.EmitContext().SetOriginal(varList, declarationList)
varList.Loc = declarationList.Loc
hoistOrAppendNode(tx.Factory().UpdateVariableStatement(varStatement, nil /*modifiers*/, varList))
continue
}
}
if result := tx.visit(statement); result != nil {
if result.Kind == ast.KindSyntaxList {
for _, node := range result.AsSyntaxList().Children {
hoistOrAppendNode(node)
}
} else {
hoistOrAppendNode(result)
}
}
}
return statements
}
func (tx *usingDeclarationTransformer) hoistImportOrExportOrHoistedDeclaration(node *ast.Statement, topLevelStatements *[]*ast.Statement) {
// NOTE: `node` has already been visited
*topLevelStatements = append(*topLevelStatements, node)
}
func (tx *usingDeclarationTransformer) hoistExportAssignment(node *ast.ExportAssignment) *ast.Statement {
if node.IsExportEquals {
return tx.hoistExportEquals(node)
} else {
return tx.hoistExportDefault(node)
}
}
func (tx *usingDeclarationTransformer) hoistExportDefault(node *ast.ExportAssignment) *ast.Statement {
// NOTE: `node` has already been visited
if tx.defaultExportBinding != nil {
// invalid case of multiple `export default` declarations. Don't assert here, just pass it through
return node.AsNode()
}
// given:
//
// export default expr;
//
// produces:
//
// // top level
// var default_1;
// export { default_1 as default };
//
// // body
// default_1 = expr;
tx.defaultExportBinding = tx.Factory().NewUniqueNameEx("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes | printer.GeneratedIdentifierFlagsFileLevel | printer.GeneratedIdentifierFlagsOptimistic})
tx.hoistBindingIdentifier(tx.defaultExportBinding /*isExport*/, true, tx.Factory().NewIdentifier("default"), node.AsNode())
// give a class or function expression an assigned name, if needed.
expression := node.Expression
innerExpression := ast.SkipOuterExpressions(expression, ast.OEKAll)
if isNamedEvaluation(tx.EmitContext(), innerExpression) {
innerExpression = transformNamedEvaluation(tx.EmitContext(), innerExpression /*ignoreEmptyStringLiteral*/, false, "default")
expression = tx.Factory().RestoreOuterExpressions(expression, innerExpression, ast.OEKAll)
}
assignment := tx.Factory().NewAssignmentExpression(tx.defaultExportBinding, expression)
return tx.Factory().NewExpressionStatement(assignment)
}
func (tx *usingDeclarationTransformer) hoistExportEquals(node *ast.ExportAssignment) *ast.Statement {
// NOTE: `node` has already been visited
if tx.exportEqualsBinding != nil {
// invalid case of multiple `export default` declarations. Don't assert here, just pass it through
return node.AsNode()
}
// given:
//
// export = expr;
//
// produces:
//
// // top level
// var default_1;
//
// try {
// // body
// default_1 = expr;
// } ...
//
// // top level suffix
// export = default_1;
tx.exportEqualsBinding = tx.Factory().NewUniqueNameEx("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes | printer.GeneratedIdentifierFlagsFileLevel | printer.GeneratedIdentifierFlagsOptimistic})
tx.EmitContext().AddVariableDeclaration(tx.exportEqualsBinding)
// give a class or function expression an assigned name, if needed.
assignment := tx.Factory().NewAssignmentExpression(tx.exportEqualsBinding, node.Expression)
return tx.Factory().NewExpressionStatement(assignment)
}
func (tx *usingDeclarationTransformer) hoistClassDeclaration(node *ast.ClassDeclaration) *ast.Statement {
// NOTE: `node` has already been visited
if node.Name() == nil && tx.defaultExportBinding != nil {
// invalid case of multiple `export default` declarations. Don't assert here, just pass it through
return node.AsNode()
}
isExported := ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport)
isDefault := ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsDefault)
// When hoisting a class declaration at the top level of a file containing a top-level `using` statement, we
// must first convert it to a class expression so that we can hoist the binding outside of the `try`.
expression := convertClassDeclarationToClassExpression(tx.EmitContext(), node)
if node.Name() != nil {
// given:
//
// using x = expr;
// class C {}
//
// produces:
//
// var x, C;
// const env_1 = { ... };
// try {
// x = __addDisposableResource(env_1, expr, false);
// C = class {};
// }
// catch (e_1) {
// env_1.error = e_1;
// env_1.hasError = true;
// }
// finally {
// __disposeResources(env_1);
// }
//
// If the class is exported, we also produce an `export { C };`
tx.hoistBindingIdentifier(tx.Factory().GetLocalName(node.AsNode()), isExported && !isDefault, nil /*exportAlias*/, node.AsNode())
expression = tx.Factory().NewAssignmentExpression(tx.Factory().GetDeclarationName(node.AsNode()), expression)
tx.EmitContext().SetOriginal(expression, node.AsNode())
tx.EmitContext().SetSourceMapRange(expression, node.Loc)
tx.EmitContext().SetCommentRange(expression, node.Loc)
if isNamedEvaluation(tx.EmitContext(), expression) {
expression = transformNamedEvaluation(tx.EmitContext(), expression, false /*ignoreEmptyStringLiteral*/, "" /*assignedName*/)
}
}
if isDefault && tx.defaultExportBinding == nil {
// In the case of a default export, we create a temporary variable that we export as the default and then
// assign to that variable.
//
// given:
//
// using x = expr;
// export default class C {}
//
// produces:
//
// export { default_1 as default };
// var x, C, default_1;
// const env_1 = { ... };
// try {
// x = __addDisposableResource(env_1, expr, false);
// default_1 = C = class {};
// }
// catch (e_1) {
// env_1.error = e_1;
// env_1.hasError = true;
// }
// finally {
// __disposeResources(env_1);
// }
//
// Though we will never reassign `default_1`, this most closely matches the specified runtime semantics.
tx.defaultExportBinding = tx.Factory().NewUniqueNameEx("_default", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsReservedInNestedScopes | printer.GeneratedIdentifierFlagsFileLevel | printer.GeneratedIdentifierFlagsOptimistic})
tx.hoistBindingIdentifier(tx.defaultExportBinding /*isExport*/, true, tx.Factory().NewIdentifier("default"), node.AsNode())
expression = tx.Factory().NewAssignmentExpression(tx.defaultExportBinding, expression)
tx.EmitContext().SetOriginal(expression, node.AsNode())
if isNamedEvaluation(tx.EmitContext(), expression) {
expression = transformNamedEvaluation(tx.EmitContext(), expression /*ignoreEmptyStringLiteral*/, false, "default")
}
}
return tx.Factory().NewExpressionStatement(expression)
}
func (tx *usingDeclarationTransformer) hoistVariableStatement(node *ast.VariableStatement) *ast.Statement {
// NOTE: `node` has already been visited
var expressions []*ast.Expression
isExported := ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport)
for _, variable := range node.DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
tx.hoistBindingElement(variable, isExported, variable)
if variable.Initializer() != nil {
expressions = append(expressions, tx.hoistInitializedVariable(variable.AsVariableDeclaration()))
}
}
if len(expressions) > 0 {
statement := tx.Factory().NewExpressionStatement(tx.Factory().InlineExpressions(expressions))
tx.EmitContext().SetOriginal(statement, node.AsNode())
tx.EmitContext().SetCommentRange(statement, node.Loc)
tx.EmitContext().SetSourceMapRange(statement, node.Loc)
return statement
}
return nil
}
func (tx *usingDeclarationTransformer) hoistInitializedVariable(node *ast.VariableDeclaration) *ast.Expression {
// NOTE: `node` has already been visited
if node.Initializer == nil {
panic("Expected initializer")
}
var target *ast.Expression
if ast.IsIdentifier(node.Name()) {
target = node.Name().Clone(tx.Factory())
tx.EmitContext().SetEmitFlags(target, tx.EmitContext().EmitFlags(target) & ^(printer.EFLocalName|printer.EFExportName))
} else {
target = transformers.ConvertBindingPatternToAssignmentPattern(tx.EmitContext(), node.Name().AsBindingPattern())
}
assignment := tx.Factory().NewAssignmentExpression(target, node.Initializer)
tx.EmitContext().SetOriginal(assignment, node.AsNode())
tx.EmitContext().SetCommentRange(assignment, node.Loc)
tx.EmitContext().SetSourceMapRange(assignment, node.Loc)
return assignment
}
func (tx *usingDeclarationTransformer) hoistBindingElement(node *ast.Node /*VariableDeclaration|BindingElement*/, isExportedDeclaration bool, original *ast.Node) {
// NOTE: `node` has already been visited
if ast.IsBindingPattern(node.Name()) {
for _, element := range node.Name().Elements() {
if element.Name() != nil {
tx.hoistBindingElement(element, isExportedDeclaration, original)
}
}
} else {
tx.hoistBindingIdentifier(node.Name(), isExportedDeclaration, nil /*exportAlias*/, original)
}
}
func (tx *usingDeclarationTransformer) hoistBindingIdentifier(node *ast.IdentifierNode, isExport bool, exportAlias *ast.IdentifierNode, original *ast.Node) {
// NOTE: `node` has already been visited
name := node
if !transformers.IsGeneratedIdentifier(tx.EmitContext(), node) {
name = name.Clone(tx.Factory())
}
if isExport {
if exportAlias == nil && !transformers.IsLocalName(tx.EmitContext(), name) {
varDecl := tx.Factory().NewVariableDeclaration(name, nil /*exclamationToken*/, nil /*type*/, nil /*initializer*/)
if original != nil {
tx.EmitContext().SetOriginal(varDecl, original)
}
tx.exportVars = append(tx.exportVars, varDecl)
return
}
var localName *ast.ModuleExportName
var exportName *ast.ModuleExportName
if exportAlias != nil {
localName = name
exportName = exportAlias
} else {
exportName = name
}
specifier := tx.Factory().NewExportSpecifier( /*isTypeOnly*/ false, localName, exportName)
if original != nil {
tx.EmitContext().SetOriginal(specifier, original)
}
if tx.exportBindings == nil {
tx.exportBindings = make(map[string]*ast.ExportSpecifierNode)
}
if _, ok := tx.exportBindings[name.Text()]; !ok {
tx.exportBindingNames = append(tx.exportBindingNames, name.Text())
}
tx.exportBindings[name.Text()] = specifier
}
tx.EmitContext().AddVariableDeclaration(name)
}
func (tx *usingDeclarationTransformer) createEnvBinding() *ast.IdentifierNode {
return tx.Factory().NewUniqueName("env")
}
func (tx *usingDeclarationTransformer) createDownlevelUsingStatements(bodyStatements []*ast.Node, envBinding *ast.IdentifierNode, async bool) []*ast.Statement {
statements := make([]*ast.Statement, 0, 2)
// produces:
//
// const env_1 = { stack: [], error: void 0, hasError: false };
//
envObject := tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList([]*ast.Expression{
tx.Factory().NewPropertyAssignment(nil /*modifiers*/, tx.Factory().NewIdentifier("stack"), nil /*postfixToken*/, nil /*typeNode*/, tx.Factory().NewArrayLiteralExpression(nil, false /*multiLine*/)),
tx.Factory().NewPropertyAssignment(nil /*modifiers*/, tx.Factory().NewIdentifier("error"), nil /*postfixToken*/, nil /*typeNode*/, tx.Factory().NewVoidZeroExpression()),
tx.Factory().NewPropertyAssignment(nil /*modifiers*/, tx.Factory().NewIdentifier("hasError"), nil /*postfixToken*/, nil /*typeNode*/, tx.Factory().NewFalseExpression()),
}), false /*multiLine*/)
envVar := tx.Factory().NewVariableDeclaration(envBinding, nil /*exclamationToken*/, nil /*typeNode*/, envObject)
envVarList := tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{envVar}), ast.NodeFlagsConst)
envVarStatement := tx.Factory().NewVariableStatement(nil /*modifiers*/, envVarList)
statements = append(statements, envVarStatement)
// when `async` is `false`, produces:
//
// try {
// <bodyStatements>
// }
// catch (e_1) {
// env_1.error = e_1;
// env_1.hasError = true;
// }
// finally {
// __disposeResources(env_1);
// }
// when `async` is `true`, produces:
//
// try {
// <bodyStatements>
// }
// catch (e_1) {
// env_1.error = e_1;
// env_1.hasError = true;
// }
// finally {
// const result_1 = __disposeResources(env_1);
// if (result_1) {
// await result_1;
// }
// }
// Unfortunately, it is necessary to use two properties to indicate an error because `throw undefined` is legal
// JavaScript.
tryBlock := tx.Factory().NewBlock(tx.Factory().NewNodeList(bodyStatements), true /*multiLine*/)
bodyCatchBinding := tx.Factory().NewUniqueName("e")
catchClause := tx.Factory().NewCatchClause(
tx.Factory().NewVariableDeclaration(
bodyCatchBinding,
nil, /*exclamationToken*/
nil, /*type*/
nil, /*initializer*/
),
tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
tx.Factory().NewExpressionStatement(
tx.Factory().NewAssignmentExpression(
tx.Factory().NewPropertyAccessExpression(envBinding, nil, tx.Factory().NewIdentifier("error"), ast.NodeFlagsNone),
bodyCatchBinding,
),
),
tx.Factory().NewExpressionStatement(
tx.Factory().NewAssignmentExpression(
tx.Factory().NewPropertyAccessExpression(envBinding, nil, tx.Factory().NewIdentifier("hasError"), ast.NodeFlagsNone),
tx.Factory().NewTrueExpression(),
),
),
}), true /*multiLine*/),
)
var finallyBlock *ast.BlockNode
if async {
result := tx.Factory().NewUniqueName("result")
finallyBlock = tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
tx.Factory().NewVariableStatement(
nil, /*modifiers*/
tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{
tx.Factory().NewVariableDeclaration(
result,
nil, /*exclamationToken*/
nil, /*type*/
tx.Factory().NewDisposeResourcesHelper(envBinding),
),
}), ast.NodeFlagsConst),
),
tx.Factory().NewIfStatement(result, tx.Factory().NewExpressionStatement(tx.Factory().NewAwaitExpression(result)), nil /*elseStatement*/),
}), true /*multiLine*/)
} else {
finallyBlock = tx.Factory().NewBlock(tx.Factory().NewNodeList([]*ast.Statement{
tx.Factory().NewExpressionStatement(
tx.Factory().NewDisposeResourcesHelper(envBinding),
),
}), true /*multiLine*/)
}
tryStatement := tx.Factory().NewTryStatement(tryBlock, catchClause, finallyBlock)
statements = append(statements, tryStatement)
return statements
}
func isUsingVariableDeclarationList(node *ast.ForInitializer) bool {
return ast.IsVariableDeclarationList(node) && getUsingKindOfVariableDeclarationList(node.AsVariableDeclarationList()) != usingKindNone
}
func getUsingKindOfVariableDeclarationList(node *ast.VariableDeclarationList) usingKind {
switch node.Flags & ast.NodeFlagsBlockScoped {
case ast.NodeFlagsAwaitUsing:
return usingKindAsync
case ast.NodeFlagsUsing:
return usingKindSync
default:
return usingKindNone
}
}
func getUsingKindOfVariableStatement(node *ast.VariableStatement) usingKind {
return getUsingKindOfVariableDeclarationList(node.DeclarationList.AsVariableDeclarationList())
}
func getUsingKind(statement *ast.Node) usingKind {
if ast.IsVariableStatement(statement) {
return getUsingKindOfVariableStatement(statement.AsVariableStatement())
}
return usingKindNone
}
func getUsingKindOfStatements(statements []*ast.Node) usingKind {
result := usingKindNone
for _, statement := range statements {
usingKind := getUsingKind(statement)
if usingKind == usingKindAsync {
return usingKindAsync
}
if usingKind > result {
result = usingKind
}
}
return result
}

View File

@@ -0,0 +1,289 @@
package estransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
func convertClassDeclarationToClassExpression(emitContext *printer.EmitContext, node *ast.ClassDeclaration) *ast.Expression {
updated := emitContext.Factory.NewClassExpression(
transformers.ExtractModifiers(emitContext, node.Modifiers(), ^ast.ModifierFlagsExportDefault),
node.Name(),
node.TypeParameters,
node.HeritageClauses,
node.Members,
)
emitContext.SetOriginal(updated, node.AsNode())
updated.Loc = node.Loc
return updated
}
func createNotNullCondition(emitContext *printer.EmitContext, left *ast.Node, right *ast.Node, invert bool) *ast.Node {
token := ast.KindExclamationEqualsEqualsToken
op := ast.KindAmpersandAmpersandToken
if invert {
token = ast.KindEqualsEqualsEqualsToken
op = ast.KindBarBarToken
}
return emitContext.Factory.NewBinaryExpression(
nil,
emitContext.Factory.NewBinaryExpression(
nil,
left,
nil,
emitContext.Factory.NewToken(token),
emitContext.Factory.NewKeywordExpression(ast.KindNullKeyword),
),
nil,
emitContext.Factory.NewToken(op),
emitContext.Factory.NewBinaryExpression(
nil,
right,
nil,
emitContext.Factory.NewToken(token),
emitContext.Factory.NewVoidZeroExpression(),
),
)
}
// superAccessState tracks super property/element accesses and super property assignments
// within async function or async generator bodies. It is embedded by both asyncTransformer
// and forawaitTransformer to share the tracking logic.
type superAccessState struct {
factory *printer.NodeFactory
// Keeps track of property names accessed on super (`super.x`) within async functions.
capturedSuperProperties *collections.OrderedSet[string]
// Whether the async function contains an element access on super (`super[x]`).
hasSuperElementAccess bool
hasSuperPropertyAssignment bool
superBinding *ast.IdentifierNode
superIndexBinding *ast.IdentifierNode
superAccessVisitor *ast.NodeVisitor
}
func (s *superAccessState) initSuperAccessVisitor(emitContext *printer.EmitContext, factory *printer.NodeFactory) {
s.factory = factory
s.superAccessVisitor = emitContext.NewNodeVisitor(s.visitSuperAccessNode)
}
// visitSuperAccessNode walks the async/generator body and replaces super property/element
// accesses with _super/_superIndex references. This is necessary because the async body
// ends up inside a generator function where `super` is not valid.
func (s *superAccessState) visitSuperAccessNode(node *ast.Node) *ast.Node {
switch node.Kind {
case ast.KindCallExpression:
call := node.AsCallExpression()
if ast.IsSuperProperty(call.Expression) {
return s.substituteCallExpressionWithSuperAccess(call, s.superAccessVisitor)
}
return s.superAccessVisitor.VisitEachChild(node)
case ast.KindPropertyAccessExpression:
if node.Expression().Kind == ast.KindSuperKeyword {
// super.x → _super.x
return s.factory.NewPropertyAccessExpression(
s.superBinding, nil, node.Name(), ast.NodeFlagsNone,
)
}
return s.superAccessVisitor.VisitEachChild(node)
case ast.KindElementAccessExpression:
if node.Expression().Kind == ast.KindSuperKeyword {
// super[x] → _superIndex(x) or _superIndex(x).value
return s.createSuperElementAccessInAsyncMethod(
node.AsElementAccessExpression().ArgumentExpression,
)
}
return s.superAccessVisitor.VisitEachChild(node)
// Don't recurse into non-arrow function scopes or classes
case ast.KindFunctionExpression, ast.KindFunctionDeclaration,
ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor,
ast.KindConstructor, ast.KindClassDeclaration, ast.KindClassExpression:
return node
default:
return s.superAccessVisitor.VisitEachChild(node)
}
}
func (s *superAccessState) substituteSuperAccessesInBody(body *ast.Node) *ast.Node {
return s.superAccessVisitor.VisitNode(body)
}
// substituteCallExpressionWithSuperAccess handles super.x(args) and super[x](args).
func (s *superAccessState) substituteCallExpressionWithSuperAccess(call *ast.CallExpression, visitor *ast.NodeVisitor) *ast.Node {
expression := call.Expression
var target *ast.Node
if ast.IsPropertyAccessExpression(expression) {
// super.x(args) → _super.x.call(this, args)
target = s.factory.NewPropertyAccessExpression(
s.superBinding, nil,
expression.AsPropertyAccessExpression().Name(), ast.NodeFlagsNone,
)
} else if ast.IsElementAccessExpression(expression) {
// super[x](args) → _superIndex(x).call(this, args) or _superIndex(x).value.call(this, args)
target = s.createSuperElementAccessInAsyncMethod(
expression.AsElementAccessExpression().ArgumentExpression,
)
} else {
return visitor.VisitEachChild(call.AsNode())
}
callTarget := s.factory.NewPropertyAccessExpression(
target, nil,
s.factory.NewIdentifier("call"), ast.NodeFlagsNone,
)
var allArgs []*ast.Node
allArgs = append(allArgs, s.factory.NewThisExpression())
if call.Arguments != nil {
visitedArgs := visitor.VisitNodes(call.Arguments)
if visitedArgs != nil {
allArgs = append(allArgs, visitedArgs.Nodes...)
}
}
result := s.factory.NewCallExpression(
callTarget, nil, nil,
s.factory.NewNodeList(allArgs), ast.NodeFlagsNone,
)
result.Loc = call.Loc
return result
}
// createSuperElementAccessInAsyncMethod creates _superIndex(x) or _superIndex(x).value.
func (s *superAccessState) createSuperElementAccessInAsyncMethod(argumentExpression *ast.Node) *ast.Node {
superIndexCall := s.factory.NewCallExpression(
s.superIndexBinding, nil, nil,
s.factory.NewNodeList([]*ast.Node{argumentExpression}),
ast.NodeFlagsNone,
)
if s.hasSuperPropertyAssignment {
return s.factory.NewPropertyAccessExpression(
superIndexCall, nil,
s.factory.NewIdentifier("value"), ast.NodeFlagsNone,
)
}
return superIndexCall
}
// createSuperAccessVariableStatement creates a variable named `_super` with accessor
// properties for the given property names.
//
// Create a variable declaration with a getter/setter (if binding) definition for each name:
//
// const _super = Object.create(null, {
// x: { get: () => super.x }, // read-only
// x: { get: () => super.x, set: (v) => super.x = v }, // read-write
// });
func (s *superAccessState) createSuperAccessVariableStatement() *ast.Node {
f := s.factory
var accessors []*ast.Node
for name := range s.capturedSuperProperties.Values() {
var descriptorProperties []*ast.Node
// getter: get: () => super.name
getterBody := f.NewPropertyAccessExpression(
f.NewKeywordExpression(ast.KindSuperKeyword), nil,
f.NewIdentifier(name), ast.NodeFlagsNone,
)
getterArrow := f.NewArrowFunction(
nil, nil,
f.NewNodeList([]*ast.Node{}),
nil, nil,
f.NewToken(ast.KindEqualsGreaterThanToken),
getterBody,
)
getter := f.NewPropertyAssignment(nil, f.NewIdentifier("get"), nil, nil, getterArrow)
descriptorProperties = append(descriptorProperties, getter)
if s.hasSuperPropertyAssignment {
// setter: set: v => super.name = v
vParam := f.NewParameterDeclaration(nil, nil, f.NewIdentifier("v"), nil, nil, nil)
superProp := f.NewPropertyAccessExpression(
f.NewKeywordExpression(ast.KindSuperKeyword), nil,
f.NewIdentifier(name), ast.NodeFlagsNone,
)
assignExpr := f.NewAssignmentExpression(superProp, f.NewIdentifier("v"))
setterArrow := f.NewArrowFunction(
nil, nil,
f.NewNodeList([]*ast.Node{vParam}),
nil, nil,
f.NewToken(ast.KindEqualsGreaterThanToken),
assignExpr,
)
setter := f.NewPropertyAssignment(nil, f.NewIdentifier("set"), nil, nil, setterArrow)
descriptorProperties = append(descriptorProperties, setter)
}
descriptor := f.NewObjectLiteralExpression(f.NewNodeList(descriptorProperties), false)
accessor := f.NewPropertyAssignment(nil, f.NewIdentifier(name), nil, nil, descriptor)
accessors = append(accessors, accessor)
}
descriptorsObject := f.NewObjectLiteralExpression(f.NewNodeList(accessors), true)
objectCreateCall := f.NewCallExpression(
f.NewPropertyAccessExpression(
f.NewIdentifier("Object"), nil,
f.NewIdentifier("create"), ast.NodeFlagsNone,
), nil, nil,
f.NewNodeList([]*ast.Node{
f.NewKeywordExpression(ast.KindNullKeyword),
descriptorsObject,
}),
ast.NodeFlagsNone,
)
decl := f.NewVariableDeclaration(s.superBinding, nil, nil, objectCreateCall)
declList := f.NewVariableDeclarationList(f.NewNodeList([]*ast.Node{decl}), ast.NodeFlagsConst)
return f.NewVariableStatement(nil, declList)
}
// trackSuperAccess records super property/element accesses and super property assignments
// for the enclosing async method body. Called from both the main visitor and auxiliary
// visitors to ensure super accesses are tracked regardless of whether the node has
// transform flags.
func (s *superAccessState) trackSuperAccess(node *ast.Node) {
if s.capturedSuperProperties == nil {
return
}
switch node.Kind {
case ast.KindPropertyAccessExpression:
if node.Expression().Kind == ast.KindSuperKeyword {
s.capturedSuperProperties.Add(node.Name().Text())
}
case ast.KindElementAccessExpression:
if node.Expression().Kind == ast.KindSuperKeyword {
s.hasSuperElementAccess = true
}
case ast.KindBinaryExpression:
if ast.IsAssignmentOperator(node.AsBinaryExpression().OperatorToken.Kind) && assignmentTargetContainsSuperProperty(node.AsBinaryExpression().Left) {
s.hasSuperPropertyAssignment = true
}
case ast.KindPrefixUnaryExpression:
if isUpdateExpression(node) && assignmentTargetContainsSuperProperty(node.AsPrefixUnaryExpression().Operand) {
s.hasSuperPropertyAssignment = true
}
case ast.KindPostfixUnaryExpression:
if isUpdateExpression(node) && assignmentTargetContainsSuperProperty(node.AsPostfixUnaryExpression().Operand) {
s.hasSuperPropertyAssignment = true
}
}
}
// createAccessorPropertyBackingField creates a private backing field for an `accessor` PropertyDeclaration.
func createAccessorPropertyBackingField(f *printer.NodeFactory, node *ast.PropertyDeclaration, modifiers *ast.ModifierList, initializer *ast.Expression) *ast.Node {
return f.UpdatePropertyDeclaration(
node,
modifiers,
f.NewGeneratedPrivateNameForNodeEx(node.Name(), printer.AutoGenerateOptions{Suffix: "_accessor_storage"}),
nil, /*postfixToken*/
nil, /*typeNode*/
initializer,
)
}