Skip to content

Commit dd5298f

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

File tree

12 files changed

+184
-31
lines changed

12 files changed

+184
-31
lines changed

compiler/interface.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,27 @@ func (c *Compiler) 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+
for i := 0; i < methodSet.Len(); i++ {
78+
if methodSet.At(i).Obj().Exported() {
79+
numMethods++
80+
}
81+
}
82+
if !references.IsNil() || numMethods != 0 {
7683
// Set the 'references' field of the runtime.typecodeID struct.
7784
globalValue := llvm.ConstNull(global.Type().ElementType())
78-
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
85+
if !references.IsNil() {
86+
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
87+
}
7988
if length != 0 {
8089
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
8190
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
8291
}
92+
if numMethods != 0 {
93+
numMethodsValue := llvm.ConstInt(c.uintptrType, uint64(numMethods), false)
94+
globalValue = llvm.ConstInsertValue(globalValue, numMethodsValue, []uint32{2})
95+
}
8396
global.SetInitializer(globalValue)
8497
global.SetLinkage(llvm.PrivateLinkage)
8598
}

src/reflect/sidetables.go

Lines changed: 11 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

src/reflect/type.go

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,20 @@ func (t Type) Kind() Kind {
142142
}
143143
}
144144

145+
// isBasic returns true if (and only if) this is a basic type.
146+
func (t Type) isBasic() bool {
147+
return t%2 == 0
148+
}
149+
150+
// isNamed returns true if (and only if) this is a named type.
151+
func (t Type) isNamed() bool {
152+
if t.isBasic() {
153+
return t>>6 != 0
154+
} else {
155+
return (t>>4)%2 != 0
156+
}
157+
}
158+
145159
// Elem returns the element type for channel, slice and array types, the
146160
// pointed-to value for pointer types, and the key type for map types.
147161
func (t Type) Elem() Type {
@@ -166,7 +180,7 @@ func (t Type) Elem() Type {
166180
func (t Type) stripPrefix() Type {
167181
// Look at the 'n' bit in the type code (see the top of this file) to see
168182
// whether this is a named type.
169-
if (t>>4)%2 != 0 {
183+
if t.isNamed() {
170184
// This is a named type. The data is stored in a sidetable.
171185
namedTypeNum := t >> 5
172186
n := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&namedNonBasicTypesSidetable)) + uintptr(namedTypeNum)*unsafe.Sizeof(uintptr(0))))
@@ -184,7 +198,11 @@ func (t Type) Field(i int) StructField {
184198
}
185199
structIdentifier := t.stripPrefix()
186200

187-
numField, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
201+
// Skip past the NumMethod field.
202+
_, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
203+
204+
// Read the NumField field.
205+
numField, p := readVarint(p)
188206
if uint(i) >= uint(numField) {
189207
panic("reflect: field index out of range")
190208
}
@@ -283,7 +301,10 @@ func (t Type) NumField() int {
283301
panic(&TypeError{"NumField"})
284302
}
285303
structIdentifier := t.stripPrefix()
286-
n, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
304+
// Skip past the NumMethod field.
305+
_, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
306+
// Read the NumField field.
307+
n, _ := readVarint(p)
287308
return int(n)
288309
}
289310

@@ -401,10 +422,49 @@ func (t Type) AssignableTo(u Type) bool {
401422
}
402423

