Skip to content

Commit 8f26b57

Browse files
Cuong Manh Lecuonglm
Cuong Manh Le
authored andcommitted
cmd/compile: split exported/non-exported methods for interface type
Currently, mhdr/methods is emitted with the same len/cap. There's no way to distinguish between exported and non-exported methods statically. This CL splits mhdr/methods into two parts, use "len" for number of exported methods, and "cap" for all methods. This fixes the bug in issue #22075, which intends to return the number of exported methods but currently return all methods. Note that with this encoding, we still can access either all/exported-only/non-exported-only methods: mhdr[:cap(mhdr)] // all methods mhdr // exported methods mhdr[len(mhdr):cap(mhdr)] // non-exported methods Thank to Matthew Dempsky (@mdempsky) for suggesting this encoding. Fixes #22075 Change-Id: If662adb03ccff27407d55a5578a0ed05a15e7cdd Reviewed-on: https://go-review.googlesource.com/c/go/+/259237 Trust: Cuong Manh Le <[email protected]> Run-TryBot: Cuong Manh Le <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Cherry Zhang <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]>
1 parent f8df205 commit 8f26b57

File tree

11 files changed

+124
-64
lines changed

11 files changed

+124
-64
lines changed

doc/go1.16.html

+8
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,14 @@ <h3 id="net"><a href="/pkg/net/">net</a></h3>
213213
with <code>"use of closed network connection"</code>.
214214
</p>
215215

216+
<h3 id="reflect"><a href="/pkg/reflect/">reflect</a></h3>
217+
218+
<p><!-- CL 259237, golang.org/issue/22075 -->
219+
For interface types and values, <a href="/pkg/reflect/#Value.Method">Method</a>,
220+
<a href="/pkg/reflect/#Value.MethodByName">MethodByName</a>, and
221+
<a href="/pkg/reflect/#Value.NumMethod">NumMethod</a> now
222+
operate on the interface's exported method set, rather than its full method set.
223+
</p>
216224

217225
<h3 id="text/template/parse"><a href="/pkg/text/template/parse/">text/template/parse</a></h3>
218226

src/cmd/compile/internal/gc/reflect.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -1275,8 +1275,9 @@ func dtypesym(t *types.Type) *obj.LSym {
12751275
}
12761276
ot = dgopkgpath(lsym, ot, tpkg)
12771277

1278+
xcount := sort.Search(n, func(i int) bool { return !types.IsExported(m[i].name.Name) })
12781279
ot = dsymptr(lsym, ot, lsym, ot+3*Widthptr+uncommonSize(t))
1279-
ot = duintptr(lsym, ot, uint64(n))
1280+
ot = duintptr(lsym, ot, uint64(xcount))
12801281
ot = duintptr(lsym, ot, uint64(n))
12811282
dataAdd := imethodSize() * n
12821283
ot = dextratype(lsym, ot, t, dataAdd)

src/internal/reflectlite/type.go

