github.com/jd-ly/tools@v0.5.7/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  	tparams := fn.Signature.Params()
   145  	for i, n := start, tparams.Len(); i < n; i++ {
   146  		fn.addParamObj(tparams.At(i))
   147  	}
   148  }
   149  
   150  // -- bounds -----------------------------------------------------------
   151  
   152  // makeBound returns a bound method wrapper (or "bound"), a synthetic
   153  // function that delegates to a concrete or interface method denoted
   154  // by obj.  The resulting function has no receiver, but has one free
   155  // variable which will be used as the method's receiver in the
   156  // tail-call.
   157  //
   158  // Use MakeClosure with such a wrapper to construct a bound method
   159  // closure.  e.g.:
   160  //
   161  //   type T int          or:  type T interface { meth() }
   162  //   func (t T) meth()
   163  //   var t T
   164  //   f := t.meth
   165  //   f() // calls t.meth()
   166  //
   167  // f is a closure of a synthetic wrapper defined as if by:
   168  //
   169  //   f := func() { return t.meth() }
   170  //
   171  // Unlike makeWrapper, makeBound need perform no indirection or field
   172  // selections because that can be done before the closure is
   173  // constructed.
   174  //
   175  // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
   176  //
   177  func makeBound(prog *Program, obj *types.Func) *Function {
   178  	prog.methodsMu.Lock()
   179  	defer prog.methodsMu.Unlock()
   180  	fn, ok := prog.bounds[obj]
   181  	if !ok {
   182  		description := fmt.Sprintf("bound method wrapper for %s", obj)
   183  		if prog.mode&LogSource != 0 {
   184  			defer logStack("%s", description)()
   185  		}
   186  		fn = &Function{
   187  			name:      obj.Name() + "$bound",
   188  			object:    obj,
   189  			Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
   190  			Synthetic: description,
   191  			Prog:      prog,
   192  			pos:       obj.Pos(),
   193  		}
   194  
   195  		fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn}
   196  		fn.FreeVars = []*FreeVar{fv}
   197  		fn.startBody()
   198  		createParams(fn, 0)
   199  		var c Call
   200  
   201  		if !isInterface(recvType(obj)) { // concrete
   202  			c.Call.Value = prog.declaredFunc(obj)
   203  			c.Call.Args = []Value{fv}
   204  		} else {
   205  			c.Call.Value = fv
   206  			c.Call.Method = obj
   207  		}
   208  		for _, arg := range fn.Params {
   209  			c.Call.Args = append(c.Call.Args, arg)
   210  		}
   211  		emitTailCall(fn, &c)
   212  		fn.finishBody()
   213  
   214  		prog.bounds[obj] = fn
   215  	}
   216  	return fn
   217  }
   218  
   219  // -- thunks -----------------------------------------------------------
   220  
   221  // makeThunk returns a thunk, a synthetic function that delegates to a
   222  // concrete or interface method denoted by sel.Obj().  The resulting
   223  // function has no receiver, but has an additional (first) regular
   224  // parameter.
   225  //
   226  // Precondition: sel.Kind() == types.MethodExpr.
   227  //
   228  //   type T int          or:  type T interface { meth() }
   229  //   func (t T) meth()
   230  //   f := T.meth
   231  //   var t T
   232  //   f(t) // calls t.meth()
   233  //
   234  // f is a synthetic wrapper defined as if by:
   235  //
   236  //   f := func(t T) { return t.meth() }
   237  //
   238  // TODO(adonovan): opt: currently the stub is created even when used
   239  // directly in a function call: C.f(i, 0).  This is less efficient
   240  // than inlining the stub.
   241  //
   242  // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
   243  //
   244  func makeThunk(prog *Program, sel *types.Selection) *Function {
   245  	if sel.Kind() != types.MethodExpr {
   246  		panic(sel)
   247  	}
   248  
   249  	key := selectionKey{
   250  		kind:     sel.Kind(),
   251  		recv:     sel.Recv(),
   252  		obj:      sel.Obj(),
   253  		index:    fmt.Sprint(sel.Index()),
   254  		indirect: sel.Indirect(),
   255  	}
   256  
   257  	prog.methodsMu.Lock()
   258  	defer prog.methodsMu.Unlock()
   259  
   260  	// Canonicalize key.recv to avoid constructing duplicate thunks.
   261  	canonRecv, ok := prog.canon.At(key.recv).(types.Type)
   262  	if !ok {
   263  		canonRecv = key.recv
   264  		prog.canon.Set(key.recv, canonRecv)
   265  	}
   266  	key.recv = canonRecv
   267  
   268  	fn, ok := prog.thunks[key]
   269  	if !ok {
   270  		fn = makeWrapper(prog, sel)
   271  		if fn.Signature.Recv() != nil {
   272  			panic(fn) // unexpected receiver
   273  		}
   274  		prog.thunks[key] = fn
   275  	}
   276  	return fn
   277  }
   278  
   279  func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
   280  	return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic())
   281  }
   282  
   283  // selectionKey is like types.Selection but a usable map key.
   284  type selectionKey struct {
   285  	kind     types.SelectionKind
   286  	recv     types.Type // canonicalized via Program.canon
   287  	obj      types.Object
   288  	index    string
   289  	indirect bool
   290  }