290 lines
10 KiB
Go
290 lines
10 KiB
Go
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,
|
|
)
|
|
}
|