Commit 24ce64d1 authored by David Crawshaw's avatar David Crawshaw

cmd/compile, runtime: new static name encoding

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: default avatarIan Lance Taylor <iant@golang.org>
parent 0a82ed5d
...@@ -53,8 +53,8 @@ const ( ...@@ -53,8 +53,8 @@ const (
MAXVALSIZE = 128 MAXVALSIZE = 128
) )
func structfieldSize() int { return 5 * Widthptr } // Sizeof(runtime.structfield{}) func structfieldSize() int { return 3 * Widthptr } // Sizeof(runtime.structfield{})
func imethodSize() int { return 3 * Widthptr } // Sizeof(runtime.imethod{}) func imethodSize() int { return 2 * Widthptr } // Sizeof(runtime.imethod{})
func uncommonSize(t *Type) int { // Sizeof(runtime.uncommontype{}) func uncommonSize(t *Type) int { // Sizeof(runtime.uncommontype{})
if t.Sym == nil && len(methods(t)) == 0 { if t.Sym == nil && len(methods(t)) == 0 {
return 0 return 0
...@@ -441,8 +441,12 @@ func dimportpath(p *Pkg) { ...@@ -441,8 +441,12 @@ func dimportpath(p *Pkg) {
} }
func dgopkgpath(s *Sym, ot int, pkg *Pkg) int { 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 { if pkg == nil {
return dgostringptr(s, ot, "") return duintxxLSym(s, ot, 0, Widthptr)
} }
if pkg == localpkg && myimportpath == "" { if pkg == localpkg && myimportpath == "" {
...@@ -451,39 +455,117 @@ func dgopkgpath(s *Sym, ot int, pkg *Pkg) int { ...@@ -451,39 +455,117 @@ func dgopkgpath(s *Sym, ot int, pkg *Pkg) int {
// go.importpath.""., which the linker will rewrite using the correct import path. // go.importpath.""., which the linker will rewrite using the correct import path.
// Every package that imports this one directly defines the symbol. // Every package that imports this one directly defines the symbol.
// See also https://groups.google.com/forum/#!topic/golang-dev/myb9s53HxGQ. // See also https://groups.google.com/forum/#!topic/golang-dev/myb9s53HxGQ.
ns := Pkglookup("importpath.\"\".", mkpkg("go")) ns := obj.Linklookup(Ctxt, `go.importpath."".`, 0)
return dsymptr(s, ot, ns, 0) return dsymptrLSym(s, ot, ns, 0)
} }
dimportpath(pkg) 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. // dextratype dumps the fields of a runtime.uncommontype.
// dataAdd is the offset in bytes after the header where the // dataAdd is the offset in bytes after the header where the
// backing array of the []method field is written (by dextratypeData). // 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) m := methods(t)
if t.Sym == nil && len(m) == 0 { if t.Sym == nil && len(m) == 0 {
return off return ot
} }
noff := int(Rnd(int64(off), int64(Widthptr))) noff := int(Rnd(int64(ot), int64(Widthptr)))
if noff != off { if noff != ot {
panic("dextratype rounding does something. :-(") Fatalf("unexpected alignment in dextratype for %s", t)
} }
off = noff
for _, a := range m { for _, a := range m {
dtypesym(a.type_) dtypesym(a.type_)
} }
ot := off ot = dgopkgpath(s, ot, typePkg(t))
s := sym
if t.Sym != nil && t != Types[t.Etype] && t != errortype {
ot = dgopkgpath(s, ot, t.Sym.Pkg)
} else {
ot = dgostringptr(s, ot, "")
}
// slice header // slice header
ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint+dataAdd) ot = dsymptr(s, ot, s, ot+Widthptr+2*Widthint+dataAdd)
...@@ -495,15 +577,28 @@ func dextratype(sym *Sym, off int, t *Type, dataAdd int) int { ...@@ -495,15 +577,28 @@ func dextratype(sym *Sym, off int, t *Type, dataAdd int) int {
return ot 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 // dextratypeData dumps the backing array for the []method field of
// runtime.uncommontype. // runtime.uncommontype.
func dextratypeData(s *Sym, ot int, t *Type) int { func dextratypeData(s *Sym, ot int, t *Type) int {
for _, a := range methods(t) { for _, a := range methods(t) {
// method
// ../../../../runtime/type.go:/method // ../../../../runtime/type.go:/method
ot = dgostringptr(s, ot, a.name) exported := exportname(a.name)
var pkg *Pkg
ot = dgopkgpath(s, ot, a.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, dtypesym(a.mtype))
ot = dmethodptr(s, ot, a.isym) ot = dmethodptr(s, ot, a.isym)
ot = dmethodptr(s, ot, a.tsym) ot = dmethodptr(s, ot, a.tsym)
...@@ -1076,6 +1171,12 @@ ok: ...@@ -1076,6 +1171,12 @@ ok:
// ../../../../runtime/type.go:/interfaceType // ../../../../runtime/type.go:/interfaceType
ot = dcommontype(s, ot, t) 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 = dsymptr(s, ot, s, ot+Widthptr+2*Widthint+uncommonSize(t))
ot = duintxx(s, ot, uint64(n), Widthint) ot = duintxx(s, ot, uint64(n), Widthint)
ot = duintxx(s, ot, uint64(n), Widthint) ot = duintxx(s, ot, uint64(n), Widthint)
...@@ -1084,8 +1185,12 @@ ok: ...@@ -1084,8 +1185,12 @@ ok:
for _, a := range m { for _, a := range m {
// ../../../../runtime/type.go:/imethod // ../../../../runtime/type.go:/imethod
ot = dgostringptr(s, ot, a.name) exported := exportname(a.name)
ot = dgopkgpath(s, ot, a.pkg) 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 = dsymptr(s, ot, dtypesym(a.type_), 0)
} }
...@@ -1148,6 +1253,11 @@ ok: ...@@ -1148,6 +1253,11 @@ ok:
} }
ot = dcommontype(s, ot, t) 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 = dsymptr(s, ot, s, ot+Widthptr+2*Widthint+uncommonSize(t))
ot = duintxx(s, ot, uint64(n), Widthint) ot = duintxx(s, ot, uint64(n), Widthint)
ot = duintxx(s, ot, uint64(n), Widthint) ot = duintxx(s, ot, uint64(n), Widthint)
...@@ -1155,28 +1265,11 @@ ok: ...@@ -1155,28 +1265,11 @@ ok:
dataAdd := n * structfieldSize() dataAdd := n * structfieldSize()
ot = dextratype(s, ot, t, dataAdd) ot = dextratype(s, ot, t, dataAdd)
for _, t1 := range t.Fields().Slice() { for _, f := range t.Fields().Slice() {
// ../../../../runtime/type.go:/structField // ../../../../runtime/type.go:/structField
if t1.Sym != nil && t1.Embedded == 0 { ot = dnameField(s, ot, f)
ot = dgostringptr(s, ot, t1.Sym.Name) ot = dsymptr(s, ot, dtypesym(f.Type), 0)
if exportname(t1.Sym.Name) { ot = duintptr(s, ot, uint64(f.Width)) // field offset
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
} }
} }
......
...@@ -47,7 +47,7 @@ func decode_inuxi(p []byte, sz int) uint64 { ...@@ -47,7 +47,7 @@ func decode_inuxi(p []byte, sz int) uint64 {
} }
func commonsize() int { return 6*Thearch.Ptrsize + 8 } // runtime._type 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 func uncommonSize() int { return 2*Thearch.Ptrsize + 2*Thearch.Intsize } // runtime.uncommontype
// Type.commonType.kind // Type.commonType.kind
...@@ -203,11 +203,11 @@ func decodetype_funcouttype(s *LSym, i int) *LSym { ...@@ -203,11 +203,11 @@ func decodetype_funcouttype(s *LSym, i int) *LSym {
// Type.StructType.fields.Slice::length // Type.StructType.fields.Slice::length
func decodetype_structfieldcount(s *LSym) int { 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 { 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) { if decodetype_hasUncommon(s) {
off += uncommonSize() off += uncommonSize()
} }
...@@ -228,24 +228,37 @@ func decodetype_stringptr(s *LSym, off int) string { ...@@ -228,24 +228,37 @@ func decodetype_stringptr(s *LSym, off int) string {
return string(r.Sym.P[r.Add : r.Add+strlen]) 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 { func decodetype_structfieldname(s *LSym, i int) string {
off := decodetype_structfieldarrayoff(s, i) off := decodetype_structfieldarrayoff(s, i)
return decodetype_stringptr(s, off) return decodetype_name(s, off)
} }
func decodetype_structfieldtype(s *LSym, i int) *LSym { func decodetype_structfieldtype(s *LSym, i int) *LSym {
off := decodetype_structfieldarrayoff(s, i) 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 { func decodetype_structfieldoffs(s *LSym, i int) int64 {
off := decodetype_structfieldarrayoff(s, i) 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 // InterfaceType.methods.length
func decodetype_ifacemethodcount(s *LSym) int64 { 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 // methodsig is a fully qualified typed method signature, like
...@@ -266,16 +279,16 @@ const ( ...@@ -266,16 +279,16 @@ const (
) )
// decode_methodsig decodes an array of method signature information. // decode_methodsig decodes an array of method signature information.
// Each element of the array is size bytes. The first word is a *string // Each element of the array is size bytes. The first word is a
// for the name, the third word is a *rtype for the funcType. // 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. // Conveniently this is the layout of both runtime.method and runtime.imethod.
func decode_methodsig(s *LSym, off, size, count int) []methodsig { func decode_methodsig(s *LSym, off, size, count int) []methodsig {
var buf bytes.Buffer var buf bytes.Buffer
var methods []methodsig var methods []methodsig
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
buf.WriteString(decodetype_stringptr(s, off)) buf.WriteString(decodetype_name(s, off))
mtypSym := decode_reloc_sym(s, int32(off+2*Thearch.Ptrsize)) mtypSym := decode_reloc_sym(s, int32(off+Thearch.Ptrsize))
buf.WriteRune('(') buf.WriteRune('(')
inCount := decodetype_funcincount(mtypSym) inCount := decodetype_funcincount(mtypSym)
...@@ -306,7 +319,7 @@ func decodetype_ifacemethods(s *LSym) []methodsig { ...@@ -306,7 +319,7 @@ func decodetype_ifacemethods(s *LSym) []methodsig {
if decodetype_kind(s)&kindMask != kindInterface { if decodetype_kind(s)&kindMask != kindInterface {
panic(fmt.Sprintf("symbol %q is not an interface", s.Name)) 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 { if r == nil {
return nil return nil
} }
...@@ -315,7 +328,7 @@ func decodetype_ifacemethods(s *LSym) []methodsig { ...@@ -315,7 +328,7 @@ func decodetype_ifacemethods(s *LSym) []methodsig {
} }
off := int(r.Add) // array of reflect.imethod values off := int(r.Add) // array of reflect.imethod values
numMethods := int(decodetype_ifacemethodcount(s)) numMethods := int(decodetype_ifacemethodcount(s))
sizeofIMethod := 3 * Thearch.Ptrsize sizeofIMethod := 2 * Thearch.Ptrsize
return decode_methodsig(s, off, sizeofIMethod, numMethods) return decode_methodsig(s, off, sizeofIMethod, numMethods)
} }
...@@ -326,7 +339,7 @@ func decodetype_methods(s *LSym) []methodsig { ...@@ -326,7 +339,7 @@ func decodetype_methods(s *LSym) []methodsig {
off := commonsize() // reflect.rtype off := commonsize() // reflect.rtype
switch decodetype_kind(s) & kindMask { switch decodetype_kind(s) & kindMask {
case kindStruct: // reflect.structType case kindStruct: // reflect.structType
off += Thearch.Ptrsize + 2*Thearch.Intsize off += 2*Thearch.Ptrsize + 2*Thearch.Intsize
case kindPtr: // reflect.ptrType case kindPtr: // reflect.ptrType
off += Thearch.Ptrsize off += Thearch.Ptrsize
case kindFunc: // reflect.funcType case kindFunc: // reflect.funcType
...@@ -351,6 +364,6 @@ func decodetype_methods(s *LSym) []methodsig { ...@@ -351,6 +364,6 @@ func decodetype_methods(s *LSym) []methodsig {
panic(fmt.Sprintf("method slice pointer in %s leads to a different symbol %s", s, r.Sym)) 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 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) return decode_methodsig(s, off, sizeofMethod, numMethods)
} }
...@@ -288,11 +288,10 @@ type typeAlg struct { ...@@ -288,11 +288,10 @@ type typeAlg struct {
// Method on non-interface type // Method on non-interface type
type method struct { type method struct {
name *string // name of method name name // name of method
pkgPath *string // nil for exported Names; otherwise import path mtyp *rtype // method type (without receiver)
mtyp *rtype // method type (without receiver) ifn unsafe.Pointer // fn used in interface call (one-word receiver)
ifn unsafe.Pointer // fn used in interface call (one-word receiver) tfn unsafe.Pointer // fn used for normal method call
tfn unsafe.Pointer // fn used for normal method call
} }
// uncommonType is present only for types with names or methods // uncommonType is present only for types with names or methods
...@@ -347,14 +346,14 @@ type funcType struct { ...@@ -347,14 +346,14 @@ type funcType struct {
// imethod represents a method on an interface type // imethod represents a method on an interface type
type imethod struct { type imethod struct {
name *string // name of method name name // name of method
pkgPath *string // nil for exported Names; otherwise import path typ *rtype // .(*FuncType) underneath
typ *rtype // .(*FuncType) underneath
} }
// interfaceType represents an interface type. // interfaceType represents an interface type.
type interfaceType struct { type interfaceType struct {
rtype `reflect:"interface"` rtype `reflect:"interface"`
pkgPath *string // import path
methods []imethod // sorted by hash methods []imethod // sorted by hash
} }
...@@ -388,17 +387,101 @@ type sliceType struct { ...@@ -388,17 +387,101 @@ type sliceType struct {
// Struct field // Struct field
type structField struct { type structField struct {
name *string // nil for embedded fields name name // name is empty for embedded fields
pkgPath *string // nil for exported Names; otherwise import path typ *rtype // type of field
typ *rtype // type of field offset uintptr // byte offset of field within struct
tag *string // nil if no tag
offset uintptr // byte offset of field within struct
} }
// structType represents a struct type. // structType represents a struct type.
type structType struct { type structType struct {
rtype `reflect:"struct"` rtype `reflect:"struct"`
fields []structField // sorted by offset 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)
} }
/* /*
...@@ -583,12 +666,14 @@ func (t *rtype) Method(i int) (m Method) { ...@@ -583,12 +666,14 @@ func (t *rtype) Method(i int) (m Method) {
panic("reflect: Method index out of range") panic("reflect: Method index out of range")
} }
p := &ut.methods[i] p := &ut.methods[i]
if p.name != nil { m.Name = p.name.name()
m.Name = *p.name
}
fl := flag(Func) fl := flag(Func)
if p.pkgPath != nil { if !p.name.isExported() {
m.PkgPath = *p.pkgPath pkgPath := p.name.pkgPath()
if pkgPath == nil {
pkgPath = ut.pkgPath
}
m.PkgPath = *pkgPath
fl |= flagStickyRO fl |= flagStickyRO
} }
if p.mtyp != nil { if p.mtyp != nil {
...@@ -620,10 +705,9 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) { ...@@ -620,10 +705,9 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) {
if ut == nil { if ut == nil {
return Method{}, false return Method{}, false
} }
var p *method
for i := range ut.methods { for i := range ut.methods {
p = &ut.methods[i] p := &ut.methods[i]
if p.name != nil && *p.name == name { if p.name.name() == name {
return t.Method(i), true return t.Method(i), true
} }
} }
...@@ -832,9 +916,13 @@ func (t *interfaceType) Method(i int) (m Method) { ...@@ -832,9 +916,13 @@ func (t *interfaceType) Method(i int) (m Method) {
return return
} }
p := &t.methods[i] p := &t.methods[i]
m.Name = *p.name m.Name = p.name.name()
if p.pkgPath != nil { if !p.name.isExported() {
m.PkgPath = *p.pkgPath pkgPath := p.name.pkgPath()
if pkgPath == nil {
pkgPath = t.pkgPath
}
m.PkgPath = *pkgPath
} }
m.Type = toType(p.typ) m.Type = toType(p.typ)
m.Index = i m.Index = i
...@@ -852,7 +940,7 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { ...@@ -852,7 +940,7 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) {
var p *imethod var p *imethod
for i := range t.methods { for i := range t.methods {
p = &t.methods[i] p = &t.methods[i]
if *p.name == name { if p.name.name() == name {
return t.Method(i), true return t.Method(i), true
} }
} }
...@@ -950,8 +1038,8 @@ func (t *structType) Field(i int) (f StructField) { ...@@ -950,8 +1038,8 @@ func (t *structType) Field(i int) (f StructField) {
} }
p := &t.fields[i] p := &t.fields[i]
f.Type = toType(p.typ) f.Type = toType(p.typ)
if p.name != nil { if name := p.name.name(); name != "" {
f.Name = *p.name f.Name = name
} else { } else {
t := f.Type t := f.Type
if t.Kind() == Ptr { if t.Kind() == Ptr {
...@@ -960,11 +1048,12 @@ func (t *structType) Field(i int) (f StructField) { ...@@ -960,11 +1048,12 @@ func (t *structType) Field(i int) (f StructField) {
f.Name = t.Name() f.Name = t.Name()
f.Anonymous = true f.Anonymous = true
} }
if p.pkgPath != nil { if t.pkgPath != nil && !p.name.isExported() {
f.PkgPath = *p.pkgPath // Fields never have an import path in their name.
f.PkgPath = *t.pkgPath
} }
if p.tag != nil { if tag := p.name.tag(); tag != "" {
f.Tag = StructTag(*p.tag) f.Tag = StructTag(tag)
} }
f.Offset = p.offset f.Offset = p.offset
...@@ -1056,8 +1145,8 @@ func (t *structType) FieldByNameFunc(match func(string) bool) (result StructFiel ...@@ -1056,8 +1145,8 @@ func (t *structType) FieldByNameFunc(match func(string) bool) (result StructFiel
// Find name and type for field f. // Find name and type for field f.
var fname string var fname string
var ntyp *rtype var ntyp *rtype
if f.name != nil { if name := f.name.name(); name != "" {
fname = *f.name fname = name
} else { } else {
// Anonymous field of type T or *T. // Anonymous field of type T or *T.
// Name taken from type. // Name taken from type.
...@@ -1122,11 +1211,12 @@ func (t *structType) FieldByName(name string) (f StructField, present bool) { ...@@ -1122,11 +1211,12 @@ func (t *structType) FieldByName(name string) (f StructField, present bool) {
if name != "" { if name != "" {
for i := range t.fields { for i := range t.fields {
tf := &t.fields[i] tf := &t.fields[i]
if tf.name == nil { tfname := tf.name.name()
if tfname == "" {
hasAnon = true hasAnon = true
continue continue
} }
if *tf.name == name { if tfname == name {
return t.Field(i), true return t.Field(i), true
} }
} }
...@@ -1278,7 +1368,7 @@ func implements(T, V *rtype) bool { ...@@ -1278,7 +1368,7 @@ func implements(T, V *rtype) bool {
for j := 0; j < len(v.methods); j++ { for j := 0; j < len(v.methods); j++ {
tm := &t.methods[i] tm := &t.methods[i]
vm := &v.methods[j] 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) { if i++; i >= len(t.methods) {
return true return true
} }
...@@ -1295,7 +1385,7 @@ func implements(T, V *rtype) bool { ...@@ -1295,7 +1385,7 @@ func implements(T, V *rtype) bool {
for j := 0; j < len(v.methods); j++ { for j := 0; j < len(v.methods); j++ {
tm := &t.methods[i] tm := &t.methods[i]
vm := &v.methods[j] 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) { if i++; i >= len(t.methods) {
return true return true
} }
...@@ -1400,16 +1490,13 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool { ...@@ -1400,16 +1490,13 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool {
for i := range t.fields { for i := range t.fields {
tf := &t.fields[i] tf := &t.fields[i]
vf := &v.fields[i] vf := &v.fields[i]
if tf.name != vf.name && (tf.name == nil || vf.name == nil || *tf.name != *vf.name) { if tf.name.name() != vf.name.name() {
return false
}
if tf.pkgPath != vf.pkgPath && (tf.pkgPath == nil || vf.pkgPath == nil || *tf.pkgPath != *vf.pkgPath) {
return false return false
} }
if tf.typ != vf.typ { if tf.typ != vf.typ {
return false 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 return false
} }
if tf.offset != vf.offset { if tf.offset != vf.offset {
......
...@@ -554,7 +554,7 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn ...@@ -554,7 +554,7 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn
panic("reflect: internal error: invalid method index") panic("reflect: internal error: invalid method index")
} }
m := &tt.methods[i] m := &tt.methods[i]
if m.pkgPath != nil { if !m.name.isExported() {
panic("reflect: " + op + " of unexported method") panic("reflect: " + op + " of unexported method")
} }
iface := (*nonEmptyInterface)(v.ptr) iface := (*nonEmptyInterface)(v.ptr)
...@@ -571,7 +571,7 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn ...@@ -571,7 +571,7 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn
panic("reflect: internal error: invalid method index") panic("reflect: internal error: invalid method index")
} }
m := &ut.methods[i] m := &ut.methods[i]
if m.pkgPath != nil { if !m.name.isExported() {
panic("reflect: " + op + " of unexported method") panic("reflect: " + op + " of unexported method")
} }
fn = unsafe.Pointer(&m.ifn) fn = unsafe.Pointer(&m.ifn)
...@@ -750,8 +750,8 @@ func (v Value) Field(i int) Value { ...@@ -750,8 +750,8 @@ func (v Value) Field(i int) Value {
// Inherit permission bits from v, but clear flagEmbedRO. // Inherit permission bits from v, but clear flagEmbedRO.
fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind()) fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())
// Using an unexported field forces flagRO. // Using an unexported field forces flagRO.
if field.pkgPath != nil { if !field.name.isExported() {
if field.name == nil { if field.name.name() == "" {
fl |= flagEmbedRO fl |= flagEmbedRO
} else { } else {
fl |= flagStickyRO fl |= flagStickyRO
......
...@@ -30,7 +30,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { ...@@ -30,7 +30,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
if canfail { if canfail {
return nil 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. // compiler has provided some good hash codes for us.
...@@ -84,19 +84,25 @@ search: ...@@ -84,19 +84,25 @@ search:
j := 0 j := 0
for k := 0; k < ni; k++ { for k := 0; k < ni; k++ {
i := &inter.mhdr[k] i := &inter.mhdr[k]
iname := i.name iname := i.name.name()
ipkgpath := i.pkgpath
itype := i._type itype := i._type
ipkg := i.name.pkgPath()
if ipkg == nil {
ipkg = inter.pkgpath
}
for ; j < nt; j++ { for ; j < nt; j++ {
t := &x.mhdr[j] t := &x.mhdr[j]
if t.name == nil { if t.mtyp == itype && t.name.name() == iname {
throw("itab t.name is nil") pkgPath := t.name.pkgPath()
} if pkgPath == nil {
if t.mtyp == itype && (t.name == iname || *t.name == *iname) && t.pkgpath == ipkgpath { pkgPath = x.pkgpath
if m != nil { }
*(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = t.ifn 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 // didn't find method
...@@ -104,7 +110,7 @@ search: ...@@ -104,7 +110,7 @@ search:
if locked != 0 { if locked != 0 {
unlock(&ifaceLock) unlock(&ifaceLock)
} }
panic(&TypeAssertionError{"", typ._string, inter.typ._string, *iname}) panic(&TypeAssertionError{"", typ._string, inter.typ._string, iname})
} }
m.bad = 1 m.bad = 1
break break
......
...@@ -6,7 +6,10 @@ ...@@ -6,7 +6,10 @@
package runtime package runtime
import "unsafe" import (
"runtime/internal/sys"
"unsafe"
)
// tflag is documented in ../reflect/type.go. // tflag is documented in ../reflect/type.go.
type tflag uint8 type tflag uint8
...@@ -152,11 +155,10 @@ func (t *functype) dotdotdot() bool { ...@@ -152,11 +155,10 @@ func (t *functype) dotdotdot() bool {
} }
type method struct { type method struct {
name *string name name
pkgpath *string mtyp *_type
mtyp *_type ifn unsafe.Pointer
ifn unsafe.Pointer tfn unsafe.Pointer
tfn unsafe.Pointer
} }
type uncommontype struct { type uncommontype struct {
...@@ -165,14 +167,14 @@ type uncommontype struct { ...@@ -165,14 +167,14 @@ type uncommontype struct {
} }
type imethod struct { type imethod struct {
name *string name name
pkgpath *string _type *_type
_type *_type
} }
type interfacetype struct { type interfacetype struct {
typ _type typ _type
mhdr []imethod pkgpath *string
mhdr []imethod
} }
type maptype struct { type maptype struct {
...@@ -220,14 +222,62 @@ type ptrtype struct { ...@@ -220,14 +222,62 @@ type ptrtype struct {
} }
type structfield struct { type structfield struct {
name *string name name
pkgpath *string typ *_type
typ *_type offset uintptr
tag *string
offset uintptr
} }
type structtype struct { type structtype struct {
typ _type typ _type
fields []structfield 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)))
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment