aboutsummaryrefslogtreecommitdiff
path: root/src/reflect
diff options
context:
space:
mode:
authorCuong Manh Le <cuong@orijtech.com>2020-10-03 01:23:47 +0700
committerCuong Manh Le <cuong.manhle.vn@gmail.com>2020-10-09 02:14:32 +0000
commit8f26b57f9afc238bdecb9b7030bc2f4364093885 (patch)
tree0b9f9dba3d2c38f61735db9faae6a0061db9f6cb /src/reflect
parentf8df205e74d5122c43f41923280451641e566ee2 (diff)
downloadgo-8f26b57f9afc238bdecb9b7030bc2f4364093885.tar.gz
go-8f26b57f9afc238bdecb9b7030bc2f4364093885.zip
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 <cuong.manhle.vn@gmail.com> Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Diffstat (limited to 'src/reflect')
-rw-r--r--src/reflect/all_test.go18
-rw-r--r--src/reflect/type.go55
-rw-r--r--src/reflect/value.go21
3 files changed, 58 insertions, 36 deletions
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index a12712d254..be15362aae 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -2995,6 +2995,14 @@ func TestUnexportedMethods(t *testing.T) {
if got := typ.NumMethod(); got != 0 {
t.Errorf("NumMethod=%d, want 0 satisfied methods", got)
}
+
+ var i unexpI
+ if got := TypeOf(&i).Elem().NumMethod(); got != 0 {
+ t.Errorf("NumMethod=%d, want 0 satisfied methods", got)
+ }
+ if got := ValueOf(&i).Elem().NumMethod(); got != 0 {
+ t.Errorf("NumMethod=%d, want 0 satisfied methods", got)
+ }
}
type InnerInt struct {
@@ -3648,21 +3656,21 @@ func TestCallPanic(t *testing.T) {
v := ValueOf(T{i, i, i, i, T2{i, i}, i, i, T2{i, i}})
badCall(func() { call(v.Field(0).Method(0)) }) // .t0.W
badCall(func() { call(v.Field(0).Elem().Method(0)) }) // .t0.W
- badCall(func() { call(v.Field(0).Method(1)) }) // .t0.w
+ badMethod(func() { call(v.Field(0).Method(1)) }) // .t0.w
badMethod(func() { call(v.Field(0).Elem().Method(2)) }) // .t0.w
ok(func() { call(v.Field(1).Method(0)) }) // .T1.Y
ok(func() { call(v.Field(1).Elem().Method(0)) }) // .T1.Y
- badCall(func() { call(v.Field(1).Method(1)) }) // .T1.y
+ badMethod(func() { call(v.Field(1).Method(1)) }) // .T1.y
badMethod(func() { call(v.Field(1).Elem().Method(2)) }) // .T1.y
ok(func() { call(v.Field(2).Method(0)) }) // .NamedT0.W
ok(func() { call(v.Field(2).Elem().Method(0)) }) // .NamedT0.W
- badCall(func() { call(v.Field(2).Method(1)) }) // .NamedT0.w
+ badMethod(func() { call(v.Field(2).Method(1)) }) // .NamedT0.w
badMethod(func() { call(v.Field(2).Elem().Method(2)) }) // .NamedT0.w
ok(func() { call(v.Field(3).Method(0)) }) // .NamedT1.Y
ok(func() { call(v.Field(3).Elem().Method(0)) }) // .NamedT1.Y
- badCall(func() { call(v.Field(3).Method(1)) }) // .NamedT1.y
+ badMethod(func() { call(v.Field(3).Method(1)) }) // .NamedT1.y
badMethod(func() { call(v.Field(3).Elem().Method(3)) }) // .NamedT1.y
ok(func() { call(v.Field(4).Field(0).Method(0)) }) // .NamedT2.T1.Y
@@ -3672,7 +3680,7 @@ func TestCallPanic(t *testing.T) {
badCall(func() { call(v.Field(5).Method(0)) }) // .namedT0.W
badCall(func() { call(v.Field(5).Elem().Method(0)) }) // .namedT0.W
- badCall(func() { call(v.Field(5).Method(1)) }) // .namedT0.w
+ badMethod(func() { call(v.Field(5).Method(1)) }) // .namedT0.w
badMethod(func() { call(v.Field(5).Elem().Method(2)) }) // .namedT0.w
badCall(func() { call(v.Field(6).Method(0)) }) // .namedT1.Y
diff --git a/src/reflect/type.go b/src/reflect/type.go
index a3a616701b..0b34ca0c94 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -386,10 +386,14 @@ type imethod struct {
// interfaceType represents an interface type.
type interfaceType struct {
rtype
- pkgPath name // import path
- methods []imethod // sorted by hash
+ pkgPath name // import path
+ expMethods []imethod // sorted by name, see runtime/type.go:interfacetype to see how it is encoded.
}
+// methods returns t's full method set, both exported and non-exported.
+func (t *interfaceType) methods() []imethod { return t.expMethods[:cap(t.expMethods)] }
+func (t *interfaceType) isEmpty() bool { return cap(t.expMethods) == 0 }
+
// mapType represents a map type.
type mapType struct {
rtype
@@ -1049,25 +1053,22 @@ func (d ChanDir) String() string {
// Method returns the i'th method in the type's method set.
func (t *interfaceType) Method(i int) (m Method) {
- if i < 0 || i >= len(t.methods) {
- return
+ if i < 0 || i >= len(t.expMethods) {
+ panic("reflect: Method index out of range")
}
- p := &t.methods[i]
+ p := &t.expMethods[i]
pname := t.nameOff(p.name)
m.Name = pname.name()
if !pname.isExported() {
- m.PkgPath = pname.pkgPath()
- if m.PkgPath == "" {
- m.PkgPath = t.pkgPath.name()
- }
+ panic("reflect: unexported method: " + pname.name())
}
m.Type = toType(t.typeOff(p.typ))
m.Index = i
return
}
-// NumMethod returns the number of interface methods in the type's method set.
-func (t *interfaceType) NumMethod() int { return len(t.methods) }
+// NumMethod returns the number of exported interface methods in the type's method set.
+func (t *interfaceType) NumMethod() int { return len(t.expMethods) }
// MethodByName method with the given name in the type's method set.
func (t *interfaceType) MethodByName(name string) (m Method, ok bool) {
@@ -1075,8 +1076,8 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) {
return
}
var p *imethod
- for i := range t.methods {
- p = &t.methods[i]
+ for i := range t.expMethods {
+ p = &t.expMethods[i]
if t.nameOff(p.name).name() == name {
return t.Method(i), true
}
@@ -1485,9 +1486,10 @@ func implements(T, V *rtype) bool {
return false
}
t := (*interfaceType)(unsafe.Pointer(T))
- if len(t.methods) == 0 {
+ if t.isEmpty() {
return true
}
+ tmethods := t.methods()
// The same algorithm applies in both cases, but the
// method tables for an interface type and a concrete type
@@ -1504,10 +1506,11 @@ func implements(T, V *rtype) bool {
if V.Kind() == Interface {
v := (*interfaceType)(unsafe.Pointer(V))
i := 0
- for j := 0; j < len(v.methods); j++ {
- tm := &t.methods[i]
+ vmethods := v.methods()
+ for j := 0; j < len(vmethods); j++ {
+ tm := &tmethods[i]
tmName := t.nameOff(tm.name)
- vm := &v.methods[j]
+ vm := &vmethods[j]
vmName := V.nameOff(vm.name)
if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) {
if !tmName.isExported() {
@@ -1523,7 +1526,7 @@ func implements(T, V *rtype) bool {
continue
}
}
- if i++; i >= len(t.methods) {
+ if i++; i >= len(tmethods) {
return true
}
}
@@ -1538,7 +1541,7 @@ func implements(T, V *rtype) bool {
i := 0
vmethods := v.methods()
for j := 0; j < int(v.mcount); j++ {
- tm := &t.methods[i]
+ tm := &tmethods[i]
tmName := t.nameOff(tm.name)
vm := vmethods[j]
vmName := V.nameOff(vm.name)
@@ -1556,7 +1559,7 @@ func implements(T, V *rtype) bool {
continue
}
}
- if i++; i >= len(t.methods) {
+ if i++; i >= len(tmethods) {
return true
}
}
@@ -1658,7 +1661,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
case Interface:
t := (*interfaceType)(unsafe.Pointer(T))
v := (*interfaceType)(unsafe.Pointer(V))
- if len(t.methods) == 0 && len(v.methods) == 0 {
+ if t.isEmpty() && v.isEmpty() {
return true
}
// Might have the same methods but still
@@ -2442,7 +2445,7 @@ func StructOf(fields []StructField) Type {
switch f.typ.Kind() {
case Interface:
ift := (*interfaceType)(unsafe.Pointer(ft))
- for im, m := range ift.methods {
+ for im, m := range ift.methods() {
if ift.nameOff(m.name).pkgPath() != "" {
// TODO(sbinet). Issue 15924.
panic("reflect: embedded interface with unexported method(s) not implemented")
@@ -3149,3 +3152,11 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) {
}
}
}
+
+func isEmptyIface(rt *rtype) bool {
+ if rt.Kind() != Interface {
+ return false
+ }
+ tt := (*interfaceType)(unsafe.Pointer(rt))
+ return len(tt.methods()) == 0
+}
diff --git a/src/reflect/value.go b/src/reflect/value.go
index a14131e1f8..bb6371b867 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -635,10 +635,11 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype *rtype, t *fu
i := methodIndex
if v.typ.Kind() == Interface {
tt := (*interfaceType)(unsafe.Pointer(v.typ))
- if uint(i) >= uint(len(tt.methods)) {
+ ttmethods := tt.methods()
+ if uint(i) >= uint(len(ttmethods)) {
panic("reflect: internal error: invalid method index")
}
- m := &tt.methods[i]
+ m := &ttmethods[i]
if !tt.nameOff(m.name).isExported() {
panic("reflect: " + op + " of unexported method")
}
@@ -812,7 +813,7 @@ func (v Value) Elem() Value {
switch k {
case Interface:
var eface interface{}
- if v.typ.NumMethod() == 0 {
+ if isEmptyIface(v.typ) {
eface = *(*interface{})(v.ptr)
} else {
eface = (interface{})(*(*interface {
@@ -1033,7 +1034,7 @@ func valueInterface(v Value, safe bool) interface{} {
// Special case: return the element inside the interface.
// Empty interface has one layout, all interfaces with
// methods have a second layout.
- if v.NumMethod() == 0 {
+ if isEmptyIface(v.typ) {
return *(*interface{})(v.ptr)
}
return *(*interface {
@@ -1908,10 +1909,11 @@ func (v Value) Type() Type {
if v.typ.Kind() == Interface {
// Method on interface.
tt := (*interfaceType)(unsafe.Pointer(v.typ))
- if uint(i) >= uint(len(tt.methods)) {
+ ttmethods := tt.methods()
+ if uint(i) >= uint(len(ttmethods)) {
panic("reflect: internal error: invalid method index")
}
- m := &tt.methods[i]
+ m := &ttmethods[i]
return v.typ.typeOff(m.typ)
}
// Method on concrete type.
@@ -2429,7 +2431,7 @@ func (v Value) assignTo(context string, dst *rtype, target unsafe.Pointer) Value
return Value{dst, nil, flag(Interface)}
}
x := valueInterface(v, false)
- if dst.NumMethod() == 0 {
+ if isEmptyIface(dst) {
*(*interface{})(target) = x
} else {
ifaceE2I(dst, x, target)
@@ -2718,10 +2720,11 @@ func cvtDirect(v Value, typ Type) Value {
func cvtT2I(v Value, typ Type) Value {
target := unsafe_New(typ.common())
x := valueInterface(v, false)
- if typ.NumMethod() == 0 {
+ rt := typ.(*rtype)
+ if isEmptyIface(rt) {
*(*interface{})(target) = x
} else {
- ifaceE2I(typ.(*rtype), x, target)
+ ifaceE2I(rt, x, target)
}
return Value{typ.common(), target, v.flag.ro() | flagIndir | flag(Interface)}
}