mirror of https://go.googlesource.com/go
470 lines
12 KiB
Go
470 lines
12 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Runtime type representation.
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"internal/abi"
|
|
"unsafe"
|
|
)
|
|
|
|
type nameOff = abi.NameOff
|
|
type typeOff = abi.TypeOff
|
|
type textOff = abi.TextOff
|
|
|
|
type _type = abi.Type
|
|
|
|
// rtype is a wrapper that allows us to define additional methods.
|
|
type rtype struct {
|
|
*abi.Type // embedding is okay here (unlike reflect) because none of this is public
|
|
}
|
|
|
|
func (t rtype) string() string {
|
|
s := t.nameOff(t.Str).Name()
|
|
if t.TFlag&abi.TFlagExtraStar != 0 {
|
|
return s[1:]
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (t rtype) uncommon() *uncommontype {
|
|
return t.Uncommon()
|
|
}
|
|
|
|
func (t rtype) name() string {
|
|
if t.TFlag&abi.TFlagNamed == 0 {
|
|
return ""
|
|
}
|
|
s := t.string()
|
|
i := len(s) - 1
|
|
sqBrackets := 0
|
|
for i >= 0 && (s[i] != '.' || sqBrackets != 0) {
|
|
switch s[i] {
|
|
case ']':
|
|
sqBrackets++
|
|
case '[':
|
|
sqBrackets--
|
|
}
|
|
i--
|
|
}
|
|
return s[i+1:]
|
|
}
|
|
|
|
// pkgpath returns the path of the package where t was defined, if
|
|
// available. This is not the same as the reflect package's PkgPath
|
|
// method, in that it returns the package path for struct and interface
|
|
// types, not just named types.
|
|
func (t rtype) pkgpath() string {
|
|
if u := t.uncommon(); u != nil {
|
|
return t.nameOff(u.PkgPath).Name()
|
|
}
|
|
switch t.Kind_ & abi.KindMask {
|
|
case abi.Struct:
|
|
st := (*structtype)(unsafe.Pointer(t.Type))
|
|
return st.PkgPath.Name()
|
|
case abi.Interface:
|
|
it := (*interfacetype)(unsafe.Pointer(t.Type))
|
|
return it.PkgPath.Name()
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// reflectOffs holds type offsets defined at run time by the reflect package.
|
|
//
|
|
// When a type is defined at run time, its *rtype data lives on the heap.
|
|
// There are a wide range of possible addresses the heap may use, that
|
|
// may not be representable as a 32-bit offset. Moreover the GC may
|
|
// one day start moving heap memory, in which case there is no stable
|
|
// offset that can be defined.
|
|
//
|
|
// To provide stable offsets, we add pin *rtype objects in a global map
|
|
// and treat the offset as an identifier. We use negative offsets that
|
|
// do not overlap with any compile-time module offsets.
|
|
//
|
|
// Entries are created by reflect.addReflectOff.
|
|
var reflectOffs struct {
|
|
lock mutex
|
|
next int32
|
|
m map[int32]unsafe.Pointer
|
|
minv map[unsafe.Pointer]int32
|
|
}
|
|
|
|
func reflectOffsLock() {
|
|
lock(&reflectOffs.lock)
|
|
if raceenabled {
|
|
raceacquire(unsafe.Pointer(&reflectOffs.lock))
|
|
}
|
|
}
|
|
|
|
func reflectOffsUnlock() {
|
|
if raceenabled {
|
|
racerelease(unsafe.Pointer(&reflectOffs.lock))
|
|
}
|
|
unlock(&reflectOffs.lock)
|
|
}
|
|
|
|
func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name {
|
|
if off == 0 {
|
|
return name{}
|
|
}
|
|
base := uintptr(ptrInModule)
|
|
for md := &firstmoduledata; md != nil; md = md.next {
|
|
if base >= md.types && base < md.etypes {
|
|
res := md.types + uintptr(off)
|
|
if res > md.etypes {
|
|
println("runtime: nameOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
|
|
throw("runtime: name offset out of range")
|
|
}
|
|
return name{Bytes: (*byte)(unsafe.Pointer(res))}
|
|
}
|
|
}
|
|
|
|
// No module found. see if it is a run time name.
|
|
reflectOffsLock()
|
|
res, found := reflectOffs.m[int32(off)]
|
|
reflectOffsUnlock()
|
|
if !found {
|
|
println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:")
|
|
for next := &firstmoduledata; next != nil; next = next.next {
|
|
println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
|
|
}
|
|
throw("runtime: name offset base pointer out of range")
|
|
}
|
|
return name{Bytes: (*byte)(res)}
|
|
}
|
|
|
|
func (t rtype) nameOff(off nameOff) name {
|
|
return resolveNameOff(unsafe.Pointer(t.Type), off)
|
|
}
|
|
|
|
func resolveTypeOff(ptrInModule unsafe.Pointer, off typeOff) *_type {
|
|
if off == 0 || off == -1 {
|
|
// -1 is the sentinel value for unreachable code.
|
|
// See cmd/link/internal/ld/data.go:relocsym.
|
|
return nil
|
|
}
|
|
base := uintptr(ptrInModule)
|
|
var md *moduledata
|
|
for next := &firstmoduledata; next != nil; next = next.next {
|
|
if base >= next.types && base < next.etypes {
|
|
md = next
|
|
break
|
|
}
|
|
}
|
|
if md == nil {
|
|
reflectOffsLock()
|
|
res := reflectOffs.m[int32(off)]
|
|
reflectOffsUnlock()
|
|
if res == nil {
|
|
println("runtime: typeOff", hex(off), "base", hex(base), "not in ranges:")
|
|
for next := &firstmoduledata; next != nil; next = next.next {
|
|
println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
|
|
}
|
|
throw("runtime: type offset base pointer out of range")
|
|
}
|
|
return (*_type)(res)
|
|
}
|
|
if t := md.typemap[off]; t != nil {
|
|
return t
|
|
}
|
|
res := md.types + uintptr(off)
|
|
if res > md.etypes {
|
|
println("runtime: typeOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
|
|
throw("runtime: type offset out of range")
|
|
}
|
|
return (*_type)(unsafe.Pointer(res))
|
|
}
|
|
|
|
func (t rtype) typeOff(off typeOff) *_type {
|
|
return resolveTypeOff(unsafe.Pointer(t.Type), off)
|
|
}
|
|
|
|
func (t rtype) textOff(off textOff) unsafe.Pointer {
|
|
if off == -1 {
|
|
// -1 is the sentinel value for unreachable code.
|
|
// See cmd/link/internal/ld/data.go:relocsym.
|
|
return unsafe.Pointer(abi.FuncPCABIInternal(unreachableMethod))
|
|
}
|
|
base := uintptr(unsafe.Pointer(t.Type))
|
|
var md *moduledata
|
|
for next := &firstmoduledata; next != nil; next = next.next {
|
|
if base >= next.types && base < next.etypes {
|
|
md = next
|
|
break
|
|
}
|
|
}
|
|
if md == nil {
|
|
reflectOffsLock()
|
|
res := reflectOffs.m[int32(off)]
|
|
reflectOffsUnlock()
|
|
if res == nil {
|
|
println("runtime: textOff", hex(off), "base", hex(base), "not in ranges:")
|
|
for next := &firstmoduledata; next != nil; next = next.next {
|
|
println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
|
|
}
|
|
throw("runtime: text offset base pointer out of range")
|
|
}
|
|
return res
|
|
}
|
|
res := md.textAddr(uint32(off))
|
|
return unsafe.Pointer(res)
|
|
}
|
|
|
|
type uncommontype = abi.UncommonType
|
|
|
|
type interfacetype = abi.InterfaceType
|
|
|
|
type maptype = abi.MapType
|
|
|
|
type arraytype = abi.ArrayType
|
|
|
|
type chantype = abi.ChanType
|
|
|
|
type slicetype = abi.SliceType
|
|
|
|
type functype = abi.FuncType
|
|
|
|
type ptrtype = abi.PtrType
|
|
|
|
type name = abi.Name
|
|
|
|
type structtype = abi.StructType
|
|
|
|
func pkgPath(n name) string {
|
|
if n.Bytes == nil || *n.Data(0)&(1<<2) == 0 {
|
|
return ""
|
|
}
|
|
i, l := n.ReadVarint(1)
|
|
off := 1 + i + l
|
|
if *n.Data(0)&(1<<1) != 0 {
|
|
i2, l2 := n.ReadVarint(off)
|
|
off += i2 + l2
|
|
}
|
|
var nameOff nameOff
|
|
copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.Data(off)))[:])
|
|
pkgPathName := resolveNameOff(unsafe.Pointer(n.Bytes), nameOff)
|
|
return pkgPathName.Name()
|
|
}
|
|
|
|
// typelinksinit scans the types from extra modules and builds the
|
|
// moduledata typemap used to de-duplicate type pointers.
|
|
func typelinksinit() {
|
|
if firstmoduledata.next == nil {
|
|
return
|
|
}
|
|
typehash := make(map[uint32][]*_type, len(firstmoduledata.typelinks))
|
|
|
|
modules := activeModules()
|
|
prev := modules[0]
|
|
for _, md := range modules[1:] {
|
|
// Collect types from the previous module into typehash.
|
|
collect:
|
|
for _, tl := range prev.typelinks {
|
|
var t *_type
|
|
if prev.typemap == nil {
|
|
t = (*_type)(unsafe.Pointer(prev.types + uintptr(tl)))
|
|
} else {
|
|
t = prev.typemap[typeOff(tl)]
|
|
}
|
|
// Add to typehash if not seen before.
|
|
tlist := typehash[t.Hash]
|
|
for _, tcur := range tlist {
|
|
if tcur == t {
|
|
continue collect
|
|
}
|
|
}
|
|
typehash[t.Hash] = append(tlist, t)
|
|
}
|
|
|
|
if md.typemap == nil {
|
|
// If any of this module's typelinks match a type from a
|
|
// prior module, prefer that prior type by adding the offset
|
|
// to this module's typemap.
|
|
tm := make(map[typeOff]*_type, len(md.typelinks))
|
|
pinnedTypemaps = append(pinnedTypemaps, tm)
|
|
md.typemap = tm
|
|
for _, tl := range md.typelinks {
|
|
t := (*_type)(unsafe.Pointer(md.types + uintptr(tl)))
|
|
for _, candidate := range typehash[t.Hash] {
|
|
seen := map[_typePair]struct{}{}
|
|
if typesEqual(t, candidate, seen) {
|
|
t = candidate
|
|
break
|
|
}
|
|
}
|
|
md.typemap[typeOff(tl)] = t
|
|
}
|
|
}
|
|
|
|
prev = md
|
|
}
|
|
}
|
|
|
|
type _typePair struct {
|
|
t1 *_type
|
|
t2 *_type
|
|
}
|
|
|
|
func toRType(t *abi.Type) rtype {
|
|
return rtype{t}
|
|
}
|
|
|
|
// typesEqual reports whether two types are equal.
|
|
//
|
|
// Everywhere in the runtime and reflect packages, it is assumed that
|
|
// there is exactly one *_type per Go type, so that pointer equality
|
|
// can be used to test if types are equal. There is one place that
|
|
// breaks this assumption: buildmode=shared. In this case a type can
|
|
// appear as two different pieces of memory. This is hidden from the
|
|
// runtime and reflect package by the per-module typemap built in
|
|
// typelinksinit. It uses typesEqual to map types from later modules
|
|
// back into earlier ones.
|
|
//
|
|
// Only typelinksinit needs this function.
|
|
func typesEqual(t, v *_type, seen map[_typePair]struct{}) bool {
|
|
tp := _typePair{t, v}
|
|
if _, ok := seen[tp]; ok {
|
|
return true
|
|
}
|
|
|
|
// mark these types as seen, and thus equivalent which prevents an infinite loop if
|
|
// the two types are identical, but recursively defined and loaded from
|
|
// different modules
|
|
seen[tp] = struct{}{}
|
|
|
|
if t == v {
|
|
return true
|
|
}
|
|
kind := t.Kind_ & abi.KindMask
|
|
if kind != v.Kind_&abi.KindMask {
|
|
return false
|
|
}
|
|
rt, rv := toRType(t), toRType(v)
|
|
if rt.string() != rv.string() {
|
|
return false
|
|
}
|
|
ut := t.Uncommon()
|
|
uv := v.Uncommon()
|
|
if ut != nil || uv != nil {
|
|
if ut == nil || uv == nil {
|
|
return false
|
|
}
|
|
pkgpatht := rt.nameOff(ut.PkgPath).Name()
|
|
pkgpathv := rv.nameOff(uv.PkgPath).Name()
|
|
if pkgpatht != pkgpathv {
|
|
return false
|
|
}
|
|
}
|
|
if abi.Bool <= kind && kind <= abi.Complex128 {
|
|
return true
|
|
}
|
|
switch kind {
|
|
case abi.String, abi.UnsafePointer:
|
|
return true
|
|
case abi.Array:
|
|
at := (*arraytype)(unsafe.Pointer(t))
|
|
av := (*arraytype)(unsafe.Pointer(v))
|
|
return typesEqual(at.Elem, av.Elem, seen) && at.Len == av.Len
|
|
case abi.Chan:
|
|
ct := (*chantype)(unsafe.Pointer(t))
|
|
cv := (*chantype)(unsafe.Pointer(v))
|
|
return ct.Dir == cv.Dir && typesEqual(ct.Elem, cv.Elem, seen)
|
|
case abi.Func:
|
|
ft := (*functype)(unsafe.Pointer(t))
|
|
fv := (*functype)(unsafe.Pointer(v))
|
|
if ft.OutCount != fv.OutCount || ft.InCount != fv.InCount {
|
|
return false
|
|
}
|
|
tin, vin := ft.InSlice(), fv.InSlice()
|
|
for i := 0; i < len(tin); i++ {
|
|
if !typesEqual(tin[i], vin[i], seen) {
|
|
return false
|
|
}
|
|
}
|
|
tout, vout := ft.OutSlice(), fv.OutSlice()
|
|
for i := 0; i < len(tout); i++ {
|
|
if !typesEqual(tout[i], vout[i], seen) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case abi.Interface:
|
|
it := (*interfacetype)(unsafe.Pointer(t))
|
|
iv := (*interfacetype)(unsafe.Pointer(v))
|
|
if it.PkgPath.Name() != iv.PkgPath.Name() {
|
|
return false
|
|
}
|
|
if len(it.Methods) != len(iv.Methods) {
|
|
return false
|
|
}
|
|
for i := range it.Methods {
|
|
tm := &it.Methods[i]
|
|
vm := &iv.Methods[i]
|
|
// Note the mhdr array can be relocated from
|
|
// another module. See #17724.
|
|
tname := resolveNameOff(unsafe.Pointer(tm), tm.Name)
|
|
vname := resolveNameOff(unsafe.Pointer(vm), vm.Name)
|
|
if tname.Name() != vname.Name() {
|
|
return false
|
|
}
|
|
if pkgPath(tname) != pkgPath(vname) {
|
|
return false
|
|
}
|
|
tityp := resolveTypeOff(unsafe.Pointer(tm), tm.Typ)
|
|
vityp := resolveTypeOff(unsafe.Pointer(vm), vm.Typ)
|
|
if !typesEqual(tityp, vityp, seen) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
case abi.Map:
|
|
mt := (*maptype)(unsafe.Pointer(t))
|
|
mv := (*maptype)(unsafe.Pointer(v))
|
|
return typesEqual(mt.Key, mv.Key, seen) && typesEqual(mt.Elem, mv.Elem, seen)
|
|
case abi.Pointer:
|
|
pt := (*ptrtype)(unsafe.Pointer(t))
|
|
pv := (*ptrtype)(unsafe.Pointer(v))
|
|
return typesEqual(pt.Elem, pv.Elem, seen)
|
|
case abi.Slice:
|
|
st := (*slicetype)(unsafe.Pointer(t))
|
|
sv := (*slicetype)(unsafe.Pointer(v))
|
|
return typesEqual(st.Elem, sv.Elem, seen)
|
|
case abi.Struct:
|
|
st := (*structtype)(unsafe.Pointer(t))
|
|
sv := (*structtype)(unsafe.Pointer(v))
|
|
if len(st.Fields) != len(sv.Fields) {
|
|
return false
|
|
}
|
|
if st.PkgPath.Name() != sv.PkgPath.Name() {
|
|
return false
|
|
}
|
|
for i := range st.Fields {
|
|
tf := &st.Fields[i]
|
|
vf := &sv.Fields[i]
|
|
if tf.Name.Name() != vf.Name.Name() {
|
|
return false
|
|
}
|
|
if !typesEqual(tf.Typ, vf.Typ, seen) {
|
|
return false
|
|
}
|
|
if tf.Name.Tag() != vf.Name.Tag() {
|
|
return false
|
|
}
|
|
if tf.Offset != vf.Offset {
|
|
return false
|
|
}
|
|
if tf.Name.IsEmbedded() != vf.Name.IsEmbedded() {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
default:
|
|
println("runtime: impossible type kind", kind)
|
|
throw("runtime: impossible type kind")
|
|
return false
|
|
}
|
|
}
|