blob: 5a482e952a4a52d5ad938b4a5ebf54fd9a9ea911 [file] [log] [blame]
// 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 types
import "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[*Named]struct{ value, pointer *MethodSet } // method sets for named N and *N
others map[Type]*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 NewMethodSet(T).
// Utility functions can thus expose an optional *MethodSetCache
// parameter to clients that care about performance.
//
func (cache *MethodSetCache) MethodSet(T Type) *MethodSet {
if cache == nil {
return NewMethodSet(T)
}
cache.mu.Lock()
defer cache.mu.Unlock()
switch T := T.(type) {
case *Named:
return cache.lookupNamed(T).value
case *Pointer:
if N, ok := T.Elem().(*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 = NewMethodSet(T)
if cache.others == nil {
cache.others = make(map[Type]*MethodSet)
}
cache.others[T] = mset
}
return mset
}
func (cache *MethodSetCache) lookupNamed(named *Named) struct{ value, pointer *MethodSet } {
if cache.named == nil {
cache.named = make(map[*Named]struct{ value, pointer *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 = NewMethodSet(named)
msets.pointer = NewMethodSet(NewPointer(named))
cache.named[named] = msets
}
return msets
}