mirror of
https://github.com/golang/go
synced 2024-11-19 02:54:42 -07:00
118786e3d6
Also: - add types.Func.FullName() (e.g. "fmt.Println", "(main.S).f") - remove rogue print stmt. - fix bad docstrings. R=gri CC=golang-dev https://golang.org/cl/11936044
374 lines
10 KiB
Go
374 lines
10 KiB
Go
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 closures of 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()
|
|
}
|
|
|
|
// MethodSet returns the method set for type typ, building wrapper
|
|
// methods as needed for embedded field promotion, and indirection for
|
|
// *T receiver types, etc.
|
|
// A nil result indicates an empty set.
|
|
//
|
|
// This function should only be called when you need to construct the
|
|
// entire method set, synthesizing all wrappers, for example during
|
|
// the processing of a MakeInterface instruction or when visiting all
|
|
// reachable functions.
|
|
//
|
|
// If you only need to look up a single method (obj), avoid this
|
|
// function and use LookupMethod instead:
|
|
//
|
|
// meth := types.MethodSet(typ).Lookup(pkg, name)
|
|
// m := prog.MethodSet(typ)[meth.Id()] // don't do this
|
|
// m := prog.LookupMethod(meth) // use this instead
|
|
//
|
|
// If you only need to enumerate the keys, use types.MethodSet
|
|
// instead.
|
|
//
|
|
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
|
|
//
|
|
// Thread-safe.
|
|
//
|
|
func (prog *Program) MethodSet(typ types.Type) MethodSet {
|
|
return prog.populateMethodSet(typ, nil)
|
|
}
|
|
|
|
// populateMethodSet returns the method set for typ, ensuring that it
|
|
// contains at least the function for meth, if that is a key.
|
|
// If meth is nil, the entire method set is populated.
|
|
//
|
|
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
|
|
//
|
|
func (prog *Program) populateMethodSet(typ types.Type, meth *types.Method) MethodSet {
|
|
tmset := methodSet(typ)
|
|
n := tmset.Len()
|
|
if n == 0 {
|
|
return nil
|
|
}
|
|
|
|
if prog.mode&LogSource != 0 {
|
|
defer logStack("populateMethodSet %s meth=%v", typ, meth)()
|
|
}
|
|
|
|
prog.methodsMu.Lock()
|
|
defer prog.methodsMu.Unlock()
|
|
|
|
mset, _ := prog.methodSets.At(typ).(MethodSet)
|
|
if mset == nil {
|
|
mset = make(MethodSet)
|
|
prog.methodSets.Set(typ, mset)
|
|
}
|
|
|
|
if len(mset) < n {
|
|
if meth != nil { // single method
|
|
id := meth.Id()
|
|
if mset[id] == nil {
|
|
mset[id] = findMethod(prog, meth)
|
|
}
|
|
} else {
|
|
// complete set
|
|
for i := 0; i < n; i++ {
|
|
meth := tmset.At(i)
|
|
if id := meth.Id(); mset[id] == nil {
|
|
mset[id] = findMethod(prog, meth)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return mset
|
|
}
|
|
|
|
func methodSet(typ types.Type) *types.MethodSet {
|
|
// TODO(adonovan): temporary workaround. Inline it away when fixed.
|
|
if _, ok := deref(typ).Underlying().(*types.Interface); ok && isPointer(typ) {
|
|
// TODO(gri): fix: go/types bug: pointer-to-interface
|
|
// has no methods---yet go/types says it has!
|
|
return new(types.MethodSet)
|
|
}
|
|
return typ.MethodSet()
|
|
}
|
|
|
|
// LookupMethod returns the Function for the specified method object,
|
|
// building wrapper methods on demand. It returns nil if the typ has
|
|
// no such method.
|
|
//
|
|
// Thread-safe.
|
|
//
|
|
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
|
|
//
|
|
func (prog *Program) LookupMethod(meth *types.Method) *Function {
|
|
return prog.populateMethodSet(meth.Recv(), meth)[meth.Id()]
|
|
}
|
|
|
|
// concreteMethod returns the concrete method denoted by obj.
|
|
// Panic ensues if there is no such method (e.g. it's a standalone
|
|
// function).
|
|
//
|
|
func (prog *Program) concreteMethod(obj *types.Func) *Function {
|
|
fn := prog.concreteMethods[obj]
|
|
if fn == nil {
|
|
panic("no concrete method: " + obj.String())
|
|
}
|
|
return fn
|
|
}
|
|
|
|
// findMethod returns the concrete Function for the method meth,
|
|
// synthesizing wrappers as needed.
|
|
//
|
|
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
|
|
//
|
|
func findMethod(prog *Program, meth *types.Method) *Function {
|
|
needsPromotion := len(meth.Index()) > 1
|
|
needsIndirection := !isPointer(recvType(meth.Func)) && 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(), meth.Func)
|
|
}
|
|
|
|
return prog.concreteMethod(meth.Func)
|
|
}
|
|
|
|
// 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.Method) *Function {
|
|
old := meth.Func.Type().(*types.Signature)
|
|
sig := types.NewSignature(nil, types.NewVar(token.NoPos, nil, "recv", typ), old.Params(), old.Results(), old.IsVariadic())
|
|
|
|
description := fmt.Sprintf("wrapper for %s", meth.Func)
|
|
if prog.mode&LogSource != 0 {
|
|
defer logStack("make %s to (%s)", description, typ)()
|
|
}
|
|
fn := &Function{
|
|
name: meth.Name(),
|
|
method: meth,
|
|
Signature: sig,
|
|
Synthetic: description,
|
|
Prog: prog,
|
|
pos: meth.Pos(),
|
|
}
|
|
fn.startBody()
|
|
fn.addSpilledParam(sig.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 := old.Recv().Type().Underlying().(*types.Interface); !ok { // concrete method
|
|
if !isPointer(old.Recv().Type()) {
|
|
v = emitLoad(fn, v)
|
|
}
|
|
c.Call.Value = prog.concreteMethod(meth.Func)
|
|
c.Call.Args = append(c.Call.Args, v)
|
|
} else {
|
|
c.Call.Method = meth.Func
|
|
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.f(i I, 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 := fmt.Sprintf("interface method wrapper for %s.%s", typ, obj)
|
|
if prog.mode&LogSource != 0 {
|
|
defer logStack("%s", description)()
|
|
}
|
|
fn = &Function{
|
|
name: obj.Name(),
|
|
object: obj,
|
|
Signature: obj.Type().(*types.Signature),
|
|
Synthetic: description,
|
|
pos: obj.Pos(),
|
|
Prog: prog,
|
|
}
|
|
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)()
|
|
}
|
|
s := obj.Type().(*types.Signature)
|
|
fn = &Function{
|
|
name: "bound$" + obj.FullName(),
|
|
Signature: types.NewSignature(nil, nil, s.Params(), s.Results(), s.IsVariadic()),
|
|
Synthetic: description,
|
|
Prog: prog,
|
|
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.concreteMethod(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
|
|
}
|