Skip to content

Commit 77ad8f7

Browse files
authored
Add {}/class-expression expandos (#885)
1 parent 892896b commit 77ad8f7

File tree

224 files changed

+2129
-2403
lines changed

Some content is hidden

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

224 files changed

+2129
-2403
lines changed

internal/ast/utilities.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,17 +1493,18 @@ func GetAssignmentDeclarationKind(bin *BinaryExpression) JSDeclarationKind {
14931493
if bin.OperatorToken.Kind != KindEqualsToken || !IsAccessExpression(bin.Left) {
14941494
return JSDeclarationKindNone
14951495
}
1496-
if IsModuleExportsAccessExpression(bin.Left) {
1496+
if IsInJSFile(bin.Left) && IsModuleExportsAccessExpression(bin.Left) {
14971497
return JSDeclarationKindModuleExports
1498-
} else if (IsModuleExportsAccessExpression(bin.Left.Expression()) || IsExportsIdentifier(bin.Left.Expression())) &&
1498+
} else if IsInJSFile(bin.Left) &&
1499+
(IsModuleExportsAccessExpression(bin.Left.Expression()) || IsExportsIdentifier(bin.Left.Expression())) &&
14991500
GetElementOrPropertyAccessName(bin.Left) != nil {
15001501
return JSDeclarationKindExportsProperty
15011502
}
1502-
if bin.Left.Expression().Kind == KindThisKeyword {
1503+
if IsInJSFile(bin.Left) && bin.Left.Expression().Kind == KindThisKeyword {
15031504
return JSDeclarationKindThisProperty
15041505
}
1505-
if bin.Left.Kind == KindPropertyAccessExpression && IsIdentifier(bin.Left.Expression()) && IsIdentifier(bin.Left.Name()) ||
1506-
bin.Left.Kind == KindElementAccessExpression && IsIdentifier(bin.Left.Expression()) {
1506+
if bin.Left.Kind == KindPropertyAccessExpression && IsEntityNameExpressionEx(bin.Left.Expression(), IsInJSFile(bin.Left)) && IsIdentifier(bin.Left.Name()) ||
1507+
bin.Left.Kind == KindElementAccessExpression && IsEntityNameExpressionEx(bin.Left.Expression(), IsInJSFile(bin.Left)) {
15071508
return JSDeclarationKindProperty
15081509
}
15091510
return JSDeclarationKindNone
@@ -1536,13 +1537,33 @@ func IsDynamicName(name *Node) bool {
15361537
}
15371538

15381539
func IsEntityNameExpression(node *Node) bool {
1539-
return node.Kind == KindIdentifier || IsPropertyAccessEntityNameExpression(node)
1540+
return IsEntityNameExpressionEx(node, false /*allowJS*/)
15401541
}
15411542

1542-
func IsPropertyAccessEntityNameExpression(node *Node) bool {
1543+
func IsEntityNameExpressionEx(node *Node, allowJS bool) bool {
1544+
if node.Kind == KindIdentifier || IsPropertyAccessEntityNameExpression(node, allowJS) {
1545+
return true
1546+
}
1547+
if allowJS {
1548+
return node.Kind == KindThisKeyword || isElementAccessEntityNameExpression(node, allowJS)
1549+
}
1550+
return false
1551+
}
1552+
1553+
func IsPropertyAccessEntityNameExpression(node *Node, allowJS bool) bool {
15431554
if node.Kind == KindPropertyAccessExpression {
15441555
expr := node.AsPropertyAccessExpression()
1545-
return expr.Name().Kind == KindIdentifier && IsEntityNameExpression(expr.Expression)
1556+
return expr.Name().Kind == KindIdentifier && IsEntityNameExpressionEx(expr.Expression, allowJS)
1557+
}
1558+
return false
1559+
}
1560+
1561+
func isElementAccessEntityNameExpression(node *Node, allowJS bool) bool {
1562+
if node.Kind == KindElementAccessExpression {
1563+
expr := node.AsElementAccessExpression()
1564+
if IsStringOrNumericLiteralLike(SkipParentheses(expr.ArgumentExpression)) {
1565+
return IsEntityNameExpressionEx(expr.Expression, allowJS)
1566+
}
15461567
}
15471568
return false
15481569
}

internal/binder/binder.go

Lines changed: 119 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const (
3737
ContainerFlagsHasLocals ContainerFlags = 1 << 5
3838
ContainerFlagsIsInterface ContainerFlags = 1 << 6
3939
ContainerFlagsIsObjectLiteralOrClassExpressionMethodOrAccessor ContainerFlags = 1 << 7
40+
ContainerFlagsIsThisContainer ContainerFlags = 1 << 8
4041
)
4142

4243
type Binder struct {
@@ -49,7 +50,7 @@ type Binder struct {
4950

5051
parent *ast.Node
5152
container *ast.Node
52-
thisParentContainer *ast.Node
53+
thisContainer *ast.Node
5354
blockScopeContainer *ast.Node
5455
lastContainer *ast.Node
5556
currentFlow *ast.FlowNode
@@ -625,7 +626,7 @@ func (b *Binder) bind(node *ast.Node) bool {
625626
case ast.KindBinaryExpression:
626627
switch ast.GetAssignmentDeclarationKind(node.AsBinaryExpression()) {
627628
case ast.JSDeclarationKindProperty:
628-
b.bindFunctionPropertyAssignment(node)
629+
b.bindExpandoPropertyAssignment(node)
629630
case ast.JSDeclarationKindThisProperty:
630631
b.bindThisPropertyAssignment(node)
631632
}
@@ -1014,74 +1015,104 @@ func addLateBoundAssignmentDeclarationToSymbol(node *ast.Node, symbol *ast.Symbo
10141015
symbol.AssignmentDeclarationMembers.Add(node)
10151016
}
10161017

1017-
func (b *Binder) bindFunctionPropertyAssignment(node *ast.Node) {
1018+
func (b *Binder) bindExpandoPropertyAssignment(node *ast.Node) {
10181019
expr := node.AsBinaryExpression()
1019-
parentName := expr.Left.Expression().Text()
1020-
symbol := b.lookupName(parentName, b.blockScopeContainer)
1020+
parent := expr.Left.Expression()
1021+
symbol := b.lookupEntity(parent, b.blockScopeContainer)
10211022
if symbol == nil {
1022-
symbol = b.lookupName(parentName, b.container)
1023+
symbol = b.lookupEntity(parent, b.container)
10231024
}
1024-
if symbol != nil && symbol.ValueDeclaration != nil {
1025-
// For an assignment 'fn.xxx = ...', where 'fn' is a previously declared function or a previously
1026-
// declared const variable initialized with a function expression or arrow function, we add expando
1027-
// property declarations to the function's symbol.
1028-
var funcSymbol *ast.Symbol
1029-
switch {
1030-
case ast.IsFunctionDeclaration(symbol.ValueDeclaration):
1031-
funcSymbol = symbol
1032-
case ast.IsVariableDeclaration(symbol.ValueDeclaration) && symbol.ValueDeclaration.Parent.Flags&ast.NodeFlagsConst != 0:
1033-
initializer := symbol.ValueDeclaration.Initializer()
1034-
if initializer != nil && ast.IsFunctionExpressionOrArrowFunction(initializer) {
1035-
funcSymbol = initializer.Symbol()
1036-
}
1025+
if symbol = getInitializerSymbol(symbol); symbol != nil {
1026+
// Fix up parent pointers since we're going to use these nodes before we bind into them
1027+
setParent(expr.Left, node)
1028+
setParent(expr.Right, node)
1029+
if ast.HasDynamicName(node) {
1030+
b.bindAnonymousDeclaration(node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.InternalSymbolNameComputed)
1031+
addLateBoundAssignmentDeclarationToSymbol(node, symbol)
1032+
} else {
1033+
b.declareSymbol(ast.GetExports(symbol), symbol, node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.SymbolFlagsPropertyExcludes)
10371034
}
1038-
if funcSymbol != nil {
1039-
// Fix up parent pointers since we're going to use these nodes before we bind into them
1040-
setParent(expr.Left, node)
1041-
setParent(expr.Right, node)
1042-
if ast.HasDynamicName(node) {
1043-
b.bindAnonymousDeclaration(node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.InternalSymbolNameComputed)
1044-
addLateBoundAssignmentDeclarationToSymbol(node, funcSymbol)
1045-
} else {
1046-
b.declareSymbol(ast.GetExports(funcSymbol), funcSymbol, node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.SymbolFlagsPropertyExcludes)
1047-
}
1035+
}
1036+
}
1037+
1038+
func getInitializerSymbol(symbol *ast.Symbol) *ast.Symbol {
1039+
if symbol == nil || symbol.ValueDeclaration == nil {
1040+
return nil
1041+
}
1042+
declaration := symbol.ValueDeclaration
1043+
// For an assignment 'fn.xxx = ...', where 'fn' is a previously declared function or a previously
1044+
// declared const variable initialized with a function expression or arrow function, we add expando
1045+
// property declarations to the function's symbol.
1046+
// This also applies to class expressions and empty object literals.
1047+
switch {
1048+
case ast.IsFunctionDeclaration(declaration) || ast.IsInJSFile(declaration) && ast.IsClassDeclaration(declaration):
1049+
return symbol
1050+
case ast.IsVariableDeclaration(declaration) &&
1051+
(declaration.Parent.Flags&ast.NodeFlagsConst != 0 || ast.IsInJSFile(declaration)):
1052+
initializer := declaration.Initializer()
1053+
if isExpandoInitializer(initializer) {
1054+
return initializer.Symbol()
1055+
}
1056+
case ast.IsBinaryExpression(declaration) && ast.IsInJSFile(declaration):
1057+
initializer := declaration.AsBinaryExpression().Right
1058+
if isExpandoInitializer(initializer) {
1059+
return initializer.Symbol()
10481060
}
10491061
}
1062+
return nil
1063+
}
1064+
1065+
func isExpandoInitializer(initializer *ast.Node) bool {
1066+
if initializer == nil {
1067+
return false
1068+
}
1069+
if ast.IsFunctionExpressionOrArrowFunction(initializer) {
1070+
return true
1071+
} else if ast.IsInJSFile(initializer) {
1072+
return ast.IsClassExpression(initializer) || (ast.IsObjectLiteralExpression(initializer) && len(initializer.AsObjectLiteralExpression().Properties.Nodes) == 0)
1073+
}
1074+
return false
10501075
}
10511076

10521077
func (b *Binder) bindThisPropertyAssignment(node *ast.Node) {
10531078
if !ast.IsInJSFile(node) {
10541079
return
10551080
}
10561081
bin := node.AsBinaryExpression()
1057-
if ast.IsPropertyAccessExpression(bin.Left) && ast.IsPrivateIdentifier(bin.Left.AsPropertyAccessExpression().Name()) {
1082+
if ast.IsPropertyAccessExpression(bin.Left) && ast.IsPrivateIdentifier(bin.Left.AsPropertyAccessExpression().Name()) ||
1083+
b.thisContainer == nil {
10581084
return
10591085
}
1060-
thisContainer := ast.GetThisContainer(node /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/, false)
1061-
switch thisContainer.Kind {
1086+
if classSymbol, symbolTable := b.getThisClassAndSymbolTable(); symbolTable != nil {
1087+
if ast.HasDynamicName(node) {
1088+
b.declareSymbolEx(symbolTable, classSymbol, node, ast.SymbolFlagsProperty, ast.SymbolFlagsNone, true /*isReplaceableByMethod*/, true /*isComputedName*/)
1089+
addLateBoundAssignmentDeclarationToSymbol(node, classSymbol)
1090+
} else {
1091+
b.declareSymbolEx(symbolTable, classSymbol, node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.SymbolFlagsNone, true /*isReplaceableByMethod*/, false /*isComputedName*/)
1092+
}
1093+
} else if b.thisContainer.Kind != ast.KindFunctionDeclaration && b.thisContainer.Kind != ast.KindFunctionExpression {
1094+
// !!! constructor functions
1095+
panic("Unhandled case in bindThisPropertyAssignment: " + b.thisContainer.Kind.String())
1096+
}
1097+
}
1098+
1099+
func (b *Binder) getThisClassAndSymbolTable() (classSymbol *ast.Symbol, symbolTable ast.SymbolTable) {
1100+
if b.thisContainer == nil {
1101+
return nil, nil
1102+
}
1103+
switch b.thisContainer.Kind {
10621104
case ast.KindFunctionDeclaration, ast.KindFunctionExpression:
10631105
// !!! constructor functions
10641106
case ast.KindConstructor, ast.KindPropertyDeclaration, ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindClassStaticBlockDeclaration:
10651107
// this.property assignment in class member -- bind to the containing class
1066-
containingClass := thisContainer.Parent
1067-
classSymbol := containingClass.Symbol()
1068-
var symbolTable ast.SymbolTable
1069-
if ast.IsStatic(thisContainer) {
1070-
symbolTable = ast.GetExports(containingClass.Symbol())
1071-
} else {
1072-
symbolTable = ast.GetMembers(containingClass.Symbol())
1073-
}
1074-
if ast.HasDynamicName(node) {
1075-
b.declareSymbolEx(symbolTable, containingClass.Symbol(), node, ast.SymbolFlagsProperty, ast.SymbolFlagsNone, true /*isReplaceableByMethod*/, true /*isComputedName*/)
1076-
addLateBoundAssignmentDeclarationToSymbol(node, classSymbol)
1108+
classSymbol = b.thisContainer.Parent.Symbol()
1109+
if ast.IsStatic(b.thisContainer) {
1110+
symbolTable = ast.GetExports(classSymbol)
10771111
} else {
1078-
b.declareSymbolEx(symbolTable, containingClass.Symbol(), node, ast.SymbolFlagsProperty|ast.SymbolFlagsAssignment, ast.SymbolFlagsNone, true /*isReplaceableByMethod*/, false /*isComputedName*/)
1112+
symbolTable = ast.GetMembers(classSymbol)
10791113
}
1080-
case ast.KindSourceFile, ast.KindModuleDeclaration:
1081-
// top-level this.property as assignment to globals is no longer supported
1082-
default:
1083-
panic("Unhandled case in bindThisPropertyAssignment: " + thisContainer.Kind.String())
10841114
}
1115+
return classSymbol, symbolTable
10851116
}
10861117

10871118
func (b *Binder) bindEnumDeclaration(node *ast.Node) {
@@ -1213,16 +1244,35 @@ func (b *Binder) bindTypeParameter(node *ast.Node) {
12131244
}
12141245
}
12151246

1247+
func (b *Binder) lookupEntity(node *ast.Node, container *ast.Node) *ast.Symbol {
1248+
if ast.IsIdentifier(node) {
1249+
return b.lookupName(node.AsIdentifier().Text, container)
1250+
}
1251+
if ast.IsPropertyAccessExpression(node) && node.AsPropertyAccessExpression().Expression.Kind == ast.KindThisKeyword ||
1252+
ast.IsElementAccessExpression(node) && node.AsElementAccessExpression().Expression.Kind == ast.KindThisKeyword {
1253+
if _, symbolTable := b.getThisClassAndSymbolTable(); symbolTable != nil {
1254+
if name := ast.GetElementOrPropertyAccessName(node); name != nil {
1255+
return symbolTable[name.Text()]
1256+
}
1257+
}
1258+
return nil
1259+
}
1260+
if symbol := getInitializerSymbol(b.lookupEntity(node.Expression(), container)); symbol != nil && symbol.Exports != nil {
1261+
if name := ast.GetElementOrPropertyAccessName(node); name != nil {
1262+
return symbol.Exports[name.Text()]
1263+
}
1264+
}
1265+
return nil
1266+
}
1267+
12161268
func (b *Binder) lookupName(name string, container *ast.Node) *ast.Symbol {
1217-
localsContainer := container.LocalsContainerData()
1218-
if localsContainer != nil {
1219-
local := localsContainer.Locals[name]
1220-
if local != nil {
1269+
if localsContainer := container.LocalsContainerData(); localsContainer != nil {
1270+
if local := localsContainer.Locals[name]; local != nil {
12211271
return core.OrElse(local.ExportSymbol, local)
12221272
}
12231273
}
1224-
declaration := container.DeclarationData()
1225-
if declaration != nil && declaration.Symbol != nil {
1274+
1275+
if declaration := container.DeclarationData(); declaration != nil && declaration.Symbol != nil {
12261276
return declaration.Symbol.Exports[name]
12271277
}
12281278
return nil
@@ -1434,7 +1484,7 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) {
14341484
// and block-container. Then after we pop out of processing the children, we restore
14351485
// these saved values.
14361486
saveContainer := b.container
1437-
saveThisParentContainer := b.thisParentContainer
1487+
saveThisContainer := b.thisContainer
14381488
savedBlockScopeContainer := b.blockScopeContainer
14391489
// Depending on what kind of node this is, we may have to adjust the current container
14401490
// and block-container. If the current node is a container, then it is automatically
@@ -1454,9 +1504,6 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) {
14541504
// for it. We must clear this so we don't accidentally move any stale data forward from
14551505
// a previous compilation.
14561506
if containerFlags&ContainerFlagsIsContainer != 0 {
1457-
if node.Kind != ast.KindArrowFunction {
1458-
b.thisParentContainer = b.container
1459-
}
14601507
b.container = node
14611508
b.blockScopeContainer = node
14621509
if containerFlags&ContainerFlagsHasLocals != 0 {
@@ -1468,6 +1515,9 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) {
14681515
b.blockScopeContainer = node
14691516
b.addToContainerChain(node)
14701517
}
1518+
if containerFlags&ContainerFlagsIsThisContainer != 0 {
1519+
b.thisContainer = node
1520+
}
14711521
if containerFlags&ContainerFlagsIsControlFlowContainer != 0 {
14721522
saveCurrentFlow := b.currentFlow
14731523
saveBreakTarget := b.currentBreakTarget
@@ -1548,7 +1598,7 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) {
15481598
b.bindChildren(node)
15491599
}
15501600
b.container = saveContainer
1551-
b.thisParentContainer = saveThisParentContainer
1601+
b.thisContainer = saveThisContainer
15521602
b.blockScopeContainer = savedBlockScopeContainer
15531603
}
15541604

@@ -2574,19 +2624,24 @@ func GetContainerFlags(node *ast.Node) ContainerFlags {
25742624
return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals
25752625
case ast.KindGetAccessor, ast.KindSetAccessor, ast.KindMethodDeclaration:
25762626
if ast.IsObjectLiteralOrClassExpressionMethodOrAccessor(node) {
2577-
return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsObjectLiteralOrClassExpressionMethodOrAccessor
2627+
return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsObjectLiteralOrClassExpressionMethodOrAccessor | ContainerFlagsIsThisContainer
25782628
}
25792629
fallthrough
2580-
case ast.KindConstructor, ast.KindFunctionDeclaration, ast.KindMethodSignature, ast.KindCallSignature, ast.KindJSDocSignature,
2581-
ast.KindFunctionType, ast.KindConstructSignature, ast.KindConstructorType, ast.KindClassStaticBlockDeclaration:
2630+
case ast.KindConstructor, ast.KindClassStaticBlockDeclaration:
2631+
return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsThisContainer
2632+
case ast.KindMethodSignature, ast.KindCallSignature, ast.KindJSDocSignature, ast.KindFunctionType, ast.KindConstructSignature, ast.KindConstructorType:
25822633
return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike
2583-
case ast.KindFunctionExpression, ast.KindArrowFunction:
2634+
case ast.KindFunctionDeclaration:
2635+
return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsThisContainer
2636+
case ast.KindFunctionExpression:
2637+
return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsFunctionExpression | ContainerFlagsIsThisContainer
2638+
case ast.KindArrowFunction:
25842639
return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals | ContainerFlagsIsFunctionLike | ContainerFlagsIsFunctionExpression
25852640
case ast.KindModuleBlock:
25862641
return ContainerFlagsIsControlFlowContainer
25872642
case ast.KindPropertyDeclaration:
25882643
if node.AsPropertyDeclaration().Initializer != nil {
2589-
return ContainerFlagsIsControlFlowContainer
2644+
return ContainerFlagsIsControlFlowContainer | ContainerFlagsIsThisContainer
25902645
} else {
25912646
return ContainerFlagsNone
25922647
}

0 commit comments

Comments
 (0)