if fn.Func.Pragma&Nosplit != 0 {
ptxt.From3.Offset |= obj.NOSPLIT
}
+ if fn.Func.ReflectMethod {
+ ptxt.From3.Offset |= obj.REFLECTMETHOD
+ }
if fn.Func.Pragma&Systemstack != 0 {
ptxt.From.Sym.Cfunc = 1
}
Endlineno int32
WBLineno int32 // line number of first write barrier
- Pragma Pragma // go:xxx function annotations
- Dupok bool // duplicate definitions ok
- Wrapper bool // is method wrapper
- Needctxt bool // function uses context register (has closure variables)
+ Pragma Pragma // go:xxx function annotations
+ Dupok bool // duplicate definitions ok
+ Wrapper bool // is method wrapper
+ Needctxt bool // function uses context register (has closure variables)
+ ReflectMethod bool // function calls reflect.Type.Method or MethodByName
}
type Op uint8
}
case OCALLINTER:
+ usemethod(n)
t := n.Left.Type
if n.List.Len() != 0 && n.List.First().Op == OAS {
break
return false
}
+// usemethod check interface method calls for uses of reflect.Type.Method.
+func usemethod(n *Node) {
+ t := n.Left.Type
+
+ // Looking for either of:
+ // Method(int) reflect.Method
+ // MethodByName(string) (reflect.Method, bool)
+ //
+ // TODO(crawshaw): improve precision of match by working out
+ // how to check the method name.
+ if n := countfield(t.Params()); n != 1 {
+ return
+ }
+ if n := countfield(t.Results()); n != 1 && n != 2 {
+ return
+ }
+ p0 := t.Params().Field(0)
+ res0 := t.Results().Field(0)
+ var res1 *Type
+ if countfield(t.Results()) == 2 {
+ res1 = t.Results().Field(1)
+ }
+
+ if res1 == nil {
+ if p0.Type.Etype != TINT {
+ return
+ }
+ } else {
+ if p0.Type.Etype != TSTRING {
+ return
+ }
+ if res1.Type.Etype != TBOOL {
+ return
+ }
+ }
+ if Tconv(res0, 0) != "reflect.Method" {
+ return
+ }
+
+ Curfn.Func.ReflectMethod = true
+}
+
func usefield(n *Node) {
if obj.Fieldtrack_enabled == 0 {
return
Leaf uint8
Seenglobl uint8
Onlist uint8
+
+ // ReflectMethod means the function may call reflect.Type.Method or
+ // reflect.Type.MethodByName. Matching is imprecise (as reflect.Type
+ // can be used through a custom interface), so ReflectMethod may be
+ // set in some cases when the reflect package is not called.
+ //
+ // Used by the linker to determine what methods can be pruned.
+ ReflectMethod bool
+
// Local means make the symbol local even when compiling Go code to reference Go
// symbols in other shared libraries, as in this mode symbols are global by
// default. "local" here means in the sense of the dynamic linker, i.e. not
if flag&NOSPLIT != 0 {
s.Nosplit = 1
}
+ if flag&REFLECTMETHOD != 0 {
+ s.ReflectMethod = true
+ }
s.Next = nil
s.Type = STEXT
s.Text = p
wrint(b, int64(s.Args))
wrint(b, int64(s.Locals))
wrint(b, int64(s.Nosplit))
- wrint(b, int64(s.Leaf)|int64(s.Cfunc)<<1)
+ flags := int64(s.Leaf) | int64(s.Cfunc)<<1
+ if s.ReflectMethod {
+ flags |= 1 << 2
+ }
+ wrint(b, flags)
n := 0
for a := s.Autom; a != nil; a = a.Link {
n++
// Only valid on functions that declare a frame size of 0.
// TODO(mwhudson): only implemented for ppc64x at present.
NOFRAME = 512
+
+ // Function can call reflect.Type.Method or reflect.Type.MethodByName.
+ REFLECTMETHOD = 1024
)
//
// 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.
// 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) {
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.
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) {
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)
AttrHidden
AttrOnList
AttrLocal
+ AttrReflectMethod
)
func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 }
func (a Attribute) Hidden() bool { return a&AttrHidden != 0 }
func (a Attribute) OnList() bool { return a&AttrOnList != 0 }
func (a Attribute) Local() bool { return a&AttrLocal != 0 }
+func (a Attribute) ReflectMethod() bool { return a&AttrReflectMethod != 0 }
func (a Attribute) CgoExport() bool {
return a.CgoExportDynamic() || a.CgoExportStatic()
// - locals [int]
// - nosplit [int]
// - flags [int]
-// 1 leaf
-// 2 C function
+// 1<<0 leaf
+// 1<<1 C function
+// 1<<2 function may call reflect.Type.Method
// - nlocal [int]
// - local [nlocal automatics]
// - pcln [pcln table]
if rduint8(f) != 0 {
s.Attr |= AttrNoSplit
}
- rdint(f) // v&1 is Leaf, currently unused
+ flags := rdint(f)
+ if flags&(1<<2) != 0 {
+ s.Attr |= AttrReflectMethod
+ }
n := rdint(f)
s.Autom = make([]Auto, n)
for i := 0; i < n; i++ {
// This file defines flags attached to various functions
// and data objects. The compilers, assemblers, and linker must
// all agree on these values.
+//
+// Keep in sync with src/cmd/internal/obj/textflag.go.
// Don't profile the marked routine. This flag is deprecated.
#define NOPROF 1
// Only valid on functions that declare a frame size of 0.
// TODO(mwhudson): only implemented for ppc64x at present.
#define NOFRAME 512
+// Function can call reflect.Type.Method or reflect.Type.MethodByName.
+#define REFLECTMETHOD = 1024
--- /dev/null
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The linker can prune methods that are not directly called or
+// assigned to interfaces, but only if reflect.Type.Method is
+// never used. Test it here.
+
+package main
+
+import "reflect"
+
+var called = false
+
+type M int
+
+func (m M) UniqueMethodName() {
+ called = true
+}
+
+var v M
+
+func main() {
+ reflect.TypeOf(v).Method(0).Func.Interface().(func(M))(v)
+ if !called {
+ panic("UniqueMethodName not called")
+ }
+}
--- /dev/null
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The linker can prune methods that are not directly called or
+// assigned to interfaces, but only if reflect.Type.MethodByName is
+// never used. Test it here.
+
+package main
+
+import reflect1 "reflect"
+
+var called = false
+
+type M int
+
+func (m M) UniqueMethodName() {
+ called = true
+}
+
+var v M
+
+type MyType interface {
+ MethodByName(string) (reflect1.Method, bool)
+}
+
+func main() {
+ var t MyType = reflect1.TypeOf(v)
+ m, _ := t.MethodByName("UniqueMethodName")
+ m.Func.Interface().(func(M))(v)
+ if !called {
+ panic("UniqueMethodName not called")
+ }
+}
--- /dev/null
+// run
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The linker can prune methods that are not directly called or
+// assigned to interfaces, but only if reflect.Type.Method is
+// never used. Test it here.
+
+package main
+
+import "reflect"
+
+var called = false
+
+type M int
+
+func (m M) UniqueMethodName() {
+ called = true
+}
+
+var v M
+
+type MyType interface {
+ Method(int) reflect.Method
+}
+
+func main() {
+ var t MyType = reflect.TypeOf(v)
+ t.Method(0).Func.Interface().(func(M))(v)
+ if !called {
+ panic("UniqueMethodName not called")
+ }
+}