Skip to content

Commit 461c9e3

Browse files
committed
reflect: revise for go1.12
related issues: golang/go#23734, golang/go#11104 Update #3
1 parent ab39c60 commit 461c9e3

File tree

2 files changed

+155
-95
lines changed

2 files changed

+155
-95
lines changed

gosrc/reflect/type.go

+63-77
Original file line numberDiff line numberDiff line change
@@ -394,16 +394,13 @@ type interfaceType struct {
394394
// mapType represents a map type.
395395
type mapType struct {
396396
rtype
397-
key *rtype // map key type
398-
elem *rtype // map element (value) type
399-
bucket *rtype // internal bucket structure
400-
keysize uint8 // size of key slot
401-
indirectkey uint8 // store ptr to key instead of key itself
402-
valuesize uint8 // size of value slot
403-
indirectvalue uint8 // store ptr to value instead of value itself
404-
bucketsize uint16 // size of bucket
405-
reflexivekey bool // true if k==k for all keys
406-
needkeyupdate bool // true if we need to update key on an overwrite
397+
key *rtype // map key type
398+
elem *rtype // map element (value) type
399+
bucket *rtype // internal bucket structure
400+
keysize uint8 // size of key slot
401+
valuesize uint8 // size of value slot
402+
bucketsize uint16 // size of bucket
403+
flags uint32
407404
}
408405

409406
// ptrType represents a pointer type.
@@ -593,6 +590,7 @@ const (
593590
kindMask = (1 << 5) - 1
594591
)
595592

593+
// String returns the name of k.
596594
func (k Kind) String() string {
597595
if int(k) < len(kindNames) {
598596
return kindNames[k]
@@ -1858,6 +1856,8 @@ func MapOf(key, elem Type) Type {
18581856
}
18591857

18601858
// Make a map type.
1859+
// Note: flag values must match those used in the TMAP case
1860+
// in ../cmd/compile/internal/gc/reflect.go:dtypesym.
18611861
var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil)
18621862
mt := **(**mapType)(unsafe.Pointer(&imap))
18631863
mt.str = resolveReflectName(newName(s, "", false))
@@ -1866,29 +1866,37 @@ func MapOf(key, elem Type) Type {
18661866
mt.key = ktyp
18671867
mt.elem = etyp
18681868
mt.bucket = bucketOf(ktyp, etyp)
1869+
mt.flags = 0
18691870
if ktyp.size > maxKeySize {
18701871
mt.keysize = uint8(ptrSize)
1871-
mt.indirectkey = 1
1872+
mt.flags |= 1 // indirect key
18721873
} else {
18731874
mt.keysize = uint8(ktyp.size)
1874-
mt.indirectkey = 0
18751875
}
18761876
if etyp.size > maxValSize {
18771877
mt.valuesize = uint8(ptrSize)
1878-
mt.indirectvalue = 1
1878+
mt.flags |= 2 // indirect value
18791879
} else {
18801880
mt.valuesize = uint8(etyp.size)
1881-
mt.indirectvalue = 0
18821881
}
18831882
mt.bucketsize = uint16(mt.bucket.size)
1884-
mt.reflexivekey = isReflexive(ktyp)
1885-
mt.needkeyupdate = needKeyUpdate(ktyp)
1883+
if isReflexive(ktyp) {
1884+
mt.flags |= 4
1885+
}
1886+
if needKeyUpdate(ktyp) {
1887+
mt.flags |= 8
1888+
}
1889+
if hashMightPanic(ktyp) {
1890+
mt.flags |= 16
1891+
}
18861892
mt.ptrToThis = 0
18871893

18881894
ti, _ := lookupCache.LoadOrStore(ckey, &mt.rtype)
18891895
return ti.(Type)
18901896
}
18911897

1898+
// TODO(crawshaw): as these funcTypeFixedN structs have no methods,
1899+
// they could be defined at runtime using the StructOf function.
18921900
type funcTypeFixed4 struct {
18931901
funcType
18941902
args [4]*rtype
@@ -2119,6 +2127,27 @@ func needKeyUpdate(t *rtype) bool {
21192127
}
21202128
}
21212129

2130+
// hashMightPanic reports whether the hash of a map key of type t might panic.
2131+
func hashMightPanic(t *rtype) bool {
2132+
switch t.Kind() {
2133+
case Interface:
2134+
return true
2135+
case Array:
2136+
tt := (*arrayType)(unsafe.Pointer(t))
2137+
return hashMightPanic(tt.elem)
2138+
case Struct:
2139+
tt := (*structType)(unsafe.Pointer(t))
2140+
for _, f := range tt.fields {
2141+
if hashMightPanic(f.typ) {
2142+
return true
2143+
}
2144+
}
2145+
return false
2146+
default:
2147+
return false
2148+
}
2149+
}
2150+
21222151
// Make sure these routines stay in sync with ../../runtime/map.go!
21232152
// These types exist only for GC, so we only fill out GC relevant info.
21242153
// Currently, that's just size and the GC program. We also fill in string
@@ -2278,43 +2307,7 @@ type structTypeUncommon struct {
22782307
u uncommonType
22792308
}
22802309

2281-
// A *rtype representing a struct is followed directly in memory by an
2282-
// array of method objects representing the methods attached to the
2283-
// struct. To get the same layout for a run time generated type, we
2284-
// need an array directly following the uncommonType memory. The types
2285-
// structTypeFixed4, ...structTypeFixedN are used to do this.
2286-
//
2287-
// A similar strategy is used for funcTypeFixed4, ...funcTypeFixedN.
2288-
2289-
// TODO(crawshaw): as these structTypeFixedN and funcTypeFixedN structs
2290-
// have no methods, they could be defined at runtime using the StructOf
2291-
// function.
2292-
2293-
type structTypeFixed4 struct {
2294-
structType
2295-
u uncommonType
2296-
m [4]method
2297-
}
2298-
2299-
type structTypeFixed8 struct {
2300-
structType
2301-
u uncommonType
2302-
m [8]method
2303-
}
2304-
2305-
type structTypeFixed16 struct {
2306-
structType
2307-
u uncommonType
2308-
m [16]method
2309-
}
2310-
2311-
type structTypeFixed32 struct {
2312-
structType
2313-
u uncommonType
2314-
m [32]method
2315-
}
2316-
2317-
// isLetter returns true if a given 'rune' is classified as a Letter.
2310+
// isLetter reports whether a given 'rune' is classified as a Letter.
23182311
func isLetter(ch rune) bool {
23192312
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
23202313
}
@@ -2571,33 +2564,26 @@ func StructOf(fields []StructField) Type {
25712564
var typ *structType
25722565
var ut *uncommonType
25732566

2574-
switch {
2575-
case len(methods) == 0:
2567+
if len(methods) == 0 {
25762568
t := new(structTypeUncommon)
25772569
typ = &t.structType
25782570
ut = &t.u
2579-
case len(methods) <= 4:
2580-
t := new(structTypeFixed4)
2581-
typ = &t.structType
2582-
ut = &t.u
2583-
copy(t.m[:], methods)
2584-
case len(methods) <= 8:
2585-
t := new(structTypeFixed8)
2586-
typ = &t.structType
2587-
ut = &t.u
2588-
copy(t.m[:], methods)
2589-
case len(methods) <= 16:
2590-
t := new(structTypeFixed16)
2591-
typ = &t.structType
2592-
ut = &t.u
2593-
copy(t.m[:], methods)
2594-
case len(methods) <= 32:
2595-
t := new(structTypeFixed32)
2596-
typ = &t.structType
2597-
ut = &t.u
2598-
copy(t.m[:], methods)
2599-
default:
2600-
panic("reflect.StructOf: too many methods")
2571+
} else {
2572+
// A *rtype representing a struct is followed directly in memory by an
2573+
// array of method objects representing the methods attached to the
2574+
// struct. To get the same layout for a run time generated type, we
2575+
// need an array directly following the uncommonType memory.
2576+
// A similar strategy is used for funcTypeFixed4, ...funcTypeFixedN.
2577+
tt := New(StructOf([]StructField{
2578+
{Name: "S", Type: TypeOf(structType{})},
2579+
{Name: "U", Type: TypeOf(uncommonType{})},
2580+
{Name: "M", Type: ArrayOf(len(methods), TypeOf(methods[0]))},
2581+
}))
2582+
2583+
typ = (*structType)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr()))
2584+
ut = (*uncommonType)(unsafe.Pointer(tt.Elem().Field(1).UnsafeAddr()))
2585+
2586+
copy(tt.Elem().Field(2).Slice(0, len(methods)).Interface().([]method), methods)
26012587
}
26022588
// TODO(sbinet): Once we allow embedding multiple types,
26032589
// methods will need to be sorted like the compiler does.

gosrc/reflect/value.go

+92-18
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,7 @@ func (v Value) InterfaceData() [2]uintptr {
10311031
func (v Value) IsNil() bool {
10321032
k := v.kind()
10331033
switch k {
1034-
case Chan, Func, Map, Ptr:
1034+
case Chan, Func, Map, Ptr, UnsafePointer:
10351035
if v.flag&flagMethod != 0 {
10361036
return false
10371037
}
@@ -1115,14 +1115,7 @@ func (v Value) MapIndex(key Value) Value {
11151115
typ := tt.elem
11161116
fl := (v.flag | key.flag).ro()
11171117
fl |= flag(typ.Kind())
1118-
if !ifaceIndir(typ) {
1119-
return Value{typ, *(*unsafe.Pointer)(e), fl}
1120-
}
1121-
// Copy result so future changes to the map
1122-
// won't change the underlying value.
1123-
c := unsafe_New(typ)
1124-
typedmemmove(typ, c, e)
1125-
return Value{typ, c, fl | flagIndir}
1118+
return copyVal(typ, fl, e)
11261119
}
11271120

11281121
// MapKeys returns a slice containing all the keys present in the map,
@@ -1152,20 +1145,96 @@ func (v Value) MapKeys() []Value {
11521145
// we can do about it.
11531146
break
11541147
}
1155-
if ifaceIndir(keyType) {
1156-
// Copy result so future changes to the map
1157-
// won't change the underlying value.
1158-
c := unsafe_New(keyType)
1159-
typedmemmove(keyType, c, key)
1160-
a[i] = Value{keyType, c, fl | flagIndir}
1161-
} else {
1162-
a[i] = Value{keyType, *(*unsafe.Pointer)(key), fl}
1163-
}
1148+
a[i] = copyVal(keyType, fl, key)
11641149
mapiternext(it)
11651150
}
11661151
return a[:i]
11671152
}
11681153

1154+
// A MapIter is an iterator for ranging over a map.
1155+
// See Value.MapRange.
1156+
type MapIter struct {
1157+
m Value
1158+
it unsafe.Pointer
1159+
}
1160+
1161+
// Key returns the key of the iterator's current map entry.
1162+
func (it *MapIter) Key() Value {
1163+
if it.it == nil {
1164+
panic("MapIter.Key called before Next")
1165+
}
1166+
if mapiterkey(it.it) == nil {
1167+
panic("MapIter.Key called on exhausted iterator")
1168+
}
1169+
1170+
t := (*mapType)(unsafe.Pointer(it.m.typ))
1171+
ktype := t.key
1172+
return copyVal(ktype, it.m.flag.ro()|flag(ktype.Kind()), mapiterkey(it.it))
1173+
}
1174+
1175+
// Value returns the value of the iterator's current map entry.
1176+
func (it *MapIter) Value() Value {
1177+
if it.it == nil {
1178+
panic("MapIter.Value called before Next")
1179+
}
1180+
if mapiterkey(it.it) == nil {
1181+
panic("MapIter.Value called on exhausted iterator")
1182+
}
1183+
1184+
t := (*mapType)(unsafe.Pointer(it.m.typ))
1185+
vtype := t.elem
1186+
return copyVal(vtype, it.m.flag.ro()|flag(vtype.Kind()), mapitervalue(it.it))
1187+
}
1188+
1189+
// Next advances the map iterator and reports whether there is another
1190+
// entry. It returns false when the iterator is exhausted; subsequent
1191+
// calls to Key, Value, or Next will panic.
1192+
func (it *MapIter) Next() bool {
1193+
if it.it == nil {
1194+
it.it = mapiterinit(it.m.typ, it.m.pointer())
1195+
} else {
1196+
if mapiterkey(it.it) == nil {
1197+
panic("MapIter.Next called on exhausted iterator")
1198+
}
1199+
mapiternext(it.it)
1200+
}
1201+
return mapiterkey(it.it) != nil
1202+
}
1203+
1204+
// MapRange returns a range iterator for a map.
1205+
// It panics if v's Kind is not Map.
1206+
//
1207+
// Call Next to advance the iterator, and Key/Value to access each entry.
1208+
// Next returns false when the iterator is exhausted.
1209+
// MapRange follows the same iteration semantics as a range statement.
1210+
//
1211+
// Example:
1212+
//
1213+
// iter := reflect.ValueOf(m).MapRange()
1214+
// for iter.Next() {
1215+
// k := iter.Key()
1216+
// v := iter.Value()
1217+
// ...
1218+
// }
1219+
//
1220+
func (v Value) MapRange() *MapIter {
1221+
v.mustBe(Map)
1222+
return &MapIter{m: v}
1223+
}
1224+
1225+
// copyVal returns a Value containing the map key or value at ptr,
1226+
// allocating a new variable as needed.
1227+
func copyVal(typ *rtype, fl flag, ptr unsafe.Pointer) Value {
1228+
if ifaceIndir(typ) {
1229+
// Copy result so future changes to the map
1230+
// won't change the underlying value.
1231+
c := unsafe_New(typ)
1232+
typedmemmove(typ, c, ptr)
1233+
return Value{typ, c, fl | flagIndir}
1234+
}
1235+
return Value{typ, *(*unsafe.Pointer)(ptr), fl}
1236+
}
1237+
11691238
// Method returns a function value corresponding to v's i'th method.
11701239
// The arguments to a Call on the returned function should not include
11711240
// a receiver; the returned function will always use v as the receiver.
@@ -2584,6 +2653,9 @@ func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer
25842653
//go:noescape
25852654
func mapiterkey(it unsafe.Pointer) (key unsafe.Pointer)
25862655

2656+
//go:noescape
2657+
func mapitervalue(it unsafe.Pointer) (value unsafe.Pointer)
2658+
25872659
//go:noescape
25882660
func mapiternext(it unsafe.Pointer)
25892661

@@ -2595,6 +2667,8 @@ func maplen(m unsafe.Pointer) int
25952667
// back into arg+retoffset before returning. If copying result bytes back,
25962668
// the caller must pass the argument frame type as argtype, so that
25972669
// call can execute appropriate write barriers during the copy.
2670+
//
2671+
//go:linkname call runtime.reflectcall
25982672
func call(argtype *rtype, fn, arg unsafe.Pointer, n uint32, retoffset uint32)
25992673

26002674
func ifaceE2I(t *rtype, src interface{}, dst unsafe.Pointer)

0 commit comments

Comments
 (0)