github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/go/ir/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 ir
     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  	"go/types"
    25  )
    26  
    27  // -- wrappers -----------------------------------------------------------
    28  
    29  // makeWrapper returns a synthetic method that delegates to the
    30  // declared method denoted by meth.Obj(), first performing any
    31  // necessary pointer indirections or field selections implied by meth.
    32  //
    33  // The resulting method's receiver type is meth.Recv().
    34  //
    35  // This function is versatile but quite subtle!  Consider the
    36  // following axes of variation when making changes:
    37  //   - optional receiver indirection
    38  //   - optional implicit field selections
    39  //   - meth.Obj() may denote a concrete or an interface method
    40  //   - the result may be a thunk or a wrapper.
    41  //
    42  // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
    43  func makeWrapper(prog *Program, sel *types.Selection) *Function {
    44  	obj := sel.Obj().(*types.Func)       // the declared function
    45  	sig := sel.Type().(*types.Signature) // type of this wrapper
    46  
    47  	var recv *types.Var // wrapper's receiver or thunk's params[0]
    48  	name := obj.Name()
    49  	var description Synthetic
    50  	var start int // first regular param
    51  	if sel.Kind() == types.MethodExpr {
    52  		name += "$thunk"
    53  		description = SyntheticThunk
    54  		recv = sig.Params().At(0)
    55  		start = 1
    56  	} else {
    57  		description = SyntheticWrapper
    58  		recv = sig.Recv()
    59  	}
    60  
    61  	if prog.mode&LogSource != 0 {
    62  		defer logStack("make %s to (%s)", description, recv.Type())()
    63  	}
    64  	fn := &Function{
    65  		name:         name,
    66  		method:       sel,
    67  		object:       obj,
    68  		Signature:    sig,
    69  		Synthetic:    description,
    70  		Prog:         prog,
    71  		functionBody: new(functionBody),
    72  	}
    73  	fn.initHTML(prog.PrintFunc)
    74  	fn.startBody()
    75  	fn.addSpilledParam(recv, nil)
    76  	createParams(fn, start)
    77  
    78  	indices := sel.Index()
    79  
    80  	var v Value = fn.Locals[0] // spilled receiver
    81  	if isPointer(sel.Recv()) {
    82  		v = emitLoad(fn, v, nil)
    83  
    84  		// For simple indirection wrappers, perform an informative nil-check:
    85  		// "value method (T).f called using nil *T pointer"
    86  		if len(indices) == 1 && !isPointer(recvType(obj)) {
    87  			var c Call
    88  			c.Call.Value = &Builtin{
    89  				name: "ir:wrapnilchk",
    90  				sig: types.NewSignatureType(nil, nil, nil,
    91  					types.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)),
    92  					types.NewTuple(anonVar(sel.Recv())), false),
    93  			}
    94  			c.Call.Args = []Value{
    95  				v,
    96  				emitConst(fn, stringConst(deref(sel.Recv()).String(), nil)),
    97  				emitConst(fn, stringConst(sel.Obj().Name(), nil)),
    98  			}
    99  			c.setType(v.Type())
   100  			v = fn.emit(&c, nil)
   101  		}
   102  	}
   103  
   104  	// Invariant: v is a pointer, either
   105  	//   value of *A receiver param, or
   106  	// address of  A spilled receiver.
   107  
   108  	// We use pointer arithmetic (FieldAddr possibly followed by
   109  	// Load) in preference to value extraction (Field possibly
   110  	// preceded by Load).
   111  
   112  	v = emitImplicitSelections(fn, v, indices[:len(indices)-1], nil)
   113  
   114  	// Invariant: v is a pointer, either
   115  	//   value of implicit *C field, or
   116  	// address of implicit  C field.
   117  
   118  	var c Call
   119  	if r := recvType(obj); !isInterface(r) { // concrete method
   120  		if !isPointer(r) {
   121  			v = emitLoad(fn, v, nil)
   122  		}
   123  		c.Call.Value = prog.declaredFunc(obj)
   124  		c.Call.Args = append(c.Call.Args, v)
   125  	} else {
   126  		c.Call.Method = obj
   127  		c.Call.Value = emitLoad(fn, v, nil)
   128  	}
   129  	for _, arg := range fn.Params[1:] {
   130  		c.Call.Args = append(c.Call.Args, arg)
   131  	}
   132  	emitTailCall(fn, &c, nil)
   133  	fn.finishBody()
   134  	return fn
   135  }
   136  
   137  // createParams creates parameters for wrapper method fn based on its
   138  // Signature.Params, which do not include the receiver.
   139  // start is the index of the first regular parameter to use.
   140  func createParams(fn *Function, start int) {
   141  	tparams := fn.Signature.Params()
   142  	for i, n := start, tparams.Len(); i < n; i++ {
   143  		fn.addParamObj(tparams.At(i), nil)
   144  	}
   145  }
   146  
   147  // -- bounds -----------------------------------------------------------
   148  
   149  // makeBound returns a bound method wrapper (or "bound"), a synthetic
   150  // function that delegates to a concrete or interface method denoted
   151  // by obj.  The resulting function has no receiver, but has one free
   152  // variable which will be used as the method's receiver in the
   153  // tail-call.
   154  //
   155  // Use MakeClosure with such a wrapper to construct a bound method
   156  // closure.  e.g.:
   157  //
   158  //	type T int          or:  type T interface { meth() }
   159  //	func (t T) meth()
   160  //	var t T
   161  //	f := t.meth
   162  //	f() // calls t.meth()
   163  //
   164  // f is a closure of a synthetic wrapper defined as if by:
   165  //
   166  //	f := func() { return t.meth() }
   167  //
   168  // Unlike makeWrapper, makeBound need perform no indirection or field
   169  // selections because that can be done before the closure is
   170  // constructed.
   171  //
   172  // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
   173  func makeBound(prog *Program, obj *types.Func) *Function {
   174  	prog.methodsMu.Lock()
   175  	defer prog.methodsMu.Unlock()
   176  	fn, ok := prog.bounds[obj]
   177  	if !ok {
   178  		if prog.mode&LogSource != 0 {
   179  			defer logStack("%s", SyntheticBound)()
   180  		}
   181  		fn = &Function{
   182  			name:         obj.Name() + "$bound",
   183  			object:       obj,
   184  			Signature:    changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
   185  			Synthetic:    SyntheticBound,
   186  			Prog:         prog,
   187  			functionBody: new(functionBody),
   188  		}
   189  		fn.initHTML(prog.PrintFunc)
   190  
   191  		fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn}
   192  		fn.FreeVars = []*FreeVar{fv}
   193  		fn.startBody()
   194  		createParams(fn, 0)
   195  		var c Call
   196  
   197  		if !isInterface(recvType(obj)) { // concrete
   198  			c.Call.Value = prog.declaredFunc(obj)
   199  			c.Call.Args = []Value{fv}
   200  		} else {
   201  			c.Call.Value = fv
   202  			c.Call.Method = obj
   203  		}
   204  		for _, arg := range fn.Params {
   205  			c.Call.Args = append(c.Call.Args, arg)
   206  		}
   207  		emitTailCall(fn, &c, nil)
   208  		fn.finishBody()
   209  
   210  		prog.bounds[obj] = fn
   211  	}
   212  	return fn
   213  }
   214  
   215  // -- thunks -----------------------------------------------------------
   216  
   217  // makeThunk returns a thunk, a synthetic function that delegates to a
   218  // concrete or interface method denoted by sel.Obj().  The resulting
   219  // function has no receiver, but has an additional (first) regular
   220  // parameter.
   221  //
   222  // Precondition: sel.Kind() == types.MethodExpr.
   223  //
   224  //	type T int          or:  type T interface { meth() }
   225  //	func (t T) meth()
   226  //	f := T.meth
   227  //	var t T
   228  //	f(t) // calls t.meth()
   229  //
   230  // f is a synthetic wrapper defined as if by:
   231  //
   232  //	f := func(t T) { return t.meth() }
   233  //
   234  // TODO(adonovan): opt: currently the stub is created even when used
   235  // directly in a function call: C.f(i, 0).  This is less efficient
   236  // than inlining the stub.
   237  //
   238  // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
   239  func makeThunk(prog *Program, sel *types.Selection) *Function {
   240  	if sel.Kind() != types.MethodExpr {
   241  		panic(sel)
   242  	}
   243  
   244  	key := selectionKey{
   245  		kind:     sel.Kind(),
   246  		recv:     sel.Recv(),
   247  		obj:      sel.Obj(),
   248  		index:    fmt.Sprint(sel.Index()),
   249  		indirect: sel.Indirect(),
   250  	}
   251  
   252  	prog.methodsMu.Lock()
   253  	defer prog.methodsMu.Unlock()
   254  
   255  	// Canonicalize key.recv to avoid constructing duplicate thunks.
   256  	canonRecv, ok := prog.canon.At(key.recv)
   257  	if !ok {
   258  		canonRecv = key.recv
   259  		prog.canon.Set(key.recv, canonRecv)
   260  	}
   261  	key.recv = canonRecv
   262  
   263  	fn, ok := prog.thunks[key]
   264  	if !ok {
   265  		fn = makeWrapper(prog, sel)
   266  		if fn.Signature.Recv() != nil {
   267  			panic(fn) // unexpected receiver
   268  		}
   269  		prog.thunks[key] = fn
   270  	}
   271  	return fn
   272  }
   273  
   274  func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
   275  	return types.NewSignatureType(recv, nil, nil, s.Params(), s.Results(), s.Variadic())
   276  }
   277  
   278  // selectionKey is like types.Selection but a usable map key.
   279  type selectionKey struct {
   280  	kind     types.SelectionKind
   281  	recv     types.Type // canonicalized via Program.canon
   282  	obj      types.Object
   283  	index    string
   284  	indirect bool
   285  }
   286  
   287  // makeInstance creates a wrapper function with signature sig that calls the generic function fn.
   288  // If targs is not nil, fn is a function and targs describes the concrete type arguments.
   289  // If targs is nil, fn is a method and the type arguments are derived from the receiver.
   290  func makeInstance(prog *Program, fn *Function, sig *types.Signature, targs *types.TypeList) *Function {
   291  	if sig.Recv() != nil {
   292  		assert(targs == nil)
   293  		// Methods don't have their own type parameters, but the receiver does
   294  		targs = deref(sig.Recv().Type()).(*types.Named).TypeArgs()
   295  	} else {
   296  		assert(targs != nil)
   297  	}
   298  
   299  	wrapper := fn.generics.At(targs)
   300  	if wrapper != nil {
   301  		return wrapper
   302  	}
   303  
   304  	var name string
   305  	if sig.Recv() != nil {
   306  		name = fn.name
   307  	} else {
   308  		name = fmt.Sprintf("%s$generic#%d", fn.name, fn.generics.Len())
   309  	}
   310  	w := &Function{
   311  		name:         name,
   312  		object:       fn.object,
   313  		Signature:    sig,
   314  		Synthetic:    SyntheticGeneric,
   315  		Prog:         prog,
   316  		functionBody: new(functionBody),
   317  	}
   318  	w.initHTML(prog.PrintFunc)
   319  	w.startBody()
   320  	if sig.Recv() != nil {
   321  		w.addParamObj(sig.Recv(), nil)
   322  	}
   323  	createParams(w, 0)
   324  	var c Call
   325  	c.Call.Value = fn
   326  	tresults := fn.Signature.Results()
   327  	if tresults.Len() == 1 {
   328  		c.typ = tresults.At(0).Type()
   329  	} else {
   330  		c.typ = tresults
   331  	}
   332  
   333  	changeType := func(v Value, typ types.Type) Value {
   334  		if types.Identical(v.Type(), typ) {
   335  			return v
   336  		}
   337  		var c ChangeType
   338  		c.X = v
   339  		c.typ = typ
   340  		return w.emit(&c, nil)
   341  	}
   342  
   343  	for i, arg := range w.Params {
   344  		if sig.Recv() != nil {
   345  			if i == 0 {
   346  				c.Call.Args = append(c.Call.Args, changeType(w.Params[0], fn.Signature.Recv().Type()))
   347  			} else {
   348  				c.Call.Args = append(c.Call.Args, changeType(arg, fn.Signature.Params().At(i-1).Type()))
   349  			}
   350  		} else {
   351  			c.Call.Args = append(c.Call.Args, changeType(arg, fn.Signature.Params().At(i).Type()))
   352  		}
   353  	}
   354  	for i := 0; i < targs.Len(); i++ {
   355  		arg := targs.At(i)
   356  		c.Call.TypeArgs = append(c.Call.TypeArgs, arg)
   357  	}
   358  	results := w.emit(&c, nil)
   359  	var ret Return
   360  	switch tresults.Len() {
   361  	case 0:
   362  	case 1:
   363  		ret.Results = []Value{changeType(results, sig.Results().At(0).Type())}
   364  	default:
   365  		for i := 0; i < tresults.Len(); i++ {
   366  			v := emitExtract(w, results, i, nil)
   367  			ret.Results = append(ret.Results, changeType(v, sig.Results().At(i).Type()))
   368  		}
   369  	}
   370  
   371  	w.Exit = w.newBasicBlock("exit")
   372  	emitJump(w, w.Exit, nil)
   373  	w.currentBlock = w.Exit
   374  	w.emit(&ret, nil)
   375  	w.currentBlock = nil
   376  
   377  	w.finishBody()
   378  
   379  	fn.generics.Set(targs, w)
   380  	return w
   381  }