GoPLS Viewer

Home|gopls/go/ssa/methods.go
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
5package ssa
6
7// This file defines utilities for population of method sets.
8
9import (
10    "fmt"
11    "go/types"
12
13    "golang.org/x/tools/internal/typeparams"
14)
15
16// MethodValue returns the Function implementing method sel, building
17// wrapper methods on demand.  It returns nil if sel denotes an
18// abstract (interface or parameterized) method.
19//
20// Precondition: sel.Kind() == MethodVal.
21//
22// Thread-safe.
23//
24// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
25func (prog *ProgramMethodValue(sel *types.Selection) *Function {
26    if sel.Kind() != types.MethodVal {
27        panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal"sel))
28    }
29    T := sel.Recv()
30    if types.IsInterface(T) {
31        return nil // abstract method (interface, possibly type param)
32    }
33    if prog.mode&LogSource != 0 {
34        defer logStack("MethodValue %s %v"Tsel)()
35    }
36
37    var m *Function
38    b := builder{created: &creator{}}
39
40    prog.methodsMu.Lock()
41    // Checks whether a type param is reachable from T.
42    // This is an expensive check. May need to be optimized later.
43    if !prog.parameterized.isParameterized(T) {
44        m = prog.addMethod(prog.createMethodSet(T), selb.created)
45    }
46    prog.methodsMu.Unlock()
47
48    if m == nil {
49        return nil // abstract method (generic)
50    }
51    for !b.done() {
52        b.buildCreated()
53        b.needsRuntimeTypes()
54    }
55    return m
56}
57
58// LookupMethod returns the implementation of the method of type T
59// identified by (pkg, name).  It returns nil if the method exists but
60// is abstract, and panics if T has no such method.
61func (prog *ProgramLookupMethod(T types.Typepkg *types.Packagename string) *Function {
62    sel := prog.MethodSets.MethodSet(T).Lookup(pkgname)
63    if sel == nil {
64        panic(fmt.Sprintf("%s has no method %s"Ttypes.Id(pkgname)))
65    }
66    return prog.MethodValue(sel)
67}
68
69// methodSet contains the (concrete) methods of a concrete type (non-interface, non-parameterized).
70type methodSet struct {
71    mapping  map[string]*Function // populated lazily
72    complete bool                 // mapping contains all methods
73}
74
75// Precondition: T is a concrete type, e.g. !isInterface(T) and not parameterized.
76// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
77func (prog *ProgramcreateMethodSet(T types.Type) *methodSet {
78    if prog.mode&SanityCheckFunctions != 0 {
79        if types.IsInterface(T) || prog.parameterized.isParameterized(T) {
80            panic("type is interface or parameterized")
81        }
82    }
83    msetok := prog.methodSets.At(T).(*methodSet)
84    if !ok {
85        mset = &methodSet{mappingmake(map[string]*Function)}
86        prog.methodSets.Set(Tmset)
87    }
88    return mset
89}
90
91// Adds any created functions to cr.
92// Precondition: T is a concrete type, e.g. !isInterface(T) and not parameterized.
93// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
94func (prog *ProgramaddMethod(mset *methodSetsel *types.Selectioncr *creator) *Function {
95    if sel.Kind() == types.MethodExpr {
96        panic(sel)
97    }
98    id := sel.Obj().Id()
99    fn := mset.mapping[id]
100    if fn == nil {
101        sel := toSelection(sel)
102        obj := sel.obj.(*types.Func)
103
104        needsPromotion := len(sel.index) > 1
105        needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.recv)
106        if needsPromotion || needsIndirection {
107            fn = makeWrapper(progselcr)
108        } else {
109            fn = prog.originFunc(obj)
110            if fn.typeparams.Len() > 0 { // instantiate
111                targs := receiverTypeArgs(obj)
112                fn = prog.lookupOrCreateInstance(fntargscr)
113            }
114        }
115        if fn.Signature.Recv() == nil {
116            panic(fn// missing receiver
117        }
118        mset.mapping[id] = fn
119    }
120    return fn
121}
122
123// RuntimeTypes returns a new unordered slice containing all
124// concrete types in the program for which a complete (non-empty)
125// method set is required at run-time.
126//
127// Thread-safe.
128//
129// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
130func (prog *ProgramRuntimeTypes() []types.Type {
131    prog.methodsMu.Lock()
132    defer prog.methodsMu.Unlock()
133
134    var res []types.Type
135    prog.methodSets.Iterate(func(T types.Typev interface{}) {
136        if v.(*methodSet).complete {
137            res = append(resT)
138        }
139    })
140    return res
141}
142
143// declaredFunc returns the concrete function/method denoted by obj.
144// Panic ensues if there is none.
145func (prog *ProgramdeclaredFunc(obj *types.Func) *Function {
146    if v := prog.packageLevelMember(obj); v != nil {
147        return v.(*Function)
148    }
149    panic("no concrete method: " + obj.String())
150}
151
152// needMethodsOf ensures that runtime type information (including the
153// complete method set) is available for the specified type T and all
154// its subcomponents.
155//
156// needMethodsOf must be called for at least every type that is an
157// operand of some MakeInterface instruction, and for the type of
158// every exported package member.
159//
160// Adds any created functions to cr.
161//
162// Precondition: T is not a method signature (*Signature with Recv()!=nil).
163// Precondition: T is not parameterized.
164//
165// Thread-safe.  (Called via Package.build from multiple builder goroutines.)
166//
167// TODO(adonovan): make this faster.  It accounts for 20% of SSA build time.
168//
169// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
170func (prog *ProgramneedMethodsOf(T types.Typecr *creator) {
171    prog.methodsMu.Lock()
172    prog.needMethods(Tfalsecr)
173    prog.methodsMu.Unlock()
174}
175
176// Precondition: T is not a method signature (*Signature with Recv()!=nil).
177// Precondition: T is not parameterized.
178// Recursive case: skip => don't create methods for T.
179//
180// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
181func (prog *ProgramneedMethods(T types.Typeskip boolcr *creator) {
182    // Each package maintains its own set of types it has visited.
183    if prevSkipok := prog.runtimeTypes.At(T).(bool); ok {
184        // needMethods(T) was previously called
185        if !prevSkip || skip {
186            return // already seen, with same or false 'skip' value
187        }
188    }
189    prog.runtimeTypes.Set(Tskip)
190
191    tmset := prog.MethodSets.MethodSet(T)
192
193    if !skip && !types.IsInterface(T) && tmset.Len() > 0 {
194        // Create methods of T.
195        mset := prog.createMethodSet(T)
196        if !mset.complete {
197            mset.complete = true
198            n := tmset.Len()
199            for i := 0i < ni++ {
200                prog.addMethod(msettmset.At(i), cr)
201            }
202        }
203    }
204
205    // Recursion over signatures of each method.
206    for i := 0i < tmset.Len(); i++ {
207        sig := tmset.At(i).Type().(*types.Signature)
208        prog.needMethods(sig.Params(), falsecr)
209        prog.needMethods(sig.Results(), falsecr)
210    }
211
212    switch t := T.(type) {
213    case *types.Basic:
214        // nop
215
216    case *types.Interface:
217        // nop---handled by recursion over method set.
218
219    case *types.Pointer:
220        prog.needMethods(t.Elem(), falsecr)
221
222    case *types.Slice:
223        prog.needMethods(t.Elem(), falsecr)
224
225    case *types.Chan:
226        prog.needMethods(t.Elem(), falsecr)
227
228    case *types.Map:
229        prog.needMethods(t.Key(), falsecr)
230        prog.needMethods(t.Elem(), falsecr)
231
232    case *types.Signature:
233        if t.Recv() != nil {
234            panic(fmt.Sprintf("Signature %s has Recv %s"tt.Recv()))
235        }
236        prog.needMethods(t.Params(), falsecr)
237        prog.needMethods(t.Results(), falsecr)
238
239    case *types.Named:
240        // A pointer-to-named type can be derived from a named
241        // type via reflection.  It may have methods too.
242        prog.needMethods(types.NewPointer(T), falsecr)
243
244        // Consider 'type T struct{S}' where S has methods.
245        // Reflection provides no way to get from T to struct{S},
246        // only to S, so the method set of struct{S} is unwanted,
247        // so set 'skip' flag during recursion.
248        prog.needMethods(t.Underlying(), truecr)
249
250    case *types.Array:
251        prog.needMethods(t.Elem(), falsecr)
252
253    case *types.Struct:
254        for in := 0t.NumFields(); i < ni++ {
255            prog.needMethods(t.Field(i).Type(), falsecr)
256        }
257
258    case *types.Tuple:
259        for in := 0t.Len(); i < ni++ {
260            prog.needMethods(t.At(i).Type(), falsecr)
261        }
262
263    case *typeparams.TypeParam:
264        panic(T// type parameters are always abstract.
265
266    case *typeparams.Union:
267        // nop
268
269    default:
270        panic(T)
271    }
272}
273
MembersX
Program.needMethods.BlockStmt.i
Program.needMethods.BlockStmt.n
Program.MethodValue.m
Program.addMethod
Program.addMethod.cr
Program.declaredFunc.v
Program.needMethodsOf
Program.needMethods.skip
Program.needMethods.BlockStmt.BlockStmt.i
Program.MethodValue.b
Program.LookupMethod.T
Program.addMethod.mset
Program.addMethod.BlockStmt.BlockStmt.BlockStmt.targs
Program.RuntimeTypes.res
Program.needMethods.prog
Program.MethodValue
Program.needMethodsOf.T
Program.needMethods.BlockStmt.BlockStmt.n
Program.LookupMethod
Program.LookupMethod.pkg
Program.createMethodSet.T
Program.addMethod.sel
Program.declaredFunc
Program.needMethods.cr
Program.addMethod.BlockStmt.sel
Program.needMethodsOf.prog
Program.LookupMethod.name
Program.LookupMethod.sel
methodSet.mapping
Program.createMethodSet.prog
Program.createMethodSet
Program.addMethod.id
Program.needMethodsOf.cr
Program.needMethods
Program.MethodValue.prog
Program.RuntimeTypes.prog
Program.RuntimeTypes
Program.declaredFunc.prog
Program.needMethods.BlockStmt.mset
Program.MethodValue.sel
Program.MethodValue.T
methodSet
Program.needMethods.T
Program.needMethods.tmset
Program.LookupMethod.prog
methodSet.complete
Program.addMethod.prog
Program.declaredFunc.obj
Program.needMethods.i
Members
X