1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 package facts
38
39 import (
40 "bytes"
41 "encoding/gob"
42 "fmt"
43 "go/types"
44 "io/ioutil"
45 "log"
46 "reflect"
47 "sort"
48 "sync"
49
50 "golang.org/x/tools/go/analysis"
51 "golang.org/x/tools/go/types/objectpath"
52 )
53
54 const debug = false
55
56
57
58
59
60
61
62
63 type Set struct {
64 pkg *types.Package
65 mu sync.Mutex
66 m map[key]analysis.Fact
67 }
68
69 type key struct {
70 pkg *types.Package
71 obj types.Object
72 t reflect.Type
73 }
74
75
76 func (s *Set) ImportObjectFact(obj types.Object, ptr analysis.Fact) bool {
77 if obj == nil {
78 panic("nil object")
79 }
80 key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(ptr)}
81 s.mu.Lock()
82 defer s.mu.Unlock()
83 if v, ok := s.m[key]; ok {
84 reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
85 return true
86 }
87 return false
88 }
89
90
91 func (s *Set) ExportObjectFact(obj types.Object, fact analysis.Fact) {
92 if obj.Pkg() != s.pkg {
93 log.Panicf("in package %s: ExportObjectFact(%s, %T): can't set fact on object belonging another package",
94 s.pkg, obj, fact)
95 }
96 key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(fact)}
97 s.mu.Lock()
98 s.m[key] = fact
99 s.mu.Unlock()
100 }
101
102 func (s *Set) AllObjectFacts(filter map[reflect.Type]bool) []analysis.ObjectFact {
103 var facts []analysis.ObjectFact
104 s.mu.Lock()
105 for k, v := range s.m {
106 if k.obj != nil && filter[k.t] {
107 facts = append(facts, analysis.ObjectFact{Object: k.obj, Fact: v})
108 }
109 }
110 s.mu.Unlock()
111 return facts
112 }
113
114
115 func (s *Set) ImportPackageFact(pkg *types.Package, ptr analysis.Fact) bool {
116 if pkg == nil {
117 panic("nil package")
118 }
119 key := key{pkg: pkg, t: reflect.TypeOf(ptr)}
120 s.mu.Lock()
121 defer s.mu.Unlock()
122 if v, ok := s.m[key]; ok {
123 reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
124 return true
125 }
126 return false
127 }
128
129
130 func (s *Set) ExportPackageFact(fact analysis.Fact) {
131 key := key{pkg: s.pkg, t: reflect.TypeOf(fact)}
132 s.mu.Lock()
133 s.m[key] = fact
134 s.mu.Unlock()
135 }
136
137 func (s *Set) AllPackageFacts(filter map[reflect.Type]bool) []analysis.PackageFact {
138 var facts []analysis.PackageFact
139 s.mu.Lock()
140 for k, v := range s.m {
141 if k.obj == nil && filter[k.t] {
142 facts = append(facts, analysis.PackageFact{Package: k.pkg, Fact: v})
143 }
144 }
145 s.mu.Unlock()
146 return facts
147 }
148
149
150 type gobFact struct {
151 PkgPath string
152 Object objectpath.Path
153 Fact analysis.Fact
154 }
155
156
157
158
159
160
161
162
163 func Decode(pkg *types.Package, read func(packagePath string) ([]byte, error)) (*Set, error) {
164
165
166 packages := importMap(pkg.Imports())
167
168
169
170 m := make(map[key]analysis.Fact)
171 for _, imp := range pkg.Imports() {
172 logf := func(format string, args ...interface{}) {
173 if debug {
174 prefix := fmt.Sprintf("in %s, importing %s: ",
175 pkg.Path(), imp.Path())
176 log.Print(prefix, fmt.Sprintf(format, args...))
177 }
178 }
179
180
181 data, err := read(imp.Path())
182 if err != nil {
183 return nil, fmt.Errorf("in %s, can't import facts for package %q: %v",
184 pkg.Path(), imp.Path(), err)
185 }
186 if len(data) == 0 {
187 continue
188 }
189 var gobFacts []gobFact
190 if err := gob.NewDecoder(bytes.NewReader(data)).Decode(&gobFacts); err != nil {
191 return nil, fmt.Errorf("decoding facts for %q: %v", imp.Path(), err)
192 }
193 if debug {
194 logf("decoded %d facts: %v", len(gobFacts), gobFacts)
195 }
196
197
198 for _, f := range gobFacts {
199 factPkg := packages[f.PkgPath]
200 if factPkg == nil {
201
202
203 logf("no package %q; discarding %v", f.PkgPath, f.Fact)
204 continue
205 }
206 key := key{pkg: factPkg, t: reflect.TypeOf(f.Fact)}
207 if f.Object != "" {
208
209 obj, err := objectpath.Object(factPkg, f.Object)
210 if err != nil {
211
212
213 logf("no object for path: %v; discarding %s", err, f.Fact)
214 continue
215 }
216 key.obj = obj
217 logf("read %T fact %s for %v", f.Fact, f.Fact, key.obj)
218 } else {
219
220 logf("read %T fact %s for %v", f.Fact, f.Fact, factPkg)
221 }
222 m[key] = f.Fact
223 }
224 }
225
226 return &Set{pkg: pkg, m: m}, nil
227 }
228
229
230
231
232
233 func (s *Set) Encode() []byte {
234
235
236
237
238
239 var gobFacts []gobFact
240
241 s.mu.Lock()
242 for k, fact := range s.m {
243 if debug {
244 log.Printf("%v => %s\n", k, fact)
245 }
246 var object objectpath.Path
247 if k.obj != nil {
248 path, err := objectpath.For(k.obj)
249 if err != nil {
250 if debug {
251 log.Printf("discarding fact %s about %s\n", fact, k.obj)
252 }
253 continue
254 }
255 object = path
256 }
257 gobFacts = append(gobFacts, gobFact{
258 PkgPath: k.pkg.Path(),
259 Object: object,
260 Fact: fact,
261 })
262 }
263 s.mu.Unlock()
264
265
266 sort.Slice(gobFacts, func(i, j int) bool {
267 x, y := gobFacts[i], gobFacts[j]
268 if x.PkgPath != y.PkgPath {
269 return x.PkgPath < y.PkgPath
270 }
271 if x.Object != y.Object {
272 return x.Object < y.Object
273 }
274 tx := reflect.TypeOf(x.Fact)
275 ty := reflect.TypeOf(y.Fact)
276 if tx != ty {
277 return tx.String() < ty.String()
278 }
279 return false
280 })
281
282 var buf bytes.Buffer
283 if len(gobFacts) > 0 {
284 if err := gob.NewEncoder(&buf).Encode(gobFacts); err != nil {
285
286 for _, gf := range gobFacts {
287 if err := gob.NewEncoder(ioutil.Discard).Encode(gf); err != nil {
288 fact := gf.Fact
289 pkgpath := reflect.TypeOf(fact).Elem().PkgPath()
290 log.Panicf("internal error: gob encoding of analysis fact %s failed: %v; please report a bug against fact %T in package %q",
291 fact, err, fact, pkgpath)
292 }
293 }
294 }
295 }
296
297 if debug {
298 log.Printf("package %q: encode %d facts, %d bytes\n",
299 s.pkg.Path(), len(gobFacts), buf.Len())
300 }
301
302 return buf.Bytes()
303 }
304
305
306
307 func (s *Set) String() string {
308 var buf bytes.Buffer
309 buf.WriteString("{")
310 for k, f := range s.m {
311 if buf.Len() > 1 {
312 buf.WriteString(", ")
313 }
314 if k.obj != nil {
315 buf.WriteString(k.obj.String())
316 } else {
317 buf.WriteString(k.pkg.Path())
318 }
319 fmt.Fprintf(&buf, ": %v", f)
320 }
321 buf.WriteString("}")
322 return buf.String()
323 }
324
View as plain text