Skip to content

Commit 2a714ea

Browse files
weswighamandrewbranch
authored andcommitted
Port object rest and spread transform (microsoft#1529)
1 parent 1041f20 commit 2a714ea

File tree

304 files changed

+3637
-4211
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

304 files changed

+3637
-4211
lines changed

internal/ast/ast.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2785,7 +2785,7 @@ func (node *ForInOrOfStatement) computeSubtreeFacts() SubtreeFacts {
27852785
return propagateSubtreeFacts(node.Initializer) |
27862786
propagateSubtreeFacts(node.Expression) |
27872787
propagateSubtreeFacts(node.Statement) |
2788-
core.IfElse(node.AwaitModifier != nil, SubtreeContainsES2018, SubtreeFactsNone)
2788+
core.IfElse(node.AwaitModifier != nil, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone)
27892789
}
27902790

27912791
func IsForInStatement(node *Node) bool {
@@ -3682,7 +3682,7 @@ func (node *BindingElement) computeSubtreeFacts() SubtreeFacts {
36823682
return propagateSubtreeFacts(node.PropertyName) |
36833683
propagateSubtreeFacts(node.name) |
36843684
propagateSubtreeFacts(node.Initializer) |
3685-
core.IfElse(node.DotDotDotToken != nil, SubtreeContainsRest, SubtreeFactsNone)
3685+
core.IfElse(node.DotDotDotToken != nil, SubtreeContainsRestOrSpread, SubtreeFactsNone)
36863686
}
36873687

36883688
func IsBindingElement(node *Node) bool {
@@ -6070,7 +6070,7 @@ func (node *YieldExpression) Clone(f NodeFactoryCoercible) *Node {
60706070
}
60716071

60726072
func (node *YieldExpression) computeSubtreeFacts() SubtreeFacts {
6073-
return propagateSubtreeFacts(node.Expression) | SubtreeContainsES2018
6073+
return propagateSubtreeFacts(node.Expression) | SubtreeContainsForAwaitOrAsyncGenerator
60746074
}
60756075

60766076
// ArrowFunction
@@ -6692,7 +6692,7 @@ func (node *SpreadElement) Clone(f NodeFactoryCoercible) *Node {
66926692
}
66936693

66946694
func (node *SpreadElement) computeSubtreeFacts() SubtreeFacts {
6695-
return propagateSubtreeFacts(node.Expression)
6695+
return propagateSubtreeFacts(node.Expression) | SubtreeContainsRestOrSpread
66966696
}
66976697

66986698
func IsSpreadElement(node *Node) bool {
@@ -7015,7 +7015,7 @@ func (node *SpreadAssignment) Clone(f NodeFactoryCoercible) *Node {
70157015
}
70167016

70177017
func (node *SpreadAssignment) computeSubtreeFacts() SubtreeFacts {
7018-
return propagateSubtreeFacts(node.Expression) | SubtreeContainsES2018 | SubtreeContainsObjectRestOrSpread
7018+
return propagateSubtreeFacts(node.Expression) | SubtreeContainsESObjectRestOrSpread | SubtreeContainsObjectRestOrSpread
70197019
}
70207020

70217021
func IsSpreadAssignment(node *Node) bool {

internal/ast/subtreefacts.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const (
2020
SubtreeContainsNullishCoalescing
2121
SubtreeContainsOptionalChaining
2222
SubtreeContainsMissingCatchClauseVariable
23-
SubtreeContainsESObjectRestOrSpread
23+
SubtreeContainsESObjectRestOrSpread // subtree has a `...` somewhere inside it, never cleared
2424
SubtreeContainsForAwaitOrAsyncGenerator
2525
SubtreeContainsAnyAwait
2626
SubtreeContainsExponentiationOperator
@@ -30,8 +30,8 @@ const (
3030

3131
SubtreeContainsLexicalThis
3232
SubtreeContainsLexicalSuper
33-
SubtreeContainsRest
34-
SubtreeContainsObjectRestOrSpread
33+
SubtreeContainsRestOrSpread // marker on any `...` - cleared on binding pattern exit
34+
SubtreeContainsObjectRestOrSpread // marker on any `{...x}` - cleared on most scope exits
3535
SubtreeContainsAwait
3636
SubtreeContainsDynamicImport
3737
SubtreeContainsClassFields
@@ -76,7 +76,7 @@ const (
7676
SubtreeExclusionsVariableDeclarationList = SubtreeExclusionsNode | SubtreeContainsObjectRestOrSpread
7777
SubtreeExclusionsParameter = SubtreeExclusionsNode
7878
SubtreeExclusionsCatchClause = SubtreeExclusionsNode | SubtreeContainsObjectRestOrSpread
79-
SubtreeExclusionsBindingPattern = SubtreeExclusionsNode | SubtreeContainsRest
79+
SubtreeExclusionsBindingPattern = SubtreeExclusionsNode | SubtreeContainsRestOrSpread
8080

8181
// Masks
8282
// - Additional bitmasks
@@ -94,15 +94,15 @@ func propagateEraseableSyntaxSubtreeFacts(child *TypeNode) SubtreeFacts {
9494

9595
func propagateObjectBindingElementSubtreeFacts(child *BindingElementNode) SubtreeFacts {
9696
facts := propagateSubtreeFacts(child)
97-
if facts&SubtreeContainsRest != 0 {
98-
facts &= ^SubtreeContainsRest
99-
facts |= SubtreeContainsObjectRestOrSpread
97+
if facts&SubtreeContainsRestOrSpread != 0 {
98+
facts &= ^SubtreeContainsRestOrSpread
99+
facts |= SubtreeContainsObjectRestOrSpread | SubtreeContainsESObjectRestOrSpread
100100
}
101101
return facts
102102
}
103103

104104
func propagateBindingElementSubtreeFacts(child *BindingElementNode) SubtreeFacts {
105-
return propagateSubtreeFacts(child) & ^SubtreeContainsRest
105+
return propagateSubtreeFacts(child) & ^SubtreeContainsRestOrSpread
106106
}
107107

108108
func propagateSubtreeFacts(child *Node) SubtreeFacts {

internal/ast/utilities.go

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3654,3 +3654,202 @@ func hasComment(kind Kind) bool {
36543654
return false
36553655
}
36563656
}
3657+
3658+
func IsAssignmentPattern(node *Node) bool {
3659+
return node.Kind == KindArrayLiteralExpression || node.Kind == KindObjectLiteralExpression
3660+
}
3661+
3662+
func GetElementsOfBindingOrAssignmentPattern(name *Node) []*Node {
3663+
switch name.Kind {
3664+
case KindObjectBindingPattern, KindArrayBindingPattern:
3665+
// `a` in `{a}`
3666+
// `a` in `[a]`
3667+
return name.AsBindingPattern().Elements.Nodes
3668+
case KindArrayLiteralExpression:
3669+
// `a` in `[a]`
3670+
return name.AsArrayLiteralExpression().Elements.Nodes
3671+
case KindObjectLiteralExpression:
3672+
// `a` in `{a}`
3673+
return name.AsObjectLiteralExpression().Properties.Nodes
3674+
}
3675+
return nil
3676+
}
3677+
3678+
func IsDeclarationBindingElement(bindingElement *Node) bool {
3679+
switch bindingElement.Kind {
3680+
case KindVariableDeclaration, KindParameter, KindBindingElement:
3681+
return true
3682+
default:
3683+
return false
3684+
}
3685+
}
3686+
3687+
/**
3688+
* Gets the name of an BindingOrAssignmentElement.
3689+
*/
3690+
func GetTargetOfBindingOrAssignmentElement(bindingElement *Node) *Node {
3691+
if IsDeclarationBindingElement(bindingElement) {
3692+
// `a` in `let { a } = ...`
3693+
// `a` in `let { a = 1 } = ...`
3694+
// `b` in `let { a: b } = ...`
3695+
// `b` in `let { a: b = 1 } = ...`
3696+
// `a` in `let { ...a } = ...`
3697+
// `{b}` in `let { a: {b} } = ...`
3698+
// `{b}` in `let { a: {b} = 1 } = ...`
3699+
// `[b]` in `let { a: [b] } = ...`
3700+
// `[b]` in `let { a: [b] = 1 } = ...`
3701+
// `a` in `let [a] = ...`
3702+
// `a` in `let [a = 1] = ...`
3703+
// `a` in `let [...a] = ...`
3704+
// `{a}` in `let [{a}] = ...`
3705+
// `{a}` in `let [{a} = 1] = ...`
3706+
// `[a]` in `let [[a]] = ...`
3707+
// `[a]` in `let [[a] = 1] = ...`
3708+
return bindingElement.Name()
3709+
}
3710+
3711+
if IsObjectLiteralElement(bindingElement) {
3712+
switch bindingElement.Kind {
3713+
case KindPropertyAssignment:
3714+
// `b` in `({ a: b } = ...)`
3715+
// `b` in `({ a: b = 1 } = ...)`
3716+
// `{b}` in `({ a: {b} } = ...)`
3717+
// `{b}` in `({ a: {b} = 1 } = ...)`
3718+
// `[b]` in `({ a: [b] } = ...)`
3719+
// `[b]` in `({ a: [b] = 1 } = ...)`
3720+
// `b.c` in `({ a: b.c } = ...)`
3721+
// `b.c` in `({ a: b.c = 1 } = ...)`
3722+
// `b[0]` in `({ a: b[0] } = ...)`
3723+
// `b[0]` in `({ a: b[0] = 1 } = ...)`
3724+
return GetTargetOfBindingOrAssignmentElement(bindingElement.Initializer())
3725+
case KindShorthandPropertyAssignment:
3726+
// `a` in `({ a } = ...)`
3727+
// `a` in `({ a = 1 } = ...)`
3728+
return bindingElement.Name()
3729+
case KindSpreadAssignment:
3730+
// `a` in `({ ...a } = ...)`
3731+
return GetTargetOfBindingOrAssignmentElement(bindingElement.AsSpreadAssignment().Expression)
3732+
}
3733+
3734+
// no target
3735+
return nil
3736+
}
3737+
3738+
if IsAssignmentExpression(bindingElement /*excludeCompoundAssignment*/, true) {
3739+
// `a` in `[a = 1] = ...`
3740+
// `{a}` in `[{a} = 1] = ...`
3741+
// `[a]` in `[[a] = 1] = ...`
3742+
// `a.b` in `[a.b = 1] = ...`
3743+
// `a[0]` in `[a[0] = 1] = ...`
3744+
return GetTargetOfBindingOrAssignmentElement(bindingElement.AsBinaryExpression().Left)
3745+
}
3746+
3747+
if IsSpreadElement(bindingElement) {
3748+
// `a` in `[...a] = ...`
3749+
return GetTargetOfBindingOrAssignmentElement(bindingElement.AsSpreadElement().Expression)
3750+
}
3751+
3752+
// `a` in `[a] = ...`
3753+
// `{a}` in `[{a}] = ...`
3754+
// `[a]` in `[[a]] = ...`
3755+
// `a.b` in `[a.b] = ...`
3756+
// `a[0]` in `[a[0]] = ...`
3757+
return bindingElement
3758+
}
3759+
3760+
func TryGetPropertyNameOfBindingOrAssignmentElement(bindingElement *Node) *Node {
3761+
switch bindingElement.Kind {
3762+
case KindBindingElement:
3763+
// `a` in `let { a: b } = ...`
3764+
// `[a]` in `let { [a]: b } = ...`
3765+
// `"a"` in `let { "a": b } = ...`
3766+
// `1` in `let { 1: b } = ...`
3767+
if bindingElement.AsBindingElement().PropertyName != nil {
3768+
propertyName := bindingElement.AsBindingElement().PropertyName
3769+
// if ast.IsPrivateIdentifier(propertyName) {
3770+
// return Debug.failBadSyntaxKind(propertyName) // !!!
3771+
// }
3772+
if IsComputedPropertyName(propertyName) && IsStringOrNumericLiteralLike(propertyName.AsComputedPropertyName().Expression) {
3773+
return propertyName.AsComputedPropertyName().Expression
3774+
}
3775+
return propertyName
3776+
}
3777+
case KindPropertyAssignment:
3778+
// `a` in `({ a: b } = ...)`
3779+
// `[a]` in `({ [a]: b } = ...)`
3780+
// `"a"` in `({ "a": b } = ...)`
3781+
// `1` in `({ 1: b } = ...)`
3782+
if bindingElement.Name() != nil {
3783+
propertyName := bindingElement.Name()
3784+
// if ast.IsPrivateIdentifier(propertyName) {
3785+
// return Debug.failBadSyntaxKind(propertyName) // !!!
3786+
// }
3787+
if IsComputedPropertyName(propertyName) && IsStringOrNumericLiteralLike(propertyName.AsComputedPropertyName().Expression) {
3788+
return propertyName.AsComputedPropertyName().Expression
3789+
}
3790+
return propertyName
3791+
}
3792+
case KindSpreadAssignment:
3793+
// `a` in `({ ...a } = ...)`
3794+
// if ast.IsPrivateIdentifier(bindingElement.Name()) {
3795+
// return Debug.failBadSyntaxKind(bindingElement.Name()) // !!!
3796+
// }
3797+
return bindingElement.Name()
3798+
}
3799+
3800+
target := GetTargetOfBindingOrAssignmentElement(bindingElement)
3801+
if target != nil && IsPropertyName(target) {
3802+
return target
3803+
}
3804+
return nil
3805+
}
3806+
3807+
/**
3808+
* Walk an AssignmentPattern to determine if it contains object rest (`...`) syntax. We cannot rely on
3809+
* propagation of `TransformFlags.ContainsObjectRestOrSpread` since it isn't propagated by default in
3810+
* ObjectLiteralExpression and ArrayLiteralExpression since we do not know whether they belong to an
3811+
* AssignmentPattern at the time the nodes are parsed.
3812+
*/
3813+
func ContainsObjectRestOrSpread(node *Node) bool {
3814+
if node.SubtreeFacts()&SubtreeContainsObjectRestOrSpread != 0 {
3815+
return true
3816+
}
3817+
if node.SubtreeFacts()&SubtreeContainsESObjectRestOrSpread != 0 {
3818+
// check for nested spread assignments, otherwise '{ x: { a, ...b } = foo } = c'
3819+
// will not be correctly interpreted by the rest/spread transformer
3820+
for _, element := range GetElementsOfBindingOrAssignmentPattern(node) {
3821+
target := GetTargetOfBindingOrAssignmentElement(element)
3822+
if target != nil && IsAssignmentPattern(target) {
3823+
if target.SubtreeFacts()&SubtreeContainsObjectRestOrSpread != 0 {
3824+
return true
3825+
}
3826+
if target.SubtreeFacts()&SubtreeContainsESObjectRestOrSpread != 0 {
3827+
if ContainsObjectRestOrSpread(target) {
3828+
return true
3829+
}
3830+
}
3831+
}
3832+
}
3833+
}
3834+
return false
3835+
}
3836+
3837+
func IsEmptyObjectLiteral(expression *Node) bool {
3838+
return expression.Kind == KindObjectLiteralExpression && len(expression.AsObjectLiteralExpression().Properties.Nodes) == 0
3839+
}
3840+
3841+
func IsEmptyArrayLiteral(expression *Node) bool {
3842+
return expression.Kind == KindArrayLiteralExpression && len(expression.AsArrayLiteralExpression().Elements.Nodes) == 0
3843+
}
3844+
3845+
func GetRestIndicatorOfBindingOrAssignmentElement(bindingElement *Node) *Node {
3846+
switch bindingElement.Kind {
3847+
case KindParameter:
3848+
return bindingElement.AsParameterDeclaration().DotDotDotToken
3849+
case KindBindingElement:
3850+
return bindingElement.AsBindingElement().DotDotDotToken
3851+
case KindSpreadElement, KindSpreadAssignment:
3852+
return bindingElement
3853+
}
3854+
return nil
3855+
}

internal/compiler/emitter.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@ func (e *emitter) getDeclarationTransformers(emitContext *printer.EmitContext, d
5656
return []*declarations.DeclarationTransformer{transform}
5757
}
5858

59-
func getModuleTransformer(emitContext *printer.EmitContext, options *core.CompilerOptions, resolver binder.ReferenceResolver, getEmitModuleFormatOfFile func(file ast.HasFileName) core.ModuleKind) *transformers.Transformer {
60-
switch options.GetEmitModuleKind() {
59+
func getModuleTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
60+
switch opts.CompilerOptions.GetEmitModuleKind() {
6161
case core.ModuleKindPreserve:
6262
// `ESModuleTransformer` contains logic for preserving CJS input syntax in `--module preserve`
63-
return moduletransforms.NewESModuleTransformer(emitContext, options, resolver, getEmitModuleFormatOfFile)
63+
return moduletransforms.NewESModuleTransformer(opts)
6464

6565
case core.ModuleKindESNext,
6666
core.ModuleKindES2022,
@@ -70,10 +70,10 @@ func getModuleTransformer(emitContext *printer.EmitContext, options *core.Compil
7070
core.ModuleKindNode16,
7171
core.ModuleKindNodeNext,
7272
core.ModuleKindCommonJS:
73-
return moduletransforms.NewImpliedModuleTransformer(emitContext, options, resolver, getEmitModuleFormatOfFile)
73+
return moduletransforms.NewImpliedModuleTransformer(opts)
7474

7575
default:
76-
return moduletransforms.NewCommonJSModuleTransformer(emitContext, options, resolver, getEmitModuleFormatOfFile)
76+
return moduletransforms.NewCommonJSModuleTransformer(opts)
7777
}
7878
}
7979

@@ -94,32 +94,40 @@ func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHo
9494
referenceResolver = binder.NewReferenceResolver(options, binder.ReferenceResolverHooks{})
9595
}
9696

97+
opts := transformers.TransformOptions{
98+
Context: emitContext,
99+
CompilerOptions: options,
100+
Resolver: referenceResolver,
101+
EmitResolver: emitResolver,
102+
GetEmitModuleFormatOfFile: host.GetEmitModuleFormatOfFile,
103+
}
104+
97105
// transform TypeScript syntax
98106
{
99107
// erase types
100-
tx = append(tx, tstransforms.NewTypeEraserTransformer(emitContext, options))
108+
tx = append(tx, tstransforms.NewTypeEraserTransformer(&opts))
101109

102110
// elide imports
103111
if importElisionEnabled {
104-
tx = append(tx, tstransforms.NewImportElisionTransformer(emitContext, options, emitResolver))
112+
tx = append(tx, tstransforms.NewImportElisionTransformer(&opts))
105113
}
106114

107115
// transform `enum`, `namespace`, and parameter properties
108-
tx = append(tx, tstransforms.NewRuntimeSyntaxTransformer(emitContext, options, referenceResolver))
116+
tx = append(tx, tstransforms.NewRuntimeSyntaxTransformer(&opts))
109117
}
110118

111119
// !!! transform legacy decorator syntax
112120
if options.GetJSXTransformEnabled() {
113-
tx = append(tx, jsxtransforms.NewJSXTransformer(emitContext, options, emitResolver))
121+
tx = append(tx, jsxtransforms.NewJSXTransformer(&opts))
114122
}
115123

116-
downleveler := estransforms.GetESTransformer(options, emitContext)
124+
downleveler := estransforms.GetESTransformer(&opts)
117125
if downleveler != nil {
118126
tx = append(tx, downleveler)
119127
}
120128

121129
// transform module syntax
122-
tx = append(tx, getModuleTransformer(emitContext, options, referenceResolver, host.GetEmitModuleFormatOfFile))
130+
tx = append(tx, getModuleTransformer(&opts))
123131
return tx
124132
}
125133

0 commit comments

Comments
 (0)