403424
func (t Type) Implements(u Type) bool {
404-
if t.Kind() != Interface {
425+
if u.Kind() != Interface {
405426
panic("reflect: non-interface type passed to Type.Implements")
406427
}
407-
return u.AssignableTo(t)
428+
if u.NumMethod() == 0 {
429+
// Empty interface, therefore implements any type.
430+
return true
431+
}
432+
// Type u has methods.
433+
if t.NumMethod() == 0 {
434+
// Type t has no methods, while u has. Therefore, t cannot implement u.
435+
return false
436+
}
437+
// Now we'd have to compare individual methods.
438+
// This has not yet been implemented.
439+
panic("unimplemented: (reflect.Type).Implements()")
440+
}
441+
442+
func (t Type) NumMethod() int {
443+
if t.isBasic() {
444+
if !t.isNamed() {
445+
// Not a named type, so can't have methods.
446+
return 0
447+
}
448+
// Named type methods are stored in a sidetable.
449+
namedTypeNum := t >> 6
450+
return int(*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&namedBasicNumMethodSidetable)) + uintptr(namedTypeNum) - 1)))
451+
} else {
452+
if !t.isNamed() {
453+
// Most non-named types cannot have methods. However, structs can
454+
// because they can embed named values.
455+
if t.Kind() == Struct {
456+
structIdentifier := t.stripPrefix()
457+
numMethod, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&structTypesSidetable)) + uintptr(structIdentifier)))
458+
return int(numMethod)
459+
}
460+
// Not a named type so can't have methods on that, and also not a
461+
// struct type that may have embedded fields.
462+
return 0
463+
}
464+
// Named non-basic type, so read the number of methods from a sidetable.
465+
namedTypeNum := t >> 5
466+
return int(*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(&namedNonBasicNumMethodSidetable)) + uintptr(namedTypeNum))))
467+
}
408468
}
409469

410470
// Comparable returns whether values of this type can be compared to each other.

src/reflect/value.go

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

516+
func (v Value) NumMethod() int {
517+
return v.Type().NumMethod()
518+
}
519+
516520
func (v Value) MapKeys() []Value {
517521
panic("unimplemented: (reflect.Value).MapKeys()")
518522
}

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. Always 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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ type (
2828
}
2929
)
3030

