cmd/compile: track reflect.Type.Method in deadcode

In addition to reflect.Value.Call, exported methods can be invoked
by the Func value in the reflect.Method struct. This CL has the
compiler track what functions get access to a legitimate reflect.Method
struct by looking for interface calls to either of:

	Method(int) reflect.Method
	MethodByName(string) (reflect.Method, bool)

This is a little overly conservative. If a user implements a type
with one of these methods without using the underlying calls on
reflect.Type, the linker will assume the worst and include all
exported methods. But it's cheap.

No change to any of the binary sizes reported in cl/20483.

For #14740

Change-Id: Ie17786395d0453ce0384d8b240ecb043b7726137
Reviewed-on: https://go-review.googlesource.com/20489
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
David Crawshaw 2016-03-10 16:15:26 -05:00
parent 4112f0f7e6
commit cc158403d6
13 changed files with 195 additions and 12 deletions

View file

@ -25,7 +25,7 @@ import (
//
// 1. direct call
// 2. through a reachable interface type
// 3. reflect.Value.Call
// 3. reflect.Value.Call / reflect.Method.Func
//
// The first case is handled by the flood fill, a directly called method
// is marked as reachable.
@ -36,8 +36,10 @@ import (
// as reachable. This is extremely conservative, but easy and correct.
//
// The third case is handled by looking to see if reflect.Value.Call is
// ever marked reachable. If it is, all bets are off and all exported
// methods of reachable types are marked reachable.
// ever marked reachable, or if a reflect.Method struct is ever
// constructed by a call to reflect.Type.Method or MethodByName. If it
// is, all bets are off and all exported methods of reachable types are
// marked reachable.
//
// Any unreached text symbols are removed from ctxt.Textp.
func deadcode(ctxt *Link) {
@ -59,7 +61,7 @@ func deadcode(ctxt *Link) {
callSymSeen := false
for {
if callSym != nil && callSym.Attr.Reachable() {
if callSym != nil && (callSym.Attr.Reachable() || d.reflectMethod) {
// Methods are called via reflection. Give up on
// static analysis, mark all exported methods of
// all reachable types as reachable.
@ -169,6 +171,7 @@ type deadcodepass struct {
markQueue []*LSym // symbols to flood fill in next pass
ifaceMethod map[methodsig]bool // methods declared in reached interfaces
markableMethods []methodref // methods of reached types
reflectMethod bool
}
func (d *deadcodepass) cleanupReloc(r *Reloc) {
@ -188,6 +191,9 @@ func (d *deadcodepass) mark(s, parent *LSym) {
if s == nil || s.Attr.Reachable() {
return
}
if s.Attr.ReflectMethod() {
d.reflectMethod = true
}
s.Attr |= AttrReachable
s.Reachparent = parent
d.markQueue = append(d.markQueue, s)