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