GoPLS Viewer

Home|gopls/go/ssa/wrappers.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 synthesis of Functions that delegate to declared
8// methods; they come in three kinds:
9//
10// (1) wrappers: methods that wrap declared methods, performing
11//     implicit pointer indirections and embedded field selections.
12//
13// (2) thunks: funcs that wrap declared methods.  Like wrappers,
14//     thunks perform indirections and field selections. The thunk's
15//     first parameter is used as the receiver for the method call.
16//
17// (3) bounds: funcs that wrap declared methods.  The bound's sole
18//     free variable, supplied by a closure, is used as the receiver
19//     for the method call.  No indirections or field selections are
20//     performed since they can be done before the call.
21
22import (
23    "fmt"
24
25    "go/token"
26    "go/types"
27)
28
29// -- wrappers -----------------------------------------------------------
30
31// makeWrapper returns a synthetic method that delegates to the
32// declared method denoted by meth.Obj(), first performing any
33// necessary pointer indirections or field selections implied by meth.
34//
35// The resulting method's receiver type is meth.Recv().
36//
37// This function is versatile but quite subtle!  Consider the
38// following axes of variation when making changes:
39//   - optional receiver indirection
40//   - optional implicit field selections
41//   - meth.Obj() may denote a concrete or an interface method
42//   - the result may be a thunk or a wrapper.
43//
44// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
45func makeWrapper(prog *Programsel *selectioncr *creator) *Function {
46    obj := sel.obj.(*types.Func)      // the declared function
47    sig := sel.typ.(*types.Signature// type of this wrapper
48
49    var recv *types.Var // wrapper's receiver or thunk's params[0]
50    name := obj.Name()
51    var description string
52    var start int // first regular param
53    if sel.kind == types.MethodExpr {
54        name += "$thunk"
55        description = "thunk"
56        recv = sig.Params().At(0)
57        start = 1
58    } else {
59        description = "wrapper"
60        recv = sig.Recv()
61    }
62
63    description = fmt.Sprintf("%s for %s"descriptionsel.obj)
64    if prog.mode&LogSource != 0 {
65        defer logStack("make %s to (%s)"descriptionrecv.Type())()
66    }
67    fn := &Function{
68        name:      name,
69        method:    sel,
70        object:    obj,
71        Signaturesig,
72        Syntheticdescription,
73        Prog:      prog,
74        pos:       obj.Pos(),
75        info:      nil// info is not set on wrappers.
76    }
77    cr.Add(fn)
78    fn.startBody()
79    fn.addSpilledParam(recv)
80    createParams(fnstart)
81
82    indices := sel.index
83
84    var v Value = fn.Locals[0// spilled receiver
85    if isPointer(sel.recv) {
86        v = emitLoad(fnv)
87
88        // For simple indirection wrappers, perform an informative nil-check:
89        // "value method (T).f called using nil *T pointer"
90        if len(indices) == 1 && !isPointer(recvType(obj)) {
91            var c Call
92            c.Call.Value = &Builtin{
93                name"ssa:wrapnilchk",
94                sigtypes.NewSignature(nil,
95                    types.NewTuple(anonVar(sel.recv), anonVar(tString), anonVar(tString)),
96                    types.NewTuple(anonVar(sel.recv)), false),
97            }
98            c.Call.Args = []Value{
99                v,
100                stringConst(deref(sel.recv).String()),
101                stringConst(sel.obj.Name()),
102            }
103            c.setType(v.Type())
104            v = fn.emit(&c)
105        }
106    }
107
108    // Invariant: v is a pointer, either
109    //   value of *A receiver param, or
110    // address of  A spilled receiver.
111
112    // We use pointer arithmetic (FieldAddr possibly followed by
113    // Load) in preference to value extraction (Field possibly
114    // preceded by Load).
115
116    v = emitImplicitSelections(fnvindices[:len(indices)-1], token.NoPos)
117
118    // Invariant: v is a pointer, either
119    //   value of implicit *C field, or
120    // address of implicit  C field.
121
122    var c Call
123    if r := recvType(obj); !types.IsInterface(r) { // concrete method
124        if !isPointer(r) {
125            v = emitLoad(fnv)
126        }
127        callee := prog.originFunc(obj)
128        if callee.typeparams.Len() > 0 {
129            callee = prog.lookupOrCreateInstance(calleereceiverTypeArgs(obj), cr)
130        }
131        c.Call.Value = callee
132        c.Call.Args = append(c.Call.Argsv)
133    } else {
134        c.Call.Method = obj
135        c.Call.Value = emitLoad(fnv// interface (possibly a typeparam)
136    }
137    for _arg := range fn.Params[1:] {
138        c.Call.Args = append(c.Call.Argsarg)
139    }
140    emitTailCall(fn, &c)
141    fn.finishBody()
142    fn.done()
143    return fn
144}
145
146// createParams creates parameters for wrapper method fn based on its
147// Signature.Params, which do not include the receiver.
148// start is the index of the first regular parameter to use.
149func createParams(fn *Functionstart int) {
150    tparams := fn.Signature.Params()
151    for in := starttparams.Len(); i < ni++ {
152        fn.addParamObj(tparams.At(i))
153    }
154}
155
156// -- bounds -----------------------------------------------------------
157
158// makeBound returns a bound method wrapper (or "bound"), a synthetic
159// function that delegates to a concrete or interface method denoted
160// by obj.  The resulting function has no receiver, but has one free
161// variable which will be used as the method's receiver in the
162// tail-call.
163//
164// Use MakeClosure with such a wrapper to construct a bound method
165// closure.  e.g.:
166//
167//    type T int          or:  type T interface { meth() }
168//    func (t T) meth()
169//    var t T
170//    f := t.meth
171//    f() // calls t.meth()
172//
173// f is a closure of a synthetic wrapper defined as if by:
174//
175//    f := func() { return t.meth() }
176//
177// Unlike makeWrapper, makeBound need perform no indirection or field
178// selections because that can be done before the closure is
179// constructed.
180//
181// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
182func makeBound(prog *Programobj *types.Funccr *creator) *Function {
183    targs := receiverTypeArgs(obj)
184    key := boundsKey{objprog.canon.List(targs)}
185
186    prog.methodsMu.Lock()
187    defer prog.methodsMu.Unlock()
188    fnok := prog.bounds[key]
189    if !ok {
190        description := fmt.Sprintf("bound method wrapper for %s"obj)
191        if prog.mode&LogSource != 0 {
192            defer logStack("%s"description)()
193        }
194        fn = &Function{
195            name:      obj.Name() + "$bound",
196            object:    obj,
197            SignaturechangeRecv(obj.Type().(*types.Signature), nil), // drop receiver
198            Syntheticdescription,
199            Prog:      prog,
200            pos:       obj.Pos(),
201            info:      nil// info is not set on wrappers.
202        }
203        cr.Add(fn)
204
205        fv := &FreeVar{name"recv"typrecvType(obj), parentfn}
206        fn.FreeVars = []*FreeVar{fv}
207        fn.startBody()
208        createParams(fn0)
209        var c Call
210
211        if !types.IsInterface(recvType(obj)) { // concrete
212            callee := prog.originFunc(obj)
213            if callee.typeparams.Len() > 0 {
214                callee = prog.lookupOrCreateInstance(calleetargscr)
215            }
216            c.Call.Value = callee
217            c.Call.Args = []Value{fv}
218        } else {
219            c.Call.Method = obj
220            c.Call.Value = fv // interface (possibly a typeparam)
221        }
222        for _arg := range fn.Params {
223            c.Call.Args = append(c.Call.Argsarg)
224        }
225        emitTailCall(fn, &c)
226        fn.finishBody()
227        fn.done()
228
229        prog.bounds[key] = fn
230    }
231    return fn
232}
233
234// -- thunks -----------------------------------------------------------
235
236// makeThunk returns a thunk, a synthetic function that delegates to a
237// concrete or interface method denoted by sel.obj.  The resulting
238// function has no receiver, but has an additional (first) regular
239// parameter.
240//
241// Precondition: sel.kind == types.MethodExpr.
242//
243//    type T int          or:  type T interface { meth() }
244//    func (t T) meth()
245//    f := T.meth
246//    var t T
247//    f(t) // calls t.meth()
248//
249// f is a synthetic wrapper defined as if by:
250//
251//    f := func(t T) { return t.meth() }
252//
253// TODO(adonovan): opt: currently the stub is created even when used
254// directly in a function call: C.f(i, 0).  This is less efficient
255// than inlining the stub.
256//
257// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
258func makeThunk(prog *Programsel *selectioncr *creator) *Function {
259    if sel.kind != types.MethodExpr {
260        panic(sel)
261    }
262
263    // Canonicalize sel.recv to avoid constructing duplicate thunks.
264    canonRecv := prog.canon.Type(sel.recv)
265    key := selectionKey{
266        kind:     sel.kind,
267        recv:     canonRecv,
268        obj:      sel.obj,
269        index:    fmt.Sprint(sel.index),
270        indirectsel.indirect,
271    }
272
273    prog.methodsMu.Lock()
274    defer prog.methodsMu.Unlock()
275
276    fnok := prog.thunks[key]
277    if !ok {
278        fn = makeWrapper(progselcr)
279        if fn.Signature.Recv() != nil {
280            panic(fn// unexpected receiver
281        }
282        prog.thunks[key] = fn
283    }
284    return fn
285}
286
287func changeRecv(s *types.Signaturerecv *types.Var) *types.Signature {
288    return types.NewSignature(recvs.Params(), s.Results(), s.Variadic())
289}
290
291// selectionKey is like types.Selection but a usable map key.
292type selectionKey struct {
293    kind     types.SelectionKind
294    recv     types.Type // canonicalized via Program.canon
295    obj      types.Object
296    index    string
297    indirect bool
298}
299
300// boundsKey is a unique for the object and a type instantiation.
301type boundsKey struct {
302    obj  types.Object // t.meth
303    inst *typeList    // canonical type instantiation list.
304}
305
306// A local version of *types.Selection.
307// Needed for some additional control, such as creating a MethodExpr for an instantiation.
308type selection struct {
309    kind     types.SelectionKind
310    recv     types.Type
311    typ      types.Type
312    obj      types.Object
313    index    []int
314    indirect bool
315}
316
317func toSelection(sel *types.Selection) *selection {
318    return &selection{
319        kind:     sel.Kind(),
320        recv:     sel.Recv(),
321        typ:      sel.Type(),
322        obj:      sel.Obj(),
323        index:    sel.Index(),
324        indirectsel.Indirect(),
325    }
326}
327
328// -- instantiations --------------------------------------------------
329
330// buildInstantiationWrapper creates a body for an instantiation
331// wrapper fn. The body calls the original generic function,
332// bracketed by ChangeType conversions on its arguments and results.
333func buildInstantiationWrapper(fn *Function) {
334    orig := fn.topLevelOrigin
335    sig := fn.Signature
336
337    fn.startBody()
338    if sig.Recv() != nil {
339        fn.addParamObj(sig.Recv())
340    }
341    createParams(fn0)
342
343    // Create body. Add a call to origin generic function
344    // and make type changes between argument and parameters,
345    // as well as return values.
346    var c Call
347    c.Call.Value = orig
348    if res := orig.Signature.Results(); res.Len() == 1 {
349        c.typ = res.At(0).Type()
350    } else {
351        c.typ = res
352    }
353
354    // parameter of instance becomes an argument to the call
355    // to the original generic function.
356    argOffset := 0
357    for iarg := range fn.Params {
358        var typ types.Type
359        if i == 0 && sig.Recv() != nil {
360            typ = orig.Signature.Recv().Type()
361            argOffset = 1
362        } else {
363            typ = orig.Signature.Params().At(i - argOffset).Type()
364        }
365        c.Call.Args = append(c.Call.ArgsemitTypeCoercion(fnargtyp))
366    }
367
368    results := fn.emit(&c)
369    var ret Return
370    switch res := sig.Results(); res.Len() {
371    case 0:
372        // no results, do nothing.
373    case 1:
374        ret.Results = []Value{emitTypeCoercion(fnresultsres.At(0).Type())}
375    default:
376        for i := 0i < sig.Results().Len(); i++ {
377            v := emitExtract(fnresultsi)
378            ret.Results = append(ret.ResultsemitTypeCoercion(fnvres.At(i).Type()))
379        }
380    }
381
382    fn.emit(&ret)
383    fn.currentBlock = nil
384
385    fn.finishBody()
386}
387
MembersX
buildInstantiationWrapper.RangeStmt_10216.arg
createParams.start
makeBound.BlockStmt.description
selectionKey.kind
selection.indirect
buildInstantiationWrapper.fn
buildInstantiationWrapper.res
buildInstantiationWrapper.BlockStmt.BlockStmt.v
makeWrapper.BlockStmt.callee
makeBound.prog
makeBound.BlockStmt.fv
makeThunk.cr
selection.kind
selection.recv
buildInstantiationWrapper.c
createParams.fn
makeBound.cr
changeRecv.recv
boundsKey.inst
selection.obj
selection.index
buildInstantiationWrapper.RangeStmt_10216.BlockStmt.typ
makeWrapper.sel
createParams.tparams
createParams.n
makeBound.BlockStmt.BlockStmt.callee
selectionKey.indirect
selection.typ
selectionKey.obj
makeWrapper.start
makeWrapper.RangeStmt_4169.arg
makeBound.BlockStmt.RangeStmt_6710.arg
makeThunk
changeRecv.s
selectionKey
makeWrapper.cr
makeWrapper.recv
makeBound
buildInstantiationWrapper.argOffset
makeWrapper.fn
toSelection
buildInstantiationWrapper
makeWrapper.v
createParams
selectionKey.recv
selectionKey.index
boundsKey
boundsKey.obj
makeWrapper.name
makeThunk.prog
selection
makeWrapper.description
buildInstantiationWrapper.sig
makeBound.BlockStmt.c
makeThunk.canonRecv
buildInstantiationWrapper.results
buildInstantiationWrapper.ret
makeWrapper
makeWrapper.indices
makeBound.key
buildInstantiationWrapper.BlockStmt.i
makeWrapper.r
createParams.i
makeBound.targs
makeWrapper.prog
makeWrapper.BlockStmt.BlockStmt.c
makeBound.obj
makeThunk.sel
changeRecv
buildInstantiationWrapper.RangeStmt_10216.i
makeWrapper.c
makeThunk.key
toSelection.sel
buildInstantiationWrapper.orig
Members
X