package ssa // This file defines utilities for method-set computation including // synthesis of wrapper methods. // // Wrappers include: // - indirection/promotion wrappers for methods of embedded fields. // - interface method wrappers for expressions I.f. // - bound method wrappers, for uncalled obj.Method closures. // TODO(adonovan): rename to wrappers.go. import ( "fmt" "go/token" "code.google.com/p/go.tools/go/types" ) // recvType returns the receiver type of method obj. func recvType(obj *types.Func) types.Type { return obj.Type().(*types.Signature).Recv().Type() } // Method returns the Function implementing method meth, building // wrapper methods on demand. // // Thread-safe. // // EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu) // func (prog *Program) Method(meth *types.Selection) *Function { if meth == nil { panic("Method(nil)") } typ := meth.Recv() if prog.mode&LogSource != 0 { defer logStack("Method %s %v", typ, meth)() } prog.methodsMu.Lock() defer prog.methodsMu.Unlock() type methodSet map[string]*Function mset, _ := prog.methodSets.At(typ).(methodSet) if mset == nil { mset = make(methodSet) prog.methodSets.Set(typ, mset) } id := meth.Obj().Id() fn := mset[id] if fn == nil { fn = findMethod(prog, meth) mset[id] = fn } return fn } // declaredFunc returns the concrete function/method denoted by obj. // Panic ensues if there is none. // func (prog *Program) declaredFunc(obj *types.Func) *Function { if v := prog.packageLevelValue(obj); v != nil { return v.(*Function) } panic("no concrete method: " + obj.String()) } // findMethod returns the concrete Function for the method meth, // synthesizing wrappers as needed. // // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) // func findMethod(prog *Program, meth *types.Selection) *Function { needsPromotion := len(meth.Index()) > 1 obj := meth.Obj().(*types.Func) needsIndirection := !isPointer(recvType(obj)) && isPointer(meth.Recv()) if needsPromotion || needsIndirection { return makeWrapper(prog, meth.Recv(), meth) } if _, ok := meth.Recv().Underlying().(*types.Interface); ok { return interfaceMethodWrapper(prog, meth.Recv(), obj) } return prog.declaredFunc(obj) } // makeWrapper returns a synthetic wrapper Function that optionally // performs receiver indirection, implicit field selections and then a // tailcall of a "promoted" method. For example, given these decls: // // type A struct {B} // type B struct {*C} // type C ... // func (*C) f() // // then makeWrapper(typ=A, obj={Func:(*C).f, Indices=[B,C,f]}) // synthesize this wrapper method: // // func (a A) f() { return a.B.C->f() } // // prog is the program to which the synthesized method will belong. // typ is the receiver type of the wrapper method. obj is the // type-checker's object for the promoted method; its Func may be a // concrete or an interface method. // // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) // func makeWrapper(prog *Program, typ types.Type, meth *types.Selection) *Function { obj := meth.Obj().(*types.Func) oldsig := obj.Type().(*types.Signature) recv := types.NewVar(token.NoPos, nil, "recv", typ) description := fmt.Sprintf("wrapper for %s", obj) if prog.mode&LogSource != 0 { defer logStack("make %s to (%s)", description, typ)() } fn := &Function{ name: obj.Name(), method: meth, Signature: changeRecv(oldsig, recv), Synthetic: description, Prog: prog, Pkg: prog.packages[obj.Pkg()], pos: obj.Pos(), } fn.startBody() fn.addSpilledParam(recv) createParams(fn) var v Value = fn.Locals[0] // spilled receiver if isPointer(typ) { // TODO(adonovan): consider emitting a nil-pointer check here // with a nice error message, like gc does. v = emitLoad(fn, v) } // Invariant: v is a pointer, either // value of *A receiver param, or // address of A spilled receiver. // We use pointer arithmetic (FieldAddr possibly followed by // Load) in preference to value extraction (Field possibly // preceded by Load). indices := meth.Index() v = emitImplicitSelections(fn, v, indices[:len(indices)-1]) // Invariant: v is a pointer, either // value of implicit *C field, or // address of implicit C field. var c Call if _, ok := oldsig.Recv().Type().Underlying().(*types.Interface); !ok { // concrete method if !isPointer(oldsig.Recv().Type()) { v = emitLoad(fn, v) } c.Call.Value = prog.declaredFunc(obj) c.Call.Args = append(c.Call.Args, v) } else { c.Call.Method = obj c.Call.Value = emitLoad(fn, v) } for _, arg := range fn.Params[1:] { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c) fn.finishBody() return fn } // createParams creates parameters for wrapper method fn based on its // Signature.Params, which do not include the receiver. // func createParams(fn *Function) { var last *Parameter tparams := fn.Signature.Params() for i, n := 0, tparams.Len(); i < n; i++ { last = fn.addParamObj(tparams.At(i)) } if fn.Signature.IsVariadic() { last.typ = types.NewSlice(last.typ) } } // Wrappers for standalone interface methods ---------------------------------- // interfaceMethodWrapper returns a synthetic wrapper function // permitting an abstract method obj to be called like a standalone // function, e.g.: // // type I interface { f(x int) R } // m := I.f // wrapper // var i I // m(i, 0) // // The wrapper is defined as if by: // // func (i I) f(x int, ...) R { // return i.f(x, ...) // } // // typ is the type of the receiver (I here). It isn't necessarily // equal to the recvType(obj) because one interface may embed another. // TODO(adonovan): more tests. // // TODO(adonovan): opt: currently the stub is created even when used // in call position: I.f(i, 0). Clearly this is suboptimal. // // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) // func interfaceMethodWrapper(prog *Program, typ types.Type, obj *types.Func) *Function { // If one interface embeds another they'll share the same // wrappers for common methods. This is safe, but it might // confuse some tools because of the implicit interface // conversion applied to the first argument. If this becomes // a problem, we should include 'typ' in the memoization key. fn, ok := prog.ifaceMethodWrappers[obj] if !ok { description := "interface method wrapper" if prog.mode&LogSource != 0 { defer logStack("(%s).%s, %s", typ, obj.Name(), description)() } fn = &Function{ name: obj.Name(), object: obj, Signature: obj.Type().(*types.Signature), Synthetic: description, pos: obj.Pos(), Prog: prog, Pkg: prog.packages[obj.Pkg()], } fn.startBody() fn.addParam("recv", typ, token.NoPos) createParams(fn) var c Call c.Call.Method = obj c.Call.Value = fn.Params[0] for _, arg := range fn.Params[1:] { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c) fn.finishBody() prog.ifaceMethodWrappers[obj] = fn } return fn } // Wrappers for bound methods ------------------------------------------------- // boundMethodWrapper returns a synthetic wrapper function that // delegates to a concrete or interface method. // The wrapper has one free variable, the method's receiver. // Use MakeClosure with such a wrapper to construct a bound-method // closure. e.g.: // // type T int or: type T interface { meth() } // func (t T) meth() // var t T // f := t.meth // f() // calls t.meth() // // f is a closure of a synthetic wrapper defined as if by: // // f := func() { return t.meth() } // // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) // func boundMethodWrapper(prog *Program, obj *types.Func) *Function { prog.methodsMu.Lock() defer prog.methodsMu.Unlock() fn, ok := prog.boundMethodWrappers[obj] if !ok { description := fmt.Sprintf("bound method wrapper for %s", obj) if prog.mode&LogSource != 0 { defer logStack("%s", description)() } fn = &Function{ name: "bound$" + obj.FullName(), Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver Synthetic: description, Prog: prog, Pkg: prog.packages[obj.Pkg()], pos: obj.Pos(), } cap := &Capture{name: "recv", typ: recvType(obj), parent: fn} fn.FreeVars = []*Capture{cap} fn.startBody() createParams(fn) var c Call if _, ok := recvType(obj).Underlying().(*types.Interface); !ok { // concrete c.Call.Value = prog.declaredFunc(obj) c.Call.Args = []Value{cap} } else { c.Call.Value = cap c.Call.Method = obj } for _, arg := range fn.Params { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c) fn.finishBody() prog.boundMethodWrappers[obj] = fn } return fn } func changeRecv(s *types.Signature, recv *types.Var) *types.Signature { return types.NewSignature(nil, recv, s.Params(), s.Results(), s.IsVariadic()) }