73 lines
2.1 KiB
Go
73 lines
2.1 KiB
Go
// Copyright 2014 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.
|
|
|
|
// This file implements a cache of method sets.
|
|
|
|
package typeutil
|
|
|
|
import (
|
|
"go/types"
|
|
"sync"
|
|
)
|
|
|
|
// A MethodSetCache records the method set of each type T for which
|
|
// MethodSet(T) is called so that repeat queries are fast.
|
|
// The zero value is a ready-to-use cache instance.
|
|
type MethodSetCache struct {
|
|
mu sync.Mutex
|
|
named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N
|
|
others map[types.Type]*types.MethodSet // all other types
|
|
}
|
|
|
|
// MethodSet returns the method set of type T. It is thread-safe.
|
|
//
|
|
// If cache is nil, this function is equivalent to types.NewMethodSet(T).
|
|
// Utility functions can thus expose an optional *MethodSetCache
|
|
// parameter to clients that care about performance.
|
|
//
|
|
func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
|
|
if cache == nil {
|
|
return types.NewMethodSet(T)
|
|
}
|
|
cache.mu.Lock()
|
|
defer cache.mu.Unlock()
|
|
|
|
switch T := T.(type) {
|
|
case *types.Named:
|
|
return cache.lookupNamed(T).value
|
|
|
|
case *types.Pointer:
|
|
if N, ok := T.Elem().(*types.Named); ok {
|
|
return cache.lookupNamed(N).pointer
|
|
}
|
|
}
|
|
|
|
// all other types
|
|
// (The map uses pointer equivalence, not type identity.)
|
|
mset := cache.others[T]
|
|
if mset == nil {
|
|
mset = types.NewMethodSet(T)
|
|
if cache.others == nil {
|
|
cache.others = make(map[types.Type]*types.MethodSet)
|
|
}
|
|
cache.others[T] = mset
|
|
}
|
|
return mset
|
|
}
|
|
|
|
func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
|
|
if cache.named == nil {
|
|
cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
|
|
}
|
|
// Avoid recomputing mset(*T) for each distinct Pointer
|
|
// instance whose underlying type is a named type.
|
|
msets, ok := cache.named[named]
|
|
if !ok {
|
|
msets.value = types.NewMethodSet(named)
|
|
msets.pointer = types.NewMethodSet(types.NewPointer(named))
|
|
cache.named[named] = msets
|
|
}
|
|
return msets
|
|
}
|