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