package runtime
import (
"internal/abi"
"unsafe"
)
type nameOff = abi .NameOff
type typeOff = abi .TypeOff
type textOff = abi .TextOff
type _type = abi .Type
type rtype struct {
*abi .Type
}
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 :]
}
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 ""
}
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 ))}
}
}
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 {
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 {
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 ()
}
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 :
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 )]
}
tlist := typehash [t .Hash ]
for _ , tcur := range tlist {
if tcur == t {
continue collect
}
}
typehash [t .Hash ] = append (tlist , t )
}
if md .typemap == nil {
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 }
}
func typesEqual (t , v *_type , seen map [_typePair ]struct {}) bool {
tp := _typePair {t , v }
if _ , ok := seen [tp ]; ok {
return true
}
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 ]
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
}
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .