Skip to content

Commit

Permalink
reflect: revise for go1.12
Browse files Browse the repository at this point in the history
related issues: golang/go#23734, golang/go#11104

Update #3
  • Loading branch information
changkun committed Jan 27, 2019
1 parent ab39c60 commit 461c9e3
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 95 deletions.
140 changes: 63 additions & 77 deletions gosrc/reflect/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,16 +394,13 @@ type interfaceType struct {
// mapType represents a map type.
type mapType struct {
rtype
key *rtype // map key type
elem *rtype // map element (value) type
bucket *rtype // internal bucket structure
keysize uint8 // size of key slot
indirectkey uint8 // store ptr to key instead of key itself
valuesize uint8 // size of value slot
indirectvalue uint8 // store ptr to value instead of value itself
bucketsize uint16 // size of bucket
reflexivekey bool // true if k==k for all keys
needkeyupdate bool // true if we need to update key on an overwrite
key *rtype // map key type
elem *rtype // map element (value) type
bucket *rtype // internal bucket structure
keysize uint8 // size of key slot
valuesize uint8 // size of value slot
bucketsize uint16 // size of bucket
flags uint32
}

// ptrType represents a pointer type.
Expand Down Expand Up @@ -593,6 +590,7 @@ const (
kindMask = (1 << 5) - 1
)

// String returns the name of k.
func (k Kind) String() string {
if int(k) < len(kindNames) {
return kindNames[k]
Expand Down Expand Up @@ -1858,6 +1856,8 @@ func MapOf(key, elem Type) Type {
}

// Make a map type.
// Note: flag values must match those used in the TMAP case
// in ../cmd/compile/internal/gc/reflect.go:dtypesym.
var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil)
mt := **(**mapType)(unsafe.Pointer(&imap))
mt.str = resolveReflectName(newName(s, "", false))
Expand All @@ -1866,29 +1866,37 @@ func MapOf(key, elem Type) Type {
mt.key = ktyp
mt.elem = etyp
mt.bucket = bucketOf(ktyp, etyp)
mt.flags = 0
if ktyp.size > maxKeySize {
mt.keysize = uint8(ptrSize)
mt.indirectkey = 1
mt.flags |= 1 // indirect key
} else {
mt.keysize = uint8(ktyp.size)
mt.indirectkey = 0
}
if etyp.size > maxValSize {
mt.valuesize = uint8(ptrSize)
mt.indirectvalue = 1
mt.flags |= 2 // indirect value
} else {
mt.valuesize = uint8(etyp.size)
mt.indirectvalue = 0
}
mt.bucketsize = uint16(mt.bucket.size)
mt.reflexivekey = isReflexive(ktyp)
mt.needkeyupdate = needKeyUpdate(ktyp)
if isReflexive(ktyp) {
mt.flags |= 4
}
if needKeyUpdate(ktyp) {
mt.flags |= 8
}
if hashMightPanic(ktyp) {
mt.flags |= 16
}
mt.ptrToThis = 0

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

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

// hashMightPanic reports whether the hash of a map key of type t might panic.
func hashMightPanic(t *rtype) bool {
switch t.Kind() {
case Interface:
return true
case Array:
tt := (*arrayType)(unsafe.Pointer(t))
return hashMightPanic(tt.elem)
case Struct:
tt := (*structType)(unsafe.Pointer(t))
for _, f := range tt.fields {
if hashMightPanic(f.typ) {
return true
}
}
return false
default:
return false
}
}

// Make sure these routines stay in sync with ../../runtime/map.go!
// These types exist only for GC, so we only fill out GC relevant info.
// Currently, that's just size and the GC program. We also fill in string
Expand Down Expand Up @@ -2278,43 +2307,7 @@ type structTypeUncommon struct {
u uncommonType
}

// A *rtype representing a struct is followed directly in memory by an
// array of method objects representing the methods attached to the
// struct. To get the same layout for a run time generated type, we
// need an array directly following the uncommonType memory. The types
// structTypeFixed4, ...structTypeFixedN are used to do this.
//
// A similar strategy is used for funcTypeFixed4, ...funcTypeFixedN.

// TODO(crawshaw): as these structTypeFixedN and funcTypeFixedN structs
// have no methods, they could be defined at runtime using the StructOf
// function.

type structTypeFixed4 struct {
structType
u uncommonType
m [4]method
}

type structTypeFixed8 struct {
structType
u uncommonType
m [8]method
}

type structTypeFixed16 struct {
structType
u uncommonType
m [16]method
}

type structTypeFixed32 struct {
structType
u uncommonType
m [32]method
}

// isLetter returns true if a given 'rune' is classified as a Letter.
// isLetter reports whether a given 'rune' is classified as a Letter.
func isLetter(ch rune) bool {
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
}
Expand Down Expand Up @@ -2571,33 +2564,26 @@ func StructOf(fields []StructField) Type {
var typ *structType
var ut *uncommonType

switch {
case len(methods) == 0:
if len(methods) == 0 {
t := new(structTypeUncommon)
typ = &t.structType
ut = &t.u
case len(methods) <= 4:
t := new(structTypeFixed4)
typ = &t.structType
ut = &t.u
copy(t.m[:], methods)
case len(methods) <= 8:
t := new(structTypeFixed8)
typ = &t.structType
ut = &t.u
copy(t.m[:], methods)
case len(methods) <= 16:
t := new(structTypeFixed16)
typ = &t.structType
ut = &t.u
copy(t.m[:], methods)
case len(methods) <= 32:
t := new(structTypeFixed32)
typ = &t.structType
ut = &t.u
copy(t.m[:], methods)
default:
panic("reflect.StructOf: too many methods")
} else {
// A *rtype representing a struct is followed directly in memory by an
// array of method objects representing the methods attached to the
// struct. To get the same layout for a run time generated type, we
// need an array directly following the uncommonType memory.
// A similar strategy is used for funcTypeFixed4, ...funcTypeFixedN.
tt := New(StructOf([]StructField{
{Name: "S", Type: TypeOf(structType{})},
{Name: "U", Type: TypeOf(uncommonType{})},
{Name: "M", Type: ArrayOf(len(methods), TypeOf(methods[0]))},
}))

typ = (*structType)(unsafe.Pointer(tt.Elem().Field(0).UnsafeAddr()))
ut = (*uncommonType)(unsafe.Pointer(tt.Elem().Field(1).UnsafeAddr()))

copy(tt.Elem().Field(2).Slice(0, len(methods)).Interface().([]method), methods)
}
// TODO(sbinet): Once we allow embedding multiple types,
// methods will need to be sorted like the compiler does.
Expand Down
110 changes: 92 additions & 18 deletions gosrc/reflect/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ func (v Value) InterfaceData() [2]uintptr {
func (v Value) IsNil() bool {
k := v.kind()
switch k {
case Chan, Func, Map, Ptr:
case Chan, Func, Map, Ptr, UnsafePointer:
if v.flag&flagMethod != 0 {
return false
}
Expand Down Expand Up @@ -1115,14 +1115,7 @@ func (v Value) MapIndex(key Value) Value {
typ := tt.elem
fl := (v.flag | key.flag).ro()
fl |= flag(typ.Kind())
if !ifaceIndir(typ) {
return Value{typ, *(*unsafe.Pointer)(e), fl}
}
// Copy result so future changes to the map
// won't change the underlying value.
c := unsafe_New(typ)
typedmemmove(typ, c, e)
return Value{typ, c, fl | flagIndir}
return copyVal(typ, fl, e)
}

// MapKeys returns a slice containing all the keys present in the map,
Expand Down Expand Up @@ -1152,20 +1145,96 @@ func (v Value) MapKeys() []Value {
// we can do about it.
break
}
if ifaceIndir(keyType) {
// Copy result so future changes to the map
// won't change the underlying value.
c := unsafe_New(keyType)
typedmemmove(keyType, c, key)
a[i] = Value{keyType, c, fl | flagIndir}
} else {
a[i] = Value{keyType, *(*unsafe.Pointer)(key), fl}
}
a[i] = copyVal(keyType, fl, key)
mapiternext(it)
}
return a[:i]
}

// A MapIter is an iterator for ranging over a map.
// See Value.MapRange.
type MapIter struct {
m Value
it unsafe.Pointer
}

// Key returns the key of the iterator's current map entry.
func (it *MapIter) Key() Value {
if it.it == nil {
panic("MapIter.Key called before Next")
}
if mapiterkey(it.it) == nil {
panic("MapIter.Key called on exhausted iterator")
}

t := (*mapType)(unsafe.Pointer(it.m.typ))
ktype := t.key
return copyVal(ktype, it.m.flag.ro()|flag(ktype.Kind()), mapiterkey(it.it))
}

// Value returns the value of the iterator's current map entry.
func (it *MapIter) Value() Value {
if it.it == nil {
panic("MapIter.Value called before Next")
}
if mapiterkey(it.it) == nil {
panic("MapIter.Value called on exhausted iterator")
}

t := (*mapType)(unsafe.Pointer(it.m.typ))
vtype := t.elem
return copyVal(vtype, it.m.flag.ro()|flag(vtype.Kind()), mapitervalue(it.it))
}

// Next advances the map iterator and reports whether there is another
// entry. It returns false when the iterator is exhausted; subsequent
// calls to Key, Value, or Next will panic.
func (it *MapIter) Next() bool {
if it.it == nil {
it.it = mapiterinit(it.m.typ, it.m.pointer())
} else {
if mapiterkey(it.it) == nil {
panic("MapIter.Next called on exhausted iterator")
}
mapiternext(it.it)
}
return mapiterkey(it.it) != nil
}

// MapRange returns a range iterator for a map.
// It panics if v's Kind is not Map.
//
// Call Next to advance the iterator, and Key/Value to access each entry.
// Next returns false when the iterator is exhausted.
// MapRange follows the same iteration semantics as a range statement.
//
// Example:
//
// iter := reflect.ValueOf(m).MapRange()
// for iter.Next() {
// k := iter.Key()
// v := iter.Value()
// ...
// }
//
func (v Value) MapRange() *MapIter {
v.mustBe(Map)
return &MapIter{m: v}
}

// copyVal returns a Value containing the map key or value at ptr,
// allocating a new variable as needed.
func copyVal(typ *rtype, fl flag, ptr unsafe.Pointer) Value {
if ifaceIndir(typ) {
// Copy result so future changes to the map
// won't change the underlying value.
c := unsafe_New(typ)
typedmemmove(typ, c, ptr)
return Value{typ, c, fl | flagIndir}
}
return Value{typ, *(*unsafe.Pointer)(ptr), fl}
}

// Method returns a function value corresponding to v's i'th method.
// The arguments to a Call on the returned function should not include
// a receiver; the returned function will always use v as the receiver.
Expand Down Expand Up @@ -2584,6 +2653,9 @@ func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer
//go:noescape
func mapiterkey(it unsafe.Pointer) (key unsafe.Pointer)

//go:noescape
func mapitervalue(it unsafe.Pointer) (value unsafe.Pointer)

//go:noescape
func mapiternext(it unsafe.Pointer)

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

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

0 comments on commit 461c9e3

Please sign in to comment.