Skip to content

Commit 55d5f1c

Browse files
committed
reflect: implement NumMethod and Implements
These two functions are needed by the encoding/json package.
1 parent a5e2b27 commit 55d5f1c

File tree

12 files changed

+282
-46
lines changed

12 files changed

+282
-46
lines changed

compiler/interface.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,34 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
7272
structGlobal := c.makeStructTypeFields(typ)
7373
references = llvm.ConstBitCast(structGlobal, global.Type())
7474
}
75-
if !references.IsNil() {
75+
methodSet := types.NewMethodSet(typ)
76+
numMethods := 0
77+
_, isInterface := typ.Underlying().(*types.Interface)
78+
for i := 0; i < methodSet.Len(); i++ {
79+
// NumMethod (unintentionally) includes unexported methods for
80+
// interface types but excludes unexported methods for other types.
81+
// This makes sure we're bug-compatible with the Go reflect package.
82+
// For a discussion, see:
83+
// https://github.com/golang/go/issues/22075 (original bug)
84+
// https://github.com/golang/go/issues/42123 (revert of bugfix)
85+
if methodSet.At(i).Obj().Exported() || isInterface {
86+
numMethods++
87+
}
88+
}
89+
if !references.IsNil() || numMethods != 0 || isInterface {
7690
// Set the 'references' field of the runtime.typecodeID struct.
7791
globalValue := llvm.ConstNull(global.Type().ElementType())
78-
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
92+
if !references.IsNil() {
93+
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
94+
}
7995
if length != 0 {
8096
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
8197
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
8298
}
99+
if numMethods != 0 {
100+
numMethodsValue := llvm.ConstInt(c.uintptrType, uint64(numMethods), false)
101+
globalValue = llvm.ConstInsertValue(globalValue, numMethodsValue, []uint32{2})
102+
}
83103
global.SetInitializer(globalValue)
84104
global.SetLinkage(llvm.PrivateLinkage)
85105
}
@@ -187,7 +207,7 @@ func getTypeCodeName(t types.Type) string {
187207
case *types.Interface:
188208
methods := make([]string, t.NumMethods())
189209
for i := 0; i < t.NumMethods(); i++ {
190-
methods[i] = getTypeCodeName(t.Method(i).Type())
210+
methods[i] = t.Method(i).Name() + ":" + getTypeCodeName(t.Method(i).Type())
191211
}
192212
return "interface:" + "{" + strings.Join(methods, ",") + "}"
193213
case *types.Map:

src/reflect/sidetables.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,22 @@ import (
44
"unsafe"
55
)
66

7+
// This stores the number of methods (for Type.NumMethod()) for each named basic
8+
// type. It is indexed by the named type number.
9+
//go:extern reflect.namedBasicNumMethodSidetable
10+
var namedBasicNumMethodSidetable byte
11+
712
// This stores a varint for each named type. Named types are identified by their
813
// name instead of by their type. The named types stored in this struct are
9-
// non-basic types: pointer, struct, and channel.
14+
// non-basic types: pointer, struct, channel, and interface.
1015
//go:extern reflect.namedNonBasicTypesSidetable
1116
var namedNonBasicTypesSidetable uintptr
1217

18+
// This stores the number of methods (for Type.NumMethods()) for each named
19+
// non-basic type. It is indexed by the named type number.
20+
//go:extern reflect.namedNonBasicNumMethodSidetable
21+
var namedNonBasicNumMethodSidetable byte
22+
1323
//go:extern reflect.structTypesSidetable
1424
var structTypesSidetable byte
1525

@@ -19,6 +29,9 @@ var structNamesSidetable byte
1929
//go:extern reflect.arrayTypesSidetable
2030
var arrayTypesSidetable byte
2131

32+
//go:extern reflect.interfaceTypesSidetable
33+
var interfaceTypesSidetable byte
34+
2235
// readStringSidetable reads a string from the given table (like
2336
// structNamesSidetable) and returns this string. No heap allocation is
2437
// necessary because it makes the string point directly to the raw bytes of the

src/reflect/type.go

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,20 @@ func (t Type) Kind() Kind {
146146
}
147147
}
148148

149+
// isBasic returns true if (and only if) this is a basic type.
150+
func (t Type) isBasic() bool {
151+
return t%2 == 0
152+
}
153+
154+
// isNamed returns true if (and only if) this is a named type.
155+
func (t Type) isNamed() bool {
156+
if t.isBasic() {
157+
return t>>6 != 0
158+
} else {
159+
return (t>>4)%2 != 0
160+
}
161+
}
162+
149163
// Elem returns the element type for channel, slice and array types, the
150164
// pointed-to value for pointer types, and the key type for map types.
151165
func (t Type) Elem() Type {
@@ -170,7 +184,7 @@ func (t Type) Elem() Type {
170184
func (t Type) stripPrefix() Type {
171185
// Look at the 'n' bit in the type code (see the top of this file) to see
172186
// whether this is a named type.
173-
if (t>>4)%2 != 0 {
187+
if t.isNamed() {
174188
// This is a named type. The data is stored in a sidetable.
175189
namedTypeNum := t >> 5
176190
n := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&namedNonBasicTypesSidetable)) + uintptr(namedTypeNum)*unsafe.Sizeof(uintptr(0))))
@@ -188,7 +202,11 @@ func (t Type) Field(i int) StructField {
188202
}
189203
structIdentifier := t.stripPrefix()
190204

191-
numField, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
205+
// Skip past the NumMethod field.
206+
_, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
207+
208+
// Read the NumField field.
209+
numField, p := readVarint(p)
192210
if uint(i) >= uint(numField) {
193211
panic("reflect: field index out of range")
194212
}
@@ -287,7 +305,10 @@ func (t Type) NumField() int {
287305
panic(&TypeError{"NumField"})
288306
}
289307
structIdentifier := t.stripPrefix()
290-
n, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
308+
// Skip past the NumMethod field.
309+
_, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
310+
// Read the NumField field.
311+
n, _ := readVarint(p)
291312
return int(n)
292313
}
293314

@@ -405,10 +426,23 @@ func (t Type) AssignableTo(u Type) bool {
405426
}
406427

407428
func (t Type) Implements(u Type) bool {
408-
if t.Kind() != Interface {
429+
if u.Kind() != Interface {
409430
panic("reflect: non-interface type passed to Type.Implements")
410431
}
411-
return u.AssignableTo(t)
432+
if u.NumMethod() == 0 {
433+
// Empty interface (not even unexported methods, see NumNethod
434+
// implementation comments), therefore implements any type.
435+
return true
436+
}
437+
// Type u has (possibly unexported) methods.
438+
if t.Kind() == Interface && t.NumMethod() == 0 {
439+
// Type t has no methods (not even unexported methods since it is an
440+
// interface), while u has. Therefore, t cannot implement u.
441+
return false
442+
}
443+
// Now we'd have to compare individual methods.
444+
// This has not yet been implemented.
445+
panic("unimplemented: (reflect.Type).Implements()")
412446
}
413447

414448
// Comparable returns whether values of this type can be compared to each other.
@@ -454,7 +488,36 @@ func (t Type) ConvertibleTo(u Type) bool {
454488
}
455489

456490
func (t Type) NumMethod() int {
457-
panic("unimplemented: (reflect.Type).NumMethod()")
491+
if t.isBasic() {
492+
if !t.isNamed() {
493+
// Not a named type, so can't have methods.
494+
return 0
495+
}
496+
// Named type methods are stored in a sidetable.
497+
namedTypeNum := t >> 6
498+
return int(*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&namedBasicNumMethodSidetable)) + uintptr(namedTypeNum) - 1)))
499+
} else {
500+
if !t.isNamed() {
501+
// Most non-named types cannot have methods. However, structs can
502+
// because they can embed named values.
503+
switch t.Kind() {
504+
case Struct:
505+
structIdentifier := t.stripPrefix()
506+
numMethod, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
507+
return int(numMethod)
508+
case Interface:
509+
interfaceIdentifier := t.stripPrefix()
510+
numMethod, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&interfaceTypesSidetable)) + uintptr(interfaceIdentifier)))
511+
return int(numMethod)
512+
default:
513+
// This type cannot have methods.
514+
return 0
515+
}
516+
}
517+
// Named non-basic type, so read the number of methods from a sidetable.
518+
namedTypeNum := t >> 5
519+
return int(*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&namedNonBasicNumMethodSidetable)) + uintptr(namedTypeNum))))
520+
}
458521
}
459522

