vendor tsgo
This commit is contained in:
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)
|
||||
}
|
||||
Reference in New Issue
Block a user