Create a byte encoding designed for static Go names.
It is intended to be a compact representation of a name
and optional tag data that can be turned into a Go string
without allocating, and describes whether or not it is
exported without unicode table.
The encoding is described in reflect/type.go:
// The first byte is a bit field containing:
//
// 1<<0 the name is exported
// 1<<1 tag data follows the name
// 1<<2 pkgPath *string follow the name and tag
//
// The next two bytes are the data length:
//
// l := uint16(data[1])<<8 | uint16(data[2])
//
// Bytes [3:3+l] are the string data.
//
// 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 import path is only set for concrete
// methods that are defined in a different package than their type.
Shrinks binary sizes:
cmd/go: 164KB (1.6%)
jujud: 1.0MB (1.5%)
For #6853.
Change-Id: I46b6591015b17936a443c9efb5009de8dfe8b609
Reviewed-on: https://go-review.googlesource.com/20968
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
MAXVALSIZE = 128
)
-func structfieldSize() int { return 5 * Widthptr } // Sizeof(runtime.structfield{})
-func imethodSize() int { return 3 * Widthptr } // Sizeof(runtime.imethod{})
+func structfieldSize() int { return 3 * Widthptr } // Sizeof(runtime.structfield{})
+func imethodSize() int { return 2 * Widthptr } // Sizeof(runtime.imethod{})
func uncommonSize(t *Type) int { // Sizeof(runtime.uncommontype{})
if t.Sym == nil && len(methods(t)) == 0 {
return 0
}
func dgopkgpath(s *Sym, ot int, pkg *Pkg) int {
+ return dgopkgpathLSym(Linksym(s), ot, pkg)
+}
+
+func dgopkgpathLSym(s *obj.LSym, ot int, pkg *Pkg) int {
if pkg == nil {
- return dgostringptr(s, ot, "")
+ return duintxxLSym(s, ot, 0, Widthptr)
}
if pkg == localpkg && myimportpath == "" {
// go.importpath.""., which the linker will rewrite using the correct import path.
// Every package that imports this one directly defines the symbol.
// See also https://groups.google.com/forum/#!topic/golang-dev/myb9s53HxGQ.
- ns := Pkglookup("importpath.\"\".", mkpkg("go"))
- return dsymptr(s, ot, ns, 0)
+ ns := obj.Linklookup(Ctxt, `go.importpath."".`, 0)
+ return dsymptrLSym(s, ot, ns, 0)
}
dimportpath(pkg)
- return dsymptr(s, ot, pkg.Pathsym, 0)
+ return dsymptrLSym(s, ot, Linksym(pkg.Pathsym), 0)
+}
+
+// isExportedField reports whether a struct field is exported.
+func isExportedField(ft *Field) bool {
+ if ft.Sym != nil && ft.Embedded == 0 {
+ return exportname(ft.Sym.Name)
+ } else {
+ if ft.Type.Sym != nil &&
+ (ft.Type.Sym.Pkg == builtinpkg || !exportname(ft.Type.Sym.Name)) {
+ return false
+ } else {
+ return true
+ }
+ }
+}
+
+// dnameField dumps a reflect.name for a struct field.
+func dnameField(s *Sym, ot int, ft *Field) int {
+ var name, tag string
+ if ft.Sym != nil && ft.Embedded == 0 {
+ name = ft.Sym.Name
+ }
+ if ft.Note != nil {
+ tag = *ft.Note
+ }
+ return dname(s, ot, name, tag, nil, isExportedField(ft))
+}
+
+var dnameCount int
+
+// dname dumps a reflect.name for a struct field or method.
+func dname(s *Sym, ot int, name, tag string, pkg *Pkg, exported bool) int {
+ if len(name) > 1<<16-1 {
+ Fatalf("name too long: %s", name)
+ }
+ if len(tag) > 1<<16-1 {
+ Fatalf("tag too long: %s", tag)
+ }
+
+ // Encode name and tag. See reflect/type.go for details.
+ var bits byte
+ l := 1 + 2 + len(name)
+ if exported {
+ bits |= 1 << 0
+ }
+ if len(tag) > 0 {
+ l += 2 + len(tag)
+ bits |= 1 << 1
+ }
+ if pkg != nil {
+ bits |= 1 << 2
+ }
+ b := make([]byte, l)
+ b[0] = bits
+ b[1] = uint8(len(name) >> 8)
+ b[2] = uint8(len(name))
+ copy(b[3:], name)
+ if len(tag) > 0 {
+ tb := b[3+len(name):]
+ tb[0] = uint8(len(tag) >> 8)
+ tb[1] = uint8(len(tag))
+ copy(tb[2:], tag)
+ }
+
+ // Very few names require a pkgPath *string (only those
+ // defined in a different package than their type). So if
+ // there is no pkgPath, we treat the name contents as string
+ // data that duplicates across packages.
+ var bsym *obj.LSym
+ if pkg == nil {
+ _, bsym = stringsym(string(b))
+ } else {
+ bsymname := fmt.Sprintf(`go.string."".methodname.%d`, dnameCount)
+ dnameCount++
+ bsym = obj.Linklookup(Ctxt, bsymname, 0)
+ bsym.P = b
+ boff := len(b)
+ boff = int(Rnd(int64(boff), int64(Widthptr)))
+ boff = dgopkgpathLSym(bsym, boff, pkg)
+ ggloblLSym(bsym, int32(boff), obj.RODATA|obj.LOCAL)
+ }
+
+ ot = dsymptrLSym(Linksym(s), ot, bsym, 0)
+
+ return ot
}
// dextratype dumps the fields of a runtime.uncommontype.
// dataAdd is the offset in bytes after the header where the
// backing array of the []method field is written (by dextratypeData).
-func dextratype(sym *Sym, off int, t *Type, dataAdd int) int {
+func dextratype(s *Sym, ot int, t *Type, dataAdd int) int {
m := methods(t)
if t.Sym == nil && len(m) == 0 {
- return off
+ return ot
}
- noff := int(Rnd(int64(off), int64(Widthptr)))
- if noff != off {
- panic("dextratype rounding does something. :-(")
+ noff := int(Rnd(int64(ot), int64(Widthptr)))
+ if noff != ot {
+ Fatalf("unexpected alignment in dextratype for %s", t)
}
- off = noff
for _, a := range m {
dtypesym(a.type_)
}
- ot := off
- s := sym
- if t.Sym != nil && t != Types[t.Etype] && t != errortype {
- ot = dgopkgpath(s, ot, t.Sym.Pkg)
- } else {
- ot = dgostringptr(s, ot, "")
- }
+ ot = dgopkgpath(s, ot, typePkg(t))
// slice header
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint+dataAdd)
return ot
}
+func typePkg(t *Type) *Pkg {
+ tsym := t.Sym
+ if tsym == nil && t.Type != nil {
+ tsym = t.Type.Sym
+ }
+ if tsym != nil && t != Types[t.Etype] && t != errortype {
+ return tsym.Pkg
+ }
+ return nil
+}
+
// dextratypeData dumps the backing array for the []method field of
// runtime.uncommontype.
func dextratypeData(s *Sym, ot int, t *Type) int {
for _, a := range methods(t) {
- // method
// ../../../../runtime/type.go:/method
- ot = dgostringptr(s, ot, a.name)
-
- ot = dgopkgpath(s, ot, a.pkg)
+ exported := exportname(a.name)
+ var pkg *Pkg
+ if !exported && a.pkg != typePkg(t) {
+ pkg = a.pkg
+ }
+ ot = dname(s, ot, a.name, "", pkg, exported)
ot = dmethodptr(s, ot, dtypesym(a.mtype))
ot = dmethodptr(s, ot, a.isym)
ot = dmethodptr(s, ot, a.tsym)
// ../../../../runtime/type.go:/interfaceType
ot = dcommontype(s, ot, t)
+ var tpkg *Pkg
+ if t.Sym != nil && t != Types[t.Etype] && t != errortype {
+ tpkg = t.Sym.Pkg
+ }
+ ot = dgopkgpath(s, ot, tpkg)
+
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint+uncommonSize(t))
ot = duintxx(s, ot, uint64(n), Widthint)
ot = duintxx(s, ot, uint64(n), Widthint)
for _, a := range m {
// ../../../../runtime/type.go:/imethod
- ot = dgostringptr(s, ot, a.name)
- ot = dgopkgpath(s, ot, a.pkg)
+ exported := exportname(a.name)
+ var pkg *Pkg
+ if !exported && a.pkg != tpkg {
+ pkg = a.pkg
+ }
+ ot = dname(s, ot, a.name, "", pkg, exported)
ot = dsymptr(s, ot, dtypesym(a.type_), 0)
}
}
ot = dcommontype(s, ot, t)
+ var pkg *Pkg
+ if t.Sym != nil {
+ pkg = t.Sym.Pkg
+ }
+ ot = dgopkgpath(s, ot, pkg)
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint+uncommonSize(t))
ot = duintxx(s, ot, uint64(n), Widthint)
ot = duintxx(s, ot, uint64(n), Widthint)
dataAdd := n * structfieldSize()
ot = dextratype(s, ot, t, dataAdd)
- for _, t1 := range t.Fields().Slice() {
+ for _, f := range t.Fields().Slice() {
// ../../../../runtime/type.go:/structField
- if t1.Sym != nil && t1.Embedded == 0 {
- ot = dgostringptr(s, ot, t1.Sym.Name)
- if exportname(t1.Sym.Name) {
- ot = dgostringptr(s, ot, "")
- } else {
- ot = dgopkgpath(s, ot, t1.Sym.Pkg)
- }
- } else {
- ot = dgostringptr(s, ot, "")
- if t1.Type.Sym != nil &&
- (t1.Type.Sym.Pkg == builtinpkg || !exportname(t1.Type.Sym.Name)) {
- ot = dgopkgpath(s, ot, localpkg)
- } else {
- ot = dgostringptr(s, ot, "")
- }
- }
-
- ot = dsymptr(s, ot, dtypesym(t1.Type), 0)
- ot = dgostrlitptr(s, ot, t1.Note)
- ot = duintptr(s, ot, uint64(t1.Width)) // field offset
+ ot = dnameField(s, ot, f)
+ ot = dsymptr(s, ot, dtypesym(f.Type), 0)
+ ot = duintptr(s, ot, uint64(f.Width)) // field offset
}
}
}
func commonsize() int { return 6*Thearch.Ptrsize + 8 } // runtime._type
-func structfieldSize() int { return 5 * Thearch.Ptrsize } // runtime.structfield
+func structfieldSize() int { return 3 * Thearch.Ptrsize } // runtime.structfield
func uncommonSize() int { return 2*Thearch.Ptrsize + 2*Thearch.Intsize } // runtime.uncommontype
// Type.commonType.kind
// Type.StructType.fields.Slice::length
func decodetype_structfieldcount(s *LSym) int {
- return int(decode_inuxi(s.P[commonsize()+Thearch.Ptrsize:], Thearch.Intsize))
+ return int(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Intsize))
}
func decodetype_structfieldarrayoff(s *LSym, i int) int {
- off := commonsize() + Thearch.Ptrsize + 2*Thearch.Intsize
+ off := commonsize() + 2*Thearch.Ptrsize + 2*Thearch.Intsize
if decodetype_hasUncommon(s) {
off += uncommonSize()
}
return string(r.Sym.P[r.Add : r.Add+strlen])
}
+// decodetype_name decodes the name from a reflect.name.
+func decodetype_name(s *LSym, off int) string {
+ r := decode_reloc(s, int32(off))
+ if r == nil {
+ return ""
+ }
+
+ data := r.Sym.P
+ namelen := int(uint16(data[1]<<8) | uint16(data[2]))
+ return string(data[3 : 3+namelen])
+
+}
+
func decodetype_structfieldname(s *LSym, i int) string {
off := decodetype_structfieldarrayoff(s, i)
- return decodetype_stringptr(s, off)
+ return decodetype_name(s, off)
}
func decodetype_structfieldtype(s *LSym, i int) *LSym {
off := decodetype_structfieldarrayoff(s, i)
- return decode_reloc_sym(s, int32(off+2*Thearch.Ptrsize))
+ return decode_reloc_sym(s, int32(off+Thearch.Ptrsize))
}
func decodetype_structfieldoffs(s *LSym, i int) int64 {
off := decodetype_structfieldarrayoff(s, i)
- return int64(decode_inuxi(s.P[off+4*Thearch.Ptrsize:], Thearch.Intsize))
+ return int64(decode_inuxi(s.P[off+2*Thearch.Ptrsize:], Thearch.Intsize))
}
// InterfaceType.methods.length
func decodetype_ifacemethodcount(s *LSym) int64 {
- return int64(decode_inuxi(s.P[commonsize()+Thearch.Ptrsize:], Thearch.Intsize))
+ return int64(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Intsize))
}
// methodsig is a fully qualified typed method signature, like
)
// decode_methodsig decodes an array of method signature information.
-// Each element of the array is size bytes. The first word is a *string
-// for the name, the third word is a *rtype for the funcType.
+// Each element of the array is size bytes. The first word is a
+// reflect.name for the name, the second word is a *rtype for the funcType.
//
// Conveniently this is the layout of both runtime.method and runtime.imethod.
func decode_methodsig(s *LSym, off, size, count int) []methodsig {
var buf bytes.Buffer
var methods []methodsig
for i := 0; i < count; i++ {
- buf.WriteString(decodetype_stringptr(s, off))
- mtypSym := decode_reloc_sym(s, int32(off+2*Thearch.Ptrsize))
+ buf.WriteString(decodetype_name(s, off))
+ mtypSym := decode_reloc_sym(s, int32(off+Thearch.Ptrsize))
buf.WriteRune('(')
inCount := decodetype_funcincount(mtypSym)
if decodetype_kind(s)&kindMask != kindInterface {
panic(fmt.Sprintf("symbol %q is not an interface", s.Name))
}
- r := decode_reloc(s, int32(commonsize()))
+ r := decode_reloc(s, int32(commonsize()+Thearch.Ptrsize))
if r == nil {
return nil
}
}
off := int(r.Add) // array of reflect.imethod values
numMethods := int(decodetype_ifacemethodcount(s))
- sizeofIMethod := 3 * Thearch.Ptrsize
+ sizeofIMethod := 2 * Thearch.Ptrsize
return decode_methodsig(s, off, sizeofIMethod, numMethods)
}
off := commonsize() // reflect.rtype
switch decodetype_kind(s) & kindMask {
case kindStruct: // reflect.structType
- off += Thearch.Ptrsize + 2*Thearch.Intsize
+ off += 2*Thearch.Ptrsize + 2*Thearch.Intsize
case kindPtr: // reflect.ptrType
off += Thearch.Ptrsize
case kindFunc: // reflect.funcType
panic(fmt.Sprintf("method slice pointer in %s leads to a different symbol %s", s, r.Sym))
}
off = int(r.Add) // array of reflect.method values
- sizeofMethod := 5 * Thearch.Ptrsize // sizeof reflect.method in program
+ sizeofMethod := 4 * Thearch.Ptrsize // sizeof reflect.method in program
return decode_methodsig(s, off, sizeofMethod, numMethods)
}
// Method on non-interface type
type method struct {
- name *string // name of method
- pkgPath *string // nil for exported Names; otherwise import path
- 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 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
}
// uncommonType is present only for types with names or methods
// imethod represents a method on an interface type
type imethod struct {
- name *string // name of method
- pkgPath *string // nil for exported Names; otherwise import path
- typ *rtype // .(*FuncType) underneath
+ name name // name of method
+ typ *rtype // .(*FuncType) underneath
}
// interfaceType represents an interface type.
type interfaceType struct {
rtype `reflect:"interface"`
+ pkgPath *string // import path
methods []imethod // sorted by hash
}
// Struct field
type structField struct {
- name *string // nil for embedded fields
- pkgPath *string // nil for exported Names; otherwise import path
- typ *rtype // type of field
- tag *string // nil if no tag
- offset uintptr // byte offset of field within struct
+ name name // name is empty for embedded fields
+ typ *rtype // type of field
+ offset uintptr // byte offset of field within struct
}
// structType represents a struct type.
type structType struct {
- rtype `reflect:"struct"`
- fields []structField // sorted by offset
+ rtype `reflect:"struct"`
+ pkgPath *string
+ fields []structField // sorted by offset
+}
+
+// name is an encoded type name with optional extra data.
+//
+// The first byte is a bit field containing:
+//
+// 1<<0 the name is exported
+// 1<<1 tag data follows the name
+// 1<<2 pkgPath *string follow the name and tag
+//
+// The next two bytes are the data length:
+//
+// l := uint16(data[1])<<8 | uint16(data[2])
+//
+// Bytes [3:3+l] are the string data.
+//
+// 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.
+type name struct {
+ bytes *byte
+}
+
+func (n *name) data(off int) *byte {
+ return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off)))
+}
+
+func (n *name) isExported() bool {
+ return (*n.bytes)&(1<<0) != 0
+}
+
+func (n *name) nameLen() int {
+ return int(uint16(*n.data(1))<<8 | uint16(*n.data(2)))
+}
+
+func (n *name) tagLen() int {
+ if *n.data(0)&(1<<1) == 0 {
+ return 0
+ }
+ off := 3 + n.nameLen()
+ return int(uint16(*n.data(off))<<8 | uint16(*n.data(off + 1)))
+}
+
+func (n *name) name() (s string) {
+ nl := n.nameLen()
+ if nl == 0 {
+ return ""
+ }
+ hdr := (*stringHeader)(unsafe.Pointer(&s))
+ hdr.Data = unsafe.Pointer(n.data(3))
+ hdr.Len = nl
+ return s
+}
+
+func (n *name) tag() (s string) {
+ tl := n.tagLen()
+ if tl == 0 {
+ return ""
+ }
+ nl := n.nameLen()
+ hdr := (*stringHeader)(unsafe.Pointer(&s))
+ hdr.Data = unsafe.Pointer(n.data(3 + nl + 2))
+ hdr.Len = tl
+ return s
+}
+
+func (n *name) pkgPath() *string {
+ if *n.data(0)&(1<<2) == 0 {
+ return nil
+ }
+ 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)))
+}
+
+// round n up to a multiple of a. a must be a power of 2.
+func round(n, a uintptr) uintptr {
+ return (n + a - 1) &^ (a - 1)
}
/*
panic("reflect: Method index out of range")
}
p := &ut.methods[i]
- if p.name != nil {
- m.Name = *p.name
- }
+ m.Name = p.name.name()
fl := flag(Func)
- if p.pkgPath != nil {
- m.PkgPath = *p.pkgPath
+ if !p.name.isExported() {
+ pkgPath := p.name.pkgPath()
+ if pkgPath == nil {
+ pkgPath = ut.pkgPath
+ }
+ m.PkgPath = *pkgPath
fl |= flagStickyRO
}
if p.mtyp != nil {
if ut == nil {
return Method{}, false
}
- var p *method
for i := range ut.methods {
- p = &ut.methods[i]
- if p.name != nil && *p.name == name {
+ p := &ut.methods[i]
+ if p.name.name() == name {
return t.Method(i), true
}
}
return
}
p := &t.methods[i]
- m.Name = *p.name
- if p.pkgPath != nil {
- m.PkgPath = *p.pkgPath
+ m.Name = p.name.name()
+ if !p.name.isExported() {
+ pkgPath := p.name.pkgPath()
+ if pkgPath == nil {
+ pkgPath = t.pkgPath
+ }
+ m.PkgPath = *pkgPath
}
m.Type = toType(p.typ)
m.Index = i
var p *imethod
for i := range t.methods {
p = &t.methods[i]
- if *p.name == name {
+ if p.name.name() == name {
return t.Method(i), true
}
}
}
p := &t.fields[i]
f.Type = toType(p.typ)
- if p.name != nil {
- f.Name = *p.name
+ if name := p.name.name(); name != "" {
+ f.Name = name
} else {
t := f.Type
if t.Kind() == Ptr {
f.Name = t.Name()
f.Anonymous = true
}
- if p.pkgPath != nil {
- f.PkgPath = *p.pkgPath
+ if t.pkgPath != nil && !p.name.isExported() {
+ // Fields never have an import path in their name.
+ f.PkgPath = *t.pkgPath
}
- if p.tag != nil {
- f.Tag = StructTag(*p.tag)
+ if tag := p.name.tag(); tag != "" {
+ f.Tag = StructTag(tag)
}
f.Offset = p.offset
// Find name and type for field f.
var fname string
var ntyp *rtype
- if f.name != nil {
- fname = *f.name
+ if name := f.name.name(); name != "" {
+ fname = name
} else {
// Anonymous field of type T or *T.
// Name taken from type.
if name != "" {
for i := range t.fields {
tf := &t.fields[i]
- if tf.name == nil {
+ tfname := tf.name.name()
+ if tfname == "" {
hasAnon = true
continue
}
- if *tf.name == name {
+ if tfname == name {
return t.Field(i), true
}
}
for j := 0; j < len(v.methods); j++ {
tm := &t.methods[i]
vm := &v.methods[j]
- if *vm.name == *tm.name && vm.pkgPath == tm.pkgPath && vm.typ == tm.typ {
+ if vm.name.name() == tm.name.name() && vm.typ == tm.typ {
if i++; i >= len(t.methods) {
return true
}
for j := 0; j < len(v.methods); j++ {
tm := &t.methods[i]
vm := &v.methods[j]
- if *vm.name == *tm.name && vm.pkgPath == tm.pkgPath && vm.mtyp == tm.typ {
+ if vm.name.name() == tm.name.name() && vm.mtyp == tm.typ {
if i++; i >= len(t.methods) {
return true
}
for i := range t.fields {
tf := &t.fields[i]
vf := &v.fields[i]
- if tf.name != vf.name && (tf.name == nil || vf.name == nil || *tf.name != *vf.name) {
- return false
- }
- if tf.pkgPath != vf.pkgPath && (tf.pkgPath == nil || vf.pkgPath == nil || *tf.pkgPath != *vf.pkgPath) {
+ if tf.name.name() != vf.name.name() {
return false
}
if tf.typ != vf.typ {
return false
}
- if tf.tag != vf.tag && (tf.tag == nil || vf.tag == nil || *tf.tag != *vf.tag) {
+ if tf.name.tag() != vf.name.tag() {
return false
}
if tf.offset != vf.offset {
panic("reflect: internal error: invalid method index")
}
m := &tt.methods[i]
- if m.pkgPath != nil {
+ if !m.name.isExported() {
panic("reflect: " + op + " of unexported method")
}
iface := (*nonEmptyInterface)(v.ptr)
panic("reflect: internal error: invalid method index")
}
m := &ut.methods[i]
- if m.pkgPath != nil {
+ if !m.name.isExported() {
panic("reflect: " + op + " of unexported method")
}
fn = unsafe.Pointer(&m.ifn)
// Inherit permission bits from v, but clear flagEmbedRO.
fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())
// Using an unexported field forces flagRO.
- if field.pkgPath != nil {
- if field.name == nil {
+ if !field.name.isExported() {
+ if field.name.name() == "" {
fl |= flagEmbedRO
} else {
fl |= flagStickyRO
if canfail {
return nil
}
- panic(&TypeAssertionError{"", typ._string, inter.typ._string, *inter.mhdr[0].name})
+ panic(&TypeAssertionError{"", typ._string, inter.typ._string, inter.mhdr[0].name.name()})
}
// compiler has provided some good hash codes for us.
j := 0
for k := 0; k < ni; k++ {
i := &inter.mhdr[k]
- iname := i.name
- ipkgpath := i.pkgpath
+ iname := i.name.name()
itype := i._type
+ ipkg := i.name.pkgPath()
+ if ipkg == nil {
+ ipkg = inter.pkgpath
+ }
for ; j < nt; j++ {
t := &x.mhdr[j]
- if t.name == nil {
- throw("itab t.name is nil")
- }
- if t.mtyp == itype && (t.name == iname || *t.name == *iname) && t.pkgpath == ipkgpath {
- if m != nil {
- *(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = t.ifn
+ if t.mtyp == itype && t.name.name() == iname {
+ pkgPath := t.name.pkgPath()
+ if pkgPath == nil {
+ pkgPath = x.pkgpath
+ }
+ if t.name.isExported() || pkgPath == ipkg {
+ if m != nil {
+ *(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = t.ifn
+ }
+ goto nextimethod
}
- goto nextimethod
}
}
// didn't find method
if locked != 0 {
unlock(&ifaceLock)
}
- panic(&TypeAssertionError{"", typ._string, inter.typ._string, *iname})
+ panic(&TypeAssertionError{"", typ._string, inter.typ._string, iname})
}
m.bad = 1
break
package runtime
-import "unsafe"
+import (
+ "runtime/internal/sys"
+ "unsafe"
+)
// tflag is documented in ../reflect/type.go.
type tflag uint8
}
type method struct {
- name *string
- pkgpath *string
- mtyp *_type
- ifn unsafe.Pointer
- tfn unsafe.Pointer
+ name name
+ mtyp *_type
+ ifn unsafe.Pointer
+ tfn unsafe.Pointer
}
type uncommontype struct {
}
type imethod struct {
- name *string
- pkgpath *string
- _type *_type
+ name name
+ _type *_type
}
type interfacetype struct {
- typ _type
- mhdr []imethod
+ typ _type
+ pkgpath *string
+ mhdr []imethod
}
type maptype struct {
}
type structfield struct {
- name *string
- pkgpath *string
- typ *_type
- tag *string
- offset uintptr
+ name name
+ typ *_type
+ offset uintptr
}
type structtype struct {
- typ _type
- fields []structfield
+ typ _type
+ pkgPath *string
+ fields []structfield
+}
+
+// name is an encoded type name with optional extra data.
+// See reflect/type.go for details.
+type name struct {
+ bytes *byte
+}
+
+func (n *name) data(off int) *byte {
+ return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off)))
+}
+
+func (n *name) isExported() bool {
+ return (*n.bytes)&(1<<0) != 0
+}
+
+func (n *name) nameLen() int {
+ return int(uint16(*n.data(1))<<8 | uint16(*n.data(2)))
+}
+
+func (n *name) tagLen() int {
+ if *n.data(0)&(1<<1) == 0 {
+ return 0
+ }
+ off := 3 + n.nameLen()
+ return int(uint16(*n.data(off))<<8 | uint16(*n.data(off + 1)))
+}
+
+func (n *name) name() (s string) {
+ nl := n.nameLen()
+ if nl == 0 {
+ return ""
+ }
+ hdr := (*stringStruct)(unsafe.Pointer(&s))
+ hdr.str = unsafe.Pointer(n.data(3))
+ hdr.len = nl
+ return s
+}
+
+func (n *name) pkgPath() *string {
+ if *n.data(0)&(1<<2) == 0 {
+ return nil
+ }
+ off := 3 + n.nameLen()
+ if tl := n.tagLen(); tl > 0 {
+ off += 2 + tl
+ }
+ off = int(round(uintptr(off), sys.PtrSize))
+ return *(**string)(unsafe.Pointer(n.data(off)))
}