Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / lsp / cache / cache.go
1 // Copyright 2019 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package cache
6
7 import (
8         "context"
9         "crypto/sha256"
10         "fmt"
11         "go/ast"
12         "go/token"
13         "go/types"
14         "html/template"
15         "io/ioutil"
16         "os"
17         "reflect"
18         "sort"
19         "strconv"
20         "sync"
21         "sync/atomic"
22         "time"
23
24         "golang.org/x/tools/internal/event"
25         "golang.org/x/tools/internal/gocommand"
26         "golang.org/x/tools/internal/lsp/debug/tag"
27         "golang.org/x/tools/internal/lsp/source"
28         "golang.org/x/tools/internal/memoize"
29         "golang.org/x/tools/internal/span"
30 )
31
32 func New(ctx context.Context, options func(*source.Options)) *Cache {
33         index := atomic.AddInt64(&cacheIndex, 1)
34         c := &Cache{
35                 id:          strconv.FormatInt(index, 10),
36                 fset:        token.NewFileSet(),
37                 options:     options,
38                 fileContent: map[span.URI]*fileHandle{},
39         }
40         return c
41 }
42
43 type Cache struct {
44         id      string
45         fset    *token.FileSet
46         options func(*source.Options)
47
48         store memoize.Store
49
50         fileMu      sync.Mutex
51         fileContent map[span.URI]*fileHandle
52 }
53
54 type fileHandle struct {
55         modTime time.Time
56         uri     span.URI
57         bytes   []byte
58         hash    string
59         err     error
60 }
61
62 func (c *Cache) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) {
63         return c.getFile(ctx, uri)
64 }
65
66 func (c *Cache) getFile(ctx context.Context, uri span.URI) (*fileHandle, error) {
67         fi, statErr := os.Stat(uri.Filename())
68         if statErr != nil {
69                 return &fileHandle{
70                         err: statErr,
71                         uri: uri,
72                 }, nil
73         }
74
75         c.fileMu.Lock()
76         fh, ok := c.fileContent[uri]
77         c.fileMu.Unlock()
78         if ok && fh.modTime.Equal(fi.ModTime()) {
79                 return fh, nil
80         }
81
82         fh, err := readFile(ctx, uri, fi.ModTime())
83         if err != nil {
84                 return nil, err
85         }
86         c.fileMu.Lock()
87         c.fileContent[uri] = fh
88         c.fileMu.Unlock()
89         return fh, nil
90 }
91
92 // ioLimit limits the number of parallel file reads per process.
93 var ioLimit = make(chan struct{}, 128)
94
95 func readFile(ctx context.Context, uri span.URI, modTime time.Time) (*fileHandle, error) {
96         select {
97         case ioLimit <- struct{}{}:
98         case <-ctx.Done():
99                 return nil, ctx.Err()
100         }
101         defer func() { <-ioLimit }()
102
103         ctx, done := event.Start(ctx, "cache.readFile", tag.File.Of(uri.Filename()))
104         _ = ctx
105         defer done()
106
107         data, err := ioutil.ReadFile(uri.Filename())
108         if err != nil {
109                 return &fileHandle{
110                         modTime: modTime,
111                         err:     err,
112                 }, nil
113         }
114         return &fileHandle{
115                 modTime: modTime,
116                 uri:     uri,
117                 bytes:   data,
118                 hash:    hashContents(data),
119         }, nil
120 }
121
122 func (c *Cache) NewSession(ctx context.Context) *Session {
123         index := atomic.AddInt64(&sessionIndex, 1)
124         options := source.DefaultOptions().Clone()
125         if c.options != nil {
126                 c.options(options)
127         }
128         s := &Session{
129                 cache:       c,
130                 id:          strconv.FormatInt(index, 10),
131                 options:     options,
132                 overlays:    make(map[span.URI]*overlay),
133                 gocmdRunner: &gocommand.Runner{},
134         }
135         event.Log(ctx, "New session", KeyCreateSession.Of(s))
136         return s
137 }
138
139 func (c *Cache) FileSet() *token.FileSet {
140         return c.fset
141 }
142
143 func (h *fileHandle) URI() span.URI {
144         return h.uri
145 }
146
147 func (h *fileHandle) Kind() source.FileKind {
148         return source.DetectLanguage("", h.uri.Filename())
149 }
150
151 func (h *fileHandle) Hash() string {
152         return h.hash
153 }
154
155 func (h *fileHandle) FileIdentity() source.FileIdentity {
156         return source.FileIdentity{
157                 URI:  h.uri,
158                 Hash: h.hash,
159                 Kind: h.Kind(),
160         }
161 }
162
163 func (h *fileHandle) Read() ([]byte, error) {
164         return h.bytes, h.err
165 }
166
167 func hashContents(contents []byte) string {
168         return fmt.Sprintf("%x", sha256.Sum256(contents))
169 }
170
171 var cacheIndex, sessionIndex, viewIndex int64
172
173 func (c *Cache) ID() string                     { return c.id }
174 func (c *Cache) MemStats() map[reflect.Type]int { return c.store.Stats() }
175
176 type packageStat struct {
177         id        packageID
178         mode      source.ParseMode
179         file      int64
180         ast       int64
181         types     int64
182         typesInfo int64
183         total     int64
184 }
185
186 func (c *Cache) PackageStats(withNames bool) template.HTML {
187         var packageStats []packageStat
188         c.store.DebugOnlyIterate(func(k, v interface{}) {
189                 switch k.(type) {
190                 case packageHandleKey:
191                         v := v.(*packageData)
192                         if v.pkg == nil {
193                                 break
194                         }
195                         var typsCost, typInfoCost int64
196                         if v.pkg.types != nil {
197                                 typsCost = typesCost(v.pkg.types.Scope())
198                         }
199                         if v.pkg.typesInfo != nil {
200                                 typInfoCost = typesInfoCost(v.pkg.typesInfo)
201                         }
202                         stat := packageStat{
203                                 id:        v.pkg.m.id,
204                                 mode:      v.pkg.mode,
205                                 types:     typsCost,
206                                 typesInfo: typInfoCost,
207                         }
208                         for _, f := range v.pkg.compiledGoFiles {
209                                 stat.file += int64(len(f.Src))
210                                 stat.ast += astCost(f.File)
211                         }
212                         stat.total = stat.file + stat.ast + stat.types + stat.typesInfo
213                         packageStats = append(packageStats, stat)
214                 }
215         })
216         var totalCost int64
217         for _, stat := range packageStats {
218                 totalCost += stat.total
219         }
220         sort.Slice(packageStats, func(i, j int) bool {
221                 return packageStats[i].total > packageStats[j].total
222         })
223         html := "<table><thead><td>Name</td><td>total = file + ast + types + types info</td></thead>\n"
224         human := func(n int64) string {
225                 return fmt.Sprintf("%.2f", float64(n)/(1024*1024))
226         }
227         var printedCost int64
228         for _, stat := range packageStats {
229                 name := stat.id
230                 if !withNames {
231                         name = "-"
232                 }
233                 html += fmt.Sprintf("<tr><td>%v (%v)</td><td>%v = %v + %v + %v + %v</td></tr>\n", name, stat.mode,
234                         human(stat.total), human(stat.file), human(stat.ast), human(stat.types), human(stat.typesInfo))
235                 printedCost += stat.total
236                 if float64(printedCost) > float64(totalCost)*.9 {
237                         break
238                 }
239         }
240         html += "</table>\n"
241         return template.HTML(html)
242 }
243
244 func astCost(f *ast.File) int64 {
245         if f == nil {
246                 return 0
247         }
248         var count int64
249         ast.Inspect(f, func(n ast.Node) bool {
250                 count += 32 // nodes are pretty small.
251                 return true
252         })
253         return count
254 }
255
256 func typesCost(scope *types.Scope) int64 {
257         cost := 64 + int64(scope.Len())*128 // types.object looks pretty big
258         for i := 0; i < scope.NumChildren(); i++ {
259                 cost += typesCost(scope.Child(i))
260         }
261         return cost
262 }
263
264 func typesInfoCost(info *types.Info) int64 {
265         // Most of these refer to existing objects, with the exception of InitOrder, Selections, and Types.
266         cost := 24*len(info.Defs) +
267                 32*len(info.Implicits) +
268                 256*len(info.InitOrder) + // these are big, but there aren't many of them.
269                 32*len(info.Scopes) +
270                 128*len(info.Selections) + // wild guess
271                 128*len(info.Types) + // wild guess
272                 32*len(info.Uses)
273         return int64(cost)
274 }