vendor tsgo
This commit is contained in:
995
tools/tsgo/internal/transformers/estransforms/async.go
Normal file
995
tools/tsgo/internal/transformers/estransforms/async.go
Normal 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
|
||||
}
|
||||
3618
tools/tsgo/internal/transformers/estransforms/classfields.go
Normal file
3618
tools/tsgo/internal/transformers/estransforms/classfields.go
Normal file
File diff suppressed because it is too large
Load Diff
28
tools/tsgo/internal/transformers/estransforms/classthis.go
Normal file
28
tools/tsgo/internal/transformers/estransforms/classthis.go
Normal 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
|
||||
}
|
||||
43
tools/tsgo/internal/transformers/estransforms/definitions.go
Normal file
43
tools/tsgo/internal/transformers/estransforms/definitions.go
Normal 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)
|
||||
}
|
||||
}
|
||||
2751
tools/tsgo/internal/transformers/estransforms/esdecorator.go
Normal file
2751
tools/tsgo/internal/transformers/estransforms/esdecorator.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
}
|
||||
856
tools/tsgo/internal/transformers/estransforms/forawait.go
Normal file
856
tools/tsgo/internal/transformers/estransforms/forawait.go
Normal 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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
537
tools/tsgo/internal/transformers/estransforms/namedevaluation.go
Normal file
537
tools/tsgo/internal/transformers/estransforms/namedevaluation.go
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
240
tools/tsgo/internal/transformers/estransforms/optionalchain.go
Normal file
240
tools/tsgo/internal/transformers/estransforms/optionalchain.go
Normal 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)
|
||||
}
|
||||
174
tools/tsgo/internal/transformers/estransforms/taggedtemplate.go
Normal file
174
tools/tsgo/internal/transformers/estransforms/taggedtemplate.go
Normal 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
|
||||
}
|
||||
50
tools/tsgo/internal/transformers/estransforms/usestrict.go
Normal file
50
tools/tsgo/internal/transformers/estransforms/usestrict.go
Normal 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()
|
||||
}
|
||||
799
tools/tsgo/internal/transformers/estransforms/using.go
Normal file
799
tools/tsgo/internal/transformers/estransforms/using.go
Normal 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
|
||||
}
|
||||
289
tools/tsgo/internal/transformers/estransforms/utilities.go
Normal file
289
tools/tsgo/internal/transformers/estransforms/utilities.go
Normal 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,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user