gitee.com/wgliang/goreporter@v0.0.0-20180902115603-df1b20f7c5d0/linters/simpler/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  // +build go1.5
     6  
     7  package ssa
     8  
     9  // This file defines synthesis of Functions that delegate to declared
    10  // methods; they come in three kinds:
    11  //
    12  // (1) wrappers: methods that wrap declared methods, performing
    13  //     implicit pointer indirections and embedded field selections.
    14  //
    15  // (2) thunks: funcs that wrap declared methods.  Like wrappers,
    16  //     thunks perform indirections and field selections. The thunk's
    17  //     first parameter is used as the receiver for the method call.
    18  //
    19  // (3) bounds: funcs that wrap declared methods.  The bound's sole
    20  //     free variable, supplied by a closure, is used as the receiver
    21  //     for the method call.  No indirections or field selections are
    22  //     performed since they can be done before the call.
    23  
    24  import (
    25  	"fmt"
    26  
    27  	"go/types"
    28  )
    29  
    30  // -- wrappers -----------------------------------------------------------
    31  
    32  // makeWrapper returns a synthetic method that delegates to the
    33  // declared method denoted by meth.Obj(), first performing any
    34  // necessary pointer indirections or field selections implied by meth.
    35  //
    36  // The resulting method's receiver type is meth.Recv().
    37  //
    38  // This function is versatile but quite subtle!  Consider the
    39  // following axes of variation when making changes:
    40  //   - optional receiver indirection
    41  //   - optional implicit field selections
    42  //   - meth.Obj() may denote a concrete or an interface method
    43  //   - the result may be a thunk or a wrapper.
    44  //
    45  // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
    46  //
    47  func makeWrapper(prog *Program, sel *types.Selection) *Function {
    48  	obj := sel.Obj().(*types.Func)       // the declared function
    49  	sig := sel.Type().(*types.Signature) // type of this wrapper
    50  
    51  	var recv *types.Var // wrapper's receiver or thunk's params[0]
    52  	name := obj.Name()
    53  	var description string
    54  	var start int // first regular param
    55  	if sel.Kind() == types.MethodExpr {
    56  		name += "$thunk"
    57  		description = "thunk"
    58  		recv = sig.Params().At(0)
    59  		start = 1
    60  	} else {
    61  		description = "wrapper"
    62  		recv = sig.Recv()
    63  	}
    64  
    65  	description = fmt.Sprintf("%s for %s", description, sel.Obj())
    66  	if prog.mode&LogSource != 0 {
    67  		defer logStack("make %s to (%s)", description, recv.Type())()
    68  	}
    69  	fn := &Function{
    70  		name:      name,
    71  		method:    sel,
    72  		object:    obj,
    73  		Signature: sig,
    74  		Synthetic: description,
    75  		Prog:      prog,
    76  		pos:       obj.Pos(),
    77  	}
    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])
   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); !isInterface(r) { // concrete method
   124  		if !isPointer(r) {
   125  			v = emitLoad(fn, v)
   126  		}
   127  		c.Call.Value = prog.declaredFunc(obj)
   128  		c.Call.Args = append(c.Call.Args, v)
   129  	} else {
   130  		c.Call.Method = obj
   131  		c.Call.Value = emitLoad(fn, v)
   132  	}
   133  	for _, arg := range fn.Params[1:] {
   134  		c.Call.Args = append(c.Call.Args, arg)
   135  	}
   136  	emitTailCall(fn, &c)
   137  	fn.finishBody()
   138  	return fn
   139  }
   140  
   141  // createParams creates parameters for wrapper method fn based on its
   142  // Signature.Params, which do not include the receiver.
   143  // start is the index of the first regular parameter to use.
   144  //
   145  func createParams(fn *Function, start int) {
   146  	var last *Parameter
   147  	tparams := fn.Signature.Params()
   148  	for i, n := start, tparams.Len(); i < n; i++ {
   149  		last = fn.addParamObj(tparams.At(i))
   150  	}
   151  	if fn.Signature.Variadic() {
   152  		last.typ = types.NewSlice(last.typ)
   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  //
   183  func makeBound(prog *Program, obj *types.Func) *Function {
   184  	prog.methodsMu.Lock()
   185  	defer prog.methodsMu.Unlock()
   186  	fn, ok := prog.bounds[obj]
   187  	if !ok {
   188  		description := fmt.Sprintf("bound method wrapper for %s", obj)
   189  		if prog.mode&LogSource != 0 {
   190  			defer logStack("%s", description)()
   191  		}
   192  		fn = &Function{
   193  			name:      obj.Name() + "$bound",
   194  			object:    obj,
   195  			Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
   196  			Synthetic: description,
   197  			Prog:      prog,
   198  			pos:       obj.Pos(),
   199  		}
   200  
   201  		fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn}
   202  		fn.FreeVars = []*FreeVar{fv}
   203  		fn.startBody()
   204  		createParams(fn, 0)
   205  		var c Call
   206  
   207  		if !isInterface(recvType(obj)) { // concrete
   208  			c.Call.Value = prog.declaredFunc(obj)
   209  			c.Call.Args = []Value{fv}
   210  		} else {
   211  			c.Call.Value = fv
   212  			c.Call.Method = obj
   213  		}
   214  		for _, arg := range fn.Params {
   215  			c.Call.Args = append(c.Call.Args, arg)
   216  		}
   217  		emitTailCall(fn, &c)
   218  		fn.finishBody()
   219  
   220  		prog.bounds[obj] = fn
   221  	}
   222  	return fn
   223  }
   224  
   225  // -- thunks -----------------------------------------------------------
   226  
   227  // makeThunk returns a thunk, a synthetic function that delegates to a
   228  // concrete or interface method denoted by sel.Obj().  The resulting
   229  // function has no receiver, but has an additional (first) regular
   230  // parameter.
   231  //
   232  // Precondition: sel.Kind() == types.MethodExpr.
   233  //
   234  //   type T int          or:  type T interface { meth() }
   235  //   func (t T) meth()
   236  //   f := T.meth
   237  //   var t T
   238  //   f(t) // calls t.meth()
   239  //
   240  // f is a synthetic wrapper defined as if by:
   241  //
   242  //   f := func(t T) { return t.meth() }
   243  //
   244  // TODO(adonovan): opt: currently the stub is created even when used
   245  // directly in a function call: C.f(i, 0).  This is less efficient
   246  // than inlining the stub.
   247  //
   248  // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
   249  //
   250  func makeThunk(prog *Program, sel *types.Selection) *Function {
   251  	if sel.Kind() != types.MethodExpr {
   252  		panic(sel)
   253  	}
   254  
   255  	key := selectionKey{
   256  		kind:     sel.Kind(),
   257  		recv:     sel.Recv(),
   258  		obj:      sel.Obj(),
   259  		index:    fmt.Sprint(sel.Index()),
   260  		indirect: sel.Indirect(),
   261  	}
   262  
   263  	prog.methodsMu.Lock()
   264  	defer prog.methodsMu.Unlock()
   265  
   266  	// Canonicalize key.recv to avoid constructing duplicate thunks.
   267  	canonRecv, ok := prog.canon.At(key.recv).(types.Type)
   268  	if !ok {
   269  		canonRecv = key.recv
   270  		prog.canon.Set(key.recv, canonRecv)
   271  	}
   272  	key.recv = canonRecv
   273  
   274  	fn, ok := prog.thunks[key]
   275  	if !ok {
   276  		fn = makeWrapper(prog, sel)
   277  		if fn.Signature.Recv() != nil {
   278  			panic(fn) // unexpected receiver
   279  		}
   280  		prog.thunks[key] = fn
   281  	}
   282  	return fn
   283  }
   284  
   285  func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
   286  	return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic())
   287  }
   288  
   289  // selectionKey is like types.Selection but a usable map key.
   290  type selectionKey struct {
   291  	kind     types.SelectionKind
   292  	recv     types.Type // canonicalized via Program.canon
   293  	obj      types.Object
   294  	index    string
   295  	indirect bool
   296  }