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