mirror of
https://github.com/golang/go
synced 2024-11-13 17:20:22 -07:00
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 <rsc@golang.org> Run-TryBot: David Crawshaw <crawshaw@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
b3bf2e7803
commit
be1b930653
@ -2388,13 +2388,13 @@ type outer struct {
|
|||||||
inner
|
inner
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*inner) m() {}
|
func (*inner) M() {}
|
||||||
func (*outer) m() {}
|
func (*outer) M() {}
|
||||||
|
|
||||||
func TestNestedMethods(t *testing.T) {
|
func TestNestedMethods(t *testing.T) {
|
||||||
typ := TypeOf((*outer)(nil))
|
typ := TypeOf((*outer)(nil))
|
||||||
if typ.NumMethod() != 1 || typ.Method(0).Func.Pointer() != ValueOf((*outer).m).Pointer() {
|
if typ.NumMethod() != 1 || typ.Method(0).Func.Pointer() != ValueOf((*outer).M).Pointer() {
|
||||||
t.Errorf("Wrong method table for outer: (m=%p)", (*outer).m)
|
t.Errorf("Wrong method table for outer: (M=%p)", (*outer).M)
|
||||||
for i := 0; i < typ.NumMethod(); i++ {
|
for i := 0; i < typ.NumMethod(); i++ {
|
||||||
m := typ.Method(i)
|
m := typ.Method(i)
|
||||||
t.Errorf("\t%d: %s %#x\n", i, m.Name, m.Func.Pointer())
|
t.Errorf("\t%d: %s %#x\n", i, m.Name, m.Func.Pointer())
|
||||||
@ -2416,18 +2416,15 @@ var unexpi unexpI = new(unexp)
|
|||||||
func TestUnexportedMethods(t *testing.T) {
|
func TestUnexportedMethods(t *testing.T) {
|
||||||
typ := TypeOf(unexpi)
|
typ := TypeOf(unexpi)
|
||||||
|
|
||||||
|
if got := typ.NumMethod(); got != 1 {
|
||||||
|
t.Error("NumMethod=%d, want 1 satisfied method", got)
|
||||||
|
}
|
||||||
if typ.Method(0).Type == nil {
|
if typ.Method(0).Type == nil {
|
||||||
t.Error("missing type for satisfied method 'f'")
|
t.Error("missing type for satisfied method 'f'")
|
||||||
}
|
}
|
||||||
if !typ.Method(0).Func.IsValid() {
|
if !typ.Method(0).Func.IsValid() {
|
||||||
t.Error("missing func for satisfied method 'f'")
|
t.Error("missing func for satisfied method 'f'")
|
||||||
}
|
}
|
||||||
if typ.Method(1).Type != nil {
|
|
||||||
t.Error("found type for unsatisfied method 'g'")
|
|
||||||
}
|
|
||||||
if typ.Method(1).Func.IsValid() {
|
|
||||||
t.Error("found func for unsatisfied method 'g'")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type InnerInt struct {
|
type InnerInt struct {
|
||||||
@ -5187,7 +5184,7 @@ func useStack(n int) {
|
|||||||
|
|
||||||
type Impl struct{}
|
type Impl struct{}
|
||||||
|
|
||||||
func (Impl) f() {}
|
func (Impl) F() {}
|
||||||
|
|
||||||
func TestValueString(t *testing.T) {
|
func TestValueString(t *testing.T) {
|
||||||
rv := ValueOf(Impl{})
|
rv := ValueOf(Impl{})
|
||||||
|
@ -763,16 +763,63 @@ func (t *rtype) pointers() bool { return t.kind&kindNoPointers == 0 }
|
|||||||
|
|
||||||
func (t *rtype) common() *rtype { return t }
|
func (t *rtype) common() *rtype { return t }
|
||||||
|
|
||||||
|
var methodCache struct {
|
||||||
|
sync.RWMutex
|
||||||
|
m map[*rtype][]method
|
||||||
|
}
|
||||||
|
|
||||||
|
// satisfiedMethods returns methods of t that satisfy an interface.
|
||||||
|
// This may include unexported methods that satisfy an interface
|
||||||
|
// defined with unexported methods in the same package as t.
|
||||||
|
func (t *rtype) satisfiedMethods() []method {
|
||||||
|
methodCache.RLock()
|
||||||
|
methods, found := methodCache.m[t]
|
||||||
|
methodCache.RUnlock()
|
||||||
|
|
||||||
|
if found {
|
||||||
|
return methods
|
||||||
|
}
|
||||||
|
|
||||||
|
ut := t.uncommon()
|
||||||
|
if ut == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
allm := ut.methods()
|
||||||
|
allSatisfied := true
|
||||||
|
for _, m := range allm {
|
||||||
|
if m.mtyp == 0 {
|
||||||
|
allSatisfied = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if allSatisfied {
|
||||||
|
methods = allm
|
||||||
|
} else {
|
||||||
|
methods = make([]method, 0, len(allm))
|
||||||
|
for _, m := range allm {
|
||||||
|
if m.mtyp != 0 {
|
||||||
|
methods = append(methods, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
methods = methods[:len(methods):len(methods)]
|
||||||
|
}
|
||||||
|
|
||||||
|
methodCache.Lock()
|
||||||
|
if methodCache.m == nil {
|
||||||
|
methodCache.m = make(map[*rtype][]method)
|
||||||
|
}
|
||||||
|
methodCache.m[t] = methods
|
||||||
|
methodCache.Unlock()
|
||||||
|
|
||||||
|
return methods
|
||||||
|
}
|
||||||
|
|
||||||
func (t *rtype) NumMethod() int {
|
func (t *rtype) NumMethod() int {
|
||||||
if t.Kind() == Interface {
|
if t.Kind() == Interface {
|
||||||
tt := (*interfaceType)(unsafe.Pointer(t))
|
tt := (*interfaceType)(unsafe.Pointer(t))
|
||||||
return tt.NumMethod()
|
return tt.NumMethod()
|
||||||
}
|
}
|
||||||
ut := t.uncommon()
|
return len(t.satisfiedMethods())
|
||||||
if ut == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int(ut.mcount)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *rtype) Method(i int) (m Method) {
|
func (t *rtype) Method(i int) (m Method) {
|
||||||
@ -780,40 +827,39 @@ func (t *rtype) Method(i int) (m Method) {
|
|||||||
tt := (*interfaceType)(unsafe.Pointer(t))
|
tt := (*interfaceType)(unsafe.Pointer(t))
|
||||||
return tt.Method(i)
|
return tt.Method(i)
|
||||||
}
|
}
|
||||||
ut := t.uncommon()
|
methods := t.satisfiedMethods()
|
||||||
|
if i < 0 || i >= len(methods) {
|
||||||
if ut == nil || i < 0 || i >= int(ut.mcount) {
|
|
||||||
panic("reflect: Method index out of range")
|
panic("reflect: Method index out of range")
|
||||||
}
|
}
|
||||||
p := ut.methods()[i]
|
p := methods[i]
|
||||||
pname := t.nameOff(p.name)
|
pname := t.nameOff(p.name)
|
||||||
m.Name = pname.name()
|
m.Name = pname.name()
|
||||||
fl := flag(Func)
|
fl := flag(Func)
|
||||||
if !pname.isExported() {
|
if !pname.isExported() {
|
||||||
m.PkgPath = pname.pkgPath()
|
m.PkgPath = pname.pkgPath()
|
||||||
if m.PkgPath == "" {
|
if m.PkgPath == "" {
|
||||||
|
ut := t.uncommon()
|
||||||
m.PkgPath = t.nameOff(ut.pkgPath).name()
|
m.PkgPath = t.nameOff(ut.pkgPath).name()
|
||||||
}
|
}
|
||||||
fl |= flagStickyRO
|
fl |= flagStickyRO
|
||||||
}
|
}
|
||||||
if p.mtyp != 0 {
|
mtyp := t.typeOff(p.mtyp)
|
||||||
mtyp := t.typeOff(p.mtyp)
|
ft := (*funcType)(unsafe.Pointer(mtyp))
|
||||||
ft := (*funcType)(unsafe.Pointer(mtyp))
|
in := make([]Type, 0, 1+len(ft.in()))
|
||||||
in := make([]Type, 0, 1+len(ft.in()))
|
in = append(in, t)
|
||||||
in = append(in, t)
|
for _, arg := range ft.in() {
|
||||||
for _, arg := range ft.in() {
|
in = append(in, arg)
|
||||||
in = append(in, arg)
|
|
||||||
}
|
|
||||||
out := make([]Type, 0, len(ft.out()))
|
|
||||||
for _, ret := range ft.out() {
|
|
||||||
out = append(out, ret)
|
|
||||||
}
|
|
||||||
mt := FuncOf(in, out, ft.IsVariadic())
|
|
||||||
m.Type = mt
|
|
||||||
tfn := t.textOff(p.tfn)
|
|
||||||
fn := unsafe.Pointer(&tfn)
|
|
||||||
m.Func = Value{mt.(*rtype), fn, fl}
|
|
||||||
}
|
}
|
||||||
|
out := make([]Type, 0, len(ft.out()))
|
||||||
|
for _, ret := range ft.out() {
|
||||||
|
out = append(out, ret)
|
||||||
|
}
|
||||||
|
mt := FuncOf(in, out, ft.IsVariadic())
|
||||||
|
m.Type = mt
|
||||||
|
tfn := t.textOff(p.tfn)
|
||||||
|
fn := unsafe.Pointer(&tfn)
|
||||||
|
m.Func = Value{mt.(*rtype), fn, fl}
|
||||||
|
|
||||||
m.Index = i
|
m.Index = i
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
@ -831,7 +877,7 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) {
|
|||||||
for i := 0; i < int(ut.mcount); i++ {
|
for i := 0; i < int(ut.mcount); i++ {
|
||||||
p := utmethods[i]
|
p := utmethods[i]
|
||||||
pname := t.nameOff(p.name)
|
pname := t.nameOff(p.name)
|
||||||
if pname.name() == name {
|
if pname.isExported() && pname.name() == name {
|
||||||
return t.Method(i), true
|
return t.Method(i), true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user