1 // Copyright 2013 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 package ssa
6 7 // This file defines utilities for population of method sets.
8 9 import (
10 "fmt"
11 "go/types"
12 13 "golang.org/x/tools/go/types/typeutil"
14 "golang.org/x/tools/internal/typesinternal"
15 )
16 17 // MethodValue returns the Function implementing method sel, building
18 // wrapper methods on demand. It returns nil if sel denotes an
19 // interface or generic method.
20 //
21 // Precondition: sel.Kind() == MethodVal.
22 //
23 // Thread-safe.
24 //
25 // Acquires prog.methodsMu.
26 func (prog *Program) MethodValue(sel *types.Selection) *Function {
27 if sel.Kind() != types.MethodVal {
28 panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel))
29 }
30 T := sel.Recv()
31 if types.IsInterface(T) {
32 return nil // interface method or type parameter
33 }
34 35 if prog.isParameterized(T) {
36 return nil // generic method
37 }
38 39 if prog.mode&LogSource != 0 {
40 defer logStack("MethodValue %s %v", T, sel)()
41 }
42 43 var b builder
44 45 m := func() *Function {
46 prog.methodsMu.Lock()
47 defer prog.methodsMu.Unlock()
48 49 // Get or create SSA method set.
50 mset, ok := prog.methodSets.At(T).(*methodSet)
51 if !ok {
52 mset = &methodSet{mapping: make(map[string]*Function)}
53 prog.methodSets.Set(T, mset)
54 }
55 56 // Get or create SSA method.
57 id := sel.Obj().Id()
58 fn, ok := mset.mapping[id]
59 if !ok {
60 obj := sel.Obj().(*types.Func)
61 needsPromotion := len(sel.Index()) > 1
62 needsIndirection := !isPointer(recvType(obj)) && isPointer(T)
63 if needsPromotion || needsIndirection {
64 fn = createWrapper(prog, toSelection(sel))
65 fn.buildshared = b.shared()
66 b.enqueue(fn)
67 } else {
68 fn = prog.objectMethod(obj, &b)
69 }
70 if fn.Signature.Recv() == nil {
71 panic(fn)
72 }
73 mset.mapping[id] = fn
74 } else {
75 b.waitForSharedFunction(fn)
76 }
77 78 return fn
79 }()
80 81 b.iterate()
82 83 return m
84 }
85 86 // objectMethod returns the Function for a given method symbol.
87 // The symbol may be an instance of a generic function. It need not
88 // belong to an existing SSA package created by a call to
89 // prog.CreatePackage.
90 //
91 // objectMethod panics if the function is not a method.
92 //
93 // Acquires prog.objectMethodsMu.
94 func (prog *Program) objectMethod(obj *types.Func, b *builder) *Function {
95 sig := obj.Type().(*types.Signature)
96 if sig.Recv() == nil {
97 panic("not a method: " + obj.String())
98 }
99 100 // Belongs to a created package?
101 if fn := prog.FuncValue(obj); fn != nil {
102 return fn
103 }
104 105 // Instantiation of generic?
106 if originObj := obj.Origin(); originObj != obj {
107 origin := prog.objectMethod(originObj, b)
108 assert(origin.typeparams.Len() > 0, "origin is not generic")
109 targs := receiverTypeArgs(obj)
110 return origin.instance(targs, b)
111 }
112 113 // Consult/update cache of methods created from types.Func.
114 prog.objectMethodsMu.Lock()
115 defer prog.objectMethodsMu.Unlock()
116 fn, ok := prog.objectMethods[obj]
117 if !ok {
118 fn = createFunction(prog, obj, obj.Name(), nil, nil, "")
119 fn.Synthetic = "from type information (on demand)"
120 fn.buildshared = b.shared()
121 b.enqueue(fn)
122 123 if prog.objectMethods == nil {
124 prog.objectMethods = make(map[*types.Func]*Function)
125 }
126 prog.objectMethods[obj] = fn
127 } else {
128 b.waitForSharedFunction(fn)
129 }
130 return fn
131 }
132 133 // LookupMethod returns the implementation of the method of type T
134 // identified by (pkg, name). It returns nil if the method exists but
135 // is an interface method or generic method, and panics if T has no such method.
136 func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
137 sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
138 if sel == nil {
139 panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
140 }
141 return prog.MethodValue(sel)
142 }
143 144 // methodSet contains the (concrete) methods of a concrete type (non-interface, non-parameterized).
145 type methodSet struct {
146 mapping map[string]*Function // populated lazily
147 }
148 149 // RuntimeTypes returns a new unordered slice containing all types in
150 // the program for which a runtime type is required.
151 //
152 // A runtime type is required for any non-parameterized, non-interface
153 // type that is converted to an interface, or for any type (including
154 // interface types) derivable from one through reflection.
155 //
156 // The methods of such types may be reachable through reflection or
157 // interface calls even if they are never called directly.
158 //
159 // Thread-safe.
160 //
161 // Acquires prog.makeInterfaceTypesMu.
162 func (prog *Program) RuntimeTypes() []types.Type {
163 prog.makeInterfaceTypesMu.Lock()
164 defer prog.makeInterfaceTypesMu.Unlock()
165 166 // Compute the derived types on demand, since many SSA clients
167 // never call RuntimeTypes, and those that do typically call
168 // it once (often within ssautil.AllFunctions, which will
169 // eventually not use it; see Go issue #69291.) This
170 // eliminates the need to eagerly compute all the element
171 // types during SSA building.
172 var runtimeTypes []types.Type
173 add := func(t types.Type) { runtimeTypes = append(runtimeTypes, t) }
174 var set typeutil.Map // for de-duping identical types
175 for t := range prog.makeInterfaceTypes {
176 typesinternal.ForEachElement(&set, &prog.MethodSets, t, add)
177 }
178 179 return runtimeTypes
180 }
181