summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/compile/internal/gc/pgen.go3
-rw-r--r--src/cmd/compile/internal/gc/syntax.go9
-rw-r--r--src/cmd/compile/internal/gc/walk.go43
-rw-r--r--src/cmd/internal/obj/link.go9
-rw-r--r--src/cmd/internal/obj/objfile.go9
-rw-r--r--src/cmd/internal/obj/textflag.go3
-rw-r--r--src/cmd/link/internal/ld/deadcode.go14
-rw-r--r--src/cmd/link/internal/ld/link.go2
-rw-r--r--src/cmd/link/internal/ld/objfile.go10
-rw-r--r--src/runtime/textflag.h4
-rw-r--r--test/reflectmethod1.go30
-rw-r--r--test/reflectmethod2.go36
-rw-r--r--test/reflectmethod3.go35
13 files changed, 195 insertions, 12 deletions
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index 359d97518c..75829aa2df 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -438,6 +438,9 @@ func compile(fn *Node) {
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
}
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index e36ae2d722..8831143e16 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -171,10 +171,11 @@ type Func struct {
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
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index be0d5ff258..0284fb613c 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -632,6 +632,7 @@ opswitch:
}
case OCALLINTER:
+ usemethod(n)
t := n.Left.Type
if n.List.Len() != 0 && n.List.First().Op == OAS {
break
@@ -3765,6 +3766,48 @@ func bounded(n *Node, max int64) bool {
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
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index be2fa7959a..db66be6bff 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -315,6 +315,15 @@ type LSym struct {
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
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index 7ff9fcaa91..fff2b9d14e 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -242,6 +242,9 @@ func flushplist(ctxt *Link, freeProgs bool) {
if flag&NOSPLIT != 0 {
s.Nosplit = 1
}
+ if flag&REFLECTMETHOD != 0 {
+ s.ReflectMethod = true
+ }
s.Next = nil
s.Type = STEXT
s.Text = p
@@ -460,7 +463,11 @@ func writesym(ctxt *Link, b *Biobuf, s *LSym) {
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++
diff --git a/src/cmd/internal/obj/textflag.go b/src/cmd/internal/obj/textflag.go
index 57ecea334c..d8a52da4af 100644
--- a/src/cmd/internal/obj/textflag.go
+++ b/src/cmd/internal/obj/textflag.go
@@ -44,4 +44,7 @@ const (
// 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
)
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 6ae2ecf2ae..3cc7b0f8db 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -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)
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index 3173d87446..0fadaf4b85 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -105,6 +105,7 @@ const (
AttrHidden
AttrOnList
AttrLocal
+ AttrReflectMethod
)
func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 }
@@ -118,6 +119,7 @@ func (a Attribute) StackCheck() bool { return a&AttrStackCheck != 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()
diff --git a/src/cmd/link/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go
index 6e243052ab..6ea845f9f9 100644
--- a/src/cmd/link/internal/ld/objfile.go
+++ b/src/cmd/link/internal/ld/objfile.go
@@ -54,8 +54,9 @@ package ld
// - 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]
@@ -264,7 +265,10 @@ overwrite:
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++ {
diff --git a/src/runtime/textflag.h b/src/runtime/textflag.h
index e11c5dc3a2..929e9b36a9 100644
--- a/src/runtime/textflag.h
+++ b/src/runtime/textflag.h
@@ -5,6 +5,8 @@
// 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
@@ -28,3 +30,5 @@
// 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
diff --git a/test/reflectmethod1.go b/test/reflectmethod1.go
new file mode 100644
index 0000000000..973bf15b8b
--- /dev/null
+++ b/test/reflectmethod1.go
@@ -0,0 +1,30 @@
+// 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")
+ }
+}
diff --git a/test/reflectmethod2.go b/test/reflectmethod2.go
new file mode 100644
index 0000000000..9ee1c245da
--- /dev/null
+++ b/test/reflectmethod2.go
@@ -0,0 +1,36 @@
+// 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")
+ }
+}
diff --git a/test/reflectmethod3.go b/test/reflectmethod3.go
new file mode 100644
index 0000000000..b423a59f77
--- /dev/null
+++ b/test/reflectmethod3.go
@@ -0,0 +1,35 @@
+// 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")
+ }
+}