+24-11
Original file line numberDiff line numberDiff line change
@@ -234,10 +234,13 @@ type imethod struct {
234234
// interfaceType represents an interface type.
235235
type interfaceType struct {
236236
rtype
237-
pkgPath name // import path
238-
methods []imethod // sorted by hash
237+
pkgPath name // import path
238+
expMethods []imethod // sorted by name, see runtime/type.go:interfacetype to see how it is encoded.
239239
}
240240

241+
func (t *interfaceType) methods() []imethod { return t.expMethods[:cap(t.expMethods)] }
242+
func (t *interfaceType) isEmpty() bool { return cap(t.expMethods) == 0 }
243+
241244
// mapType represents a map type.
242245
type mapType struct {
243246
rtype
@@ -695,7 +698,7 @@ func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
695698
}
696699

697700
// NumMethod returns the number of interface methods in the type's method set.
698-
func (t *interfaceType) NumMethod() int { return len(t.methods) }
701+
func (t *interfaceType) NumMethod() int { return len(t.expMethods) }
699702

700703
// TypeOf returns the reflection Type that represents the dynamic type of i.
701704
// If i is a nil interface value, TypeOf returns nil.
@@ -732,9 +735,10 @@ func implements(T, V *rtype) bool {
732735
return false
733736
}
734737
t := (*interfaceType)(unsafe.Pointer(T))
735-
if len(t.methods) == 0 {
738+
if t.isEmpty() {
736739
return true
737740
}
741+
tmethods := t.methods()
738742

739743
// The same algorithm applies in both cases, but the
740744
// method tables for an interface type and a concrete type
@@ -751,10 +755,11 @@ func implements(T, V *rtype) bool {
751755
if V.Kind() == Interface {
752756
v := (*interfaceType)(unsafe.Pointer(V))
753757
i := 0
754-
for j := 0; j < len(v.methods); j++ {
755-
tm := &t.methods[i]
758+
vmethods := v.methods()
759+
for j := 0; j < len(vmethods); j++ {
760+
tm := &tmethods[i]
756761
tmName := t.nameOff(tm.name)
757-
vm := &v.methods[j]
762+
vm := &vmethods[j]
758763
vmName := V.nameOff(vm.name)
759764
if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) {
760765
if !tmName.isExported() {
@@ -770,7 +775,7 @@ func implements(T, V *rtype) bool {
770775
continue
771776
}
772777
}
773-
if i++; i >= len(t.methods) {
778+
if i++; i >= len(tmethods) {
774779
return true
775780
}
776781
}
@@ -785,7 +790,7 @@ func implements(T, V *rtype) bool {
785790
i := 0
786791
vmethods := v.methods()
787792
for j := 0; j < int(v.mcount); j++ {
788-
tm := &t.methods[i]
793+
tm := &tmethods[i]
789794
tmName := t.nameOff(tm.name)
790795
vm := vmethods[j]
791796
vmName := V.nameOff(vm.name)
@@ -803,7 +808,7 @@ func implements(T, V *rtype) bool {
803808
continue
804809
}
805810
}
806-
if i++; i >= len(t.methods) {
811+
if i++; i >= len(tmethods) {
807812
return true
808813
}
809814
}
@@ -897,7 +902,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
897902
case Interface:
898903
t := (*interfaceType)(unsafe.Pointer(T))
899904
v := (*interfaceType)(unsafe.Pointer(V))
900-
if len(t.methods) == 0 && len(v.methods) == 0 {
905+
if t.isEmpty() && v.isEmpty() {
901906
return true
902907
}
903908
// Might have the same methods but still
@@ -962,3 +967,11 @@ func toType(t *rtype) Type {
962967
func ifaceIndir(t *rtype) bool {
963968
return t.kind&kindDirectIface == 0
964969
}
970+
971+
func isEmptyIface(t *rtype) bool {
972+
if t.Kind() != Interface {
973+
return false
974+
}
975+
tt := (*interfaceType)(unsafe.Pointer(t))
976+
return tt.isEmpty()
977+
}

src/internal/reflectlite/value.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func (v Value) Elem() Value {
228228
switch k {
229229
case Interface:
230230
var eface interface{}
231-
if v.typ.NumMethod() == 0 {
231+
if isEmptyIface(v.typ) {
232232
eface = *(*interface{})(v.ptr)
233233
} else {
234234
eface = (interface{})(*(*interface {
@@ -433,7 +433,7 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value
433433
return Value{dst, nil, flag(Interface)}
434434
}
435435
x := valueInterface(v)
436-
if dst.NumMethod() == 0 {
436+
if isEmptyIface(dst) {
437437
*(*interface{})(target) = x
438438
} else {
439439
ifaceE2I(dst, x, target)

src/reflect/all_test.go

+13-5
Original file line numberDiff line numberDiff line change
@@ -2995,6 +2995,14 @@ func TestUnexportedMethods(t *testing.T) {
29952995
if got := typ.NumMethod(); got != 0 {
29962996
t.Errorf("NumMethod=%d, want 0 satisfied methods", got)
29972997
}
2998+
2999+
var i unexpI
3000+
if got := TypeOf(&i).Elem().NumMethod(); got != 0 {
3001+
t.Errorf("NumMethod=%d, want 0 satisfied methods", got)
3002+
}
3003+
if got := ValueOf(&i).Elem().NumMethod(); got != 0 {
3004+
t.Errorf("NumMethod=%d, want 0 satisfied methods", got)
3005+
}
29983006
}
29993007

30003008
type InnerInt struct {
@@ -3648,21 +3656,21 @@ func TestCallPanic(t *testing.T) {
36483656
v := ValueOf(T{i, i, i, i, T2{i, i}, i, i, T2{i, i}})
36493657
badCall(func() { call(v.Field(0).Method(0)) }) // .t0.W
36503658
badCall(func() { call(v.Field(0).Elem().Method(0)) }) // .t0.W
3651-
badCall(func() { call(v.Field(0).Method(1)) }) // .t0.w
3659+
badMethod(func() { call(v.Field(0).Method(1)) }) // .t0.w
36523660
badMethod(func() { call(v.Field(0).Elem().Method(2)) }) // .t0.w
36533661
ok(func() { call(v.Field(1).Method(0)) }) // .T1.Y
36543662
ok(func() { call(v.Field(1).Elem().Method(0)) }) // .T1.Y
3655-
badCall(func() { call(v.Field(1).Method(1)) }) // .T1.y
3663+
badMethod(func() { call(v.Field(1).Method(1)) }) // .T1.y
36563664
badMethod(func() { call(v.Field(1).Elem().Method(2)) }) // .T1.y
36573665

36583666
ok(func() { call(v.Field(2).Method(0)) }) // .NamedT0.W
36593667
ok(func() { call(v.Field(2).Elem().Method(0)) }) // .NamedT0.W
3660-
badCall(func() { call(v.Field(2).Method(1)) }) // .NamedT0.w
3668+
badMethod(func() { call(v.Field(2).Method(1)) }) // .NamedT0.w
36613669
badMethod(func() { call(v.Field(2).Elem().Method(2)) }) // .NamedT0.w
36623670

36633671
ok(func() { call(v.Field(3).Method(0)) }) // .NamedT1.Y
36643672
ok(func() { call(v.Field(3).Elem().Method(0)) }) // .NamedT1.Y
3665-
badCall(func() { call(v.Field(3).Method(1)) }) // .NamedT1.y
3673+
badMethod(func() { call(v.Field(3).Method(1)) }) // .NamedT1.y
36663674
badMethod(func() { call(v.Field(3).Elem().Method(3)) }) // .NamedT1.y
36673675

36683676
ok(func() { call(v.Field(4).Field(0).Method(0)) }) // .NamedT2.T1.Y
@@ -3672,7 +3680,7 @@ func TestCallPanic(t *testing.T) {
36723680

36733681
badCall(func() { call(v.Field(5).Method(0)) }) // .namedT0.W
36743682
badCall(func() { call(v.Field(5).Elem().Method(0)) }) // .namedT0.W
3675-
badCall(func() { call(v.Field(5).Method(1)) }) // .namedT0.w
3683+
badMethod(func() { call(v.Field(5).Method(1)) }) // .namedT0.w
36763684
badMethod(func() { call(v.Field(5).Elem().Method(2)) }) // .namedT0.w
36773685

36783686
badCall(func() { call(v.Field(6).Method(0)) }) // .namedT1.Y

src/reflect/type.go

+33-22
Original file line numberDiff line numberDiff line change
@@ -386,10 +386,14 @@ type imethod struct {
386386
// interfaceType represents an interface type.
387387
type interfaceType struct {
388388
rtype
389-
pkgPath name // import path
390-
methods []imethod // sorted by hash
389+
pkgPath name // import path
390+
expMethods []imethod // sorted by name, see runtime/type.go:interfacetype to see how it is encoded.
391391
}
392392

393+
// methods returns t's full method set, both exported and non-exported.
394+
func (t *interfaceType) methods() []imethod { return t.expMethods[:cap(t.expMethods)] }
395+
func (t *interfaceType) isEmpty() bool { return cap(t.expMethods) == 0 }
396+
393397
// mapType represents a map type.
394398
type mapType struct {
395399
rtype
@@ -1049,34 +1053,31 @@ func (d ChanDir) String() string {
10491053

10501054
// Method returns the i'th method in the type's method set.
10511055
func (t *interfaceType) Method(i int) (m Method) {
1052-
if i < 0 || i >= len(t.methods) {
1053-
return
1056+
if i < 0 || i >= len(t.expMethods) {
1057+
panic("reflect: Method index out of range")
10541058
}
1055-
p := &t.methods[i]
1059+
p := &t.expMethods[i]
10561060
pname := t.nameOff(p.name)
10571061
m.Name = pname.name()
10581062
if !pname.isExported() {
1059-
m.PkgPath = pname.pkgPath()
1060-
if m.PkgPath == "" {
1061-
m.PkgPath = t.pkgPath.name()
1062-
}
1063+
panic("reflect: unexported method: " + pname.name())
10631064
}
10641065
m.Type = toType(t.typeOff(p.typ))
10651066
m.Index = i
10661067
return
10671068
}
10681069

1069-
// NumMethod returns the number of interface methods in the type's method set.
1070-
func (t *interfaceType) NumMethod() int { return len(t.methods) }
1070+
// NumMethod returns the number of exported interface methods in the type's method set.
1071+
func (t *interfaceType) NumMethod() int { return len(t.expMethods) }
10711072

10721073
// MethodByName method with the given name in the type's method set.
10731074
func (t *interfaceType) MethodByName(name string) (m Method, ok bool) {
10741075
if t == nil {
10751076
return
10761077
}
10771078
var p *imethod
1078-
for i := range t.methods {
1079-
p = &t.methods[i]
1079+
for i := range t.expMethods {
1080+
p = &t.expMethods[i]
10801081
if t.nameOff(p.name).name() == name {
10811082
return t.Method(i), true
10821083
}
@@ -1485,9 +1486,10 @@ func implements(T, V *rtype) bool {
14851486
return false
14861487
}
14871488
t := (*interfaceType)(unsafe.Pointer(T))
1488-
if len(t.methods) == 0 {
1489+
if t.isEmpty() {
14891490
return true
14901491
}
1492+
tmethods := t.methods()
14911493

14921494
// The same algorithm applies in both cases, but the
14931495
// method tables for an interface type and a concrete type
@@ -1504,10 +1506,11 @@ func implements(T, V *rtype) bool {
15041506
if V.Kind() == Interface {
15051507
v := (*interfaceType)(unsafe.Pointer(V))
15061508
i := 0
1507-
for j := 0; j < len(v.methods); j++ {
1508-
tm := &t.methods[i]
1509+
vmethods := v.methods()
1510+
for j := 0; j < len(vmethods); j++ {
1511+
tm := &tmethods[i]
15091512
tmName := t.nameOff(tm.name)
1510-
vm := &v.methods[j]
1513+
vm := &vmethods[j]
15111514
vmName := V.nameOff(vm.name)
15121515
if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) {
15131516
if !tmName.isExported() {
@@ -1523,7 +1526,7 @@ func implements(T, V *rtype) bool {
15231526
continue
15241527
}
15251528
}
1526-
if i++; i >= len(t.methods) {
1529+
if i++; i >= len(tmethods) {
15271530
return true
15281531
}
15291532
}
@@ -1538,7 +1541,7 @@ func implements(T, V *rtype) bool {
15381541
i := 0
15391542
vmethods := v.methods()
15401543
for j := 0; j < int(v.mcount); j++ {
1541-
tm := &t.methods[i]
1544+
tm := &tmethods[i]
15421545
tmName := t.nameOff(tm.name)
15431546
vm := vmethods[j]
15441547
vmName := V.nameOff(vm.name)
@@ -1556,7 +1559,7 @@ func implements(T, V *rtype) bool {
15561559
continue
15571560
}
15581561
}
1559-
if i++; i >= len(t.methods) {
1562+
if i++; i >= len(tmethods) {
15601563
return true
15611564
}
15621565
}
@@ -1658,7 +1661,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
16581661
case Interface:
16591662
t := (*interfaceType)(unsafe.Pointer(T))
16601663
v := (*interfaceType)(unsafe.Pointer(V))
1661-
if len(t.methods) == 0 && len(v.methods) == 0 {
1664+
if t.isEmpty() && v.isEmpty() {
16621665
return true
16631666
}
16641667
// Might have the same methods but still
@@ -2442,7 +2445,7 @@ func StructOf(fields []StructField) Type {
24422445
switch f.typ.Kind() {
24432446
case Interface:
24442447
ift := (*interfaceType)(unsafe.Pointer(ft))
2445-
for im, m := range ift.methods {
2448+
for im, m := range ift.methods() {
24462449
if ift.nameOff(m.name).pkgPath() != "" {
24472450
// TODO(sbinet). Issue 15924.
24482451
panic("reflect: embedded interface with unexported method(s) not implemented")
@@ -3149,3 +3152,11 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) {
31493152
}
31503153
}
31513154
}
3155+
3156+
func isEmptyIface(rt *rtype) bool {
3157+
if rt.Kind() != Interface {
3158+
return false
3159+
}
3160+
tt := (*interfaceType)(unsafe.Pointer(rt))
3161+
return len(tt.methods()) == 0
3162+
}

0 commit comments

Comments
 (0)