// 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 }