Skip to content

Commit e772e8f

Browse files
authored
Add typesafe sync.Map wrapper (#777)
1 parent 74c01ca commit e772e8f

File tree

4 files changed

+86
-35
lines changed

4 files changed

+86
-35
lines changed

internal/collections/syncmap.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package collections
2+
3+
import "sync"
4+
5+
type SyncMap[K comparable, V any] struct {
6+
m sync.Map
7+
}
8+
9+
func (s *SyncMap[K, V]) Load(key K) (value V, ok bool) {
10+
val, ok := s.m.Load(key)
11+
if !ok {
12+
return
13+
}
14+
return val.(V), true
15+
}
16+
17+
func (s *SyncMap[K, V]) Store(key K, value V) {
18+
s.m.Store(key, value)
19+
}
20+
21+
func (s *SyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
22+
actualAny, loaded := s.m.LoadOrStore(key, value)
23+
return actualAny.(V), loaded
24+
}
25+
26+
func (s *SyncMap[K, V]) Delete(key K) {
27+
s.m.Delete(key)
28+
}
29+
30+
func (s *SyncMap[K, V]) Clear() {
31+
s.m.Clear()
32+
}
33+
34+
func (s *SyncMap[K, V]) Range(f func(key K, value V) bool) {
35+
s.m.Range(func(key, value any) bool {
36+
return f(key.(K), value.(V))
37+
})
38+
}
39+
40+
// Size returns the approximate number of items in the map.
41+
// Note that this is not a precise count, as the map may be modified
42+
// concurrently while this method is running.
43+
func (s *SyncMap[K, V]) Size() int {
44+
count := 0
45+
s.m.Range(func(_, _ any) bool {
46+
count++
47+
return true
48+
})
49+
return count
50+
}
51+
52+
func (s *SyncMap[K, V]) ToMap() map[K]V {
53+
m := make(map[K]V, s.Size())
54+
s.m.Range(func(key, value any) bool {
55+
m[key.(K)] = value.(V)
56+
return true
57+
})
58+
return m
59+
}

internal/project/documentregistry.go

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"sync"
55

66
"github.com/microsoft/typescript-go/internal/ast"
7+
"github.com/microsoft/typescript-go/internal/collections"
78
"github.com/microsoft/typescript-go/internal/core"
89
"github.com/microsoft/typescript-go/internal/parser"
910
"github.com/microsoft/typescript-go/internal/scanner"
@@ -34,7 +35,7 @@ type registryEntry struct {
3435
// multiple LanguageService instances.
3536
type documentRegistry struct {
3637
options tspath.ComparePathsOptions
37-
documents sync.Map
38+
documents collections.SyncMap[registryKey, *registryEntry]
3839
}
3940

4041
func newDocumentRegistry(options tspath.ComparePathsOptions) *documentRegistry {
@@ -70,8 +71,7 @@ func (r *documentRegistry) releaseDocument(file *ast.SourceFile, compilerOptions
7071
}
7172

7273
func (r *documentRegistry) releaseDocumentWithKey(key registryKey) {
73-
if entryAny, ok := r.documents.Load(key); ok {
74-
entry := entryAny.(*registryEntry)
74+
if entry, ok := r.documents.Load(key); ok {
7575
entry.mu.Lock()
7676
defer entry.mu.Unlock()
7777
entry.refCount--
@@ -87,10 +87,9 @@ func (r *documentRegistry) getDocumentWorker(
8787
key registryKey,
8888
) *ast.SourceFile {
8989
scriptTarget := core.IfElse(scriptInfo.scriptKind == core.ScriptKindJSON, core.ScriptTargetJSON, compilerOptions.GetEmitScriptTarget())
90-
if entryAny, ok := r.documents.Load(key); ok {
90+
if entry, ok := r.documents.Load(key); ok {
9191
// We have an entry for this file. However, it may be for a different version of
9292
// the script snapshot. If so, update it appropriately.
93-
entry := entryAny.(*registryEntry)
9493
if entry.sourceFile.Version != scriptInfo.version {
9594
sourceFile := parser.ParseSourceFile(scriptInfo.fileName, scriptInfo.path, scriptInfo.text, scriptTarget, scanner.JSDocParsingModeParseAll)
9695
sourceFile.Version = scriptInfo.version
@@ -104,11 +103,10 @@ func (r *documentRegistry) getDocumentWorker(
104103
// Have never seen this file with these settings. Create a new source file for it.
105104
sourceFile := parser.ParseSourceFile(scriptInfo.fileName, scriptInfo.path, scriptInfo.text, scriptTarget, scanner.JSDocParsingModeParseAll)
106105
sourceFile.Version = scriptInfo.version
107-
entryAny, _ := r.documents.LoadOrStore(key, &registryEntry{
106+
entry, _ := r.documents.LoadOrStore(key, &registryEntry{
108107
sourceFile: sourceFile,
109108
refCount: 0,
110109
})
111-
entry := entryAny.(*registryEntry)
112110
entry.mu.Lock()
113111
defer entry.mu.Unlock()
114112
entry.refCount++
@@ -118,10 +116,5 @@ func (r *documentRegistry) getDocumentWorker(
118116

119117
// size should only be used for testing.
120118
func (r *documentRegistry) size() int {
121-
count := 0
122-
r.documents.Range(func(_, _ any) bool {
123-
count++
124-
return true
125-
})
126-
return count
119+
return r.documents.Size()
127120
}

internal/testutil/harnessutil/harnessutil.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -429,19 +429,19 @@ type cachedCompilerHost struct {
429429
options *core.CompilerOptions
430430
}
431431

432-
var sourceFileCache sync.Map
432+
var sourceFileCache collections.SyncMap[sourceFileCacheKey, *ast.SourceFile]
433+
434+
type sourceFileCacheKey struct {
435+
core.SourceFileAffectingCompilerOptions
436+
fileName string
437+
path tspath.Path
438+
languageVersion core.ScriptTarget
439+
text string
440+
}
433441

434442
func (h *cachedCompilerHost) GetSourceFile(fileName string, path tspath.Path, languageVersion core.ScriptTarget) *ast.SourceFile {
435443
text, _ := h.FS().ReadFile(fileName)
436444

437-
type sourceFileCacheKey struct {
438-
core.SourceFileAffectingCompilerOptions
439-
fileName string
440-
path tspath.Path
441-
languageVersion core.ScriptTarget
442-
text string
443-
}
444-
445445
key := sourceFileCacheKey{
446446
SourceFileAffectingCompilerOptions: h.options.SourceFileAffecting(),
447447
fileName: fileName,
@@ -451,7 +451,7 @@ func (h *cachedCompilerHost) GetSourceFile(fileName string, path tspath.Path, la
451451
}
452452

453453
if cached, ok := sourceFileCache.Load(key); ok {
454-
return cached.(*ast.SourceFile)
454+
return cached
455455
}
456456

457457
// !!! dedupe with compiler.compilerHost
@@ -464,7 +464,7 @@ func (h *cachedCompilerHost) GetSourceFile(fileName string, path tspath.Path, la
464464
}
465465

466466
result, _ := sourceFileCache.LoadOrStore(key, sourceFile)
467-
return result.(*ast.SourceFile)
467+
return result
468468
}
469469

470470
func createCompilerHost(fs vfs.FS, defaultLibraryPath string, options *core.CompilerOptions, currentDirectory string) compiler.CompilerHost {

internal/vfs/cachedvfs/cachedvfs.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
package cachedvfs
22

33
import (
4-
"sync"
5-
4+
"github.com/microsoft/typescript-go/internal/collections"
65
"github.com/microsoft/typescript-go/internal/vfs"
76
)
87

98
type FS struct {
109
fs vfs.FS
1110

12-
directoryExistsCache sync.Map // map[string]bool
13-
fileExistsCache sync.Map // map[string]bool
14-
getAccessibleEntriesCache sync.Map // map[string]vfs.Entries
15-
realpathCache sync.Map // map[string]string
16-
statCache sync.Map // map[string]vfs.FileInfo
11+
directoryExistsCache collections.SyncMap[string, bool]
12+
fileExistsCache collections.SyncMap[string, bool]
13+
getAccessibleEntriesCache collections.SyncMap[string, vfs.Entries]
14+
realpathCache collections.SyncMap[string, string]
15+
statCache collections.SyncMap[string, vfs.FileInfo]
1716
}
1817

1918
var _ vfs.FS = (*FS)(nil)
@@ -32,7 +31,7 @@ func (fsys *FS) ClearCache() {
3231

3332
func (fsys *FS) DirectoryExists(path string) bool {
3433
if ret, ok := fsys.directoryExistsCache.Load(path); ok {
35-
return ret.(bool)
34+
return ret
3635
}
3736
ret := fsys.fs.DirectoryExists(path)
3837
fsys.directoryExistsCache.Store(path, ret)
@@ -41,7 +40,7 @@ func (fsys *FS) DirectoryExists(path string) bool {
4140

4241
func (fsys *FS) FileExists(path string) bool {
4342
if ret, ok := fsys.fileExistsCache.Load(path); ok {
44-
return ret.(bool)
43+
return ret
4544
}
4645
ret := fsys.fs.FileExists(path)
4746
fsys.fileExistsCache.Store(path, ret)
@@ -50,7 +49,7 @@ func (fsys *FS) FileExists(path string) bool {
5049

5150
func (fsys *FS) GetAccessibleEntries(path string) vfs.Entries {
5251
if ret, ok := fsys.getAccessibleEntriesCache.Load(path); ok {
53-
return ret.(vfs.Entries)
52+
return ret
5453
}
5554
ret := fsys.fs.GetAccessibleEntries(path)
5655
fsys.getAccessibleEntriesCache.Store(path, ret)
@@ -63,7 +62,7 @@ func (fsys *FS) ReadFile(path string) (contents string, ok bool) {
6362

6463
func (fsys *FS) Realpath(path string) string {
6564
if ret, ok := fsys.realpathCache.Load(path); ok {
66-
return ret.(string)
65+
return ret
6766
}
6867
ret := fsys.fs.Realpath(path)
6968
fsys.realpathCache.Store(path, ret)

0 commit comments

Comments
 (0)