]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: treat reflect.Value.Method like Call
authorDavid Crawshaw <crawshaw@golang.org>
Fri, 11 Mar 2016 00:32:04 +0000 (19:32 -0500)
committerDavid Crawshaw <crawshaw@golang.org>
Fri, 11 Mar 2016 22:07:02 +0000 (22:07 +0000)
Fixes #14740

Change-Id: Iad8d971c21977b0a1f4ef55a08bb180a8125e976
Reviewed-on: https://go-review.googlesource.com/20562
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/cmd/link/internal/ld/deadcode.go
test/reflectmethod4.go [new file with mode: 0644]

index 3cc7b0f8db0583138baea2259f14a673231a94be..a2286eb872a8b54112e411818d8f1d45bc67cdff 100644 (file)
@@ -25,7 +25,7 @@ import (
 //
 //     1. direct call
 //     2. through a reachable interface type
-//     3. reflect.Value.Call / reflect.Method.Func
+//     3. reflect.Value.Call, .Method, or reflect.Method.Func
 //
 // The first case is handled by the flood fill, a directly called method
 // is marked as reachable.
@@ -35,11 +35,12 @@ import (
 // against the interface method signatures, if it matches it is marked
 // 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, 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.
+// The third case is handled by looking to see if any of:
+//     - reflect.Value.Call is reachable
+//     - reflect.Value.Method is reachable
+//     - reflect.Type.Method or MethodByName is called.
+// If any of these happen, 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) {
@@ -58,14 +59,17 @@ func deadcode(ctxt *Link) {
        d.flood()
 
        callSym := Linkrlookup(ctxt, "reflect.Value.Call", 0)
-       callSymSeen := false
+       methSym := Linkrlookup(ctxt, "reflect.Value.Method", 0)
+       reflectSeen := false
 
        for {
-               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.
-                       callSymSeen = true
+               if !reflectSeen {
+                       if d.reflectMethod || (callSym != nil && callSym.Attr.Reachable()) || (methSym != nil && methSym.Attr.Reachable()) {
+                               // Methods might be called via reflection. Give up on
+                               // static analysis, mark all exported methods of
+                               // all reachable types as reachable.
+                               reflectSeen = true
+                       }
                }
 
                // Mark all methods that could satisfy a discovered
@@ -74,7 +78,7 @@ func deadcode(ctxt *Link) {
                // in the last pass.
                var rem []methodref
                for _, m := range d.markableMethods {
-                       if (callSymSeen && m.isExported()) || d.ifaceMethod[m.m] {
+                       if (reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
                                d.markMethod(m)
                        } else {
                                rem = append(rem, m)
diff --git a/test/reflectmethod4.go b/test/reflectmethod4.go
new file mode 100644 (file)
index 0000000..037b3da
--- /dev/null
@@ -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.Value.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.ValueOf(v).Method(0).Interface().(func())()
+       if !called {
+               panic("UniqueMethodName not called")
+       }
+}