2019-05-15 10:24:49 -06:00
|
|
|
// Copyright 2019 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package cache
|
|
|
|
|
|
|
|
import (
|
2019-05-30 07:03:31 -06:00
|
|
|
"context"
|
2019-05-17 10:15:22 -06:00
|
|
|
"crypto/sha1"
|
|
|
|
"fmt"
|
2019-05-17 08:51:19 -06:00
|
|
|
"go/token"
|
2020-02-03 15:07:45 -07:00
|
|
|
"reflect"
|
2019-05-29 12:55:52 -06:00
|
|
|
"strconv"
|
|
|
|
"sync/atomic"
|
2019-05-17 08:51:19 -06:00
|
|
|
|
2019-05-29 12:55:52 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/debug"
|
2019-05-15 10:24:49 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/source"
|
2019-05-30 07:03:31 -06:00
|
|
|
"golang.org/x/tools/internal/memoize"
|
2019-05-17 08:51:19 -06:00
|
|
|
"golang.org/x/tools/internal/span"
|
2019-05-15 10:24:49 -06:00
|
|
|
)
|
|
|
|
|
2020-02-28 08:30:03 -07:00
|
|
|
func New(ctx context.Context, options func(*source.Options)) *Cache {
|
2019-05-29 12:55:52 -06:00
|
|
|
index := atomic.AddInt64(&cacheIndex, 1)
|
2020-02-18 18:59:37 -07:00
|
|
|
c := &Cache{
|
2019-10-10 18:48:16 -06:00
|
|
|
fs: &nativeFileSystem{},
|
|
|
|
id: strconv.FormatInt(index, 10),
|
|
|
|
fset: token.NewFileSet(),
|
|
|
|
options: options,
|
2019-05-17 08:51:19 -06:00
|
|
|
}
|
2020-02-28 08:30:03 -07:00
|
|
|
if di := debug.GetInstance(ctx); di != nil {
|
|
|
|
di.State.AddCache(debugCache{c})
|
|
|
|
}
|
2019-05-29 12:55:52 -06:00
|
|
|
return c
|
2019-05-15 10:24:49 -06:00
|
|
|
}
|
|
|
|
|
2020-02-18 18:59:37 -07:00
|
|
|
type Cache struct {
|
2019-10-10 18:48:16 -06:00
|
|
|
fs source.FileSystem
|
|
|
|
id string
|
|
|
|
fset *token.FileSet
|
|
|
|
options func(*source.Options)
|
2019-05-30 07:03:31 -06:00
|
|
|
|
|
|
|
store memoize.Store
|
|
|
|
}
|
|
|
|
|
|
|
|
type fileKey struct {
|
|
|
|
identity source.FileIdentity
|
|
|
|
}
|
|
|
|
|
|
|
|
type fileHandle struct {
|
2020-02-18 18:59:37 -07:00
|
|
|
cache *Cache
|
2019-05-30 07:03:31 -06:00
|
|
|
underlying source.FileHandle
|
|
|
|
handle *memoize.Handle
|
|
|
|
}
|
|
|
|
|
|
|
|
type fileData struct {
|
|
|
|
memoize.NoCopy
|
|
|
|
bytes []byte
|
|
|
|
hash string
|
|
|
|
err error
|
2019-05-15 10:24:49 -06:00
|
|
|
}
|
|
|
|
|
2020-02-18 18:59:37 -07:00
|
|
|
func (c *Cache) GetFile(uri span.URI) source.FileHandle {
|
2020-01-06 16:08:39 -07:00
|
|
|
underlying := c.fs.GetFile(uri)
|
2019-05-30 07:03:31 -06:00
|
|
|
key := fileKey{
|
|
|
|
identity: underlying.Identity(),
|
|
|
|
}
|
|
|
|
h := c.store.Bind(key, func(ctx context.Context) interface{} {
|
|
|
|
data := &fileData{}
|
|
|
|
data.bytes, data.hash, data.err = underlying.Read(ctx)
|
|
|
|
return data
|
|
|
|
})
|
|
|
|
return &fileHandle{
|
|
|
|
cache: c,
|
|
|
|
underlying: underlying,
|
|
|
|
handle: h,
|
|
|
|
}
|
2019-05-31 17:41:39 -06:00
|
|
|
}
|
|
|
|
|
2020-02-28 08:30:03 -07:00
|
|
|
func (c *Cache) NewSession(ctx context.Context) *Session {
|
2019-05-29 12:55:52 -06:00
|
|
|
index := atomic.AddInt64(&sessionIndex, 1)
|
2020-02-18 18:59:37 -07:00
|
|
|
s := &Session{
|
2019-12-17 14:53:57 -07:00
|
|
|
cache: c,
|
|
|
|
id: strconv.FormatInt(index, 10),
|
2020-01-30 10:25:09 -07:00
|
|
|
options: source.DefaultOptions(),
|
2019-12-17 14:53:57 -07:00
|
|
|
overlays: make(map[span.URI]*overlay),
|
2019-05-15 10:24:49 -06:00
|
|
|
}
|
2020-02-28 08:30:03 -07:00
|
|
|
if di := debug.GetInstance(ctx); di != nil {
|
|
|
|
di.State.AddSession(DebugSession{s})
|
|
|
|
}
|
2019-05-29 12:55:52 -06:00
|
|
|
return s
|
2019-05-15 10:24:49 -06:00
|
|
|
}
|
2019-05-17 08:51:19 -06:00
|
|
|
|
2020-02-18 18:59:37 -07:00
|
|
|
func (c *Cache) FileSet() *token.FileSet {
|
2019-05-17 08:51:19 -06:00
|
|
|
return c.fset
|
|
|
|
}
|
2019-05-17 10:15:22 -06:00
|
|
|
|
2019-05-30 07:03:31 -06:00
|
|
|
func (h *fileHandle) FileSystem() source.FileSystem {
|
|
|
|
return h.cache
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *fileHandle) Identity() source.FileIdentity {
|
|
|
|
return h.underlying.Identity()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *fileHandle) Read(ctx context.Context) ([]byte, string, error) {
|
|
|
|
v := h.handle.Get(ctx)
|
|
|
|
if v == nil {
|
|
|
|
return nil, "", ctx.Err()
|
|
|
|
}
|
|
|
|
data := v.(*fileData)
|
|
|
|
return data.bytes, data.hash, data.err
|
|
|
|
}
|
|
|
|
|
2019-05-17 10:15:22 -06:00
|
|
|
func hashContents(contents []byte) string {
|
|
|
|
// TODO: consider whether sha1 is the best choice here
|
|
|
|
// This hash is used for internal identity detection only
|
|
|
|
return fmt.Sprintf("%x", sha1.Sum(contents))
|
|
|
|
}
|
2019-05-29 12:55:52 -06:00
|
|
|
|
|
|
|
var cacheIndex, sessionIndex, viewIndex int64
|
|
|
|
|
2020-02-18 18:59:37 -07:00
|
|
|
type debugCache struct{ *Cache }
|
2019-05-29 12:55:52 -06:00
|
|
|
|
2020-02-18 18:59:37 -07:00
|
|
|
func (c *Cache) ID() string { return c.id }
|
2020-02-03 15:07:45 -07:00
|
|
|
func (c debugCache) FileSet() *token.FileSet { return c.fset }
|
|
|
|
func (c debugCache) MemStats() map[reflect.Type]int { return c.store.Stats() }
|