460523
func (t Type) Name() string {

src/reflect/value.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,10 @@ func maskAndShift(value, offset, size uintptr) uintptr {
532532
return (uintptr(value) >> (offset * 8)) & mask
533533
}
534534

535+
func (v Value) NumMethod() int {
536+
return v.Type().NumMethod()
537+
}
538+
535539
func (v Value) MapKeys() []Value {
536540
panic("unimplemented: (reflect.Value).MapKeys()")
537541
}

src/runtime/interface.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ type typecodeID struct {
107107

108108
// The array length, for array types.
109109
length uintptr
110+
111+
// Number of methods on this type. Usually 0 for non-named types.
112+
numMethods uintptr
110113
}
111114

112115
// structField is used by the compiler to pass information to the interface

testdata/reflect.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,18 @@ type (
2626
next *linkedList `description:"chain"`
2727
foo int
2828
}
29+
myitf interface {
30+
Foo()
31+
bar()
32+
baz()
33+
}
2934
)
3035

36+
// To test Type.NumMethod.
37+
func (n myint) foo() {}
38+
func (n myint) bar() {}
39+
func (n myint) Baz() {}
40+
3141
func main() {
3242
println("matching types")
3343
println(reflect.TypeOf(int(3)) == reflect.TypeOf(int(5)))
@@ -111,6 +121,16 @@ func main() {
111121
&linkedList{
112122
foo: 42,
113123
},
124+
// interfaces
125+
struct {
126+
a interface{}
127+
b interface {
128+
Foo()
129+
bar()
130+
}
131+
c myitf
132+
d *myitf
133+
}{},
114134
} {
115135
showValue(reflect.ValueOf(v), "")
116136
}
@@ -284,6 +304,9 @@ func showValue(rv reflect.Value, indent string) {
284304
if !rt.Comparable() {
285305
print(" comparable=false")
286306
}
307+
if rt.NumMethod() != 0 {
308+
print(" methods=", rt.NumMethod())
309+
}
287310
println()
288311
switch rt.Kind() {
289312
case reflect.Bool:
@@ -339,7 +362,9 @@ func showValue(rv reflect.Value, indent string) {
339362
for i := 0; i < rv.NumField(); i++ {
340363
field := rt.Field(i)
341364
println(indent+" field:", i, field.Name)
342-
println(indent+" tag:", field.Tag)
365+
if field.Tag != "" {
366+
println(indent+" tag:", field.Tag)
367+
}
343368
println(indent+" embedded:", field.Anonymous)
344369
showValue(rv.Field(i), indent+" ")
345370
}

testdata/reflect.txt

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ reflect type: complex64
5151
complex: (+1.200000e+000+3.000000e-001i)
5252
reflect type: complex128
5353
complex: (+1.300000e+000+4.000000e-001i)
54-
reflect type: int
54+
reflect type: int methods=1
5555
int: 32
5656
reflect type: string
5757
string: foo 3
@@ -77,7 +77,7 @@ reflect type: ptr
7777
reflect type: ptr
7878
pointer: true interface
7979
nil: false
80-
reflect type: interface settable=true
80+
reflect type: interface settable=true methods=1
8181
interface
8282
nil: true
8383
reflect type: ptr
@@ -230,28 +230,24 @@ reflect type: map comparable=false
230230
nil: false
231231
reflect type: struct
232232
struct: 0
233-
reflect type: struct
233+
reflect type: struct methods=1
234234
struct: 1
235235
field: 0 error
236-
tag:
237236
embedded: true
238-
reflect type: interface
237+
reflect type: interface methods=1
239238
interface
240239
nil: true
241240
reflect type: struct
242241
struct: 3
243242
field: 0 a
244-
tag:
245243
embedded: false
246244
reflect type: uint8
247245
uint: 42
248246
field: 1 b
249-
tag:
250247
embedded: false
251248
reflect type: int16
252249
int: 321
253250
field: 2 c
254-
tag:
255251
embedded: false
256252
reflect type: int8
257253
int: 123
@@ -263,27 +259,22 @@ reflect type: struct comparable=false
263259
reflect type: int
264260
int: 5
265261
field: 1 some
266-
tag:
267262
embedded: false
268263
reflect type: struct
269264
struct: 2
270265
field: 0 X
271-
tag:
272266
embedded: false
273267
reflect type: int16
274268
int: -5
275269
field: 1 Y
276-
tag:
277270
embedded: false
278271
reflect type: int16
279272
int: 3
280273
field: 2 zero
281-
tag:
282274
embedded: false
283275
reflect type: struct
284276
struct: 0
285277
field: 3 buf
286-
tag:
287278
embedded: false
288279
reflect type: slice comparable=false
289280
slice: uint8 2 2
@@ -296,7 +287,6 @@ reflect type: struct comparable=false
296287
reflect type: uint8
297288
uint: 111
298289
field: 4 Buf
299-
tag:
300290
embedded: false
301291
reflect type: slice comparable=false
302292
slice: uint8 1 1
@@ -317,10 +307,31 @@ reflect type: ptr
317307
pointer: false struct
318308
nil: true
319309
field: 1 foo
320-
tag:
321310
embedded: false
322311
reflect type: int
323312
int: 42
313+
reflect type: struct
314+
struct: 4
315+
field: 0 a
316+
embedded: false
317+
reflect type: interface
318+
interface
319+
nil: true
320+
field: 1 b
321+
embedded: false
322+
reflect type: interface methods=2
323+
interface
324+
nil: true
325+
field: 2 c
326+
embedded: false
327+
reflect type: interface methods=3
328+
interface
329+
nil: true
330+
field: 3 d
331+
embedded: false
332+
reflect type: ptr
333+
pointer: false interface
334+
nil: true
324335

325336
sizes:
326337
int8 1 8

0 commit comments

Comments
 (0)