blob: 1150a53208c3d81c187e2025631403385b12d58a [file] [log] [blame]
// 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_ & kindMask {
case kindStruct:
st := (*structtype)(unsafe.Pointer(t.Type))
return st.PkgPath.Name()
case kindInterface:
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_ & kindMask
if kind != v.Kind_&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 kindBool <= kind && kind <= kindComplex128 {
return true
}
switch kind {
case kindString, kindUnsafePointer:
return true
case kindArray:
at := (*arraytype)(unsafe.Pointer(t))
av := (*arraytype)(unsafe.Pointer(v))
return typesEqual(at.Elem, av.Elem, seen) && at.Len == av.Len
case kindChan:
ct := (*chantype)(unsafe.Pointer(t))
cv := (*chantype)(unsafe.Pointer(v))
return ct.Dir == cv.Dir && typesEqual(ct.Elem, cv.Elem, seen)
case kindFunc:
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 kindInterface:
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 kindMap:
mt := (*maptype)(unsafe.Pointer(t))
mv := (*maptype)(unsafe.Pointer(v))
return typesEqual(mt.Key, mv.Key, seen) && typesEqual(mt.Elem, mv.Elem, seen)
case kindPtr:
pt := (*ptrtype)(unsafe.Pointer(t))
pv := (*ptrtype)(unsafe.Pointer(v))
return typesEqual(pt.Elem, pv.Elem, seen)
case kindSlice:
st := (*slicetype)(unsafe.Pointer(t))
sv := (*slicetype)(unsafe.Pointer(v))
return typesEqual(st.Elem, sv.Elem, seen)
case kindStruct:
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
}
}