Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / 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         select {
83         case ioLimit <- struct{}{}:
84         case <-ctx.Done():
85                 return nil, ctx.Err()
86         }
87         defer func() { <-ioLimit }()
88
89         fh = readFile(ctx, uri, fi.ModTime())
90         c.fileMu.Lock()
91         c.fileContent[uri] = fh
92         c.fileMu.Unlock()
93         return fh, nil
94 }
95
96 // ioLimit limits the number of parallel file reads per process.
97 var ioLimit = make(chan struct{}, 128)
98
99 func readFile(ctx context.Context, uri span.URI, modTime time.Time) *fileHandle {
100         ctx, done := event.Start(ctx, "cache.readFile", tag.File.Of(uri.Filename()))
101         _ = ctx
102         defer done()
103
104         data, err := ioutil.ReadFile(uri.Filename())
105         if err != nil {
106                 return &fileHandle{
107                         modTime: modTime,
108                         err:     err,
109                 }
110         }
111         return &fileHandle{
112                 modTime: modTime,
113                 uri:     uri,
114                 bytes:   data,
115                 hash:    hashContents(data),
116         }
117 }
118
119 func (c *Cache) NewSession(ctx context.Context) *Session {
120         index := atomic.AddInt64(&sessionIndex, 1)
121         options := source.DefaultOptions().Clone()
122         if c.options != nil {
123                 c.options(options)
124         }
125         s := &Session{
126                 cache:       c,
127                 id:          strconv.FormatInt(index, 10),
128                 options:     options,
129                 overlays:    make(map[span.URI]*overlay),
130                 gocmdRunner: &gocommand.Runner{},
131         }
132         event.Log(ctx, "New session", KeyCreateSession.Of(s))
133         return s
134 }
135
136 func (c *Cache) FileSet() *token.FileSet {
137         return c.fset
138 }
139
140 func (h *fileHandle) URI() span.URI {
141         return h.uri
142 }
143
144 func (h *fileHandle) Kind() source.FileKind {
145         return source.DetectLanguage("", h.uri.Filename())
146 }
147
148 func (h *fileHandle) Hash() string {
149         return h.hash
150 }
151
152 func (h *fileHandle) FileIdentity() source.FileIdentity {
153         return source.FileIdentity{
154                 URI:  h.uri,
155                 Hash: h.hash,
156                 Kind: h.Kind(),
157         }
158 }
159
160 func (h *fileHandle) Read() ([]byte, error) {
161         return h.bytes, h.err
162 }
163
164 func hashContents(contents []byte) string {
165         return fmt.Sprintf("%x", sha256.Sum256(contents))
166 }
167
168 var cacheIndex, sessionIndex, viewIndex int64
169
170 func (c *Cache) ID() string                     { return c.id }
171 func (c *Cache) MemStats() map[reflect.Type]int { return c.store.Stats() }
172
173 type packageStat struct {
174         id        packageID
175         mode      source.ParseMode
176         file      int64
177         ast       int64
178         types     int64
179         typesInfo int64
180         total     int64
181 }
182
183 func (c *Cache) PackageStats(withNames bool) template.HTML {
184         var packageStats []packageStat
185         c.store.DebugOnlyIterate(func(k, v interface{}) {
186                 switch k.(type) {
187                 case packageHandleKey:
188                         v := v.(*packageData)
189                         if v.pkg == nil {
190                                 break
191                         }
192                         var typsCost, typInfoCost int64
193                         if v.pkg.types != nil {
194                                 typsCost = typesCost(v.pkg.types.Scope())
195                         }
196                         if v.pkg.typesInfo != nil {
197                                 typInfoCost = typesInfoCost(v.pkg.typesInfo)
198                         }
199                         stat := packageStat{
200                                 id:        v.pkg.m.id,
201                                 mode:      v.pkg.mode,
202                                 types:     typsCost,
203                                 typesInfo: typInfoCost,
204                         }
205                         for _, f := range v.pkg.compiledGoFiles {
206                                 stat.file += int64(len(f.Src))
207                                 stat.ast += astCost(f.File)
208                         }
209                         stat.total = stat.file + stat.ast + stat.types + stat.typesInfo
210                         packageStats = append(packageStats, stat)
211                 }
212         })
213         var totalCost int64
214         for _, stat := range packageStats {
215                 totalCost += stat.total
216         }
217         sort.Slice(packageStats, func(i, j int) bool {
218                 return packageStats[i].total > packageStats[j].total
219         })
220         html := "<table><thead><td>Name</td><td>total = file + ast + types + types info</td></thead>\n"
221         human := func(n int64) string {
222                 return fmt.Sprintf("%.2f", float64(n)/(1024*1024))
223         }
224         var printedCost int64
225         for _, stat := range packageStats {
226                 name := stat.id
227                 if !withNames {
228                         name = "-"
229                 }
230                 html += fmt.Sprintf("<tr><td>%v (%v)</td><td>%v = %v + %v + %v + %v</td></tr>\n", name, stat.mode,
231                         human(stat.total), human(stat.file), human(stat.ast), human(stat.types), human(stat.typesInfo))
232                 printedCost += stat.total
233                 if float64(printedCost) > float64(totalCost)*.9 {
234                         break
235                 }
236         }
237         html += "</table>\n"
238         return template.HTML(html)
239 }
240
241 func astCost(f *ast.File) int64 {
242         if f == nil {
243                 return 0
244         }
245         var count int64
246         ast.Inspect(f, func(n ast.Node) bool {
247                 count += 32 // nodes are pretty small.
248                 return true
249         })
250         return count
251 }
252
253 func typesCost(scope *types.Scope) int64 {
254         cost := 64 + int64(scope.Len())*128 // types.object looks pretty big
255         for i := 0; i < scope.NumChildren(); i++ {
256                 cost += typesCost(scope.Child(i))
257         }
258         return cost
259 }
260
261 func typesInfoCost(info *types.Info) int64 {
262         // Most of these refer to existing objects, with the exception of InitOrder, Selections, and Types.
263         cost := 24*len(info.Defs) +
264                 32*len(info.Implicits) +
265                 256*len(info.InitOrder) + // these are big, but there aren't many of them.
266                 32*len(info.Scopes) +
267                 128*len(info.Selections) + // wild guess
268                 128*len(info.Types) + // wild guess
269                 32*len(info.Uses)
270         return int64(cost)
271 }