31+
// To test Type.NumMethod.
32+
func (n myint) foo() {}
33+
func (n myint) bar() {}
34+
func (n myint) Baz() {}
35+
3136
func main() {
3237
println("matching types")
3338
println(reflect.TypeOf(int(3)) == reflect.TypeOf(int(5)))
@@ -111,6 +116,10 @@ func main() {
111116
&linkedList{
112117
foo: 42,
113118
},
119+
// interfaces
120+
struct {
121+
x interface{}
122+
}{},
114123
} {
115124
showValue(reflect.ValueOf(v), "")
116125
}
@@ -281,6 +290,9 @@ func showValue(rv reflect.Value, indent string) {
281290
if !rt.Comparable() {
282291
print(" comparable=false")
283292
}
293+
if rt.NumMethod() != 0 {
294+
print(" methods=", rt.NumMethod())
295+
}
284296
println()
285297
switch rt.Kind() {
286298
case reflect.Bool:

testdata/reflect.txt

Lines changed: 12 additions & 4 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,12 +230,12 @@ 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
236236
tag:
237237
embedded: true
238-
reflect type: interface
238+
reflect type: interface methods=1
239239
interface
240240
nil: true
241241
reflect type: struct
@@ -321,6 +321,14 @@ reflect type: ptr
321321
embedded: false
322322
reflect type: int
323323
int: 42
324+
reflect type: struct
325+
struct: 1
326+
field: 0 x
327+
tag:
328+
embedded: false
329+
reflect type: interface
330+
interface
331+
nil: true
324332

325333
sizes:
326334
int8 1 8

transform/reflect.go

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ type typeCodeAssignmentState struct {
120120
// all. If it is false, namedNonBasicTypesSidetable will contain simple
121121
// monotonically increasing numbers.
122122
needsNamedNonBasicTypesSidetable bool
123+
124+
// These two slices contain the number of methods on named types, and are
125+
// stored in the output binary with similar names.
126+
namedBasicNumMethodSidetable []byte
127+
namedNonBasicNumMethodSidetable []byte
123128
}
124129

125130
// assignTypeCodes is used to assign a type code to each type in the program
@@ -138,7 +143,7 @@ func assignTypeCodes(mod llvm.Module, typeSlice typeInfoSlice) {
138143
arrayTypes: make(map[string]int),
139144
structTypes: make(map[string]int),
140145
structNames: make(map[string]int),
141-
needsNamedNonBasicTypesSidetable: len(getUses(mod.NamedGlobal("reflect.namedNonBasicTypesSidetable"))) != 0,
146+
needsNamedNonBasicTypesSidetable: len(getUses(mod.NamedGlobal("reflect.namedNonBasicTypesSidetable"))) != 0 || len(getUses(mod.NamedGlobal("reflect.namedNonBasicNumMethodSidetable"))) != 0,
142147
needsStructTypesSidetable: len(getUses(mod.NamedGlobal("reflect.structTypesSidetable"))) != 0,
143148
needsStructNamesSidetable: len(getUses(mod.NamedGlobal("reflect.structNamesSidetable"))) != 0,
144149
needsArrayTypesSidetable: len(getUses(mod.NamedGlobal("reflect.arrayTypesSidetable"))) != 0,
@@ -157,7 +162,21 @@ func assignTypeCodes(mod llvm.Module, typeSlice typeInfoSlice) {
157162

158163
// Only create this sidetable when it is necessary.
159164
if state.needsNamedNonBasicTypesSidetable {
160-
global := replaceGlobalIntWithArray(mod, "reflect.namedNonBasicTypesSidetable", state.namedNonBasicTypesSidetable)
165+
if len(getUses(mod.NamedGlobal("reflect.namedNonBasicTypesSidetable"))) != 0 {
166+
global := replaceGlobalIntWithArray(mod, "reflect.namedNonBasicTypesSidetable", state.namedNonBasicTypesSidetable)
167+
global.SetLinkage(llvm.InternalLinkage)
168+
global.SetUnnamedAddr(true)
169+
global.SetGlobalConstant(true)
170+
}
171+
if len(getUses(mod.NamedGlobal("reflect.namedNonBasicNumMethodSidetable"))) != 0 {
172+
global := replaceGlobalIntWithArray(mod, "reflect.namedNonBasicNumMethodSidetable", state.namedNonBasicNumMethodSidetable)
173+
global.SetLinkage(llvm.InternalLinkage)
174+
global.SetUnnamedAddr(true)
175+
global.SetGlobalConstant(true)
176+
}
177+
}
178+
if len(getUses(mod.NamedGlobal("reflect.namedBasicNumMethodSidetable"))) != 0 {
179+
global := replaceGlobalIntWithArray(mod, "reflect.namedBasicNumMethodSidetable", state.namedBasicNumMethodSidetable)
161180
global.SetLinkage(llvm.InternalLinkage)
162181
global.SetUnnamedAddr(true)
163182
global.SetGlobalConstant(true)
@@ -189,9 +208,12 @@ func (state *typeCodeAssignmentState) getTypeCodeNum(typecode llvm.Value) *big.I
189208
// Note: see src/reflect/type.go for bit allocations.
190209
class, value := getClassAndValueFromTypeCode(typecode)
191210
name := ""
211+
var namedNumMethods uint64 // number of methods for a named type
192212
if class == "named" {
193213
name = value
194-
typecode = llvm.ConstExtractValue(typecode.Initializer(), []uint32{0})
214+
initializer := typecode.Initializer()
215+
typecode = llvm.ConstExtractValue(initializer, []uint32{0})
216+
namedNumMethods = llvm.ConstExtractValue(initializer, []uint32{2}).ZExtValue()
195217
class, value = getClassAndValueFromTypeCode(typecode)
196218
}
197219
if class == "basic" {
@@ -205,7 +227,7 @@ func (state *typeCodeAssignmentState) getTypeCodeNum(typecode llvm.Value) *big.I
205227
}
206228
if name != "" {
207229
// This type is named, set the upper bits to the name ID.
208-
num |= int64(state.getBasicNamedTypeNum(name)) << 5
230+
num |= int64(state.getBasicNamedTypeNum(name, namedNumMethods)) << 5
209231
}
210232
return big.NewInt(num << 1)
211233
} else {
@@ -248,6 +270,14 @@ func (state *typeCodeAssignmentState) getTypeCodeNum(typecode llvm.Value) *big.I
248270
index := len(state.namedNonBasicTypesSidetable)
249271
state.namedNonBasicTypesSidetable = append(state.namedNonBasicTypesSidetable, 0)
250272
state.namedNonBasicTypes[name] = index
273+
// Also store the number of methods.
274+
if index != len(state.namedNonBasicNumMethodSidetable) {
275+
panic("unexpected side table length")
276+
}
277+
if uint64(byte(namedNumMethods)) != namedNumMethods {
278+
panic("too many methods for type " + name)
279+
}
280+
state.namedNonBasicNumMethodSidetable = append(state.namedNonBasicNumMethodSidetable, byte(namedNumMethods))
251281
// Get the typecode of the underlying type (which could be the
252282
// element type in the case of pointers, for example).
253283
num = state.getNonBasicTypeCode(class, typecode)
@@ -316,12 +346,19 @@ func getClassAndValueFromTypeCode(typecode llvm.Value) (class, value string) {
316346
// getBasicNamedTypeNum returns an appropriate (unique) number for the given
317347
// named type. If the name already has a number that number is returned, else a
318348
// new number is returned. The number is always non-zero.
319-
func (state *typeCodeAssignmentState) getBasicNamedTypeNum(name string) int {
349+
func (state *typeCodeAssignmentState) getBasicNamedTypeNum(name string, numMethods uint64) int {
320350
if num, ok := state.namedBasicTypes[name]; ok {
321351
return num
322352
}
323353
num := len(state.namedBasicTypes) + 1
324354
state.namedBasicTypes[name] = num
355+
if uint64(byte(numMethods)) != numMethods {
356+
panic("too many methods for type " + name)
357+
}
358+
if len(state.namedBasicNumMethodSidetable) != num-1 {
359+
panic("unexpected side table length")
360+
}
361+
state.namedBasicNumMethodSidetable = append(state.namedBasicNumMethodSidetable, byte(numMethods))
325362
return num
326363
}
327364

@@ -381,15 +418,21 @@ func (state *typeCodeAssignmentState) getStructTypeNum(typecode llvm.Value) int
381418
}
382419

383420
// Get the fields this struct type contains.
384-
// The struct number will be the start index of
385-
structTypeGlobal := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0}).Operand(0).Initializer()
421+
// The struct number will be the start index into
422+
// reflect.structTypesSidetable.
423+
typecodeID := typecode.Initializer()
424+
structTypeGlobal := llvm.ConstExtractValue(typecodeID, []uint32{0}).Operand(0).Initializer()
386425
numFields := structTypeGlobal.Type().ArrayLength()
387-
388-
// The first data that is stored in the struct sidetable is the number of
389-
// fields this struct contains. This is usually just a single byte because
390-
// most structs don't contain that many fields, but make it a varint just
391-
// to be sure.
392-
buf := makeVarint(uint64(numFields))
426+
numMethods := llvm.ConstExtractValue(typecodeID, []uint32{2}).ZExtValue()
427+
428+
// The first element that is stored in the struct sidetable is the number
429+
// of methods this struct has. It is used by Type.NumMethod().
430+
buf := makeVarint(numMethods)
431+
// The second element that is stored in the struct sidetable is the number
432+
// of fields this struct contains. This is usually just a single byte
433+
// because most structs don't contain that many fields, but make it a varint
434+
// just to be sure.
435+
buf = append(buf, makeVarint(uint64(numFields))...)
393436

394437
// Iterate over every field in the struct.
395438
// Every field is stored sequentially in the struct sidetable. Fields can

0 commit comments

Comments
 (0)