"sync"
"testing"
"time"
+ "unicode"
+ "unicode/utf8"
"unsafe"
)
}
}
+func TestStructOf(t *testing.T) {
+ // check construction and use of type not in binary
+ fields := []StructField{
+ StructField{
+ Name: "S",
+ Tag: "s",
+ Type: TypeOf(""),
+ },
+ StructField{
+ Name: "X",
+ Tag: "x",
+ Type: TypeOf(byte(0)),
+ },
+ StructField{
+ Name: "Y",
+ Type: TypeOf(uint64(0)),
+ },
+ StructField{
+ Name: "Z",
+ Type: TypeOf([3]uint16{}),
+ },
+ }
+
+ st := StructOf(fields)
+ v := New(st).Elem()
+ runtime.GC()
+ v.FieldByName("X").Set(ValueOf(byte(2)))
+ v.FieldByIndex([]int{1}).Set(ValueOf(byte(1)))
+ runtime.GC()
+
+ s := fmt.Sprint(v.Interface())
+ want := `{ 1 0 [0 0 0]}`
+ if s != want {
+ t.Errorf("constructed struct = %s, want %s", s, want)
+ }
+
+ // check the size, alignment and field offsets
+ stt := TypeOf(struct {
+ String string
+ X byte
+ Y uint64
+ Z [3]uint16
+ }{})
+ if st.Size() != stt.Size() {
+ t.Errorf("constructed struct size = %v, want %v", st.Size(), stt.Size())
+ }
+ if st.Align() != stt.Align() {
+ t.Errorf("constructed struct align = %v, want %v", st.Align(), stt.Align())
+ }
+ if st.FieldAlign() != stt.FieldAlign() {
+ t.Errorf("constructed struct field align = %v, want %v", st.FieldAlign(), stt.FieldAlign())
+ }
+ for i := 0; i < st.NumField(); i++ {
+ o1 := st.Field(i).Offset
+ o2 := stt.Field(i).Offset
+ if o1 != o2 {
+ t.Errorf("constructed struct field %v offset = %v, want %v", i, o1, o2)
+ }
+ }
+
+ // check duplicate names
+ shouldPanic(func() {
+ StructOf([]StructField{
+ StructField{Name: "string", Type: TypeOf("")},
+ StructField{Name: "string", Type: TypeOf("")},
+ })
+ })
+ shouldPanic(func() {
+ StructOf([]StructField{
+ StructField{Type: TypeOf("")},
+ StructField{Name: "string", Type: TypeOf("")},
+ })
+ })
+ shouldPanic(func() {
+ StructOf([]StructField{
+ StructField{Type: TypeOf("")},
+ StructField{Type: TypeOf("")},
+ })
+ })
+ // check that type already in binary is found
+ checkSameType(t, Zero(StructOf(fields[2:3])).Interface(), struct{ Y uint64 }{})
+}
+
+func TestStructOfExportRules(t *testing.T) {
+ type S1 struct{}
+ type s2 struct{}
+ type ΦType struct{}
+ type φType struct{}
+
+ testPanic := func(i int, mustPanic bool, f func()) {
+ defer func() {
+ err := recover()
+ if err == nil && mustPanic {
+ t.Errorf("test-%d did not panic", i)
+ }
+ if err != nil && !mustPanic {
+ t.Errorf("test-%d panicked: %v\n", i, err)
+ }
+ }()
+ f()
+ }
+
+ for i, test := range []struct {
+ field StructField
+ mustPanic bool
+ exported bool
+ }{
+ {
+ field: StructField{Name: "", Type: TypeOf(S1{})},
+ mustPanic: false,
+ exported: true,
+ },
+ {
+ field: StructField{Name: "", Type: TypeOf((*S1)(nil))},
+ mustPanic: false,
+ exported: true,
+ },
+ {
+ field: StructField{Name: "", Type: TypeOf(s2{})},
+ mustPanic: false,
+ exported: false,
+ },
+ {
+ field: StructField{Name: "", Type: TypeOf((*s2)(nil))},
+ mustPanic: false,
+ exported: false,
+ },
+ {
+ field: StructField{Name: "", Type: TypeOf(S1{}), PkgPath: "other/pkg"},
+ mustPanic: true,
+ exported: true,
+ },
+ {
+ field: StructField{Name: "", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
+ mustPanic: true,
+ exported: true,
+ },
+ {
+ field: StructField{Name: "", Type: TypeOf(s2{}), PkgPath: "other/pkg"},
+ mustPanic: true,
+ exported: false,
+ },
+ {
+ field: StructField{Name: "", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
+ mustPanic: true,
+ exported: false,
+ },
+ {
+ field: StructField{Name: "S", Type: TypeOf(S1{})},
+ mustPanic: false,
+ exported: true,
+ },
+ {
+ field: StructField{Name: "S", Type: TypeOf((*S1)(nil))},
+ mustPanic: false,
+ exported: true,
+ },
+ {
+ field: StructField{Name: "S", Type: TypeOf(s2{})},
+ mustPanic: false,
+ exported: true,
+ },
+ {
+ field: StructField{Name: "S", Type: TypeOf((*s2)(nil))},
+ mustPanic: false,
+ exported: true,
+ },
+ {
+ field: StructField{Name: "s", Type: TypeOf(S1{})},
+ mustPanic: true,
+ exported: false,
+ },
+ {
+ field: StructField{Name: "s", Type: TypeOf((*S1)(nil))},
+ mustPanic: true,
+ exported: false,
+ },
+ {
+ field: StructField{Name: "s", Type: TypeOf(s2{})},
+ mustPanic: true,
+ exported: false,
+ },
+ {
+ field: StructField{Name: "s", Type: TypeOf((*s2)(nil))},
+ mustPanic: true,
+ exported: false,
+ },
+ {
+ field: StructField{Name: "s", Type: TypeOf(S1{}), PkgPath: "other/pkg"},
+ mustPanic: true, // TODO(sbinet): creating a name with a package path
+ exported: false,
+ },
+ {
+ field: StructField{Name: "s", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
+ mustPanic: true, // TODO(sbinet): creating a name with a package path
+ exported: false,
+ },
+ {
+ field: StructField{Name: "s", Type: TypeOf(s2{}), PkgPath: "other/pkg"},
+ mustPanic: true, // TODO(sbinet): creating a name with a package path
+ exported: false,
+ },
+ {
+ field: StructField{Name: "s", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
+ mustPanic: true, // TODO(sbinet): creating a name with a package path
+ exported: false,
+ },
+ {
+ field: StructField{Name: "", Type: TypeOf(ΦType{})},
+ mustPanic: true, // TODO(sbinet): creating a struct with UTF-8 fields not supported
+ exported: true,
+ },
+ {
+ field: StructField{Name: "", Type: TypeOf(φType{})},
+ mustPanic: true, // TODO(sbinet): creating a struct with UTF-8 fields not supported
+ exported: false,
+ },
+ {
+ field: StructField{Name: "Φ", Type: TypeOf(0)},
+ mustPanic: false,
+ exported: true,
+ },
+ {
+ field: StructField{Name: "φ", Type: TypeOf(0)},
+ mustPanic: false,
+ exported: false,
+ },
+ } {
+ testPanic(i, test.mustPanic, func() {
+ typ := StructOf([]StructField{test.field})
+ if typ == nil {
+ t.Errorf("test-%d: error creating struct type", i)
+ return
+ }
+ field := typ.Field(0)
+ n := field.Name
+ if n == "" {
+ n = field.Type.Name()
+ }
+ exported := isExported(n)
+ if exported != test.exported {
+ t.Errorf("test-%d: got exported=%v want exported=%v", exported, test.exported)
+ }
+ })
+ }
+}
+
+// isExported reports whether name is an exported Go symbol
+// (that is, whether it begins with an upper-case letter).
+//
+func isExported(name string) bool {
+ ch, _ := utf8.DecodeRuneInString(name)
+ return unicode.IsUpper(ch)
+}
+
+func TestStructOfGC(t *testing.T) {
+ type T *uintptr
+ tt := TypeOf(T(nil))
+ fields := []StructField{
+ {Name: "X", Type: tt},
+ {Name: "Y", Type: tt},
+ }
+ st := StructOf(fields)
+
+ const n = 10000
+ var x []interface{}
+ for i := 0; i < n; i++ {
+ v := New(st).Elem()
+ for j := 0; j < v.NumField(); j++ {
+ p := new(uintptr)
+ *p = uintptr(i*n + j)
+ v.Field(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.NumField(); j++ {
+ k := v.Field(j).Elem().Interface()
+ if k != uintptr(i*n+j) {
+ t.Errorf("lost x[%d].%c = %d, want %d", i, "XY"[j], k, i*n+j)
+ }
+ }
+ }
+}
+
+func TestStructOfAlg(t *testing.T) {
+ st := StructOf([]StructField{{Name: "X", Tag: "x", Type: TypeOf(int(0))}})
+ v1 := New(st).Elem()
+ v2 := New(st).Elem()
+ if !DeepEqual(v1.Interface(), v1.Interface()) {
+ t.Errorf("constructed struct %v not equal to itself", v1.Interface())
+ }
+ v1.FieldByName("X").Set(ValueOf(int(1)))
+ if i1, i2 := v1.Interface(), v2.Interface(); DeepEqual(i1, i2) {
+ t.Errorf("constructed structs %v and %v should not be equal", i1, i2)
+ }
+
+ st = StructOf([]StructField{{Name: "X", Tag: "x", Type: TypeOf([]int(nil))}})
+ v1 = New(st).Elem()
+ shouldPanic(func() { _ = v1.Interface() == v1.Interface() })
+}
+
+func TestStructOfGenericAlg(t *testing.T) {
+ st1 := StructOf([]StructField{
+ {Name: "X", Tag: "x", Type: TypeOf(int64(0))},
+ {Name: "Y", Type: TypeOf(string(""))},
+ })
+ st := StructOf([]StructField{
+ {Name: "S0", Type: st1},
+ {Name: "S1", Type: st1},
+ })
+
+ for _, table := range []struct {
+ rt Type
+ idx []int
+ }{
+ {
+ rt: st,
+ idx: []int{0, 1},
+ },
+ {
+ rt: st1,
+ idx: []int{1},
+ },
+ {
+ rt: StructOf(
+ []StructField{
+ {Name: "XX", Type: TypeOf([0]int{})},
+ {Name: "YY", Type: TypeOf("")},
+ },
+ ),
+ idx: []int{1},
+ },
+ {
+ rt: StructOf(
+ []StructField{
+ {Name: "XX", Type: TypeOf([0]int{})},
+ {Name: "YY", Type: TypeOf("")},
+ {Name: "ZZ", Type: TypeOf([2]int{})},
+ },
+ ),
+ idx: []int{1},
+ },
+ {
+ rt: StructOf(
+ []StructField{
+ {Name: "XX", Type: TypeOf([1]int{})},
+ {Name: "YY", Type: TypeOf("")},
+ },
+ ),
+ idx: []int{1},
+ },
+ {
+ rt: StructOf(
+ []StructField{
+ {Name: "XX", Type: TypeOf([1]int{})},
+ {Name: "YY", Type: TypeOf("")},
+ {Name: "ZZ", Type: TypeOf([1]int{})},
+ },
+ ),
+ idx: []int{1},
+ },
+ {
+ rt: StructOf(
+ []StructField{
+ {Name: "XX", Type: TypeOf([2]int{})},
+ {Name: "YY", Type: TypeOf("")},
+ {Name: "ZZ", Type: TypeOf([2]int{})},
+ },
+ ),
+ idx: []int{1},
+ },
+ {
+ rt: StructOf(
+ []StructField{
+ {Name: "XX", Type: TypeOf(int64(0))},
+ {Name: "YY", Type: TypeOf(byte(0))},
+ {Name: "ZZ", Type: TypeOf("")},
+ },
+ ),
+ idx: []int{2},
+ },
+ {
+ rt: StructOf(
+ []StructField{
+ {Name: "XX", Type: TypeOf(int64(0))},
+ {Name: "YY", Type: TypeOf(int64(0))},
+ {Name: "ZZ", Type: TypeOf("")},
+ {Name: "AA", Type: TypeOf([1]int64{})},
+ },
+ ),
+ idx: []int{2},
+ },
+ } {
+ v1 := New(table.rt).Elem()
+ v2 := New(table.rt).Elem()
+
+ if !DeepEqual(v1.Interface(), v1.Interface()) {
+ t.Errorf("constructed struct %v not equal to itself", v1.Interface())
+ }
+
+ v1.FieldByIndex(table.idx).Set(ValueOf("abc"))
+ v2.FieldByIndex(table.idx).Set(ValueOf("def"))
+ if i1, i2 := v1.Interface(), v2.Interface(); DeepEqual(i1, i2) {
+ t.Errorf("constructed structs %v and %v should not be equal", i1, i2)
+ }
+
+ abc := "abc"
+ v1.FieldByIndex(table.idx).Set(ValueOf(abc))
+ val := "+" + abc + "-"
+ v2.FieldByIndex(table.idx).Set(ValueOf(val[1:4]))
+ if i1, i2 := v1.Interface(), v2.Interface(); !DeepEqual(i1, i2) {
+ t.Errorf("constructed structs %v and %v should be equal", i1, i2)
+ }
+
+ // Test hash
+ m := MakeMap(MapOf(table.rt, TypeOf(int(0))))
+ m.SetMapIndex(v1, ValueOf(1))
+ if i1, i2 := v1.Interface(), v2.Interface(); !m.MapIndex(v2).IsValid() {
+ t.Errorf("constructed structs %#v and %#v have different hashes", i1, i2)
+ }
+
+ v2.FieldByIndex(table.idx).Set(ValueOf("abc"))
+ if i1, i2 := v1.Interface(), v2.Interface(); !DeepEqual(i1, i2) {
+ t.Errorf("constructed structs %v and %v should be equal", i1, i2)
+ }
+
+ if i1, i2 := v1.Interface(), v2.Interface(); !m.MapIndex(v2).IsValid() {
+ t.Errorf("constructed structs %v and %v have different hashes", i1, i2)
+ }
+ }
+}
+
+func TestStructOfDirectIface(t *testing.T) {
+ {
+ type T struct{ X [1]*byte }
+ i1 := Zero(TypeOf(T{})).Interface()
+ v1 := ValueOf(&i1).Elem()
+ p1 := v1.InterfaceData()[1]
+
+ i2 := Zero(StructOf([]StructField{
+ {
+ Name: "X",
+ Type: ArrayOf(1, TypeOf((*int8)(nil))),
+ },
+ })).Interface()
+ v2 := ValueOf(&i2).Elem()
+ p2 := v2.InterfaceData()[1]
+
+ if p1 != 0 {
+ t.Errorf("got p1=%v. want=%v", p1, nil)
+ }
+
+ if p2 != 0 {
+ t.Errorf("got p2=%v. want=%v", p2, nil)
+ }
+ }
+ {
+ type T struct{ X [0]*byte }
+ i1 := Zero(TypeOf(T{})).Interface()
+ v1 := ValueOf(&i1).Elem()
+ p1 := v1.InterfaceData()[1]
+
+ i2 := Zero(StructOf([]StructField{
+ {
+ Name: "X",
+ Type: ArrayOf(0, TypeOf((*int8)(nil))),
+ },
+ })).Interface()
+ v2 := ValueOf(&i2).Elem()
+ p2 := v2.InterfaceData()[1]
+
+ if p1 == 0 {
+ t.Errorf("got p1=%v. want=not-%v", p1, nil)
+ }
+
+ if p2 == 0 {
+ t.Errorf("got p2=%v. want=not-%v", p2, nil)
+ }
+ }
+}
+
+type StructI int
+
+func (i StructI) Get() int { return int(i) }
+
+type StructIPtr int
+
+func (i *StructIPtr) Get() int { return int(*i) }
+
+func TestStructOfWithInterface(t *testing.T) {
+ const want = 42
+ type Iface interface {
+ Get() int
+ }
+ for i, table := range []struct {
+ typ Type
+ val Value
+ impl bool
+ }{
+ {
+ typ: TypeOf(StructI(want)),
+ val: ValueOf(StructI(want)),
+ impl: true,
+ },
+ {
+ typ: PtrTo(TypeOf(StructI(want))),
+ val: ValueOf(func() interface{} {
+ v := StructI(want)
+ return &v
+ }()),
+ impl: true,
+ },
+ {
+ typ: PtrTo(TypeOf(StructIPtr(want))),
+ val: ValueOf(func() interface{} {
+ v := StructIPtr(want)
+ return &v
+ }()),
+ impl: true,
+ },
+ {
+ typ: TypeOf(StructIPtr(want)),
+ val: ValueOf(StructIPtr(want)),
+ impl: false,
+ },
+ // {
+ // typ: TypeOf((*Iface)(nil)).Elem(), // FIXME(sbinet): fix method.ifn/tfn
+ // val: ValueOf(StructI(want)),
+ // impl: true,
+ // },
+ } {
+ rt := StructOf(
+ []StructField{
+ {
+ Name: "",
+ PkgPath: "",
+ Type: table.typ,
+ },
+ },
+ )
+ rv := New(rt).Elem()
+ rv.Field(0).Set(table.val)
+
+ if _, ok := rv.Interface().(Iface); ok != table.impl {
+ if table.impl {
+ t.Errorf("test-%d: type=%v fails to implement Iface.\n", i, table.typ)
+ } else {
+ t.Errorf("test-%d: type=%v should NOT implement Iface\n", table.typ)
+ }
+ continue
+ }
+
+ if !table.impl {
+ continue
+ }
+
+ v := rv.Interface().(Iface).Get()
+ if v != want {
+ t.Errorf("test-%d: x.Get()=%v. want=%v\n", i, v, want)
+ }
+
+ fct := rv.MethodByName("Get")
+ out := fct.Call(nil)
+ if !DeepEqual(out[0].Interface(), want) {
+ t.Errorf("test-%d: x.Get()=%v. want=%v\n", i, out[0].Interface(), want)
+ }
+ }
+}
+
func TestChanOf(t *testing.T) {
// check construction and use of type not in binary
type T string
return (n + a - 1) &^ (a - 1)
}
+func newName(n, tag, pkgPath string, exported bool) name {
+ if len(n) > 1<<16-1 {
+ panic("reflect.nameFrom: name too long: " + n)
+ }
+ if len(tag) > 1<<16-1 {
+ panic("reflect.nameFrom: tag too long: " + tag)
+ }
+
+ var bits byte
+ l := 1 + 2 + len(n)
+ if exported {
+ bits |= 1 << 0
+ }
+ if len(tag) > 0 {
+ l += 2 + len(tag)
+ bits |= 1 << 1
+ }
+ if pkgPath != "" {
+ bits |= 1 << 2
+ }
+
+ b := make([]byte, l)
+ b[0] = bits
+ b[1] = uint8(len(n) >> 8)
+ b[2] = uint8(len(n))
+ copy(b[3:], n)
+ if len(tag) > 0 {
+ tb := b[3+len(n):]
+ tb[0] = uint8(len(tag) >> 8)
+ tb[1] = uint8(len(tag))
+ copy(tb[2:], tag)
+ }
+
+ if pkgPath != "" {
+ panic("reflect: creating a name with a package path is not supported")
+ }
+
+ return name{bytes: &b[0]}
+}
+
/*
* The compiler knows the exact layout of all the data structures above.
* The compiler does not know about the data structures and methods below.
}
switch t.Kind() {
case Struct:
- type u struct {
- structType
- u uncommonType
- }
- return &(*u)(unsafe.Pointer(t)).u
+ return &(*structTypeWithMethods)(unsafe.Pointer(t)).u
case Ptr:
type u struct {
ptrType
return ret
}
-// The lookupCache caches ChanOf, MapOf, and SliceOf lookups.
+// The lookupCache caches ArrayOf, ChanOf, MapOf and SliceOf lookups.
var lookupCache struct {
sync.RWMutex
m map[cacheKey]*rtype
return cachePut(ckey, &slice.rtype)
}
+// structTypeWithMethods is a structType created at runtime with StructOf.
+// It is needed to pin the []method slice from its associated uncommonType struct.
+// Keep in sync with the memory layout of structType.
+type structTypeWithMethods struct {
+ structType
+ u uncommonType
+}
+
+// The structLookupCache caches StructOf lookups.
+// StructOf does not share the common lookupCache since we need to pin
+// the *structType and its associated *uncommonType (especially the
+// []method slice field of that uncommonType.)
+var structLookupCache struct {
+ sync.RWMutex
+ m map[uint32][]*structTypeWithMethods // keyed by hash calculated in StructOf
+}
+
+// StructOf returns the struct type containing fields.
+// The Offset and Index fields are ignored and computed as they would be
+// by the compiler.
+//
+// StructOf does not support creating structs with UTF-8 field names or
+// UTF-8 (embedded) type names.
+// This limitation may be lifted eventually.
+func StructOf(fields []StructField) Type {
+ var (
+ hash = fnv1(0, []byte("struct {")...)
+ size uintptr
+ typalign uint8
+ comparable = true
+ hashable = true
+ typ = new(structTypeWithMethods)
+
+ fs = make([]structField, len(fields))
+ repr = make([]byte, 0, 64)
+ fset = map[string]struct{}{} // fields' names
+
+ hasPtr = false // records whether at least one struct-field is a pointer
+ hasGCProg = false // records whether a struct-field type has a GCProg
+ )
+
+ repr = append(repr, "struct {"...)
+ for i, field := range fields {
+ if field.Type == nil {
+ panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type")
+ }
+ f := runtimeStructField(field)
+ ft := f.typ
+ if ft.kind&kindGCProg != 0 {
+ hasGCProg = true
+ }
+ if ft.pointers() {
+ hasPtr = true
+ }
+
+ name := ""
+ // Update string and hash
+ if f.name.nameLen() > 0 {
+ hash = fnv1(hash, []byte(f.name.name())...)
+ repr = append(repr, (" " + f.name.name())...)
+ name = f.name.name()
+ } else {
+ // Embedded field
+ if f.typ.Kind() == Ptr {
+ // Embedded ** and *interface{} are illegal
+ elem := ft.Elem()
+ if k := elem.Kind(); k == Ptr || k == Interface {
+ panic("reflect.StructOf: illegal anonymous field type " + ft.string)
+ }
+ name = elem.String()
+ } else {
+ name = ft.string
+ }
+ // TODO(sbinet) check for syntactically impossible type names?
+
+ switch f.typ.Kind() {
+ case Interface:
+ ift := (*interfaceType)(unsafe.Pointer(ft))
+ for im, m := range ift.methods {
+ if m.name.pkgPath() != nil {
+ // TODO(sbinet)
+ panic("reflect: embedded interface with unexported method(s) not implemented")
+ }
+
+ var (
+ ifield = i
+ imethod = im
+ ifn Value
+ tfn Value
+ )
+
+ if ft.kind&kindDirectIface != 0 {
+ tfn = MakeFunc(m.typ, func(in []Value) []Value {
+ var args []Value
+ var recv = in[0]
+ if len(in) > 1 {
+ args = in[1:]
+ }
+ return recv.Field(ifield).Method(imethod).Call(args)
+ })
+ ifn = MakeFunc(m.typ, func(in []Value) []Value {
+ var args []Value
+ var recv = in[0]
+ if len(in) > 1 {
+ args = in[1:]
+ }
+ return recv.Field(ifield).Method(imethod).Call(args)
+ })
+
+ } else {
+ tfn = MakeFunc(m.typ, func(in []Value) []Value {
+ var args []Value
+ var recv = in[0]
+ if len(in) > 1 {
+ args = in[1:]
+ }
+ return recv.Field(ifield).Method(imethod).Call(args)
+ })
+ ifn = MakeFunc(m.typ, func(in []Value) []Value {
+ var args []Value
+ var recv = Indirect(in[0])
+ if len(in) > 1 {
+ args = in[1:]
+ }
+ return recv.Field(ifield).Method(imethod).Call(args)
+ })
+
+ }
+
+ typ.u.methods = append(
+ typ.u.methods,
+ method{
+ name: m.name,
+ mtyp: m.typ,
+ ifn: unsafe.Pointer(&ifn),
+ tfn: unsafe.Pointer(&tfn),
+ },
+ )
+ }
+ case Ptr:
+ ptr := (*ptrType)(unsafe.Pointer(ft))
+ if unt := ptr.uncommon(); unt != nil {
+ for _, m := range unt.methods {
+ if m.name.pkgPath() != nil {
+ // TODO(sbinet)
+ panic("reflect: embedded interface with unexported method(s) not implemented")
+ }
+ typ.u.methods = append(typ.u.methods, m)
+ }
+ }
+ if unt := ptr.elem.uncommon(); unt != nil {
+ for _, m := range unt.methods {
+ if m.name.pkgPath() != nil {
+ // TODO(sbinet)
+ panic("reflect: embedded interface with unexported method(s) not implemented")
+ }
+ typ.u.methods = append(typ.u.methods, m)
+ }
+ }
+ default:
+ if unt := ft.uncommon(); unt != nil {
+ for _, m := range unt.methods {
+ if m.name.pkgPath() != nil {
+ // TODO(sbinet)
+ panic("reflect: embedded interface with unexported method(s) not implemented")
+ }
+ typ.u.methods = append(typ.u.methods, m)
+ }
+ }
+ }
+ }
+ if _, dup := fset[name]; dup {
+ panic("reflect.StructOf: duplicate field " + name)
+ }
+ fset[name] = struct{}{}
+
+ hash = fnv1(hash, byte(ft.hash>>24), byte(ft.hash>>16), byte(ft.hash>>8), byte(ft.hash))
+
+ repr = append(repr, (" " + ft.string)...)
+ if f.name.tagLen() > 0 {
+ hash = fnv1(hash, []byte(f.name.tag())...)
+ repr = append(repr, (" " + strconv.Quote(f.name.tag()))...)
+ }
+ if i < len(fields)-1 {
+ repr = append(repr, ';')
+ }
+
+ comparable = comparable && (ft.alg.equal != nil)
+ hashable = hashable && (ft.alg.hash != nil)
+
+ f.offset = align(size, uintptr(ft.align))
+ if ft.align > typalign {
+ typalign = ft.align
+ }
+ size = f.offset + ft.size
+
+ fs[i] = f
+ }
+ if len(fs) > 0 {
+ repr = append(repr, ' ')
+ }
+ repr = append(repr, '}')
+ hash = fnv1(hash, '}')
+ str := string(repr)
+
+ // Round the size up to be a multiple of the alignment.
+ size = align(size, uintptr(typalign))
+
+ // Make the struct type.
+ var istruct interface{} = struct{}{}
+ prototype := *(**structType)(unsafe.Pointer(&istruct))
+ typ.structType = *prototype
+ typ.structType.fields = fs
+
+ // Look in cache
+ structLookupCache.RLock()
+ for _, t := range structLookupCache.m[hash] {
+ if haveIdenticalUnderlyingType(&typ.rtype, &t.rtype) {
+ structLookupCache.RUnlock()
+ return &t.rtype
+ }
+ }
+ structLookupCache.RUnlock()
+
+ // not in cache, lock and retry
+ structLookupCache.Lock()
+ defer structLookupCache.Unlock()
+ if structLookupCache.m == nil {
+ structLookupCache.m = make(map[uint32][]*structTypeWithMethods)
+ }
+ for _, t := range structLookupCache.m[hash] {
+ if haveIdenticalUnderlyingType(&typ.rtype, &t.rtype) {
+ return &t.rtype
+ }
+ }
+
+ // Look in known types.
+ for _, t := range typesByString(str) {
+ if haveIdenticalUnderlyingType(&typ.rtype, t) {
+ // even if 't' wasn't a structType with methods, we should be ok
+ // as the 'u uncommonType' field won't be accessed except when
+ // tflag&tflagUncommon is set.
+ tt := (*structTypeWithMethods)(unsafe.Pointer(t))
+ structLookupCache.m[hash] = append(structLookupCache.m[hash], tt)
+ return &tt.rtype
+ }
+ }
+
+ typ.string = str
+ typ.hash = hash
+ typ.size = size
+ typ.align = typalign
+ typ.fieldAlign = typalign
+ if len(typ.u.methods) > 0 {
+ typ.tflag |= tflagUncommon
+ }
+ if !hasPtr {
+ typ.kind |= kindNoPointers
+ } else {
+ typ.kind &^= kindNoPointers
+ }
+
+ if hasGCProg {
+ lastPtrField := 0
+ for i, ft := range fs {
+ if ft.typ.pointers() {
+ lastPtrField = i
+ }
+ }
+ prog := []byte{0, 0, 0, 0} // will be length of prog
+ for i, ft := range fs {
+ if i > lastPtrField {
+ // gcprog should not include anything for any field after
+ // the last field that contains pointer data
+ break
+ }
+ // FIXME(sbinet) handle padding, fields smaller than a word
+ elemGC := (*[1 << 30]byte)(unsafe.Pointer(ft.typ.gcdata))[:]
+ elemPtrs := ft.typ.ptrdata / ptrSize
+ switch {
+ case ft.typ.kind&kindGCProg == 0 && ft.typ.ptrdata != 0:
+ // Element is small with pointer mask; use as literal bits.
+ mask := elemGC
+ // Emit 120-bit chunks of full bytes (max is 127 but we avoid using partial bytes).
+ var n uintptr
+ for n := elemPtrs; n > 120; n -= 120 {
+ prog = append(prog, 120)
+ prog = append(prog, mask[:15]...)
+ mask = mask[15:]
+ }
+ prog = append(prog, byte(n))
+ prog = append(prog, mask[:(n+7)/8]...)
+ case ft.typ.kind&kindGCProg != 0:
+ // Element has GC program; emit one element.
+ elemProg := elemGC[4 : 4+*(*uint32)(unsafe.Pointer(&elemGC[0]))-1]
+ prog = append(prog, elemProg...)
+ }
+ // Pad from ptrdata to size.
+ elemWords := ft.typ.size / ptrSize
+ if elemPtrs < elemWords {
+ // Emit literal 0 bit, then repeat as needed.
+ prog = append(prog, 0x01, 0x00)
+ if elemPtrs+1 < elemWords {
+ prog = append(prog, 0x81)
+ prog = appendVarint(prog, elemWords-elemPtrs-1)
+ }
+ }
+ }
+ *(*uint32)(unsafe.Pointer(&prog[0])) = uint32(len(prog) - 4)
+ typ.kind |= kindGCProg
+ typ.gcdata = &prog[0]
+ } else {
+ typ.kind &^= kindGCProg
+ bv := new(bitVector)
+ addTypeBits(bv, 0, typ.common())
+ if len(bv.data) > 0 {
+ typ.gcdata = &bv.data[0]
+ }
+ }
+ typ.ptrdata = typeptrdata(typ.common())
+ typ.alg = new(typeAlg)
+ if hashable {
+ typ.alg.hash = func(p unsafe.Pointer, seed uintptr) uintptr {
+ o := seed
+ for _, ft := range typ.fields {
+ pi := unsafe.Pointer(uintptr(p) + ft.offset)
+ o = ft.typ.alg.hash(pi, o)
+ }
+ return o
+ }
+ }
+
+ if comparable {
+ typ.alg.equal = func(p, q unsafe.Pointer) bool {
+ for _, ft := range typ.fields {
+ pi := unsafe.Pointer(uintptr(p) + ft.offset)
+ qi := unsafe.Pointer(uintptr(q) + ft.offset)
+ if !ft.typ.alg.equal(pi, qi) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+ switch {
+ case len(fs) == 1 && !ifaceIndir(fs[0].typ):
+ // structs of 1 direct iface type can be direct
+ typ.kind |= kindDirectIface
+ default:
+ typ.kind &^= kindDirectIface
+ }
+
+ structLookupCache.m[hash] = append(structLookupCache.m[hash], typ)
+ return &typ.rtype
+}
+
+func runtimeStructField(field StructField) structField {
+ exported := field.PkgPath == ""
+ if field.Name == "" {
+ t := field.Type
+ if t.Kind() == Ptr {
+ t = t.Elem()
+ }
+ exported = isExported(t.Name())
+ } else if exported {
+ b0 := field.Name[0]
+ if ('a' <= b0 && b0 <= 'z') || b0 == '_' {
+ panic("reflect.StructOf: field \"" + field.Name + "\" is unexported but has no PkgPath")
+ }
+ }
+
+ return structField{
+ name: newName(field.Name, string(field.Tag), field.PkgPath, exported),
+ typ: field.Type.common(),
+ offset: 0,
+ }
+}
+
+func isExported(s string) bool {
+ if s == "" {
+ return false
+ }
+ // FIXME(sbinet): handle utf8/runes (see https://golang.org/issue/15064)
+ // TODO: turn rtype.string into a reflect.name type, and put the exported
+ // bit on there which can be checked here with field.Type.(*rtype).string.isExported()
+ // When done, remove the documented limitation of StructOf.
+ r := s[0]
+ switch {
+ case 'A' <= r && r <= 'Z':
+ return true
+ case r == '_' || 'a' <= r && r <= 'z':
+ return false
+ default:
+ panic("reflect.StructOf: creating a struct with UTF-8 fields is not supported yet")
+ }
+}
+
+// typeptrdata returns the length in bytes of the prefix of t
+// containing pointer data. Anything after this offset is scalar data.
+// keep in sync with ../cmd/compile/internal/gc/reflect.go
+func typeptrdata(t *rtype) uintptr {
+ if !t.pointers() {
+ return 0
+ }
+ switch t.Kind() {
+ case Struct:
+ st := (*structType)(unsafe.Pointer(t))
+ // find the last field that has pointers.
+ field := 0
+ for i := range st.fields {
+ ft := st.fields[i].typ
+ if ft.pointers() {
+ field = i
+ }
+ }
+ f := st.fields[field]
+ return f.offset + f.typ.ptrdata
+
+ default:
+ panic("reflect.typeptrdata: unexpected type, " + t.String())
+ }
+ return 0
+}
+
// See cmd/compile/internal/gc/reflect.go for derivation of constant.
const maxPtrmaskBytes = 2048