...

Source file src/golang.org/x/tools/go/ssa/wrappers.go

Documentation: golang.org/x/tools/go/ssa

     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  func makeWrapper(prog *Program, sel *selection, cr *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", 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  		info:      nil, // info is not set on wrappers.
    76  	}
    77  	cr.Add(fn)
    78  	fn.startBody()
    79  	fn.addSpilledParam(recv)
    80  	createParams(fn, start)
    81  
    82  	indices := sel.index
    83  
    84  	var v Value = fn.Locals[0] // spilled receiver
    85  	if isPointer(sel.recv) {
    86  		v = emitLoad(fn, v)
    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  				sig: types.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(fn, v, indices[: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(fn, v)
   126  		}
   127  		callee := prog.originFunc(obj)
   128  		if callee.typeparams.Len() > 0 {
   129  			callee = prog.lookupOrCreateInstance(callee, receiverTypeArgs(obj), cr)
   130  		}
   131  		c.Call.Value = callee
   132  		c.Call.Args = append(c.Call.Args, v)
   133  	} else {
   134  		c.Call.Method = obj
   135  		c.Call.Value = emitLoad(fn, v) // interface (possibly a typeparam)
   136  	}
   137  	for _, arg := range fn.Params[1:] {
   138  		c.Call.Args = append(c.Call.Args, arg)
   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.
   149  func createParams(fn *Function, start int) {
   150  	tparams := fn.Signature.Params()
   151  	for i, n := start, tparams.Len(); i < n; i++ {
   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)
   182  func makeBound(prog *Program, obj *types.Func, cr *creator) *Function {
   183  	targs := receiverTypeArgs(obj)
   184  	key := boundsKey{obj, prog.canon.List(targs)}
   185  
   186  	prog.methodsMu.Lock()
   187  	defer prog.methodsMu.Unlock()
   188  	fn, ok := 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  			Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
   198  			Synthetic: description,
   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", typ: recvType(obj), parent: fn}
   206  		fn.FreeVars = []*FreeVar{fv}
   207  		fn.startBody()
   208  		createParams(fn, 0)
   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(callee, targs, cr)
   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.Args, arg)
   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)
   258  func makeThunk(prog *Program, sel *selection, cr *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  		indirect: sel.indirect,
   271  	}
   272  
   273  	prog.methodsMu.Lock()
   274  	defer prog.methodsMu.Unlock()
   275  
   276  	fn, ok := prog.thunks[key]
   277  	if !ok {
   278  		fn = makeWrapper(prog, sel, cr)
   279  		if fn.Signature.Recv() != nil {
   280  			panic(fn) // unexpected receiver
   281  		}
   282  		prog.thunks[key] = fn
   283  	}
   284  	return fn
   285  }
   286  
   287  func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
   288  	return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic())
   289  }
   290  
   291  // selectionKey is like types.Selection but a usable map key.
   292  type 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.
   301  type 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.
   308  type 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  
   317  func 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  		indirect: sel.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.
   333  func 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(fn, 0)
   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 i, arg := 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.Args, emitTypeCoercion(fn, arg, typ))
   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(fn, results, res.At(0).Type())}
   375  	default:
   376  		for i := 0; i < sig.Results().Len(); i++ {
   377  			v := emitExtract(fn, results, i)
   378  			ret.Results = append(ret.Results, emitTypeCoercion(fn, v, res.At(i).Type()))
   379  		}
   380  	}
   381  
   382  	fn.emit(&ret)
   383  	fn.currentBlock = nil
   384  
   385  	fn.finishBody()
   386  }
   387  

View as plain text