diff options
Diffstat (limited to 'libgo/go/reflect/type.go')
-rw-r--r-- | libgo/go/reflect/type.go | 356 |
1 files changed, 300 insertions, 56 deletions
diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index a930d64607..5cbf7e577a 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -16,6 +16,7 @@ package reflect import ( + "runtime" "strconv" "sync" "unsafe" @@ -98,6 +99,9 @@ type Type interface { // ConvertibleTo returns true if a value of the type is convertible to type u. ConvertibleTo(u Type) bool + // Comparable returns true if values of this type are comparable. + Comparable() bool + // Methods applicable only to some types, depending on Kind. // The methods allowed for each kind are: // @@ -244,19 +248,21 @@ const ( // with a unique tag like `reflect:"array"` or `reflect:"ptr"` // so that code cannot convert from, say, *arrayType to *ptrType. type rtype struct { - kind uint8 // enumeration for C - align int8 // alignment of variable with this type - fieldAlign uint8 // alignment of struct field with this type - _ uint8 // unused/padding - size uintptr // size in bytes - hash uint32 // hash of type; avoids computation in hash tables + kind uint8 // enumeration for C + align int8 // alignment of variable with this type + fieldAlign uint8 // alignment of struct field with this type + _ uint8 // unused/padding + size uintptr + hash uint32 // hash of type; avoids computation in hash tables hashfn uintptr // hash function code equalfn uintptr // equality function code - string *string // string form; unnecessary but undeniably useful - *uncommonType // (relatively) uncommon fields - ptrToThis *rtype // type for pointer to this type, if used in binary or has methods + gc unsafe.Pointer // garbage collection data + string *string // string form; unnecessary but undeniably useful + *uncommonType // (relatively) uncommon fields + ptrToThis *rtype // type for pointer to this type, if used in binary or has methods + zero unsafe.Pointer // pointer to zero value } // Method on non-interface type @@ -328,8 +334,6 @@ type mapType struct { rtype `reflect:"map"` key *rtype // map key type elem *rtype // map element (value) type - // bucket *rtype // internal bucket structure - // hmap *rtype // internal map header } // ptrType represents a pointer type. @@ -398,11 +402,11 @@ type Method struct { Index int // index for Type.Method } -// High bit says whether type has -// embedded pointers,to help garbage collector. const ( - kindMask = 0x7f - kindNoPointers = 0x80 + kindDirectIface = 1 << 5 + kindGCProg = 1 << 6 // Type.gc points to GC program + kindNoPointers = 1 << 7 + kindMask = (1 << 5) - 1 ) func (k Kind) String() string { @@ -498,6 +502,8 @@ func (t *rtype) FieldAlign() int { return int(t.fieldAlign) } func (t *rtype) Kind() Kind { return Kind(t.kind & kindMask) } +func (t *rtype) pointers() bool { return t.kind&kindNoPointers == 0 } + func (t *rtype) common() *rtype { return t } func (t *uncommonType) Method(i int) (m Method) { @@ -508,7 +514,7 @@ func (t *uncommonType) Method(i int) (m Method) { if p.name != nil { m.Name = *p.name } - fl := flag(Func) << flagKindShift + fl := flag(Func) if p.pkgPath != nil { m.PkgPath = *p.pkgPath fl |= flagRO @@ -1123,12 +1129,24 @@ func (t *rtype) ptrTo() *rtype { p.uncommonType = nil p.ptrToThis = nil + p.zero = unsafe.Pointer(&make([]byte, p.size)[0]) p.elem = t + if t.kind&kindNoPointers != 0 { + p.gc = unsafe.Pointer(&ptrDataGCProg) + } else { + p.gc = unsafe.Pointer(&ptrGC{ + width: p.size, + op: _GC_PTR, + off: 0, + elemgc: t.gc, + end: _GC_END, + }) + } + q := canonicalize(&p.rtype) p = (*ptrType)(unsafe.Pointer(q.(*rtype))) - ptrMap.m[t] = p ptrMap.Unlock() return &p.rtype } @@ -1167,6 +1185,34 @@ func (t *rtype) ConvertibleTo(u Type) bool { return convertOp(uu, t) != nil } +func (t *rtype) Comparable() bool { + switch t.Kind() { + case Bool, Int, Int8, Int16, Int32, Int64, + Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, + Float32, Float64, Complex64, Complex128, + Chan, Interface, Ptr, String, UnsafePointer: + return true + + case Func, Map, Slice: + return false + + case Array: + return (*arrayType)(unsafe.Pointer(t)).elem.Comparable() + + case Struct: + tt := (*structType)(unsafe.Pointer(t)) + for i := range tt.fields { + if !tt.fields[i].typ.Comparable() { + return false + } + } + return true + + default: + panic("reflect: impossible") + } +} + // implements returns true if the type V implements the interface type T. func implements(T, V *rtype) bool { if T.Kind() != Interface { @@ -1401,11 +1447,6 @@ type chanGC struct { end uintptr // _GC_END } -type badGC struct { - width uintptr - end uintptr -} - // ChanOf returns the channel type with the given direction and element type. // For example, if t represents int, ChanOf(RecvDir, t) represents <-chan int. // @@ -1464,9 +1505,18 @@ func ChanOf(dir ChanDir, t Type) Type { ch.elem = typ ch.uncommonType = nil ch.ptrToThis = nil + ch.zero = unsafe.Pointer(&make([]byte, ch.size)[0]) + + ch.gc = unsafe.Pointer(&chanGC{ + width: ch.size, + op: _GC_CHAN_PTR, + off: 0, + typ: &ch.rtype, + end: _GC_END, + }) // INCORRECT. Uncomment to check that TestChanOfGC fails when ch.gc is wrong. - //ch.gc = unsafe.Pointer(&badGC{width: ch.size, end: _GC_END}) + // ch.gc = unsafe.Pointer(&badGC{width: ch.size, end: _GC_END}) return cachePut(ckey, &ch.rtype) } @@ -1509,10 +1559,19 @@ func MapOf(key, elem Type) Type { mt.key = ktyp mt.elem = etyp - // mt.bucket = bucketOf(ktyp, etyp) - // mt.hmap = hMapOf(mt.bucket) mt.uncommonType = nil mt.ptrToThis = nil + mt.zero = unsafe.Pointer(&make([]byte, mt.size)[0]) + // mt.gc = unsafe.Pointer(&ptrGC{ + // width: unsafe.Sizeof(uintptr(0)), + // op: _GC_PTR, + // off: 0, + // elemgc: nil, + // end: _GC_END, + // }) + + // TODO(cmang): Generate GC data for Map elements. + mt.gc = unsafe.Pointer(&ptrDataGCProg) // INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues // fail when mt.gc is wrong. @@ -1521,53 +1580,156 @@ func MapOf(key, elem Type) Type { return cachePut(ckey, &mt.rtype) } -// Make sure these routines stay in sync with ../../pkg/runtime/hashmap.c! +// gcProg is a helper type for generatation of GC pointer info. +type gcProg struct { + gc []byte + size uintptr // size of type in bytes + hasPtr bool +} + +func (gc *gcProg) append(v byte) { + gc.align(unsafe.Sizeof(uintptr(0))) + gc.appendWord(v) +} + +// Appends t's type info to the current program. +func (gc *gcProg) appendProg(t *rtype) { + gc.align(uintptr(t.align)) + if !t.pointers() { + gc.size += t.size + return + } + switch t.Kind() { + default: + panic("reflect: non-pointer type marked as having pointers") + case Ptr, UnsafePointer, Chan, Func, Map: + gc.appendWord(bitsPointer) + case Slice: + gc.appendWord(bitsPointer) + gc.appendWord(bitsScalar) + gc.appendWord(bitsScalar) + case String: + gc.appendWord(bitsPointer) + gc.appendWord(bitsScalar) + case Array: + c := t.Len() + e := t.Elem().common() + for i := 0; i < c; i++ { + gc.appendProg(e) + } + case Interface: + gc.appendWord(bitsMultiWord) + if t.NumMethod() == 0 { + gc.appendWord(bitsEface) + } else { + gc.appendWord(bitsIface) + } + case Struct: + c := t.NumField() + for i := 0; i < c; i++ { + gc.appendProg(t.Field(i).Type.common()) + } + gc.align(uintptr(t.align)) + } +} + +func (gc *gcProg) appendWord(v byte) { + ptrsize := unsafe.Sizeof(uintptr(0)) + if gc.size%ptrsize != 0 { + panic("reflect: unaligned GC program") + } + nptr := gc.size / ptrsize + for uintptr(len(gc.gc)) < nptr/2+1 { + gc.gc = append(gc.gc, 0x44) // BitsScalar + } + gc.gc[nptr/2] &= ^(3 << ((nptr%2)*4 + 2)) + gc.gc[nptr/2] |= v << ((nptr%2)*4 + 2) + gc.size += ptrsize + if v == bitsPointer { + gc.hasPtr = true + } +} + +func (gc *gcProg) finalize() (unsafe.Pointer, bool) { + if gc.size == 0 { + return nil, false + } + ptrsize := unsafe.Sizeof(uintptr(0)) + gc.align(ptrsize) + nptr := gc.size / ptrsize + for uintptr(len(gc.gc)) < nptr/2+1 { + gc.gc = append(gc.gc, 0x44) // BitsScalar + } + // If number of words is odd, repeat the mask twice. + // Compiler does the same. + if nptr%2 != 0 { + for i := uintptr(0); i < nptr; i++ { + gc.appendWord(extractGCWord(gc.gc, i)) + } + } + return unsafe.Pointer(&gc.gc[0]), gc.hasPtr +} + +func extractGCWord(gc []byte, i uintptr) byte { + return (gc[i/2] >> ((i%2)*4 + 2)) & 3 +} + +func (gc *gcProg) align(a uintptr) { + gc.size = align(gc.size, a) +} + +// These constants must stay in sync with ../runtime/mgc0.h. +const ( + bitsScalar = 1 + bitsPointer = 2 + bitsMultiWord = 3 + + bitsIface = 2 + bitsEface = 3 +) + +// Make sure these routines stay in sync with ../../runtime/hashmap.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 // for possible debugging use. const ( - _BUCKETSIZE = 8 - _MAXKEYSIZE = 128 - _MAXVALSIZE = 128 + bucketSize = 8 + maxKeySize = 128 + maxValSize = 128 ) func bucketOf(ktyp, etyp *rtype) *rtype { - if ktyp.size > _MAXKEYSIZE { + if ktyp.size > maxKeySize { ktyp = PtrTo(ktyp).(*rtype) } - if etyp.size > _MAXVALSIZE { + if etyp.size > maxValSize { etyp = PtrTo(etyp).(*rtype) } ptrsize := unsafe.Sizeof(uintptr(0)) - gc := make([]uintptr, 1) // first entry is size, filled in at the end - offset := _BUCKETSIZE * unsafe.Sizeof(uint8(0)) // topbits - gc = append(gc, _GC_PTR, offset, 0 /*self pointer set below*/) // overflow - offset += ptrsize - + var gc gcProg + // topbits + for i := 0; i < int(bucketSize*unsafe.Sizeof(uint8(0))/ptrsize); i++ { + gc.append(bitsScalar) + } // keys - if ktyp.kind&kindNoPointers == 0 { - gc = append(gc, _GC_ARRAY_START, offset, _BUCKETSIZE, ktyp.size) - gc = appendGCProgram(gc, ktyp) - gc = append(gc, _GC_ARRAY_NEXT) + for i := 0; i < bucketSize; i++ { + gc.appendProg(ktyp) } - offset += _BUCKETSIZE * ktyp.size - // values - if etyp.kind&kindNoPointers == 0 { - gc = append(gc, _GC_ARRAY_START, offset, _BUCKETSIZE, etyp.size) - gc = appendGCProgram(gc, etyp) - gc = append(gc, _GC_ARRAY_NEXT) + for i := 0; i < bucketSize; i++ { + gc.appendProg(etyp) + } + // overflow + gc.append(bitsPointer) + if runtime.GOARCH == "amd64p32" { + gc.append(bitsScalar) } - offset += _BUCKETSIZE * etyp.size - - gc = append(gc, _GC_END) - gc[0] = offset - gc[3] = uintptr(unsafe.Pointer(&gc[0])) // set self pointer b := new(rtype) - b.size = offset - // b.gc = unsafe.Pointer(&gc[0]) + b.size = gc.size + // b.gc[0], _ = gc.finalize() + b.kind |= kindGCProg s := "bucket(" + *ktyp.string + "," + *etyp.string + ")" b.string = &s return b @@ -1575,8 +1737,7 @@ func bucketOf(ktyp, etyp *rtype) *rtype { // Take the GC program for "t" and append it to the GC program "gc". func appendGCProgram(gc []uintptr, t *rtype) []uintptr { - // p := t.gc - var p unsafe.Pointer + p := t.gc p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(uintptr(0))) // skip size loop: for { @@ -1687,9 +1848,22 @@ func SliceOf(t Type) Type { slice.elem = typ slice.uncommonType = nil slice.ptrToThis = nil + slice.zero = unsafe.Pointer(&make([]byte, slice.size)[0]) + + if typ.size == 0 { + slice.gc = unsafe.Pointer(&sliceEmptyGCProg) + } else { + slice.gc = unsafe.Pointer(&sliceGC{ + width: slice.size, + op: _GC_SLICE, + off: 0, + elemgc: typ.gc, + end: _GC_END, + }) + } // INCORRECT. Uncomment to check that TestSliceOfOfGC fails when slice.gc is wrong. - //slice.gc = unsafe.Pointer(&badGC{width: slice.size, end: _GC_END}) + // slice.gc = unsafe.Pointer(&badGC{width: slice.size, end: _GC_END}) return cachePut(ckey, &slice.rtype) } @@ -1702,6 +1876,8 @@ func SliceOf(t Type) Type { // // TODO(rsc): Unexported for now. Export once the alg field is set correctly // for the type. This may require significant work. +// +// TODO(rsc): TestArrayOf is also disabled. Re-enable. func arrayOf(count int, elem Type) Type { typ := elem.(*rtype) slice := SliceOf(elem) @@ -1720,6 +1896,7 @@ func arrayOf(count int, elem Type) Type { prototype := *(**arrayType)(unsafe.Pointer(&iarray)) array := new(arrayType) *array = *prototype + // TODO: Set extra kind bits correctly. array.string = &s // gccgo uses a different hash. @@ -1740,8 +1917,10 @@ func arrayOf(count int, elem Type) Type { array.fieldAlign = typ.fieldAlign // TODO: array.alg // TODO: array.gc + // TODO: array.uncommonType = nil array.ptrToThis = nil + array.zero = unsafe.Pointer(&make([]byte, array.size)[0]) array.len = uintptr(count) array.slice = slice.(*rtype) @@ -1790,3 +1969,68 @@ func toType(p *rtype) Type { } return canonicalize(p) } + +// ifaceIndir reports whether t is stored indirectly in an interface value. +func ifaceIndir(t *rtype) bool { + return t.kind&kindDirectIface == 0 +} + +// Layout matches runtime.BitVector (well enough). +type bitVector struct { + n uint32 // number of bits + data []byte +} + +// append a bit pair to the bitmap. +func (bv *bitVector) append2(bits uint8) { + // assume bv.n is a multiple of 2, since append2 is the only operation. + if bv.n%8 == 0 { + bv.data = append(bv.data, 0) + } + bv.data[bv.n/8] |= bits << (bv.n % 8) + bv.n += 2 +} + +func addTypeBits(bv *bitVector, offset *uintptr, t *rtype) { + *offset = align(*offset, uintptr(t.align)) + if t.kind&kindNoPointers != 0 { + *offset += t.size + return + } + + switch Kind(t.kind & kindMask) { + case Chan, Func, Map, Ptr, Slice, String, UnsafePointer: + // 1 pointer at start of representation + for bv.n < 2*uint32(*offset/uintptr(ptrSize)) { + bv.append2(bitsScalar) + } + bv.append2(bitsPointer) + + case Interface: + // 2 pointers + for bv.n < 2*uint32(*offset/uintptr(ptrSize)) { + bv.append2(bitsScalar) + } + bv.append2(bitsPointer) + bv.append2(bitsPointer) + + case Array: + // repeat inner type + tt := (*arrayType)(unsafe.Pointer(t)) + for i := 0; i < int(tt.len); i++ { + addTypeBits(bv, offset, tt.elem) + } + + case Struct: + // apply fields + tt := (*structType)(unsafe.Pointer(t)) + start := *offset + for i := range tt.fields { + f := &tt.fields[i] + off := start + f.offset + addTypeBits(bv, &off, f.typ) + } + } + + *offset += t.size +} |