Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3d4a760
The object spread part of object rest/spread
weswigham Jul 15, 2025
a18f522
Core res downlevel ported, still need to transform function-like para…
weswigham Jul 29, 2025
6d5334a
Refactor flattenContext back into existence, but more go-like, small …
weswigham Jul 30, 2025
49ca9a1
Add missing non-debug !!!s
weswigham Aug 6, 2025
93f7690
Add missing ctor indirection
weswigham Aug 6, 2025
83785d6
Another missing binding/assignment indirection that ends up being imp…
weswigham Aug 6, 2025
3c55c1a
Fix sequencing and nil crashes
weswigham Aug 6, 2025
6db0fc6
We supposedly dont support es5 so the helper should be unreachable, b…
weswigham Aug 6, 2025
f9fb178
Detect rest in assignment expressions correctly, fix helper field nam…
weswigham Aug 6, 2025
9cb6894
Fix mistaken skipInitializers flag set crash
weswigham Aug 6, 2025
8645a89
Lexical vs variable environment is more strictly named now, use the c…
weswigham Aug 6, 2025
42d5bf6
Fix off by one in spread emit logic
weswigham Aug 6, 2025
6fa4dba
Fix initializers on parameters following destructured rest params
weswigham Aug 6, 2025
5943f57
Fix and comment on subtree fact flag usage a bit more - these are a l…
weswigham Aug 7, 2025
a4387d1
Accept baselines
weswigham Aug 7, 2025
5549c5f
Fix lints
weswigham Aug 7, 2025
24d7afd
Merge branch 'main' into objspread
weswigham Aug 7, 2025
8acf8d0
ctx is now threaded here, no need to TODO
weswigham Aug 7, 2025
9ef5d80
FullSignature change reconcile
weswigham Aug 7, 2025
7637ff5
Merge branch 'main' into objspread
weswigham Aug 15, 2025
8b29367
Use options object instead of context
weswigham Aug 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 5 additions & 5 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -2754,7 +2754,7 @@ func (node *ForInOrOfStatement) computeSubtreeFacts() SubtreeFacts {
return propagateSubtreeFacts(node.Initializer) |
propagateSubtreeFacts(node.Expression) |
propagateSubtreeFacts(node.Statement) |
core.IfElse(node.AwaitModifier != nil, SubtreeContainsES2018, SubtreeFactsNone)
core.IfElse(node.AwaitModifier != nil, SubtreeContainsForAwaitOrAsyncGenerator, SubtreeFactsNone)
}

func IsForInStatement(node *Node) bool {
Expand Down Expand Up @@ -3651,7 +3651,7 @@ func (node *BindingElement) computeSubtreeFacts() SubtreeFacts {
return propagateSubtreeFacts(node.PropertyName) |
propagateSubtreeFacts(node.name) |
propagateSubtreeFacts(node.Initializer) |
core.IfElse(node.DotDotDotToken != nil, SubtreeContainsRest, SubtreeFactsNone)
core.IfElse(node.DotDotDotToken != nil, SubtreeContainsRestOrSpread, SubtreeFactsNone)
}

func IsBindingElement(node *Node) bool {
Expand Down Expand Up @@ -6039,7 +6039,7 @@ func (node *YieldExpression) Clone(f NodeFactoryCoercible) *Node {
}

func (node *YieldExpression) computeSubtreeFacts() SubtreeFacts {
return propagateSubtreeFacts(node.Expression) | SubtreeContainsES2018
return propagateSubtreeFacts(node.Expression) | SubtreeContainsForAwaitOrAsyncGenerator
}

// ArrowFunction
Expand Down Expand Up @@ -6661,7 +6661,7 @@ func (node *SpreadElement) Clone(f NodeFactoryCoercible) *Node {
}

func (node *SpreadElement) computeSubtreeFacts() SubtreeFacts {
return propagateSubtreeFacts(node.Expression)
return propagateSubtreeFacts(node.Expression) | SubtreeContainsRestOrSpread
}

func IsSpreadElement(node *Node) bool {
Expand Down Expand Up @@ -6984,7 +6984,7 @@ func (node *SpreadAssignment) Clone(f NodeFactoryCoercible) *Node {
}

func (node *SpreadAssignment) computeSubtreeFacts() SubtreeFacts {
return propagateSubtreeFacts(node.Expression) | SubtreeContainsES2018 | SubtreeContainsObjectRestOrSpread
return propagateSubtreeFacts(node.Expression) | SubtreeContainsESObjectRestOrSpread | SubtreeContainsObjectRestOrSpread
}

func IsSpreadAssignment(node *Node) bool {
Expand Down
16 changes: 8 additions & 8 deletions internal/ast/subtreefacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const (
SubtreeContainsNullishCoalescing
SubtreeContainsOptionalChaining
SubtreeContainsMissingCatchClauseVariable
SubtreeContainsESObjectRestOrSpread
SubtreeContainsESObjectRestOrSpread // subtree has a `...` somewhere inside it, never cleared
SubtreeContainsForAwaitOrAsyncGenerator
SubtreeContainsAnyAwait
SubtreeContainsExponentiationOperator
Expand All @@ -30,8 +30,8 @@ const (

SubtreeContainsLexicalThis
SubtreeContainsLexicalSuper
SubtreeContainsRest
SubtreeContainsObjectRestOrSpread
SubtreeContainsRestOrSpread // marker on any `...` - cleared on binding pattern exit
SubtreeContainsObjectRestOrSpread // marker on any `{...x}` - cleared on most scope exits
SubtreeContainsAwait
SubtreeContainsDynamicImport
SubtreeContainsClassFields
Expand Down Expand Up @@ -76,7 +76,7 @@ const (
SubtreeExclusionsVariableDeclarationList = SubtreeExclusionsNode | SubtreeContainsObjectRestOrSpread
SubtreeExclusionsParameter = SubtreeExclusionsNode
SubtreeExclusionsCatchClause = SubtreeExclusionsNode | SubtreeContainsObjectRestOrSpread
SubtreeExclusionsBindingPattern = SubtreeExclusionsNode | SubtreeContainsRest
SubtreeExclusionsBindingPattern = SubtreeExclusionsNode | SubtreeContainsRestOrSpread

// Masks
// - Additional bitmasks
Expand All @@ -94,15 +94,15 @@ func propagateEraseableSyntaxSubtreeFacts(child *TypeNode) SubtreeFacts {

func propagateObjectBindingElementSubtreeFacts(child *BindingElementNode) SubtreeFacts {
facts := propagateSubtreeFacts(child)
if facts&SubtreeContainsRest != 0 {
facts &= ^SubtreeContainsRest
facts |= SubtreeContainsObjectRestOrSpread
if facts&SubtreeContainsRestOrSpread != 0 {
facts &= ^SubtreeContainsRestOrSpread
facts |= SubtreeContainsObjectRestOrSpread | SubtreeContainsESObjectRestOrSpread
}
return facts
}

func propagateBindingElementSubtreeFacts(child *BindingElementNode) SubtreeFacts {
return propagateSubtreeFacts(child) & ^SubtreeContainsRest
return propagateSubtreeFacts(child) & ^SubtreeContainsRestOrSpread
}

func propagateSubtreeFacts(child *Node) SubtreeFacts {
Expand Down
199 changes: 199 additions & 0 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -3635,3 +3635,202 @@ func GetSemanticJsxChildren(children []*JsxChild) []*JsxChild {
}
})
}

func IsAssignmentPattern(node *Node) bool {
return node.Kind == KindArrayLiteralExpression || node.Kind == KindObjectLiteralExpression
}

func GetElementsOfBindingOrAssignmentPattern(name *Node) []*Node {
switch name.Kind {
case KindObjectBindingPattern, KindArrayBindingPattern:
// `a` in `{a}`
// `a` in `[a]`
return name.AsBindingPattern().Elements.Nodes
case KindArrayLiteralExpression:
// `a` in `[a]`
return name.AsArrayLiteralExpression().Elements.Nodes
case KindObjectLiteralExpression:
// `a` in `{a}`
return name.AsObjectLiteralExpression().Properties.Nodes
}
return nil
}

func IsDeclarationBindingElement(bindingElement *Node) bool {
switch bindingElement.Kind {
case KindVariableDeclaration, KindParameter, KindBindingElement:
return true
default:
return false
}
}

/**
* Gets the name of an BindingOrAssignmentElement.
*/
func GetTargetOfBindingOrAssignmentElement(bindingElement *Node) *Node {
if IsDeclarationBindingElement(bindingElement) {
// `a` in `let { a } = ...`
// `a` in `let { a = 1 } = ...`
// `b` in `let { a: b } = ...`
// `b` in `let { a: b = 1 } = ...`
// `a` in `let { ...a } = ...`
// `{b}` in `let { a: {b} } = ...`
// `{b}` in `let { a: {b} = 1 } = ...`
// `[b]` in `let { a: [b] } = ...`
// `[b]` in `let { a: [b] = 1 } = ...`
// `a` in `let [a] = ...`
// `a` in `let [a = 1] = ...`
// `a` in `let [...a] = ...`
// `{a}` in `let [{a}] = ...`
// `{a}` in `let [{a} = 1] = ...`
// `[a]` in `let [[a]] = ...`
// `[a]` in `let [[a] = 1] = ...`
return bindingElement.Name()
}

if IsObjectLiteralElement(bindingElement) {
switch bindingElement.Kind {
case KindPropertyAssignment:
// `b` in `({ a: b } = ...)`
// `b` in `({ a: b = 1 } = ...)`
// `{b}` in `({ a: {b} } = ...)`
// `{b}` in `({ a: {b} = 1 } = ...)`
// `[b]` in `({ a: [b] } = ...)`
// `[b]` in `({ a: [b] = 1 } = ...)`
// `b.c` in `({ a: b.c } = ...)`
// `b.c` in `({ a: b.c = 1 } = ...)`
// `b[0]` in `({ a: b[0] } = ...)`
// `b[0]` in `({ a: b[0] = 1 } = ...)`
return GetTargetOfBindingOrAssignmentElement(bindingElement.Initializer())
case KindShorthandPropertyAssignment:
// `a` in `({ a } = ...)`
// `a` in `({ a = 1 } = ...)`
return bindingElement.Name()
case KindSpreadAssignment:
// `a` in `({ ...a } = ...)`
return GetTargetOfBindingOrAssignmentElement(bindingElement.AsSpreadAssignment().Expression)
}

// no target
return nil
}

if IsAssignmentExpression(bindingElement /*excludeCompoundAssignment*/, true) {
// `a` in `[a = 1] = ...`
// `{a}` in `[{a} = 1] = ...`
// `[a]` in `[[a] = 1] = ...`
// `a.b` in `[a.b = 1] = ...`
// `a[0]` in `[a[0] = 1] = ...`
return GetTargetOfBindingOrAssignmentElement(bindingElement.AsBinaryExpression().Left)
}

if IsSpreadElement(bindingElement) {
// `a` in `[...a] = ...`
return GetTargetOfBindingOrAssignmentElement(bindingElement.AsSpreadElement().Expression)
}

// `a` in `[a] = ...`
// `{a}` in `[{a}] = ...`
// `[a]` in `[[a]] = ...`
// `a.b` in `[a.b] = ...`
// `a[0]` in `[a[0]] = ...`
return bindingElement
}

func TryGetPropertyNameOfBindingOrAssignmentElement(bindingElement *Node) *Node {
switch bindingElement.Kind {
case KindBindingElement:
// `a` in `let { a: b } = ...`
// `[a]` in `let { [a]: b } = ...`
// `"a"` in `let { "a": b } = ...`
// `1` in `let { 1: b } = ...`
if bindingElement.AsBindingElement().PropertyName != nil {
propertyName := bindingElement.AsBindingElement().PropertyName
// if ast.IsPrivateIdentifier(propertyName) {
// return Debug.failBadSyntaxKind(propertyName) // !!!
// }
if IsComputedPropertyName(propertyName) && IsStringOrNumericLiteralLike(propertyName.AsComputedPropertyName().Expression) {
return propertyName.AsComputedPropertyName().Expression
}
return propertyName
}
case KindPropertyAssignment:
// `a` in `({ a: b } = ...)`
// `[a]` in `({ [a]: b } = ...)`
// `"a"` in `({ "a": b } = ...)`
// `1` in `({ 1: b } = ...)`
if bindingElement.Name() != nil {
propertyName := bindingElement.Name()
// if ast.IsPrivateIdentifier(propertyName) {
// return Debug.failBadSyntaxKind(propertyName) // !!!
// }
if IsComputedPropertyName(propertyName) && IsStringOrNumericLiteralLike(propertyName.AsComputedPropertyName().Expression) {
return propertyName.AsComputedPropertyName().Expression
}
return propertyName
}
case KindSpreadAssignment:
// `a` in `({ ...a } = ...)`
// if ast.IsPrivateIdentifier(bindingElement.Name()) {
// return Debug.failBadSyntaxKind(bindingElement.Name()) // !!!
// }
return bindingElement.Name()
}

target := GetTargetOfBindingOrAssignmentElement(bindingElement)
if target != nil && IsPropertyName(target) {
return target
}
return nil
}

/**
* Walk an AssignmentPattern to determine if it contains object rest (`...`) syntax. We cannot rely on
* propagation of `TransformFlags.ContainsObjectRestOrSpread` since it isn't propagated by default in
* ObjectLiteralExpression and ArrayLiteralExpression since we do not know whether they belong to an
* AssignmentPattern at the time the nodes are parsed.
*/
func ContainsObjectRestOrSpread(node *Node) bool {
if node.SubtreeFacts()&SubtreeContainsObjectRestOrSpread != 0 {
return true
}
if node.SubtreeFacts()&SubtreeContainsESObjectRestOrSpread != 0 {
// check for nested spread assignments, otherwise '{ x: { a, ...b } = foo } = c'
// will not be correctly interpreted by the rest/spread transformer
for _, element := range GetElementsOfBindingOrAssignmentPattern(node) {
target := GetTargetOfBindingOrAssignmentElement(element)
if target != nil && IsAssignmentPattern(target) {
if target.SubtreeFacts()&SubtreeContainsObjectRestOrSpread != 0 {
return true
}
if target.SubtreeFacts()&SubtreeContainsESObjectRestOrSpread != 0 {
if ContainsObjectRestOrSpread(target) {
return true
}
}
}
}
}
return false
}

func IsEmptyObjectLiteral(expression *Node) bool {
return expression.Kind == KindObjectLiteralExpression && len(expression.AsObjectLiteralExpression().Properties.Nodes) == 0
}

func IsEmptyArrayLiteral(expression *Node) bool {
return expression.Kind == KindArrayLiteralExpression && len(expression.AsArrayLiteralExpression().Elements.Nodes) == 0
}

func GetRestIndicatorOfBindingOrAssignmentElement(bindingElement *Node) *Node {
switch bindingElement.Kind {
case KindParameter:
return bindingElement.AsParameterDeclaration().DotDotDotToken
case KindBindingElement:
return bindingElement.AsBindingElement().DotDotDotToken
case KindSpreadElement, KindSpreadAssignment:
return bindingElement
}
return nil
}
Loading
Loading