github.com/pankona/gometalinter@v2.0.11+incompatible/_linters/src/golang.org/x/tools/go/ssa/wrappers.go (about)

     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 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  
    22  import (
    23  	"fmt"
    24  
    25  	"go/types"
    26  )
    27  
    28  // -- wrappers -----------------------------------------------------------
    29  
    30  // makeWrapper returns a synthetic method that delegates to the
    31  // declared method denoted by meth.Obj(), first performing any
    32  // necessary pointer indirections or field selections implied by meth.
    33  //
    34  // The resulting method's receiver type is meth.Recv().
    35  //
    36  // This function is versatile but quite subtle!  Consider the
    37  // following axes of variation when making changes:
    38  //   - optional receiver indirection
    39  //   - optional implicit field selections
    40  //   - meth.Obj() may denote a concrete or an interface method
    41  //   - the result may be a thunk or a wrapper.
    42  //
    43  // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
    44  //
    45  func makeWrapper(prog *Program, sel *types.Selection) *Function {
    46  	obj := sel.Obj().(*types.Func)       // the declared function
    47  	sig := sel.Type().(*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", description, sel.Obj())
    64  	if prog.mode&LogSource != 0 {
    65  		defer logStack("make %s to (%s)", description, recv.Type())()
    66  	}
    67  	fn := &Function{
    68  		name:      name,
    69  		method:    sel,
    70  		object:    obj,
    71  		Signature: sig,
    72  		Synthetic: description,
    73  		Prog:      prog,
    74  		pos:       obj.Pos(),
    75  	}
    76  	fn.startBody()
    77  	fn.addSpilledParam(recv)
    78  	createParams(fn, start)
    79  
    80  	indices := sel.Index()
    81  
    82  	var v Value = fn.Locals[0] // spilled receiver
    83  	if isPointer(sel.Recv()) {
    84  		v = emitLoad(fn, v)
    85  
    86  		// For simple indirection wrappers, perform an informative nil-check:
    87  		// "value method (T).f called using nil *T pointer"
    88  		if len(indices) == 1 && !isPointer(recvType(obj)) {
    89  			var c Call
    90  			c.Call.Value = &Builtin{
    91  				name: "ssa:wrapnilchk",
    92  				sig: types.NewSignature(nil,
    93  					types.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)),
    94  					types.NewTuple(anonVar(sel.Recv())), false),
    95  			}
    96  			c.Call.Args = []Value{
    97  				v,
    98  				stringConst(deref(sel.Recv()).String()),
    99  				stringConst(sel.Obj().Name()),
   100  			}
   101  			c.setType(v.Type())
   102  			v = fn.emit(&c)
   103  		}
   104  	}
   105  
   106  	// Invariant: v is a pointer, either
   107  	//   value of *A receiver param, or
   108  	// address of  A spilled receiver.
   109  
   110  	// We use pointer arithmetic (FieldAddr possibly followed by
   111  	// Load) in preference to value extraction (Field possibly
   112  	// preceded by Load).
   113  
   114  	v = emitImplicitSelections(fn, v, indices[:len(indices)-1])
   115  
   116  	// Invariant: v is a pointer, either
   117  	//   value of implicit *C field, or
   118  	// address of implicit  C field.
   119  
   120  	var c Call
   121  	if r := recvType(obj); !isInterface(r) { // concrete method
   122  		if !isPointer(r) {
   123  			v = emitLoad(fn, v)
   124  		}
   125  		c.Call.Value = prog.declaredFunc(obj)
   126  		c.Call.Args = append(c.Call.Args, v)
   127  	} else {
   128  		c.Call.Method = obj
   129  		c.Call.Value = emitLoad(fn, v)
   130  	}
   131  	for _, arg := range fn.Params[1:] {
   132  		c.Call.Args = append(c.Call.Args, arg)
   133  	}
   134  	emitTailCall(fn, &c)
   135  	fn.finishBody()
   136  	return fn
   137  }
   138  
   139  // createParams creates parameters for wrapper method fn based on its
   140  // Signature.Params, which do not include the receiver.
   141  // start is the index of the first regular parameter to use.
   142  //
   143  func createParams(fn *Function, start int) {
   144  	var last *Parameter
   145  	tparams := fn.Signature.Params()
   146  	for i, n := start, tparams.Len(); i < n; i++ {
   147  		last = fn.addParamObj(tparams.At(i))
   148  	}
   149  	if fn.Signature.Variadic() {
   150  		last.typ = types.NewSlice(last.typ)
   151  	}
   152  }
   153  
   154  // -- bounds -----------------------------------------------------------
   155  
   156  // makeBound returns a bound method wrapper (or "bound"), a synthetic
   157  // function that delegates to a concrete or interface method denoted
   158  // by obj.  The resulting function has no receiver, but has one free
   159  // variable which will be used as the method's receiver in the
   160  // tail-call.
   161  //
   162  // Use MakeClosure with such a wrapper to construct a bound method
   163  // closure.  e.g.:
   164  //
   165  //   type T int          or:  type T interface { meth() }
   166  //   func (t T) meth()
   167  //   var t T
   168  //   f := t.meth
   169  //   f() // calls t.meth()
   170  //
   171  // f is a closure of a synthetic wrapper defined as if by:
   172  //
   173  //   f := func() { return t.meth() }
   174  //
   175  // Unlike makeWrapper, makeBound need perform no indirection or field
   176  // selections because that can be done before the closure is
   177  // constructed.
   178  //
   179  // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
   180  //
   181  func makeBound(prog *Program, obj *types.Func) *Function {
   182  	prog.methodsMu.Lock()
   183  	defer prog.methodsMu.Unlock()
   184  	fn, ok := prog.bounds[obj]
   185  	if !ok {
   186  		description := fmt.Sprintf("bound method wrapper for %s", obj)
   187  		if prog.mode&LogSource != 0 {
   188  			defer logStack("%s", description)()
   189  		}
   190  		fn = &Function{
   191  			name:      obj.Name() + "$bound",
   192  			object:    obj,
   193  			Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
   194  			Synthetic: description,
   195  			Prog:      prog,
   196  			pos:       obj.Pos(),
   197  		}
   198  
   199  		fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn}
   200  		fn.FreeVars = []*FreeVar{fv}
   201  		fn.startBody()
   202  		createParams(fn, 0)
   203  		var c Call
   204  
   205  		if !isInterface(recvType(obj)) { // concrete
   206  			c.Call.Value = prog.declaredFunc(obj)
   207  			c.Call.Args = []Value{fv}
   208  		} else {
   209  			c.Call.Value = fv
   210  			c.Call.Method = obj
   211  		}
   212  		for _, arg := range fn.Params {
   213  			c.Call.Args = append(c.Call.Args, arg)
   214  		}
   215  		emitTailCall(fn, &c)
   216  		fn.finishBody()
   217  
   218  		prog.bounds[obj] = fn
   219  	}
   220  	return fn
   221  }
   222  
   223  // -- thunks -----------------------------------------------------------
   224  
   225  // makeThunk returns a thunk, a synthetic function that delegates to a
   226  // concrete or interface method denoted by sel.Obj().  The resulting
   227  // function has no receiver, but has an additional (first) regular
   228  // parameter.
   229  //
   230  // Precondition: sel.Kind() == types.MethodExpr.
   231  //
   232  //   type T int          or:  type T interface { meth() }
   233  //   func (t T) meth()
   234  //   f := T.meth
   235  //   var t T
   236  //   f(t) // calls t.meth()
   237  //
   238  // f is a synthetic wrapper defined as if by:
   239  //
   240  //   f := func(t T) { return t.meth() }
   241  //
   242  // TODO(adonovan): opt: currently the stub is created even when used
   243  // directly in a function call: C.f(i, 0).  This is less efficient
   244  // than inlining the stub.
   245  //
   246  // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
   247  //
   248  func makeThunk(prog *Program, sel *types.Selection) *Function {
   249  	if sel.Kind() != types.MethodExpr {
   250  		panic(sel)
   251  	}
   252  
   253  	key := selectionKey{
   254  		kind:     sel.Kind(),
   255  		recv:     sel.Recv(),
   256  		obj:      sel.Obj(),
   257  		index:    fmt.Sprint(sel.Index()),
   258  		indirect: sel.Indirect(),
   259  	}
   260  
   261  	prog.methodsMu.Lock()
   262  	defer prog.methodsMu.Unlock()
   263  
   264  	// Canonicalize key.recv to avoid constructing duplicate thunks.
   265  	canonRecv, ok := prog.canon.At(key.recv).(types.Type)
   266  	if !ok {
   267  		canonRecv = key.recv
   268  		prog.canon.Set(key.recv, canonRecv)
   269  	}
   270  	key.recv = canonRecv
   271  
   272  	fn, ok := prog.thunks[key]
   273  	if !ok {
   274  		fn = makeWrapper(prog, sel)
   275  		if fn.Signature.Recv() != nil {
   276  			panic(fn) // unexpected receiver
   277  		}
   278  		prog.thunks[key] = fn
   279  	}
   280  	return fn
   281  }
   282  
   283  func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
   284  	return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic())
   285  }
   286  
   287  // selectionKey is like types.Selection but a usable map key.
   288  type selectionKey struct {
   289  	kind     types.SelectionKind
   290  	recv     types.Type // canonicalized via Program.canon
   291  	obj      types.Object
   292  	index    string
   293  	indirect bool
   294  }