diff options
Diffstat (limited to 'src/reflect/type.go')
| -rw-r--r-- | src/reflect/type.go | 572 |
1 files changed, 380 insertions, 192 deletions
diff --git a/src/reflect/type.go b/src/reflect/type.go index 8f13acf26e..2ceb3d3f66 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -242,6 +242,11 @@ const ( // tflag is used by an rtype to signal what extra type information is // available in the memory directly following the rtype value. +// +// tflag values must be kept in sync with copies in: +// cmd/compile/internal/gc/reflect.go +// cmd/link/internal/ld/decodesym.go +// runtime/type.go type tflag uint8 const ( @@ -256,7 +261,13 @@ const ( // u uncommonType // } // u := &(*tUncommon)(unsafe.Pointer(t)).u - tflagUncommon tflag = 1 + tflagUncommon tflag = 1 << 0 + + // tflagExtraStar means the name in the str field has an + // extraneous '*' prefix. This is because for most types T in + // a program, the type *T also exists and reusing the str data + // saves binary size. + tflagExtraStar tflag = 1 << 1 ) // rtype is the common implementation of most values. @@ -273,7 +284,8 @@ type rtype struct { kind uint8 // enumeration for C alg *typeAlg // algorithm table gcdata *byte // garbage collection data - string string // string form; unnecessary but undeniably useful + str nameOff // string form + _ int32 // unused; keeps rtype always a multiple of ptrSize } // a copy of runtime.typeAlg @@ -288,10 +300,10 @@ type typeAlg struct { // Method on non-interface type type method struct { - name name // name of method - mtyp *rtype // method type (without receiver) - ifn unsafe.Pointer // fn used in interface call (one-word receiver) - tfn unsafe.Pointer // fn used for normal method call + name nameOff // name of method + mtyp typeOff // method type (without receiver) + ifn textOff // fn used in interface call (one-word receiver) + tfn textOff // fn used for normal method call } // uncommonType is present only for types with names or methods @@ -299,8 +311,9 @@ type method struct { // Using a pointer to this struct reduces the overall size required // to describe an unnamed type with no methods. type uncommonType struct { - pkgPath *string // import path; nil for built-in types like int, string - methods []method // methods associated with type + pkgPath nameOff // import path; empty for built-in types like int, string + mcount uint16 // number of methods + moff uint16 // offset from this uncommontype to [mcount]method } // ChanDir represents a channel type's direction. @@ -346,14 +359,14 @@ type funcType struct { // imethod represents a method on an interface type type imethod struct { - name name // name of method - typ *rtype // .(*FuncType) underneath + name nameOff // name of method + typ typeOff // .(*FuncType) underneath } // interfaceType represents an interface type. type interfaceType struct { rtype `reflect:"interface"` - pkgPath *string // import path + pkgPath name // import path methods []imethod // sorted by hash } @@ -395,7 +408,7 @@ type structField struct { // structType represents a struct type. type structType struct { rtype `reflect:"struct"` - pkgPath *string + pkgPath name fields []structField // sorted by offset } @@ -405,7 +418,7 @@ type structType struct { // // 1<<0 the name is exported // 1<<1 tag data follows the name -// 1<<2 pkgPath *string follow the name and tag +// 1<<2 pkgPath nameOff follows the name and tag // // The next two bytes are the data length: // @@ -416,27 +429,29 @@ type structType struct { // If tag data follows then bytes 3+l and 3+l+1 are the tag length, // with the data following. // -// If the import path follows, then ptrSize bytes at the end of -// the data form a *string. The pointer is aligned to its width. -// The import path is only set for concrete methods that are defined -// in a different package than their type. +// If the import path follows, then 4 bytes at the end of +// the data form a nameOff. The import path is only set for concrete +// methods that are defined in a different package than their type. +// +// If a name starts with "*", then the exported bit represents +// whether the pointed to type is exported. type name struct { bytes *byte } -func (n *name) data(off int) *byte { +func (n name) data(off int) *byte { return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off))) } -func (n *name) isExported() bool { +func (n name) isExported() bool { return (*n.bytes)&(1<<0) != 0 } -func (n *name) nameLen() int { +func (n name) nameLen() int { return int(uint16(*n.data(1))<<8 | uint16(*n.data(2))) } -func (n *name) tagLen() int { +func (n name) tagLen() int { if *n.data(0)&(1<<1) == 0 { return 0 } @@ -444,7 +459,10 @@ func (n *name) tagLen() int { return int(uint16(*n.data(off))<<8 | uint16(*n.data(off + 1))) } -func (n *name) name() (s string) { +func (n name) name() (s string) { + if n.bytes == nil { + return "" + } nl := n.nameLen() if nl == 0 { return "" @@ -455,7 +473,7 @@ func (n *name) name() (s string) { return s } -func (n *name) tag() (s string) { +func (n name) tag() (s string) { tl := n.tagLen() if tl == 0 { return "" @@ -467,16 +485,18 @@ func (n *name) tag() (s string) { return s } -func (n *name) pkgPath() *string { - if *n.data(0)&(1<<2) == 0 { - return nil +func (n name) pkgPath() string { + if n.bytes == nil || *n.data(0)&(1<<2) == 0 { + return "" } off := 3 + n.nameLen() if tl := n.tagLen(); tl > 0 { off += 2 + tl } - off = int(round(uintptr(off), ptrSize)) - return *(**string)(unsafe.Pointer(n.data(off))) + var nameOff int32 + copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:]) + pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n.bytes), nameOff))} + return pkgPathName.name() } // round n up to a multiple of a. a must be a power of 2. @@ -589,11 +609,69 @@ var kindNames = []string{ UnsafePointer: "unsafe.Pointer", } -func (t *uncommonType) PkgPath() string { - if t == nil || t.pkgPath == nil { - return "" +func (t *uncommonType) methods() []method { + return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff)))[:t.mcount:t.mcount] +} + +// resolveNameOff resolves a name offset from a base pointer. +// The (*rtype).nameOff method is a convenience wrapper for this function. +// Implemented in the runtime package. +func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer + +// resolveTypeOff resolves an *rtype offset from a base type. +// The (*rtype).typeOff method is a convenience wrapper for this function. +// Implemented in the runtime package. +func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer + +// resolveTextOff resolves an function pointer offset from a base type. +// The (*rtype).textOff method is a convenience wrapper for this function. +// Implemented in the runtime package. +func resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer + +// addReflectOff adds a pointer to the reflection lookup map in the runtime. +// It returns a new ID that can be used as a typeOff or textOff, and will +// be resolved correctly. Implemented in the runtime package. +func addReflectOff(ptr unsafe.Pointer) int32 + +// resolveReflectType adds a name to the reflection lookup map in the runtime. +// It returns a new nameOff that can be used to refer to the pointer. +func resolveReflectName(n name) nameOff { + return nameOff(addReflectOff(unsafe.Pointer(n.bytes))) +} + +// resolveReflectType adds a *rtype to the reflection lookup map in the runtime. +// It returns a new typeOff that can be used to refer to the pointer. +func resolveReflectType(t *rtype) typeOff { + return typeOff(addReflectOff(unsafe.Pointer(t))) +} + +// resolveReflectText adds a function pointer to the reflection lookup map in +// the runtime. It returns a new textOff that can be used to refer to the +// pointer. +func resolveReflectText(ptr unsafe.Pointer) textOff { + return textOff(addReflectOff(ptr)) +} + +type nameOff int32 // offset to a name +type typeOff int32 // offset to an *rtype +type textOff int32 // offset from top of text section + +func (t *rtype) nameOff(off nameOff) name { + if off == 0 { + return name{} } - return *t.pkgPath + return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))} +} + +func (t *rtype) typeOff(off typeOff) *rtype { + if off == 0 { + return nil + } + return (*rtype)(resolveTypeOff(unsafe.Pointer(t), int32(off))) +} + +func (t *rtype) textOff(off textOff) unsafe.Pointer { + return resolveTextOff(unsafe.Pointer(t), int32(off)) } func (t *rtype) uncommon() *uncommonType { @@ -602,7 +680,7 @@ func (t *rtype) uncommon() *uncommonType { } switch t.Kind() { case Struct: - return &(*structTypeWithMethods)(unsafe.Pointer(t)).u + return &(*structTypeUncommon)(unsafe.Pointer(t)).u case Ptr: type u struct { ptrType @@ -654,7 +732,13 @@ func (t *rtype) uncommon() *uncommonType { } } -func (t *rtype) String() string { return t.string } +func (t *rtype) String() string { + s := t.nameOff(t.str).name() + if t.tflag&tflagExtraStar != 0 { + return s[1:] + } + return s +} func (t *rtype) Size() uintptr { return t.size } @@ -688,7 +772,7 @@ func (t *rtype) NumMethod() int { if ut == nil { return 0 } - return len(ut.methods) + return int(ut.mcount) } func (t *rtype) Method(i int) (m Method) { @@ -698,22 +782,23 @@ func (t *rtype) Method(i int) (m Method) { } ut := t.uncommon() - if ut == nil || i < 0 || i >= len(ut.methods) { + if ut == nil || i < 0 || i >= int(ut.mcount) { panic("reflect: Method index out of range") } - p := &ut.methods[i] - m.Name = p.name.name() + p := ut.methods()[i] + pname := t.nameOff(p.name) + m.Name = pname.name() fl := flag(Func) - if !p.name.isExported() { - pkgPath := p.name.pkgPath() - if pkgPath == nil { - pkgPath = ut.pkgPath + if !pname.isExported() { + m.PkgPath = pname.pkgPath() + if m.PkgPath == "" { + m.PkgPath = t.nameOff(ut.pkgPath).name() } - m.PkgPath = *pkgPath fl |= flagStickyRO } - if p.mtyp != nil { - ft := (*funcType)(unsafe.Pointer(p.mtyp)) + if p.mtyp != 0 { + mtyp := t.typeOff(p.mtyp) + ft := (*funcType)(unsafe.Pointer(mtyp)) in := make([]Type, 0, 1+len(ft.in())) in = append(in, t) for _, arg := range ft.in() { @@ -723,9 +808,10 @@ func (t *rtype) Method(i int) (m Method) { for _, ret := range ft.out() { out = append(out, ret) } - mt := FuncOf(in, out, p.mtyp.IsVariadic()) + mt := FuncOf(in, out, ft.IsVariadic()) m.Type = mt - fn := unsafe.Pointer(&p.tfn) + tfn := t.textOff(p.tfn) + fn := unsafe.Pointer(&tfn) m.Func = Value{mt.(*rtype), fn, fl} } m.Index = i @@ -741,9 +827,11 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) { if ut == nil { return Method{}, false } - for i := range ut.methods { - p := &ut.methods[i] - if p.name.name() == name { + utmethods := ut.methods() + for i := 0; i < int(ut.mcount); i++ { + p := utmethods[i] + pname := t.nameOff(p.name) + if pname.name() == name { return t.Method(i), true } } @@ -751,7 +839,11 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) { } func (t *rtype) PkgPath() string { - return t.uncommon().PkgPath() + ut := t.uncommon() + if ut == nil { + return "" + } + return t.nameOff(ut.pkgPath).name() } func hasPrefix(s, prefix string) bool { @@ -759,33 +851,37 @@ func hasPrefix(s, prefix string) bool { } func (t *rtype) Name() string { - if hasPrefix(t.string, "map[") { + s := t.String() + if hasPrefix(s, "map[") { + return "" + } + if hasPrefix(s, "struct {") { return "" } - if hasPrefix(t.string, "struct {") { + if hasPrefix(s, "chan ") { return "" } - if hasPrefix(t.string, "chan ") { + if hasPrefix(s, "chan<-") { return "" } - if hasPrefix(t.string, "chan<-") { + if hasPrefix(s, "func(") { return "" } - if hasPrefix(t.string, "func(") { + if hasPrefix(s, "interface {") { return "" } - switch t.string[0] { + switch s[0] { case '[', '*', '<': return "" } - i := len(t.string) - 1 + i := len(s) - 1 for i >= 0 { - if t.string[i] == '.' { + if s[i] == '.' { break } i-- } - return t.string[i+1:] + return s[i+1:] } func (t *rtype) ChanDir() ChanDir { @@ -914,7 +1010,7 @@ func (t *rtype) Out(i int) Type { } func (t *funcType) in() []*rtype { - uadd := uintptr(unsafe.Sizeof(*t)) + uadd := unsafe.Sizeof(*t) if t.tflag&tflagUncommon != 0 { uadd += unsafe.Sizeof(uncommonType{}) } @@ -922,7 +1018,7 @@ func (t *funcType) in() []*rtype { } func (t *funcType) out() []*rtype { - uadd := uintptr(unsafe.Sizeof(*t)) + uadd := unsafe.Sizeof(*t) if t.tflag&tflagUncommon != 0 { uadd += unsafe.Sizeof(uncommonType{}) } @@ -952,15 +1048,15 @@ func (t *interfaceType) Method(i int) (m Method) { return } p := &t.methods[i] - m.Name = p.name.name() - if !p.name.isExported() { - pkgPath := p.name.pkgPath() - if pkgPath == nil { - pkgPath = t.pkgPath + pname := t.nameOff(p.name) + m.Name = pname.name() + if !pname.isExported() { + m.PkgPath = pname.pkgPath() + if m.PkgPath == "" { + m.PkgPath = t.pkgPath.name() } - m.PkgPath = *pkgPath } - m.Type = toType(p.typ) + m.Type = toType(t.typeOff(p.typ)) m.Index = i return } @@ -976,7 +1072,7 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { var p *imethod for i := range t.methods { p = &t.methods[i] - if p.name.name() == name { + if t.nameOff(p.name).name() == name { return t.Method(i), true } } @@ -1096,9 +1192,9 @@ func (t *structType) Field(i int) (f StructField) { f.Name = t.Name() f.Anonymous = true } - if t.pkgPath != nil && !p.name.isExported() { + if !p.name.isExported() { // Fields never have an import path in their name. - f.PkgPath = *t.pkgPath + f.PkgPath = t.pkgPath.name() } if tag := p.name.tag(); tag != "" { f.Tag = StructTag(tag) @@ -1317,7 +1413,7 @@ func (t *rtype) ptrTo() *rtype { } // Look in known types. - s := "*" + t.string + s := "*" + t.String() for _, tt := range typesByString(s) { p = (*ptrType)(unsafe.Pointer(tt)) if p.elem == t { @@ -1334,7 +1430,7 @@ func (t *rtype) ptrTo() *rtype { prototype := *(**ptrType)(unsafe.Pointer(&iptr)) *p = *prototype - p.string = s + p.str = resolveReflectName(newName(s, "", "", false)) // For the type structures linked into the binary, the // compiler provides a good hash of the string. @@ -1416,7 +1512,7 @@ func implements(T, V *rtype) bool { for j := 0; j < len(v.methods); j++ { tm := &t.methods[i] vm := &v.methods[j] - if vm.name.name() == tm.name.name() && vm.typ == tm.typ { + if V.nameOff(vm.name).name() == t.nameOff(tm.name).name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) { if i++; i >= len(t.methods) { return true } @@ -1430,10 +1526,11 @@ func implements(T, V *rtype) bool { return false } i := 0 - for j := 0; j < len(v.methods); j++ { + vmethods := v.methods() + for j := 0; j < int(v.mcount); j++ { tm := &t.methods[i] - vm := &v.methods[j] - if vm.name.name() == tm.name.name() && vm.mtyp == tm.typ { + vm := vmethods[j] + if V.nameOff(vm.name).name() == t.nameOff(tm.name).name() && V.typeOff(vm.mtyp) == t.typeOff(tm.typ) { if i++; i >= len(t.methods) { return true } @@ -1558,30 +1655,48 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool { } // typelinks is implemented in package runtime. -// It returns a slice of all the 'typelink' information in the binary, -// which is to say a slice of known types, sorted by string. +// It returns a slice of the sections in each module, +// and a slice of *rtype offsets in each module. +// +// The types in each module are sorted by string. That is, the first +// two linked types of the first module are: +// +// d0 := sections[0] +// t1 := (*rtype)(add(d0, offset[0][0])) +// t2 := (*rtype)(add(d0, offset[0][1])) +// +// and +// +// t1.String() < t2.String() +// // Note that strings are not unique identifiers for types: // there can be more than one with a given string. // Only types we might want to look up are included: // pointers, channels, maps, slices, and arrays. -func typelinks() [][]*rtype +func typelinks() (sections []unsafe.Pointer, offset [][]int32) + +func rtypeOff(section unsafe.Pointer, off int32) *rtype { + return (*rtype)(add(section, uintptr(off))) +} // typesByString returns the subslice of typelinks() whose elements have // the given string representation. // It may be empty (no known types with that string) or may have // multiple elements (multiple types with that string). func typesByString(s string) []*rtype { - typs := typelinks() + sections, offset := typelinks() var ret []*rtype - for _, typ := range typs { + for offsI, offs := range offset { + section := sections[offsI] + // We are looking for the first index i where the string becomes >= s. - // This is a copy of sort.Search, with f(h) replaced by (*typ[h].string >= s). - i, j := 0, len(typ) + // This is a copy of sort.Search, with f(h) replaced by (*typ[h].String() >= s). + i, j := 0, len(offs) for i < j { h := i + (j-i)/2 // avoid overflow when computing h // i ≤ h < j - if !(typ[h].string >= s) { + if !(rtypeOff(section, offs[h]).String() >= s) { i = h + 1 // preserves f(i-1) == false } else { j = h // preserves f(j) == true @@ -1592,17 +1707,12 @@ func typesByString(s string) []*rtype { // Having found the first, linear scan forward to find the last. // We could do a second binary search, but the caller is going // to do a linear scan anyway. - j = i - for j < len(typ) && typ[j].string == s { - j++ - } - - if j > i { - if ret == nil { - ret = typ[i:j:j] - } else { - ret = append(ret, typ[i:j]...) + for j := i; j < len(offs); j++ { + typ := rtypeOff(section, offs[j]) + if typ.String() != s { + break } + ret = append(ret, typ) } } return ret @@ -1695,11 +1805,11 @@ func ChanOf(dir ChanDir, t Type) Type { lookupCache.Unlock() panic("reflect.ChanOf: invalid dir") case SendDir: - s = "chan<- " + typ.string + s = "chan<- " + typ.String() case RecvDir: - s = "<-chan " + typ.string + s = "<-chan " + typ.String() case BothDir: - s = "chan " + typ.string + s = "chan " + typ.String() } for _, tt := range typesByString(s) { ch := (*chanType)(unsafe.Pointer(tt)) @@ -1714,7 +1824,7 @@ func ChanOf(dir ChanDir, t Type) Type { ch := new(chanType) *ch = *prototype ch.dir = uintptr(dir) - ch.string = s + ch.str = resolveReflectName(newName(s, "", "", false)) ch.hash = fnv1(typ.hash, 'c', byte(dir)) ch.elem = typ @@ -1744,7 +1854,7 @@ func MapOf(key, elem Type) Type { } // Look in known types. - s := "map[" + ktyp.string + "]" + etyp.string + s := "map[" + ktyp.String() + "]" + etyp.String() for _, tt := range typesByString(s) { mt := (*mapType)(unsafe.Pointer(tt)) if mt.key == ktyp && mt.elem == etyp { @@ -1756,7 +1866,7 @@ func MapOf(key, elem Type) Type { var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil) mt := new(mapType) *mt = **(**mapType)(unsafe.Pointer(&imap)) - mt.string = s + mt.str = resolveReflectName(newName(s, "", "", false)) mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash)) mt.key = ktyp mt.elem = etyp @@ -1914,7 +2024,7 @@ func FuncOf(in, out []Type, variadic bool) Type { } // Populate the remaining fields of ft and store in cache. - ft.string = str + ft.str = resolveReflectName(newName(str, "", "", false)) funcLookupCache.m[hash] = append(funcLookupCache.m[hash], &ft.rtype) return &ft.rtype @@ -1930,9 +2040,9 @@ func funcStr(ft *funcType) string { } if ft.IsVariadic() && i == int(ft.inCount)-1 { repr = append(repr, "..."...) - repr = append(repr, (*sliceType)(unsafe.Pointer(t)).elem.string...) + repr = append(repr, (*sliceType)(unsafe.Pointer(t)).elem.String()...) } else { - repr = append(repr, t.string...) + repr = append(repr, t.String()...) } } repr = append(repr, ')') @@ -1946,7 +2056,7 @@ func funcStr(ft *funcType) string { if i > 0 { repr = append(repr, ", "...) } - repr = append(repr, t.string...) + repr = append(repr, t.String()...) } if len(out) > 1 { repr = append(repr, ')') @@ -2111,8 +2221,8 @@ func bucketOf(ktyp, etyp *rtype) *rtype { b.ptrdata = ptrdata b.kind = kind b.gcdata = gcdata - s := "bucket(" + ktyp.string + "," + etyp.string + ")" - b.string = s + s := "bucket(" + ktyp.String() + "," + etyp.String() + ")" + b.str = resolveReflectName(newName(s, "", "", false)) return b } @@ -2128,7 +2238,7 @@ func SliceOf(t Type) Type { } // Look in known types. - s := "[]" + typ.string + s := "[]" + typ.String() for _, tt := range typesByString(s) { slice := (*sliceType)(unsafe.Pointer(tt)) if slice.elem == typ { @@ -2141,28 +2251,63 @@ func SliceOf(t Type) Type { prototype := *(**sliceType)(unsafe.Pointer(&islice)) slice := new(sliceType) *slice = *prototype - slice.string = s + slice.tflag = 0 + slice.str = resolveReflectName(newName(s, "", "", false)) slice.hash = fnv1(typ.hash, '[') slice.elem = typ 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.) +// the memory associated with *structTypeFixedN. var structLookupCache struct { sync.RWMutex - m map[uint32][]*structTypeWithMethods // keyed by hash calculated in StructOf + m map[uint32][]interface { + common() *rtype + } // keyed by hash calculated in StructOf +} + +type structTypeUncommon struct { + structType + 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 } // StructOf returns the struct type containing fields. @@ -2179,7 +2324,7 @@ func StructOf(fields []StructField) Type { typalign uint8 comparable = true hashable = true - typ = new(structTypeWithMethods) + methods []method fs = make([]structField, len(fields)) repr = make([]byte, 0, 64) @@ -2215,11 +2360,11 @@ func StructOf(fields []StructField) Type { // 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) + panic("reflect.StructOf: illegal anonymous field type " + ft.String()) } name = elem.String() } else { - name = ft.string + name = ft.String() } // TODO(sbinet) check for syntactically impossible type names? @@ -2227,12 +2372,13 @@ func StructOf(fields []StructField) Type { case Interface: ift := (*interfaceType)(unsafe.Pointer(ft)) for im, m := range ift.methods { - if m.name.pkgPath() != nil { + if ift.nameOff(m.name).pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } var ( + mtyp = ift.typeOff(m.typ) ifield = i imethod = im ifn Value @@ -2240,7 +2386,7 @@ func StructOf(fields []StructField) Type { ) if ft.kind&kindDirectIface != 0 { - tfn = MakeFunc(m.typ, func(in []Value) []Value { + tfn = MakeFunc(mtyp, func(in []Value) []Value { var args []Value var recv = in[0] if len(in) > 1 { @@ -2248,7 +2394,7 @@ func StructOf(fields []StructField) Type { } return recv.Field(ifield).Method(imethod).Call(args) }) - ifn = MakeFunc(m.typ, func(in []Value) []Value { + ifn = MakeFunc(mtyp, func(in []Value) []Value { var args []Value var recv = in[0] if len(in) > 1 { @@ -2256,9 +2402,8 @@ func StructOf(fields []StructField) Type { } return recv.Field(ifield).Method(imethod).Call(args) }) - } else { - tfn = MakeFunc(m.typ, func(in []Value) []Value { + tfn = MakeFunc(mtyp, func(in []Value) []Value { var args []Value var recv = in[0] if len(in) > 1 { @@ -2266,7 +2411,7 @@ func StructOf(fields []StructField) Type { } return recv.Field(ifield).Method(imethod).Call(args) }) - ifn = MakeFunc(m.typ, func(in []Value) []Value { + ifn = MakeFunc(mtyp, func(in []Value) []Value { var args []Value var recv = Indirect(in[0]) if len(in) > 1 { @@ -2274,47 +2419,62 @@ func StructOf(fields []StructField) Type { } 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), - }, - ) + methods = append(methods, method{ + name: resolveReflectName(ift.nameOff(m.name)), + mtyp: resolveReflectType(mtyp), + ifn: resolveReflectText(unsafe.Pointer(&ifn)), + tfn: resolveReflectText(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 { + for _, m := range unt.methods() { + mname := ptr.nameOff(m.name) + if mname.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } - typ.u.methods = append(typ.u.methods, m) + methods = append(methods, method{ + name: resolveReflectName(mname), + mtyp: resolveReflectType(ptr.typeOff(m.mtyp)), + ifn: resolveReflectText(ptr.textOff(m.ifn)), + tfn: resolveReflectText(ptr.textOff(m.tfn)), + }) } } if unt := ptr.elem.uncommon(); unt != nil { - for _, m := range unt.methods { - if m.name.pkgPath() != nil { + for _, m := range unt.methods() { + mname := ptr.nameOff(m.name) + if mname.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } - typ.u.methods = append(typ.u.methods, m) + methods = append(methods, method{ + name: resolveReflectName(mname), + mtyp: resolveReflectType(ptr.elem.typeOff(m.mtyp)), + ifn: resolveReflectText(ptr.elem.textOff(m.ifn)), + tfn: resolveReflectText(ptr.elem.textOff(m.tfn)), + }) } } default: if unt := ft.uncommon(); unt != nil { - for _, m := range unt.methods { - if m.name.pkgPath() != nil { + for _, m := range unt.methods() { + mname := ft.nameOff(m.name) + if mname.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } - typ.u.methods = append(typ.u.methods, m) + methods = append(methods, method{ + name: resolveReflectName(mname), + mtyp: resolveReflectType(ft.typeOff(m.mtyp)), + ifn: resolveReflectText(ft.textOff(m.ifn)), + tfn: resolveReflectText(ft.textOff(m.tfn)), + }) + } } } @@ -2326,7 +2486,7 @@ func StructOf(fields []StructField) Type { hash = fnv1(hash, byte(ft.hash>>24), byte(ft.hash>>16), byte(ft.hash>>8), byte(ft.hash)) - repr = append(repr, (" " + ft.string)...) + 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()))...) @@ -2346,6 +2506,49 @@ func StructOf(fields []StructField) Type { fs[i] = f } + + var typ *structType + var ut *uncommonType + var typPin interface { + common() *rtype + } // structTypeFixedN + + switch { + case len(methods) == 0: + t := new(structTypeUncommon) + typ = &t.structType + ut = &t.u + typPin = t + case len(methods) <= 4: + t := new(structTypeFixed4) + typ = &t.structType + ut = &t.u + copy(t.m[:], methods) + typPin = t + case len(methods) <= 8: + t := new(structTypeFixed8) + typ = &t.structType + ut = &t.u + copy(t.m[:], methods) + typPin = t + case len(methods) <= 16: + t := new(structTypeFixed16) + typ = &t.structType + ut = &t.u + copy(t.m[:], methods) + typPin = t + case len(methods) <= 32: + t := new(structTypeFixed32) + typ = &t.structType + ut = &t.u + copy(t.m[:], methods) + typPin = t + default: + panic("reflect.StructOf: too many methods") + } + ut.mcount = uint16(len(methods)) + ut.moff = uint16(unsafe.Sizeof(uncommonType{})) + if len(fs) > 0 { repr = append(repr, ' ') } @@ -2359,15 +2562,16 @@ func StructOf(fields []StructField) Type { // Make the struct type. var istruct interface{} = struct{}{} prototype := *(**structType)(unsafe.Pointer(&istruct)) - typ.structType = *prototype - typ.structType.fields = fs + *typ = *prototype + typ.fields = fs // Look in cache structLookupCache.RLock() - for _, t := range structLookupCache.m[hash] { - if haveIdenticalUnderlyingType(&typ.rtype, &t.rtype) { + for _, st := range structLookupCache.m[hash] { + t := st.common() + if haveIdenticalUnderlyingType(&typ.rtype, t) { structLookupCache.RUnlock() - return &t.rtype + return t } } structLookupCache.RUnlock() @@ -2376,11 +2580,14 @@ func StructOf(fields []StructField) Type { structLookupCache.Lock() defer structLookupCache.Unlock() if structLookupCache.m == nil { - structLookupCache.m = make(map[uint32][]*structTypeWithMethods) + structLookupCache.m = make(map[uint32][]interface { + common() *rtype + }) } - for _, t := range structLookupCache.m[hash] { - if haveIdenticalUnderlyingType(&typ.rtype, &t.rtype) { - return &t.rtype + for _, st := range structLookupCache.m[hash] { + t := st.common() + if haveIdenticalUnderlyingType(&typ.rtype, t) { + return t } } @@ -2390,18 +2597,17 @@ func StructOf(fields []StructField) Type { // 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 + structLookupCache.m[hash] = append(structLookupCache.m[hash], t) + return t } } - typ.string = str + typ.str = resolveReflectName(newName(str, "", "", false)) typ.hash = hash typ.size = size typ.align = typalign typ.fieldAlign = typalign - if len(typ.u.methods) > 0 { + if len(methods) > 0 { typ.tflag |= tflagUncommon } if !hasPtr { @@ -2501,18 +2707,18 @@ func StructOf(fields []StructField) Type { typ.kind &^= kindDirectIface } - structLookupCache.m[hash] = append(structLookupCache.m[hash], typ) + structLookupCache.m[hash] = append(structLookupCache.m[hash], typPin) return &typ.rtype } func runtimeStructField(field StructField) structField { exported := field.PkgPath == "" if field.Name == "" { - t := field.Type + t := field.Type.(*rtype) if t.Kind() == Ptr { - t = t.Elem() + t = t.Elem().(*rtype) } - exported = isExported(t.Name()) + exported = t.nameOff(t.str).isExported() } else if exported { b0 := field.Name[0] if ('a' <= b0 && b0 <= 'z') || b0 == '_' { @@ -2520,6 +2726,7 @@ func runtimeStructField(field StructField) structField { } } + _ = resolveReflectType(field.Type.common()) return structField{ name: newName(field.Name, string(field.Tag), field.PkgPath, exported), typ: field.Type.common(), @@ -2527,25 +2734,6 @@ func runtimeStructField(field StructField) structField { } } -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 @@ -2595,7 +2783,7 @@ func ArrayOf(count int, elem Type) Type { } // Look in known types. - s := "[" + strconv.Itoa(count) + "]" + typ.string + s := "[" + strconv.Itoa(count) + "]" + typ.String() for _, tt := range typesByString(s) { array := (*arrayType)(unsafe.Pointer(tt)) if array.elem == typ { @@ -2608,7 +2796,7 @@ func ArrayOf(count int, elem Type) Type { prototype := *(**arrayType)(unsafe.Pointer(&iarray)) array := new(arrayType) *array = *prototype - array.string = s + array.str = resolveReflectName(newName(s, "", "", false)) array.hash = fnv1(typ.hash, '[') for n := uint32(count); n > 0; n >>= 8 { array.hash = fnv1(array.hash, byte(n)) @@ -2862,11 +3050,11 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin var s string if rcvr != nil { - s = "methodargs(" + rcvr.string + ")(" + t.string + ")" + s = "methodargs(" + rcvr.String() + ")(" + t.String() + ")" } else { - s = "funcargs(" + t.string + ")" + s = "funcargs(" + t.String() + ")" } - x.string = s + x.str = resolveReflectName(newName(s, "", "", false)) // cache result for future callers if layoutCache.m == nil { |
