1// Copyright 2014 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// This file implements a cache of method sets. 6 7package typeutil 8 9import ( 10 "go/types" 11 "sync" 12 13 "golang.org/x/tools/internal/aliases" 14) 15 16// A MethodSetCache records the method set of each type T for which 17// MethodSet(T) is called so that repeat queries are fast. 18// The zero value is a ready-to-use cache instance. 19type MethodSetCache struct { 20 mu sync.Mutex 21 named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N 22 others map[types.Type]*types.MethodSet // all other types 23} 24 25// MethodSet returns the method set of type T. It is thread-safe. 26// 27// If cache is nil, this function is equivalent to types.NewMethodSet(T). 28// Utility functions can thus expose an optional *MethodSetCache 29// parameter to clients that care about performance. 30func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet { 31 if cache == nil { 32 return types.NewMethodSet(T) 33 } 34 cache.mu.Lock() 35 defer cache.mu.Unlock() 36 37 switch T := aliases.Unalias(T).(type) { 38 case *types.Named: 39 return cache.lookupNamed(T).value 40 41 case *types.Pointer: 42 if N, ok := aliases.Unalias(T.Elem()).(*types.Named); ok { 43 return cache.lookupNamed(N).pointer 44 } 45 } 46 47 // all other types 48 // (The map uses pointer equivalence, not type identity.) 49 mset := cache.others[T] 50 if mset == nil { 51 mset = types.NewMethodSet(T) 52 if cache.others == nil { 53 cache.others = make(map[types.Type]*types.MethodSet) 54 } 55 cache.others[T] = mset 56 } 57 return mset 58} 59 60func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } { 61 if cache.named == nil { 62 cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet }) 63 } 64 // Avoid recomputing mset(*T) for each distinct Pointer 65 // instance whose underlying type is a named type. 66 msets, ok := cache.named[named] 67 if !ok { 68 msets.value = types.NewMethodSet(named) 69 msets.pointer = types.NewMethodSet(types.NewPointer(named)) 70 cache.named[named] = msets 71 } 72 return msets 73} 74