Skip to content

Commit 148a0ef

Browse files
authored
Replace unsafe.Pointer for seen symbol tables with unique ID (#2712)
1 parent df56d26 commit 148a0ef

1 file changed

Lines changed: 50 additions & 23 deletions

File tree

internal/checker/symbolaccessibility.go

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package checker
22

33
import (
4-
"reflect"
54
"slices"
6-
"unsafe"
75

86
"github.com/microsoft/typescript-go/internal/ast"
97
"github.com/microsoft/typescript-go/internal/core"
@@ -143,7 +141,7 @@ func (ch *Checker) getWithAlternativeContainers(container *ast.Symbol, symbol *a
143141
container.Flags&leftMeaning == 0) &&
144142
container.Flags&ast.SymbolFlagsType != 0 &&
145143
ch.getDeclaredTypeOfSymbol(container).flags&TypeFlagsObject != 0 {
146-
ch.someSymbolTableInScope(enclosingDeclaration, func(t ast.SymbolTable, _ bool, _ bool, _ *ast.Node) bool {
144+
ch.someSymbolTableInScope(enclosingDeclaration, func(t ast.SymbolTable, _ symbolTableID, _ bool, _ bool, _ *ast.Node) bool {
147145
for _, s := range t {
148146
if s.Flags&leftMeaning != 0 && ch.getTypeOfSymbol(s) == ch.getDeclaredTypeOfSymbol(container) {
149147
firstVariableMatch = s
@@ -378,7 +376,7 @@ func (ch *Checker) getAccessibleSymbolChain(
378376
meaning ast.SymbolFlags,
379377
useOnlyExternalAliasing bool,
380378
) []*ast.Symbol {
381-
return ch.getAccessibleSymbolChainEx(accessibleSymbolChainContext{symbol, enclosingDeclaration, meaning, useOnlyExternalAliasing, make(map[ast.SymbolId]map[unsafe.Pointer]struct{})})
379+
return ch.getAccessibleSymbolChainEx(accessibleSymbolChainContext{symbol, enclosingDeclaration, meaning, useOnlyExternalAliasing, make(map[ast.SymbolId]map[symbolTableID]struct{})})
382380
}
383381

384382
func (ch *Checker) GetAccessibleSymbolChain(
@@ -395,7 +393,35 @@ type accessibleSymbolChainContext struct {
395393
enclosingDeclaration *ast.Node
396394
meaning ast.SymbolFlags
397395
useOnlyExternalAliasing bool
398-
visitedSymbolTablesMap map[ast.SymbolId]map[unsafe.Pointer]struct{}
396+
visitedSymbolTablesMap map[ast.SymbolId]map[symbolTableID]struct{}
397+
}
398+
399+
// symbolTableID uniquely identifies a symbol table by encoding its source.
400+
// The high 2 bits encode the kind (locals, exports, members, globals),
401+
// and the remaining bits encode the NodeId or SymbolId of the source.
402+
type symbolTableID uint64
403+
404+
const (
405+
stKindLocals symbolTableID = iota << 62
406+
stKindExports
407+
stKindMembers
408+
stKindGlobals
409+
)
410+
411+
func symbolTableIDFromLocals(node *ast.Node) symbolTableID {
412+
return stKindLocals | symbolTableID(ast.GetNodeId(node))
413+
}
414+
415+
func symbolTableIDFromExports(sym *ast.Symbol) symbolTableID {
416+
return stKindExports | symbolTableID(ast.GetSymbolId(sym))
417+
}
418+
419+
func symbolTableIDFromMembers(sym *ast.Symbol) symbolTableID {
420+
return stKindMembers | symbolTableID(ast.GetSymbolId(sym))
421+
}
422+
423+
func symbolTableIDFromGlobals() symbolTableID {
424+
return stKindGlobals
399425
}
400426

401427
func (ch *Checker) getAccessibleSymbolChainEx(ctx accessibleSymbolChainContext) []*ast.Symbol {
@@ -407,7 +433,7 @@ func (ch *Checker) getAccessibleSymbolChainEx(ctx accessibleSymbolChainContext)
407433
}
408434
// Go from enclosingDeclaration to the first scope we check, so the cache is keyed off the scope and thus shared more
409435
var firstRelevantLocation *ast.Node
410-
ch.someSymbolTableInScope(ctx.enclosingDeclaration, func(_ ast.SymbolTable, _ bool, _ bool, node *ast.Node) bool {
436+
ch.someSymbolTableInScope(ctx.enclosingDeclaration, func(_ ast.SymbolTable, _ symbolTableID, _ bool, _ bool, node *ast.Node) bool {
411437
firstRelevantLocation = node
412438
return true
413439
})
@@ -423,8 +449,8 @@ func (ch *Checker) getAccessibleSymbolChainEx(ctx accessibleSymbolChainContext)
423449

424450
var result []*ast.Symbol
425451

426-
ch.someSymbolTableInScope(ctx.enclosingDeclaration, func(t ast.SymbolTable, ignoreQualification bool, isLocalNameLookup bool, _ *ast.Node) bool {
427-
res := ch.getAccessibleSymbolChainFromSymbolTable(ctx, t, ignoreQualification, isLocalNameLookup)
452+
ch.someSymbolTableInScope(ctx.enclosingDeclaration, func(t ast.SymbolTable, tableId symbolTableID, ignoreQualification bool, isLocalNameLookup bool, _ *ast.Node) bool {
453+
res := ch.getAccessibleSymbolChainFromSymbolTable(ctx, t, tableId, ignoreQualification, isLocalNameLookup)
428454
if len(res) > 0 {
429455
result = res
430456
return true
@@ -438,30 +464,30 @@ func (ch *Checker) getAccessibleSymbolChainEx(ctx accessibleSymbolChainContext)
438464
/**
439465
* @param {ignoreQualification} boolean Set when a symbol is being looked for through the exports of another symbol (meaning we have a route to qualify it already)
440466
*/
441-
func (ch *Checker) getAccessibleSymbolChainFromSymbolTable(ctx accessibleSymbolChainContext, t ast.SymbolTable, ignoreQualification bool, isLocalNameLookup bool) []*ast.Symbol {
467+
func (ch *Checker) getAccessibleSymbolChainFromSymbolTable(ctx accessibleSymbolChainContext, t ast.SymbolTable, tableId symbolTableID, ignoreQualification bool, isLocalNameLookup bool) []*ast.Symbol {
442468
symId := ast.GetSymbolId(ctx.symbol)
443469
visitedSymbolTables, ok := ctx.visitedSymbolTablesMap[symId]
444470
if !ok {
445-
visitedSymbolTables = make(map[unsafe.Pointer]struct{})
471+
visitedSymbolTables = make(map[symbolTableID]struct{})
446472
ctx.visitedSymbolTablesMap[symId] = visitedSymbolTables
447473
}
448474

449-
id := reflect.ValueOf(t).UnsafePointer() // TODO: Is this seriously the only way to check reference equality of maps?
450-
_, present := visitedSymbolTables[id]
475+
_, present := visitedSymbolTables[tableId]
451476
if present {
452477
return nil
453478
}
454-
visitedSymbolTables[id] = struct{}{}
479+
visitedSymbolTables[tableId] = struct{}{}
455480

456-
res := ch.trySymbolTable(ctx, t, ignoreQualification, isLocalNameLookup)
481+
res := ch.trySymbolTable(ctx, t, tableId == stKindGlobals, ignoreQualification, isLocalNameLookup)
457482

458-
delete(visitedSymbolTables, id)
483+
delete(visitedSymbolTables, tableId)
459484
return res
460485
}
461486

462487
func (ch *Checker) trySymbolTable(
463488
ctx accessibleSymbolChainContext,
464489
symbols ast.SymbolTable,
490+
isGlobals bool,
465491
ignoreQualification bool,
466492
isLocalNameLookup bool,
467493
) []*ast.Symbol {
@@ -506,7 +532,7 @@ func (ch *Checker) trySymbolTable(
506532
}
507533

508534
// If there's no result and we're looking at the global symbol table, treat `globalThis` like an alias and try to lookup thru that
509-
if reflect.ValueOf(ch.globals).UnsafePointer() == reflect.ValueOf(symbols).UnsafePointer() {
535+
if isGlobals {
510536
return ch.getCandidateListForSymbol(ctx, ch.globalThisSymbol, ch.globalThisSymbol, ignoreQualification)
511537
}
512538
return nil
@@ -553,7 +579,8 @@ func (ch *Checker) getCandidateListForSymbol(
553579
if candidateTable == nil {
554580
return nil
555581
}
556-
accessibleSymbolsFromExports := ch.getAccessibleSymbolChainFromSymbolTable(ctx, candidateTable /*ignoreQualification*/, true, false)
582+
candidateTableId := symbolTableIDFromExports(resolvedImportedSymbol)
583+
accessibleSymbolsFromExports := ch.getAccessibleSymbolChainFromSymbolTable(ctx, candidateTable, candidateTableId /*ignoreQualification*/, true, false)
557584
if len(accessibleSymbolsFromExports) == 0 {
558585
return nil
559586
}
@@ -606,7 +633,7 @@ func (ch *Checker) canQualifySymbol(
606633

607634
func (ch *Checker) needsQualification(symbol *ast.Symbol, enclosingDeclaration *ast.Node, meaning ast.SymbolFlags) bool {
608635
qualify := false
609-
ch.someSymbolTableInScope(enclosingDeclaration, func(symbolTable ast.SymbolTable, _ bool, _ bool, _ *ast.Node) bool {
636+
ch.someSymbolTableInScope(enclosingDeclaration, func(symbolTable ast.SymbolTable, _ symbolTableID, _ bool, _ bool, _ *ast.Node) bool {
610637
// If symbol of this name is not available in the symbol table we are ok
611638
res, ok := symbolTable[symbol.Name]
612639
if !ok || res == nil {
@@ -664,12 +691,12 @@ func isPropertyOrMethodDeclarationSymbol(symbol *ast.Symbol) bool {
664691

665692
func (ch *Checker) someSymbolTableInScope(
666693
enclosingDeclaration *ast.Node,
667-
callback func(symbolTable ast.SymbolTable, ignoreQualification bool, isLocalNameLookup bool, scopeNode *ast.Node) bool,
694+
callback func(symbolTable ast.SymbolTable, tableId symbolTableID, ignoreQualification bool, isLocalNameLookup bool, scopeNode *ast.Node) bool,
668695
) bool {
669696
for location := enclosingDeclaration; location != nil; location = location.Parent {
670697
// Locals of a source file are not in scope (because they get merged into the global symbol table)
671698
if canHaveLocals(location) && location.Locals() != nil && !ast.IsGlobalSourceFile(location) {
672-
if callback(location.Locals(), false, true, location) {
699+
if callback(location.Locals(), symbolTableIDFromLocals(location.AsNode()), false, true, location) {
673700
return true
674701
}
675702
}
@@ -679,7 +706,7 @@ func (ch *Checker) someSymbolTableInScope(
679706
break
680707
}
681708
sym := ch.getSymbolOfDeclaration(location)
682-
if callback(sym.Exports, false, true, location) {
709+
if callback(sym.Exports, symbolTableIDFromExports(sym), false, true, location) {
683710
return true
684711
}
685712
case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration:
@@ -701,13 +728,13 @@ func (ch *Checker) someSymbolTableInScope(
701728
table[key] = memberSymbol
702729
}
703730
}
704-
if table != nil && callback(table, false, false, location) {
731+
if table != nil && callback(table, symbolTableIDFromMembers(sym), false, false, location) {
705732
return true
706733
}
707734
}
708735
}
709736

710-
return callback(ch.globals, false, true, nil)
737+
return callback(ch.globals, symbolTableIDFromGlobals(), false, true, nil)
711738
}
712739

713740
/**

0 commit comments

Comments
 (0)