11package compiler
22
33import (
4+ "errors"
45 "fmt"
5- "sort"
66 "time"
77)
88
99type DeclCache struct {
10- decls map [string ]* Decl
10+ importPath string
11+ importedPaths []string
12+ importDecls []* Decl
13+ typeDecls []* Decl
14+ varDecls []* Decl
15+ funcDecls []* Decl
16+
1117 changed bool
1218 stats declCacheStats
1319}
1420
21+ // NewDeclCache creates a new empty enabled decl cache for the package with
22+ // the given import path.
23+ //
24+ // The given import path is used as a secondary check to ensure we do not get
25+ // hashing collisions when reading a cache from storage.
26+ func NewDeclCache (importPath string ) * DeclCache {
27+ return & DeclCache {importPath : importPath }
28+ }
29+
1530var declCacheGlobalStats declCacheStats
1631
1732type declCacheStats struct {
18- reads int // number of times Read was called (should be 0 or 1 unless global)
19- writes int // number of times Write was called (should be 0 or 1 unless global)
20- loadedCount int // number of declarations loaded from storage
21- addedCount int // number of declarations added to the cache
22- skippedCount int // number of declarations not cached (for being generic or non-unique)
23- usedCount int // number of times a declaration was retrieved from the cache
24- readDur time.Duration
25- writeDur time.Duration
33+ reads int // number of times Read was called (should be 0 or 1 unless global)
34+ writes int // number of times Write was called (should be 0 or 1 unless global)
35+ readCount int // number of declarations loaded from storage
36+ writeCount int // number of declarations written to storage
37+ readDur time.Duration
38+ writeDur time.Duration
2639}
2740
28- // Chacnged reports whether the cache has had declarations added to it since
29- // it was created or last read from storage or created empty.
30- //
31- // If there have been no changes, the cache does not need to be written back
32- // to storage. Typically declarations will only be added when the cache is
33- // empty and not loaded from storage since otherwise all the needed declarations
34- // (other than those not being cached) would already be present.
35- func (dc * DeclCache ) Changed () bool {
36- return dc != nil && dc .changed
37- }
41+ var (
42+ errReadDisabled = errors .New (`attempted to read a DeclCache that is disabled (nil)` )
43+ errWriteDisabled = errors .New (`attempted to write a DeclCache that is disabled (nil)` )
44+ )
3845
39- func (dc * DeclCache ) GetDecl (fullname string ) * Decl {
46+ // ImportPath is the import path for the package this cache is for.
47+ func (dc * DeclCache ) ImportPath () string {
4048 if dc == nil {
41- return nil // cache is disabled
49+ return `[ disabled]`
4250 }
43- if decl , ok := dc .decls [fullname ]; ok {
44- dc .stats .usedCount ++
45- declCacheGlobalStats .usedCount ++
51+ return dc .importPath
52+ }
4653
47- fmt .Printf (">>> GetDecl(%q): Found\n " , fullname ) // TODO(grantnelson-wf): REMOVE
48- return decl
49- }
54+ // Changed reports whether the cache has had declarations set to it since
55+ // it was created (or last read from or write to storage). If there have been
56+ // no changes, the cache does not need to be written back to storage.
57+ func (dc * DeclCache ) Changed () bool {
58+ return dc != nil && dc .changed
59+ }
5060
51- fmt .Printf (">>> GetDecl(%q): Miss\n " , fullname ) // TODO(grantnelson-wf): REMOVE
52- return nil
61+ // HasCache indicates that this cache contains at least one declaration in it.
62+ func (dc * DeclCache ) HasCache () bool {
63+ return dc .count () > 0
5364}
5465
55- func (dc * DeclCache ) PutDecl (decl * Decl ) {
66+ // count reports how many declarations are currently in the cache.
67+ func (dc * DeclCache ) count () int {
5668 if dc == nil {
57- return // cache is disabled
58- }
59-
60- if decl .ForGeneric {
61- // Do not cache declarations for generic instantiations.
62- // The type arguments may come from a package depending on this one
63- // and not on one of this package's dependencies.
64- //
65- // If one of this package's dependencies changes, the cache will not be used.
66- // However, currently, changes to packages depending on this one
67- // may change and this package's cache may still be used.
68- // Therefore, if the package depending on this one changes a type from
69- // being blocking to non-blocking or vice versa, the cached declaration
70- // may be invalid.
71- //
72- // TODO(grantnelson-wf): We could improve this by tracking which packages
73- // the type arguments come from and allow caching if those are all from
74- // dependencies. Since we don't use cache when a dependency changes,
75- // (i.e. has stale cache) we can use generics in that case. We just can't
76- // cache the instantiations that come from packages depending on this one
77- // since those don't invalidate dependent packages' caches.
78- dc .stats .skippedCount ++
79- declCacheGlobalStats .skippedCount ++
80- fmt .Printf (">>> PutDecl(%q): Generic\n " , decl .FullName ) // TODO(grantnelson-wf): REMOVE
81- return
82- }
83-
84- if ! isUniqueDeclFullName (decl .FullName ) {
85- // Only cache declarations with unique names.
86- dc .stats .skippedCount ++
87- declCacheGlobalStats .skippedCount ++
88- fmt .Printf (">>> PutDecl(%q): Not Unique\n " , decl .FullName ) // TODO(grantnelson-wf): REMOVE
89- return
69+ return 0
9070 }
71+ return len (dc .importDecls ) + len (dc .typeDecls ) + len (dc .varDecls ) + len (dc .funcDecls )
72+ }
9173
92- if existing , ok := dc .decls [decl .FullName ]; ok {
93- if existing != decl {
94- panic (fmt .Errorf (`decl cache conflict: different decls with same name: %q` , decl .FullName ))
95- }
96- fmt .Printf (">>> PutDecl(%q): Dup\n " , decl .FullName ) // TODO(grantnelson-wf): REMOVE
97- return
74+ func (dc * DeclCache ) GetDecls () (importedPaths []string , importDecls , typeDecls , varDecls , funcDecls []* Decl ) {
75+ if dc == nil {
76+ return nil , nil , nil , nil , nil // cache is disabled
9877 }
78+ return dc .importedPaths , dc .importDecls , dc .typeDecls , dc .varDecls , dc .funcDecls
79+ }
9980
100- if dc .decls == nil {
101- dc .decls = map [string ]* Decl {}
81+ func (dc * DeclCache ) SetDecls (importedPaths []string , importDecls , typeDecls , varDecls , funcDecls []* Decl ) {
82+ if dc == nil {
83+ return // cache is disabled
10284 }
103- dc .decls [decl .FullName ] = decl
85+ dc .importedPaths = importedPaths
86+ dc .importDecls = importDecls
87+ dc .typeDecls = typeDecls
88+ dc .varDecls = varDecls
89+ dc .funcDecls = funcDecls
10490 dc .changed = true
105- dc .stats .addedCount ++
106- declCacheGlobalStats .addedCount ++
91+ }
10792
108- fmt .Printf (">>> PutDecl(%q): Added\n " , decl .FullName ) // TODO(grantnelson-wf): REMOVE
93+ type serializableDeclCache struct {
94+ ImportPath string
95+ ImportedPaths []string
96+ ImportDecls []* Decl
97+ TypeDecls []* Decl
98+ VarDecls []* Decl
99+ FuncDecls []* Decl
109100}
110101
111102func (dc * DeclCache ) Read (decode func (any ) error ) error {
112103 if dc == nil {
113- return nil // cache is disabled
104+ return errReadDisabled
114105 }
115106
116- dc .stats .reads ++
117- declCacheGlobalStats .reads ++
118107 start := time .Now ()
119- var count int
120- if err := decode (& count ); err != nil {
121- return err
122- }
123108
124- if dc .decls == nil {
125- dc .decls = map [string ]* Decl {}
109+ ser := serializableDeclCache {}
110+ if err := decode (& ser ); err != nil {
111+ return err
126112 }
127- for i := 0 ; i < count ; i ++ {
128- decl := & Decl {}
129- if err := decode (decl ); err != nil {
130- return err
131- }
132- if decl .ForGeneric || ! isUniqueDeclFullName (decl .FullName ) {
133- // These declarations should not have been cached.
134- continue
135- }
136- dc .decls [decl .FullName ] = decl
113+ if dc .importPath != ser .ImportPath {
114+ return fmt .Errorf (`read cache for import path %q when wanting %q` , dc .importPath , ser .ImportPath )
137115 }
138116
139- dc .stats .loadedCount += len (dc .decls )
140- declCacheGlobalStats .loadedCount += len (dc .decls )
117+ // Only modify the cache after everything has been checked.
118+ dc .importedPaths = ser .ImportedPaths
119+ dc .importDecls = ser .ImportDecls
120+ dc .typeDecls = ser .TypeDecls
121+ dc .varDecls = ser .VarDecls
122+ dc .funcDecls = ser .FuncDecls
123+ dc .changed = false
124+
125+ dc .stats .reads ++
126+ declCacheGlobalStats .reads ++
127+
128+ declCount := dc .count ()
129+ dc .stats .readCount += declCount
130+ declCacheGlobalStats .readCount += declCount
131+
141132 dur := time .Since (start )
142133 dc .stats .readDur += dur
143134 declCacheGlobalStats .readDur += dur
@@ -146,28 +137,31 @@ func (dc *DeclCache) Read(decode func(any) error) error {
146137
147138func (dc * DeclCache ) Write (encode func (any ) error ) error {
148139 if dc == nil {
149- return nil // cache is disabled
140+ return errWriteDisabled
150141 }
151142
152- dc .stats .writes ++
153- declCacheGlobalStats .writes ++
154143 start := time .Now ()
155- count := len (dc .decls )
156- if err := encode (count ); err != nil {
144+
145+ ser := serializableDeclCache {
146+ ImportedPaths : dc .importedPaths ,
147+ ImportDecls : dc .importDecls ,
148+ ImportPath : dc .importPath ,
149+ TypeDecls : dc .typeDecls ,
150+ VarDecls : dc .varDecls ,
151+ FuncDecls : dc .funcDecls ,
152+ }
153+ if err := encode (ser ); err != nil {
157154 return err
158155 }
156+ dc .changed = false
159157
160- names := make ([]string , 0 , count )
161- for name := range dc .decls {
162- names = append (names , name )
163- }
164- sort .Strings (names )
158+ dc .stats .writes ++
159+ declCacheGlobalStats .writes ++
160+
161+ declCount := dc .count ()
162+ dc .stats .writeCount += declCount
163+ declCacheGlobalStats .writeCount += declCount
165164
166- for _ , name := range names {
167- if err := encode (dc .decls [name ]); err != nil {
168- return err
169- }
170- }
171165 dur := time .Since (start )
172166 dc .stats .writeDur += dur
173167 declCacheGlobalStats .writeDur += dur
@@ -178,18 +172,14 @@ func (dc *DeclCache) String() string {
178172 if dc == nil {
179173 return `[disabled]`
180174 }
181- return dc .stats .String ()
175+ return dc .stats .String () + `, ` + dc . importPath
182176}
183177
184178func (s declCacheStats ) String () string {
185- return fmt .Sprintf (`reads=%d, writes=%d, ` +
186- `loaded=%d, added=%d, skipped=%d, used=%d, ` +
187- `readDur=%s, writeDur=%s` ,
188- s .reads , s .writes ,
189- s .loadedCount , s .addedCount , s .skippedCount , s .usedCount ,
190- s .readDur .String (), s .writeDur .String ())
179+ return fmt .Sprintf (`reads=%d, writes=%d, readCount=%d, writeCount=%d, readDur=%s, writeDur=%s` ,
180+ s .reads , s .writes , s .readCount , s .writeCount , s .readDur .String (), s .writeDur .String ())
191181}
192182
193183func GlobalDeclCacheStats () string {
194- return declCacheGlobalStats .String ()
184+ return declCacheGlobalStats .String () + `, [global]`
195185}
0 commit comments