From 9c8d9fe73695f57ffb16a2880361be7fd9f95f44 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Wed, 9 Oct 2013 16:35:59 -0400 Subject: [PATCH] go.tools/pointer: support reflect.Method{,ByName}. R=crawshaw CC=golang-dev https://golang.org/cl/14589043 --- pointer/reflect.go | 99 ++++++++++++++++++++++++++++++++- pointer/testdata/funcreflect.go | 39 +++++++++++++ 2 files changed, 136 insertions(+), 2 deletions(-) diff --git a/pointer/reflect.go b/pointer/reflect.go index b305ae39..781f1d59 100644 --- a/pointer/reflect.go +++ b/pointer/reflect.go @@ -906,5 +906,100 @@ func ext۰reflect۰rtype۰Key(a *analysis, cgn *cgnode) { }) } -func ext۰reflect۰rtype۰Method(a *analysis, cgn *cgnode) {} -func ext۰reflect۰rtype۰MethodByName(a *analysis, cgn *cgnode) {} +// ---------- func (*rtype) Method(int) (Method, bool) ---------- +// ---------- func (*rtype) MethodByName(string) (Method, bool) ---------- + +// result = MethodByName(t, name) +// result = Method(t, _) +type rtypeMethodByNameConstraint struct { + cgn *cgnode + name string // name of method; "" for unknown + t nodeid // (ptr) + result nodeid +} + +func (c *rtypeMethodByNameConstraint) String() string { + return fmt.Sprintf("n%d = (*reflect.rtype).MethodByName(n%d, %q)", c.result, c.t, c.name) +} + +func (c *rtypeMethodByNameConstraint) ptr() nodeid { + return c.t +} + +func (c *rtypeMethodByNameConstraint) addMethod(a *analysis, meth *types.Selection) { + // type Method struct { + // 0 __identity__ + // 1 Name string + // 2 PkgPath string + // 3 Type Type + // 4 Func Value + // 5 Index int + // } + fn := a.prog.Method(meth) + + // a.offsetOf(Type) is 3. + if id := c.result + 3; a.addLabel(id, a.makeRtype(changeRecv(fn.Signature))) { + a.addWork(id) + } + // a.offsetOf(Func) is 4. + if id := c.result + 4; a.addLabel(id, a.objectNode(nil, fn)) { + a.addWork(id) + } +} + +// changeRecv returns sig with Recv prepended to Params(). +func changeRecv(sig *types.Signature) *types.Signature { + params := sig.Params() + n := params.Len() + p2 := make([]*types.Var, n+1) + p2[0] = sig.Recv() + for i := 0; i < n; i++ { + p2[i+1] = params.At(i) + } + return types.NewSignature(nil, nil, types.NewTuple(p2...), sig.Results(), sig.IsVariadic()) +} + +func (c *rtypeMethodByNameConstraint) solve(a *analysis, _ *node, delta nodeset) { + for tObj := range delta { + T := a.nodes[tObj].obj.data.(types.Type) + + // We don't use Lookup(c.name) when c.name != "" to avoid + // ambiguity: >1 unexported methods could match. + mset := T.MethodSet() + for i, n := 0, mset.Len(); i < n; i++ { + sel := mset.At(i) + if c.name == "" || c.name == sel.Obj().Name() { + c.addMethod(a, sel) + } + } + } +} + +func ext۰reflect۰rtype۰MethodByName(a *analysis, cgn *cgnode) { + // If we have access to the callsite, + // and the argument is a string constant, + // return only that method. + var name string + if site := cgn.callersite; site != nil { + if c, ok := site.instr.Common().Args[0].(*ssa.Const); ok { + name = exact.StringVal(c.Value) + } + } + + a.addConstraint(&rtypeMethodByNameConstraint{ + cgn: cgn, + name: name, + t: a.funcParams(cgn.obj), + result: a.funcResults(cgn.obj), + }) +} + +func ext۰reflect۰rtype۰Method(a *analysis, cgn *cgnode) { + // No-one ever calls Method with a constant argument, + // so we don't specialize that case. + a.addConstraint(&rtypeMethodByNameConstraint{ + cgn: cgn, + t: a.funcParams(cgn.obj), + result: a.funcResults(cgn.obj), + }) +} diff --git a/pointer/testdata/funcreflect.go b/pointer/testdata/funcreflect.go index 7fe8ca8a..c3846b2f 100644 --- a/pointer/testdata/funcreflect.go +++ b/pointer/testdata/funcreflect.go @@ -38,7 +38,46 @@ func reflectTypeInOut() { print(reflect.Zero(reflect.TypeOf(3).Out(0)).Interface()) // @types } +type T struct{} + +func (T) F() {} +func (T) g(int) {} + +type U struct{} + +func (U) F(int) {} +func (U) g(string) {} + +var nonconst string + +func reflectTypeMethodByName() { + TU := reflect.TypeOf([]interface{}{T{}, U{}}[0]) + print(reflect.Zero(TU)) // @types T | U + + F, _ := TU.MethodByName("F") + print(reflect.Zero(F.Type)) // @types func(T) | func(U, int) + print(F.Func) // @pointsto (main.T).F | (main.U).F + + g, _ := TU.MethodByName("g") + print(reflect.Zero(g.Type)) // @types func(T, int) | func(U, string) + print(g.Func) // @pointsto (main.T).g | (main.U).g + + // Non-literal method names are treated less precisely. + U := reflect.TypeOf(U{}) + X, _ := U.MethodByName(nonconst) + print(reflect.Zero(X.Type)) // @types func(U, int) | func(U, string) + print(X.Func) // @pointsto (main.U).F | (main.U).g +} + +func reflectTypeMethod() { + m := reflect.TypeOf(T{}).Method(0) + print(reflect.Zero(m.Type)) // @types func(T) | func(T, int) + print(m.Func) // @pointsto (main.T).F | (main.T).g +} + func main() { //reflectValueCall() reflectTypeInOut() + reflectTypeMethodByName() + reflectTypeMethod() }