Skip to content

Commit be1b930

Browse files
committed
reflect: hide unexported methods that do not satisfy interfaces
Fixes #15673 Change-Id: Ib36d8db3299a93d92665dbde012d52c2c5332ac0 Reviewed-on: https://go-review.googlesource.com/23253 Reviewed-by: Russ Cox <[email protected]> Run-TryBot: David Crawshaw <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent b3bf2e7 commit be1b930

File tree

2 files changed

+81
-38
lines changed

2 files changed

+81
-38
lines changed

src/reflect/all_test.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,13 +2388,13 @@ type outer struct {
23882388
inner
23892389
}
23902390

2391-
func (*inner) m() {}
2392-
func (*outer) m() {}
2391+
func (*inner) M() {}
2392+
func (*outer) M() {}
23932393

23942394
func TestNestedMethods(t *testing.T) {
23952395
typ := TypeOf((*outer)(nil))
2396-
if typ.NumMethod() != 1 || typ.Method(0).Func.Pointer() != ValueOf((*outer).m).Pointer() {
2397-
t.Errorf("Wrong method table for outer: (m=%p)", (*outer).m)
2396+
if typ.NumMethod() != 1 || typ.Method(0).Func.Pointer() != ValueOf((*outer).M).Pointer() {
2397+
t.Errorf("Wrong method table for outer: (M=%p)", (*outer).M)
23982398
for i := 0; i < typ.NumMethod(); i++ {
23992399
m := typ.Method(i)
24002400
t.Errorf("\t%d: %s %#x\n", i, m.Name, m.Func.Pointer())
@@ -2416,18 +2416,15 @@ var unexpi unexpI = new(unexp)
24162416
func TestUnexportedMethods(t *testing.T) {
24172417
typ := TypeOf(unexpi)
24182418

2419+
if got := typ.NumMethod(); got != 1 {
2420+
t.Error("NumMethod=%d, want 1 satisfied method", got)
2421+
}
24192422
if typ.Method(0).Type == nil {
24202423
t.Error("missing type for satisfied method 'f'")
24212424
}
24222425
if !typ.Method(0).Func.IsValid() {
24232426
t.Error("missing func for satisfied method 'f'")
24242427
}
2425-
if typ.Method(1).Type != nil {
2426-
t.Error("found type for unsatisfied method 'g'")
2427-
}
2428-
if typ.Method(1).Func.IsValid() {
2429-
t.Error("found func for unsatisfied method 'g'")
2430-
}
24312428
}
24322429

24332430
type InnerInt struct {
@@ -5187,7 +5184,7 @@ func useStack(n int) {
51875184

51885185
type Impl struct{}
51895186

5190-
func (Impl) f() {}
5187+
func (Impl) F() {}
51915188

51925189
func TestValueString(t *testing.T) {
51935190
rv := ValueOf(Impl{})

src/reflect/type.go

Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -763,57 +763,103 @@ func (t *rtype) pointers() bool { return t.kind&kindNoPointers == 0 }
763763

764764
func (t *rtype) common() *rtype { return t }
765765

766+
var methodCache struct {
767+
sync.RWMutex
768+
m map[*rtype][]method
769+
}
770+
771+
// satisfiedMethods returns methods of t that satisfy an interface.
772+
// This may include unexported methods that satisfy an interface
773+
// defined with unexported methods in the same package as t.
774+
func (t *rtype) satisfiedMethods() []method {
775+
methodCache.RLock()
776+
methods, found := methodCache.m[t]
777+
methodCache.RUnlock()
778+
779+
if found {
780+
return methods
781+
}
782+
783+
ut := t.uncommon()
784+
if ut == nil {
785+
return nil
786+
}
787+
allm := ut.methods()
788+
allSatisfied := true
789+
for _, m := range allm {
790+
if m.mtyp == 0 {
791+
allSatisfied = false
792+
break
793+
}
794+
}
795+
if allSatisfied {
796+
methods = allm
797+
} else {
798+
methods = make([]method, 0, len(allm))
799+
for _, m := range allm {
800+
if m.mtyp != 0 {
801+
methods = append(methods, m)
802+
}
803+
}
804+
methods = methods[:len(methods):len(methods)]
805+
}
806+
807+
methodCache.Lock()
808+
if methodCache.m == nil {
809+
methodCache.m = make(map[*rtype][]method)
810+
}
811+
methodCache.m[t] = methods
812+
methodCache.Unlock()
813+
814+
return methods
815+
}
816+
766817
func (t *rtype) NumMethod() int {
767818
if t.Kind() == Interface {
768819
tt := (*interfaceType)(unsafe.Pointer(t))
769820
return tt.NumMethod()
770821
}
771-
ut := t.uncommon()
772-
if ut == nil {
773-
return 0
774-
}
775-
return int(ut.mcount)
822+
return len(t.satisfiedMethods())
776823
}
777824

778825
func (t *rtype) Method(i int) (m Method) {
779826
if t.Kind() == Interface {
780827
tt := (*interfaceType)(unsafe.Pointer(t))
781828
return tt.Method(i)
782829
}
783-
ut := t.uncommon()
784-
785-
if ut == nil || i < 0 || i >= int(ut.mcount) {
830+
methods := t.satisfiedMethods()
831+
if i < 0 || i >= len(methods) {
786832
panic("reflect: Method index out of range")
787833
}
788-
p := ut.methods()[i]
834+
p := methods[i]
789835
pname := t.nameOff(p.name)
790836
m.Name = pname.name()
791837
fl := flag(Func)
792838
if !pname.isExported() {
793839
m.PkgPath = pname.pkgPath()
794840
if m.PkgPath == "" {
841+
ut := t.uncommon()
795842
m.PkgPath = t.nameOff(ut.pkgPath).name()
796843
}
797844
fl |= flagStickyRO
798845
}
799-
if p.mtyp != 0 {
800-
mtyp := t.typeOff(p.mtyp)
801-
ft := (*funcType)(unsafe.Pointer(mtyp))
802-
in := make([]Type, 0, 1+len(ft.in()))
803-
in = append(in, t)
804-
for _, arg := range ft.in() {
805-
in = append(in, arg)
806-
}
807-
out := make([]Type, 0, len(ft.out()))
808-
for _, ret := range ft.out() {
809-
out = append(out, ret)
810-
}
811-
mt := FuncOf(in, out, ft.IsVariadic())
812-
m.Type = mt
813-
tfn := t.textOff(p.tfn)
814-
fn := unsafe.Pointer(&tfn)
815-
m.Func = Value{mt.(*rtype), fn, fl}
846+
mtyp := t.typeOff(p.mtyp)
847+
ft := (*funcType)(unsafe.Pointer(mtyp))
848+
in := make([]Type, 0, 1+len(ft.in()))
849+
in = append(in, t)
850+
for _, arg := range ft.in() {
851+
in = append(in, arg)
816852
}
853+
out := make([]Type, 0, len(ft.out()))
854+
for _, ret := range ft.out() {
855+
out = append(out, ret)
856+
}
857+
mt := FuncOf(in, out, ft.IsVariadic())
858+
m.Type = mt
859+
tfn := t.textOff(p.tfn)
860+
fn := unsafe.Pointer(&tfn)
861+
m.Func = Value{mt.(*rtype), fn, fl}
862+
817863
m.Index = i
818864
return m
819865
}
@@ -831,7 +877,7 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) {
831877
for i := 0; i < int(ut.mcount); i++ {
832878
p := utmethods[i]
833879
pname := t.nameOff(p.name)
834-
if pname.name() == name {
880+
if pname.isExported() && pname.name() == name {
835881
return t.Method(i), true
836882
}
837883
}

0 commit comments

Comments
 (0)