case TPTR32:
case TPTR64:
+ // NOTE: Any changes here need to be made to reflect.PtrTo as well.
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
if(!haspointers(t->type) || t->type->etype == TUINT8) {
// struct Hchan*
case TCHAN:
+ // NOTE: Any changes here need to be made to reflect.ChanOf as well.
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
ot = duintptr(s, ot, GC_CHAN_PTR);
// struct Hmap*
case TMAP:
+ // NOTE: Any changes here need to be made to reflect.MapOf as well.
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
ot = duintptr(s, ot, GC_MAP_PTR);
if(t->bound < -1)
fatal("dgcsym1: invalid bound, %T", t);
if(isslice(t)) {
+ // NOTE: Any changes here need to be made to reflect.SliceOf as well.
// struct { byte* array; uint32 len; uint32 cap; }
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
}
*off += t->width;
} else {
+ // NOTE: Any changes here need to be made to reflect.ArrayOf as well,
+ // at least once ArrayOf's gc info is implemented and ArrayOf is exported.
+ // struct { byte* array; uint32 len; uint32 cap; }
if(t->bound < 1 || !haspointers(t->type)) {
*off += t->width;
} else if(gcinline(t)) {
"os"
. "reflect"
"runtime"
+ "sort"
"sync"
"testing"
"time"
}
}
+func TestPtrToGC(t *testing.T) {
+ type T *uintptr
+ tt := TypeOf(T(nil))
+ pt := PtrTo(tt)
+ const n = 100
+ var x []interface{}
+ for i := 0; i < n; i++ {
+ v := New(pt)
+ p := new(*uintptr)
+ *p = new(uintptr)
+ **p = uintptr(i)
+ v.Elem().Set(ValueOf(p).Convert(pt))
+ x = append(x, v.Interface())
+ }
+ runtime.GC()
+
+ for i, xi := range x {
+ k := ValueOf(xi).Elem().Elem().Elem().Interface().(uintptr)
+ if k != uintptr(i) {
+ t.Errorf("lost x[%d] = %d, want %d", i, k, i)
+ }
+ }
+}
+
func TestAddr(t *testing.T) {
var p struct {
X, Y int
type T int
st := SliceOf(TypeOf(T(1)))
v := MakeSlice(st, 10, 10)
+ runtime.GC()
for i := 0; i < v.Len(); i++ {
v.Index(i).Set(ValueOf(T(i)))
+ runtime.GC()
}
s := fmt.Sprint(v.Interface())
want := "[0 1 2 3 4 5 6 7 8 9]"
checkSameType(t, Zero(SliceOf(TypeOf(T1(1)))).Interface(), []T1{})
}
+func TestSliceOfGC(t *testing.T) {
+ type T *uintptr
+ tt := TypeOf(T(nil))
+ st := SliceOf(tt)
+ const n = 100
+ var x []interface{}
+ for i := 0; i < n; i++ {
+ v := MakeSlice(st, n, n)
+ for j := 0; j < v.Len(); j++ {
+ p := new(uintptr)
+ *p = uintptr(i*n + j)
+ v.Index(j).Set(ValueOf(p).Convert(tt))
+ }
+ x = append(x, v.Interface())
+ }
+ runtime.GC()
+
+ for i, xi := range x {
+ v := ValueOf(xi)
+ for j := 0; j < v.Len(); j++ {
+ k := v.Index(j).Elem().Interface()
+ if k != uintptr(i*n+j) {
+ t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
+ }
+ }
+ }
+}
+
func TestChanOf(t *testing.T) {
// check construction and use of type not in binary
type T string
ct := ChanOf(BothDir, TypeOf(T("")))
v := MakeChan(ct, 2)
+ runtime.GC()
v.Send(ValueOf(T("hello")))
+ runtime.GC()
v.Send(ValueOf(T("world")))
+ runtime.GC()
sv1, _ := v.Recv()
sv2, _ := v.Recv()
checkSameType(t, Zero(ChanOf(BothDir, TypeOf(T1(1)))).Interface(), (chan T1)(nil))
}
+func TestChanOfGC(t *testing.T) {
+ done := make(chan bool, 1)
+ go func() {
+ select {
+ case <-done:
+ case <-time.After(5 * time.Second):
+ panic("deadlock in TestChanOfGC")
+ }
+ }()
+
+ defer func() {
+ done <- true
+ }()
+
+ type T *uintptr
+ tt := TypeOf(T(nil))
+ ct := ChanOf(BothDir, tt)
+
+ // NOTE: The garbage collector handles allocated channels specially,
+ // so we have to save pointers to channels in x; the pointer code will
+ // use the gc info in the newly constructed chan type.
+ const n = 100
+ var x []interface{}
+ for i := 0; i < n; i++ {
+ v := MakeChan(ct, n)
+ for j := 0; j < n; j++ {
+ p := new(uintptr)
+ *p = uintptr(i*n + j)
+ v.Send(ValueOf(p).Convert(tt))
+ }
+ pv := New(ct)
+ pv.Elem().Set(v)
+ x = append(x, pv.Interface())
+ }
+ runtime.GC()
+
+ for i, xi := range x {
+ v := ValueOf(xi).Elem()
+ for j := 0; j < n; j++ {
+ pv, _ := v.Recv()
+ k := pv.Elem().Interface()
+ if k != uintptr(i*n+j) {
+ t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
+ }
+ }
+ }
+}
+
func TestMapOf(t *testing.T) {
// check construction and use of type not in binary
type K string
type V float64
v := MakeMap(MapOf(TypeOf(K("")), TypeOf(V(0))))
+ runtime.GC()
v.SetMapIndex(ValueOf(K("a")), ValueOf(V(1)))
+ runtime.GC()
s := fmt.Sprint(v.Interface())
want := "map[a:1]"
// check that type already in binary is found
checkSameType(t, Zero(MapOf(TypeOf(V(0)), TypeOf(K("")))).Interface(), map[V]K(nil))
+
+ // check that invalid key type panics
+ shouldPanic(func() { MapOf(TypeOf((func())(nil)), TypeOf(false)) })
+}
+
+func TestMapOfGCKeys(t *testing.T) {
+ type T *uintptr
+ tt := TypeOf(T(nil))
+ mt := MapOf(tt, TypeOf(false))
+
+ // NOTE: The garbage collector handles allocated maps specially,
+ // so we have to save pointers to maps in x; the pointer code will
+ // use the gc info in the newly constructed map type.
+ const n = 100
+ var x []interface{}
+ for i := 0; i < n; i++ {
+ v := MakeMap(mt)
+ for j := 0; j < n; j++ {
+ p := new(uintptr)
+ *p = uintptr(i*n + j)
+ v.SetMapIndex(ValueOf(p).Convert(tt), ValueOf(true))
+ }
+ pv := New(mt)
+ pv.Elem().Set(v)
+ x = append(x, pv.Interface())
+ }
+ runtime.GC()
+
+ for i, xi := range x {
+ v := ValueOf(xi).Elem()
+ var out []int
+ for _, kv := range v.MapKeys() {
+ out = append(out, int(kv.Elem().Interface().(uintptr)))
+ }
+ sort.Ints(out)
+ for j, k := range out {
+ if k != i*n+j {
+ t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
+ }
+ }
+ }
+}
+
+func TestMapOfGCValues(t *testing.T) {
+ type T *uintptr
+ tt := TypeOf(T(nil))
+ mt := MapOf(TypeOf(1), tt)
+
+ // NOTE: The garbage collector handles allocated maps specially,
+ // so we have to save pointers to maps in x; the pointer code will
+ // use the gc info in the newly constructed map type.
+ const n = 100
+ var x []interface{}
+ for i := 0; i < n; i++ {
+ v := MakeMap(mt)
+ for j := 0; j < n; j++ {
+ p := new(uintptr)
+ *p = uintptr(i*n + j)
+ v.SetMapIndex(ValueOf(j), ValueOf(p).Convert(tt))
+ }
+ pv := New(mt)
+ pv.Elem().Set(v)
+ x = append(x, pv.Interface())
+ }
+ runtime.GC()
+
+ for i, xi := range x {
+ v := ValueOf(xi).Elem()
+ for j := 0; j < n; j++ {
+ k := v.MapIndex(ValueOf(j)).Elem().Interface().(uintptr)
+ if k != uintptr(i*n+j) {
+ t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
+ }
+ }
+ }
}
type B1 struct {
// with a unique tag like `reflect:"array"` or `reflect:"ptr"`
// so that code cannot convert from, say, *arrayType to *ptrType.
type rtype struct {
- size uintptr // size in bytes
- hash uint32 // hash of type; avoids computation in hash tables
- _ uint8 // unused/padding
- align uint8 // alignment of variable with this type
- fieldAlign uint8 // alignment of struct field with this type
- kind uint8 // enumeration for C
- alg *uintptr // algorithm table (../runtime/runtime.h:/Alg)
- gc uintptr // 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
+ size uintptr // size in bytes
+ hash uint32 // hash of type; avoids computation in hash tables
+ _ uint8 // unused/padding
+ align uint8 // alignment of variable with this type
+ fieldAlign uint8 // alignment of struct field with this type
+ kind uint8 // enumeration for C
+ alg *uintptr // algorithm table (../runtime/runtime.h:/Alg)
+ 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
}
// Method on non-interface type
fields []structField // sorted by offset
}
+// NOTE: These are copied from ../runtime/mgc0.h.
+// They must be kept in sync.
+const (
+ _GC_END = iota
+ _GC_PTR
+ _GC_APTR
+ _GC_ARRAY_START
+ _GC_ARRAY_NEXT
+ _GC_CALL
+ _GC_MAP_PTR
+ _GC_CHAN_PTR
+ _GC_STRING
+ _GC_EFACE
+ _GC_IFACE
+ _GC_SLICE
+ _GC_REGION
+ _GC_NUM_INSTR
+)
+
/*
* The compiler knows the exact layout of all the data structures above.
* The compiler does not know about the data structures and methods below.
// High bit says whether type has
// embedded pointers,to help garbage collector.
-const kindMask = 0x7f
+const (
+ kindMask = 0x7f
+ kindNoPointers = 0x80
+)
func (k Kind) String() string {
if int(k) < len(kindNames) {
m map[*rtype]*ptrType
}
+// garbage collection bytecode program for pointer to memory without pointers.
+// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
+type ptrDataGC struct {
+ width uintptr // sizeof(ptr)
+ op uintptr // _GC_APTR
+ off uintptr // 0
+ end uintptr // _GC_END
+}
+
+var ptrDataGCProg = ptrDataGC{
+ width: unsafe.Sizeof((*byte)(nil)),
+ op: _GC_APTR,
+ off: 0,
+ end: _GC_END,
+}
+
+// garbage collection bytecode program for pointer to memory with pointers.
+// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
+type ptrGC struct {
+ width uintptr // sizeof(ptr)
+ op uintptr // _GC_PTR
+ off uintptr // 0
+ elemgc unsafe.Pointer // element gc type
+ end uintptr // _GC_END
+}
+
// PtrTo returns the pointer type with element t.
// For example, if t represents type Foo, PtrTo(t) represents *Foo.
func PtrTo(t Type) Type {
p.ptrToThis = nil
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,
+ })
+ }
+ // INCORRECT. Uncomment to check that TestPtrToGC fails when p.gc is wrong.
+ //p.gc = unsafe.Pointer(&badGC{width: p.size, end: _GC_END})
+
ptrMap.m[t] = p
ptrMap.Unlock()
return &p.rtype
return t
}
+// garbage collection bytecode program for chan or map.
+// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
+type chanMapGC struct {
+ width uintptr // sizeof(map)
+ op uintptr // _GC_MAP_PTR or _GC_CHAN_PTR
+ off uintptr // 0
+ typ *rtype // map type
+ 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.
//
ch.uncommonType = nil
ch.ptrToThis = nil
+ ch.gc = unsafe.Pointer(&chanMapGC{
+ 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})
+
return cachePut(ckey, &ch.rtype)
}
+func ismapkey(*rtype) bool // implemented in runtime
+
// MapOf returns the map type with the given key and element types.
// For example, if k represents int and e represents string,
// MapOf(k, e) represents map[int]string.
//
// If the key type is not a valid map key type (that is, if it does
-// not implement Go's == operator), MapOf panics. TODO(rsc).
+// not implement Go's == operator), MapOf panics.
func MapOf(key, elem Type) Type {
ktyp := key.(*rtype)
etyp := elem.(*rtype)
- // TODO: Check for invalid key types.
+ if !ismapkey(ktyp) {
+ panic("reflect.MapOf: invalid key type " + ktyp.String())
+ }
// Look in cache.
ckey := cacheKey{Map, ktyp, etyp, 0}
mt.uncommonType = nil
mt.ptrToThis = nil
+ mt.gc = unsafe.Pointer(&chanMapGC{
+ width: mt.size,
+ op: _GC_MAP_PTR,
+ off: 0,
+ typ: &mt.rtype,
+ end: _GC_END,
+ })
+
+ // INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues
+ // fail when mt.gc is wrong.
+ //mt.gc = unsafe.Pointer(&badGC{width: mt.size, end: _GC_END})
+
return cachePut(ckey, &mt.rtype)
}
+// garbage collection bytecode program for slice of non-zero-length values.
+// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
+type sliceGC struct {
+ width uintptr // sizeof(slice)
+ op uintptr // _GC_SLICE
+ off uintptr // 0
+ elemgc unsafe.Pointer // element gc program
+ end uintptr // _GC_END
+}
+
+// garbage collection bytecode program for slice of zero-length values.
+// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
+type sliceEmptyGC struct {
+ width uintptr // sizeof(slice)
+ op uintptr // _GC_APTR
+ off uintptr // 0
+ end uintptr // _GC_END
+}
+
+var sliceEmptyGCProg = sliceEmptyGC{
+ width: unsafe.Sizeof([]byte(nil)),
+ op: _GC_APTR,
+ off: 0,
+ end: _GC_END,
+}
+
// SliceOf returns the slice type with element type t.
// For example, if t represents int, SliceOf(t) represents []int.
func SliceOf(t Type) Type {
slice.uncommonType = nil
slice.ptrToThis = nil
+ 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})
+
return cachePut(ckey, &slice.rtype)
}
/// interfaces to go runtime
//
+void
+reflect·ismapkey(Type *typ, bool ret)
+{
+ ret = typ != nil && typ->alg->hash != runtime·nohash;
+ FLUSH(&ret);
+}
+
Hmap*
runtime·makemap_c(MapType *typ, int64 hint)
{
// len Length of an array
// elemsize Size (in bytes) of an element
// size Size (in bytes)
+//
+// NOTE: There is a copy of these in ../reflect/type.go.
+// They must be kept in sync.
enum {
GC_END, // End of object, loop or subroutine. Args: none
GC_PTR, // A typed pointer. Args: (off, objgc)