Source File
type.go
Belonging Package
internal/abi
// Copyright 2023 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.
package abi
import (
)
// Type is the runtime representation of a Go type.
//
// Be careful about accessing this type at build time, as the version
// of this type in the compiler/linker may not have the same layout
// as the version in the target binary, due to pointer width
// differences and any experiments. Use cmd/compile/internal/rttype
// or the functions in compiletype.go to access this type instead.
// (TODO: this admonition applies to every type in this package.
// Put it in some shared location?)
type Type struct {
Size_ uintptr
PtrBytes uintptr // number of (prefix) bytes in the type that can contain pointers
Hash uint32 // hash of type; avoids computation in hash tables
TFlag TFlag // extra type information flags
Align_ uint8 // alignment of variable with this type
FieldAlign_ uint8 // alignment of struct field with this type
Kind_ Kind // enumeration for C
// function for comparing objects of this type
// (ptr to object A, ptr to object B) -> ==?
Equal func(unsafe.Pointer, unsafe.Pointer) bool
// GCData stores the GC type data for the garbage collector.
// If the KindGCProg bit is set in kind, GCData is a GC program.
// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
GCData *byte
Str NameOff // string form
PtrToThis TypeOff // type for pointer to this type, may be zero
}
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint8
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Pointer
Slice
String
Struct
UnsafePointer
)
const (
// TODO (khr, drchase) why aren't these in TFlag? Investigate, fix if possible.
KindDirectIface Kind = 1 << 5
KindGCProg Kind = 1 << 6 // Type.gc points to GC program
KindMask Kind = (1 << 5) - 1
)
// TFlag is used by a Type to signal what extra type information is
// available in the memory directly following the Type value.
type TFlag uint8
const (
// TFlagUncommon means that there is a data with a type, UncommonType,
// just beyond the shared-per-type common data. That is, the data
// for struct types will store their UncommonType at one offset, the
// data for interface types will store their UncommonType at a different
// offset. UncommonType is always accessed via a pointer that is computed
// using trust-us-we-are-the-implementors pointer arithmetic.
//
// For example, if t.Kind() == Struct and t.tflag&TFlagUncommon != 0,
// then t has UncommonType data and it can be accessed as:
//
// type structTypeUncommon struct {
// structType
// u UncommonType
// }
// u := &(*structTypeUncommon)(unsafe.Pointer(t)).u
TFlagUncommon TFlag = 1 << 0
// TFlagExtraStar means the name in the str field has an
// extraneous '*' prefix. This is because for most types T in
// a program, the type *T also exists and reusing the str data
// saves binary size.
TFlagExtraStar TFlag = 1 << 1
// TFlagNamed means the type has a name.
TFlagNamed TFlag = 1 << 2
// TFlagRegularMemory means that equal and hash functions can treat
// this type as a single region of t.size bytes.
TFlagRegularMemory TFlag = 1 << 3
// TFlagUnrolledBitmap marks special types that are unrolled-bitmap
// versions of types with GC programs.
// These types need to be deallocated when the underlying object
// is freed.
TFlagUnrolledBitmap TFlag = 1 << 4
)
// NameOff is the offset to a name from moduledata.types. See resolveNameOff in runtime.
type NameOff int32
// TypeOff is the offset to a type from moduledata.types. See resolveTypeOff in runtime.
type TypeOff int32
// TextOff is an offset from the top of a text section. See (rtype).textOff in runtime.
type TextOff int32
// String returns the name of k.
func ( Kind) () string {
if int() < len(kindNames) {
return kindNames[]
}
return kindNames[0]
}
var kindNames = []string{
Invalid: "invalid",
Bool: "bool",
Int: "int",
Int8: "int8",
Int16: "int16",
Int32: "int32",
Int64: "int64",
Uint: "uint",
Uint8: "uint8",
Uint16: "uint16",
Uint32: "uint32",
Uint64: "uint64",
Uintptr: "uintptr",
Float32: "float32",
Float64: "float64",
Complex64: "complex64",
Complex128: "complex128",
Array: "array",
Chan: "chan",
Func: "func",
Interface: "interface",
Map: "map",
Pointer: "ptr",
Slice: "slice",
String: "string",
Struct: "struct",
UnsafePointer: "unsafe.Pointer",
}
// TypeOf returns the abi.Type of some value.
func ( any) *Type {
:= *(*EmptyInterface)(unsafe.Pointer(&))
// Types are either static (for compiler-created types) or
// heap-allocated but always reachable (for reflection-created
// types, held in the central map). So there is no need to
// escape types. noescape here help avoid unnecessary escape
// of v.
return (*Type)(NoEscape(unsafe.Pointer(.Type)))
}
func ( *Type) () Kind { return .Kind_ & KindMask }
func ( *Type) () bool {
return .TFlag&TFlagNamed != 0
}
// Pointers reports whether t contains pointers.
func ( *Type) () bool { return .PtrBytes != 0 }
// IfaceIndir reports whether t is stored indirectly in an interface value.
func ( *Type) () bool {
return .Kind_&KindDirectIface == 0
}
// isDirectIface reports whether t is stored directly in an interface value.
func ( *Type) () bool {
return .Kind_&KindDirectIface != 0
}
func ( *Type) (, uintptr) []byte {
return unsafe.Slice(.GCData, int())[:]
}
// Method on non-interface type
type Method struct {
Name NameOff // name of method
Mtyp TypeOff // method type (without receiver)
Ifn TextOff // fn used in interface call (one-word receiver)
Tfn TextOff // fn used for normal method call
}
// UncommonType is present only for defined types or types with methods
// (if T is a defined type, the uncommonTypes for T and *T have methods).
// Using a pointer to this struct reduces the overall size required
// to describe a non-defined type with no methods.
type UncommonType struct {
PkgPath NameOff // import path; empty for built-in types like int, string
Mcount uint16 // number of methods
Xcount uint16 // number of exported methods
Moff uint32 // offset from this uncommontype to [mcount]Method
_ uint32 // unused
}
func ( *UncommonType) () []Method {
if .Mcount == 0 {
return nil
}
return (*[1 << 16]Method)(addChecked(unsafe.Pointer(), uintptr(.Moff), "t.mcount > 0"))[:.Mcount:.Mcount]
}
func ( *UncommonType) () []Method {
if .Xcount == 0 {
return nil
}
return (*[1 << 16]Method)(addChecked(unsafe.Pointer(), uintptr(.Moff), "t.xcount > 0"))[:.Xcount:.Xcount]
}
// addChecked returns p+x.
//
// The whySafe string is ignored, so that the function still inlines
// as efficiently as p+x, but all call sites should use the string to
// record why the addition is safe, which is to say why the addition
// does not cause x to advance to the very end of p's allocation
// and therefore point incorrectly at the next block in memory.
func addChecked( unsafe.Pointer, uintptr, string) unsafe.Pointer {
return unsafe.Pointer(uintptr() + )
}
// Imethod represents a method on an interface type
type Imethod struct {
Name NameOff // name of method
Typ TypeOff // .(*FuncType) underneath
}
// ArrayType represents a fixed array type.
type ArrayType struct {
Type
Elem *Type // array element type
Slice *Type // slice type
Len uintptr
}
// Len returns the length of t if t is an array type, otherwise 0
func ( *Type) () int {
if .Kind() == Array {
return int((*ArrayType)(unsafe.Pointer()).Len)
}
return 0
}
func ( *Type) () *Type {
return
}
type ChanDir int
const (
RecvDir ChanDir = 1 << iota // <-chan
SendDir // chan<-
BothDir = RecvDir | SendDir // chan
InvalidDir ChanDir = 0
)
// ChanType represents a channel type
type ChanType struct {
Type
Elem *Type
Dir ChanDir
}
type structTypeUncommon struct {
StructType
u UncommonType
}
// ChanDir returns the direction of t if t is a channel type, otherwise InvalidDir (0).
func ( *Type) () ChanDir {
if .Kind() == Chan {
:= (*ChanType)(unsafe.Pointer())
return .Dir
}
return InvalidDir
}
// Uncommon returns a pointer to T's "uncommon" data if there is any, otherwise nil
func ( *Type) () *UncommonType {
if .TFlag&TFlagUncommon == 0 {
return nil
}
switch .Kind() {
case Struct:
return &(*structTypeUncommon)(unsafe.Pointer()).u
case Pointer:
type struct {
PtrType
UncommonType
}
return &(*)(unsafe.Pointer()).
case Func:
type struct {
FuncType
UncommonType
}
return &(*)(unsafe.Pointer()).
case Slice:
type struct {
SliceType
UncommonType
}
return &(*)(unsafe.Pointer()).
case Array:
type struct {
ArrayType
UncommonType
}
return &(*)(unsafe.Pointer()).
case Chan:
type struct {
ChanType
UncommonType
}
return &(*)(unsafe.Pointer()).
case Map:
type struct {
MapType
UncommonType
}
return &(*)(unsafe.Pointer()).
case Interface:
type struct {
InterfaceType
UncommonType
}
return &(*)(unsafe.Pointer()).
default:
type struct {
Type
UncommonType
}
return &(*)(unsafe.Pointer()).
}
}
// Elem returns the element type for t if t is an array, channel, map, pointer, or slice, otherwise nil.
func ( *Type) () *Type {
switch .Kind() {
case Array:
:= (*ArrayType)(unsafe.Pointer())
return .Elem
case Chan:
:= (*ChanType)(unsafe.Pointer())
return .Elem
case Map:
:= (*MapType)(unsafe.Pointer())
return .Elem
case Pointer:
:= (*PtrType)(unsafe.Pointer())
return .Elem
case Slice:
:= (*SliceType)(unsafe.Pointer())
return .Elem
}
return nil
}
// StructType returns t cast to a *StructType, or nil if its tag does not match.
func ( *Type) () *StructType {
if .Kind() != Struct {
return nil
}
return (*StructType)(unsafe.Pointer())
}
// MapType returns t cast to a *MapType, or nil if its tag does not match.
func ( *Type) () *MapType {
if .Kind() != Map {
return nil
}
return (*MapType)(unsafe.Pointer())
}
// ArrayType returns t cast to a *ArrayType, or nil if its tag does not match.
func ( *Type) () *ArrayType {
if .Kind() != Array {
return nil
}
return (*ArrayType)(unsafe.Pointer())
}
// FuncType returns t cast to a *FuncType, or nil if its tag does not match.
func ( *Type) () *FuncType {
if .Kind() != Func {
return nil
}
return (*FuncType)(unsafe.Pointer())
}
// InterfaceType returns t cast to a *InterfaceType, or nil if its tag does not match.
func ( *Type) () *InterfaceType {
if .Kind() != Interface {
return nil
}
return (*InterfaceType)(unsafe.Pointer())
}
// Size returns the size of data with type t.
func ( *Type) () uintptr { return .Size_ }
// Align returns the alignment of data with type t.
func ( *Type) () int { return int(.Align_) }
func ( *Type) () int { return int(.FieldAlign_) }
type InterfaceType struct {
Type
PkgPath Name // import path
Methods []Imethod // sorted by hash
}
func ( *Type) () []Method {
:= .Uncommon()
if == nil {
return nil
}
return .ExportedMethods()
}
func ( *Type) () int {
if .Kind() == Interface {
:= (*InterfaceType)(unsafe.Pointer())
return .NumMethod()
}
return len(.ExportedMethods())
}
// NumMethod returns the number of interface methods in the type's method set.
func ( *InterfaceType) () int { return len(.Methods) }
type MapType struct {
Type
Key *Type
Elem *Type
Bucket *Type // internal type representing a hash bucket
// function for hashing keys (ptr to key, seed) -> hash
Hasher func(unsafe.Pointer, uintptr) uintptr
KeySize uint8 // size of key slot
ValueSize uint8 // size of elem slot
BucketSize uint16 // size of bucket
Flags uint32
}
// Note: flag values must match those used in the TMAP case
// in ../cmd/compile/internal/reflectdata/reflect.go:writeType.
func ( *MapType) () bool { // store ptr to key instead of key itself
return .Flags&1 != 0
}
func ( *MapType) () bool { // store ptr to elem instead of elem itself
return .Flags&2 != 0
}
func ( *MapType) () bool { // true if k==k for all keys
return .Flags&4 != 0
}
func ( *MapType) () bool { // true if we need to update key on an overwrite
return .Flags&8 != 0
}
func ( *MapType) () bool { // true if hash function might panic
return .Flags&16 != 0
}
func ( *Type) () *Type {
if .Kind() == Map {
return (*MapType)(unsafe.Pointer()).Key
}
return nil
}
type SliceType struct {
Type
Elem *Type // slice element type
}
// funcType represents a function type.
//
// A *Type for each in and out parameter is stored in an array that
// directly follows the funcType (and possibly its uncommonType). So
// a function type with one method, one input, and one output is:
//
// struct {
// funcType
// uncommonType
// [2]*rtype // [0] is in, [1] is out
// }
type FuncType struct {
Type
InCount uint16
OutCount uint16 // top bit is set if last input parameter is ...
}
func ( *FuncType) ( int) *Type {
return .InSlice()[]
}
func ( *FuncType) () int {
return int(.InCount)
}
func ( *FuncType) () int {
return int(.OutCount & (1<<15 - 1))
}
func ( *FuncType) ( int) *Type {
return (.OutSlice()[])
}
func ( *FuncType) () []*Type {
:= unsafe.Sizeof(*)
if .TFlag&TFlagUncommon != 0 {
+= unsafe.Sizeof(UncommonType{})
}
if .InCount == 0 {
return nil
}
return (*[1 << 16]*Type)(addChecked(unsafe.Pointer(), , "t.inCount > 0"))[:.InCount:.InCount]
}
func ( *FuncType) () []*Type {
:= uint16(.NumOut())
if == 0 {
return nil
}
:= unsafe.Sizeof(*)
if .TFlag&TFlagUncommon != 0 {
+= unsafe.Sizeof(UncommonType{})
}
return (*[1 << 17]*Type)(addChecked(unsafe.Pointer(), , "outCount > 0"))[.InCount : .InCount+ : .InCount+]
}
func ( *FuncType) () bool {
return .OutCount&(1<<15) != 0
}
type PtrType struct {
Type
Elem *Type // pointer element (pointed at) type
}
type StructField struct {
Name Name // name is always non-empty
Typ *Type // type of field
Offset uintptr // byte offset of field
}
func ( *StructField) () bool {
return .Name.IsEmbedded()
}
type StructType struct {
Type
PkgPath Name
Fields []StructField
}
// 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 nameOff follows the name and tag
// 1<<3 the name is of an embedded (a.k.a. anonymous) field
//
// Following that, there is a varint-encoded length of the name,
// followed by the name itself.
//
// If tag data is present, it also has a varint-encoded length
// followed by the tag itself.
//
// If the import path follows, then 4 bytes at the end of
// the data form a nameOff. The import path is only set for concrete
// methods that are defined in a different package than their type.
//
// If a name starts with "*", then the exported bit represents
// whether the pointed to type is exported.
//
// Note: this encoding must match here and in:
// cmd/compile/internal/reflectdata/reflect.go
// cmd/link/internal/ld/decodesym.go
type Name struct {
Bytes *byte
}
// DataChecked does pointer arithmetic on n's Bytes, and that arithmetic is asserted to
// be safe for the reason in whySafe (which can appear in a backtrace, etc.)
func ( Name) ( int, string) *byte {
return (*byte)(addChecked(unsafe.Pointer(.Bytes), uintptr(), ))
}
// Data does pointer arithmetic on n's Bytes, and that arithmetic is asserted to
// be safe because the runtime made the call (other packages use DataChecked)
func ( Name) ( int) *byte {
return (*byte)(addChecked(unsafe.Pointer(.Bytes), uintptr(), "the runtime doesn't need to give you a reason"))
}
// IsExported returns "is n exported?"
func ( Name) () bool {
return (*.Bytes)&(1<<0) != 0
}
// HasTag returns true iff there is tag data following this name
func ( Name) () bool {
return (*.Bytes)&(1<<1) != 0
}
// IsEmbedded returns true iff n is embedded (an anonymous field).
func ( Name) () bool {
return (*.Bytes)&(1<<3) != 0
}
// ReadVarint parses a varint as encoded by encoding/binary.
// It returns the number of encoded bytes and the encoded value.
func ( Name) ( int) (int, int) {
:= 0
for := 0; ; ++ {
:= *.DataChecked(+, "read varint")
+= int(&0x7f) << (7 * )
if &0x80 == 0 {
return + 1,
}
}
}
// IsBlank indicates whether n is "_".
func ( Name) () bool {
if .Bytes == nil {
return false
}
, := .ReadVarint(1)
return == 1 && *.Data(2) == '_'
}
// writeVarint writes n to buf in varint form. Returns the
// number of bytes written. n must be nonnegative.
// Writes at most 10 bytes.
func writeVarint( []byte, int) int {
for := 0; ; ++ {
:= byte( & 0x7f)
>>= 7
if == 0 {
[] =
return + 1
}
[] = | 0x80
}
}
// Name returns the tag string for n, or empty if there is none.
func ( Name) () string {
if .Bytes == nil {
return ""
}
, := .ReadVarint(1)
return unsafe.String(.DataChecked(1+, "non-empty string"), )
}
// Tag returns the tag string for n, or empty if there is none.
func ( Name) () string {
if !.HasTag() {
return ""
}
, := .ReadVarint(1)
, := .ReadVarint(1 + + )
return unsafe.String(.DataChecked(1+++, "non-empty string"), )
}
func (, string, , bool) Name {
if len() >= 1<<29 {
panic("abi.NewName: name too long: " + [:1024] + "...")
}
if len() >= 1<<29 {
panic("abi.NewName: tag too long: " + [:1024] + "...")
}
var [10]byte
var [10]byte
:= writeVarint([:], len())
:= writeVarint([:], len())
var byte
:= 1 + + len()
if {
|= 1 << 0
}
if len() > 0 {
+= + len()
|= 1 << 1
}
if {
|= 1 << 3
}
:= make([]byte, )
[0] =
copy([1:], [:])
copy([1+:], )
if len() > 0 {
:= [1++len():]
copy(, [:])
copy([:], )
}
return Name{Bytes: &[0]}
}
const (
TraceArgsLimit = 10 // print no more than 10 args/components
TraceArgsMaxDepth = 5 // no more than 5 layers of nesting
// maxLen is a (conservative) upper bound of the byte stream length. For
// each arg/component, it has no more than 2 bytes of data (size, offset),
// and no more than one {, }, ... at each level (it cannot have both the
// data and ... unless it is the last one, just be conservative). Plus 1
// for _endSeq.
TraceArgsMaxLen = (TraceArgsMaxDepth*3+2)*TraceArgsLimit + 1
)
// Populate the data.
// The data is a stream of bytes, which contains the offsets and sizes of the
// non-aggregate arguments or non-aggregate fields/elements of aggregate-typed
// arguments, along with special "operators". Specifically,
// - for each non-aggregate arg/field/element, its offset from FP (1 byte) and
// size (1 byte)
// - special operators:
// - 0xff - end of sequence
// - 0xfe - print { (at the start of an aggregate-typed argument)
// - 0xfd - print } (at the end of an aggregate-typed argument)
// - 0xfc - print ... (more args/fields/elements)
// - 0xfb - print _ (offset too large)
const (
TraceArgsEndSeq = 0xff
TraceArgsStartAgg = 0xfe
TraceArgsEndAgg = 0xfd
TraceArgsDotdotdot = 0xfc
TraceArgsOffsetTooLarge = 0xfb
TraceArgsSpecial = 0xf0 // above this are operators, below this are ordinary offsets
)
// MaxPtrmaskBytes is the maximum length of a GC ptrmask bitmap,
// which holds 1-bit entries describing where pointers are in a given type.
// Above this length, the GC information is recorded as a GC program,
// which can express repetition compactly. In either form, the
// information is used by the runtime to initialize the heap bitmap,
// and for large types (like 128 or more words), they are roughly the
// same speed. GC programs are never much larger and often more
// compact. (If large arrays are involved, they can be arbitrarily
// more compact.)
//
// The cutoff must be large enough that any allocation large enough to
// use a GC program is large enough that it does not share heap bitmap
// bytes with any other objects, allowing the GC program execution to
// assume an aligned start and not use atomic operations. In the current
// runtime, this means all malloc size classes larger than the cutoff must
// be multiples of four words. On 32-bit systems that's 16 bytes, and
// all size classes >= 16 bytes are 16-byte aligned, so no real constraint.
// On 64-bit systems, that's 32 bytes, and 32-byte alignment is guaranteed
// for size classes >= 256 bytes. On a 64-bit system, 256 bytes allocated
// is 32 pointers, the bits for which fit in 4 bytes. So MaxPtrmaskBytes
// must be >= 4.
//
// We used to use 16 because the GC programs do have some constant overhead
// to get started, and processing 128 pointers seems to be enough to
// amortize that overhead well.
//
// To make sure that the runtime's chansend can call typeBitsBulkBarrier,
// we raised the limit to 2048, so that even 32-bit systems are guaranteed to
// use bitmaps for objects up to 64 kB in size.
const MaxPtrmaskBytes = 2048
The pages are generated with Golds v0.7.0-preview. (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 @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |