// 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 ( "unsafe" ) // 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_ uint8 // 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 uint 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 = 1 << 5 KindGCProg = 1 << 6 // Type.gc points to GC program KindMask = (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 (k Kind) String() string { if int(k) < len(kindNames) { return kindNames[k] } 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", } func (t *Type) Kind() Kind { return Kind(t.Kind_ & KindMask) } func (t *Type) HasName() bool { return t.TFlag&TFlagNamed != 0 } func (t *Type) Pointers() bool { return t.PtrBytes != 0 } // IfaceIndir reports whether t is stored indirectly in an interface value. func (t *Type) IfaceIndir() bool { return t.Kind_&KindDirectIface == 0 } // isDirectIface reports whether t is stored directly in an interface value. func (t *Type) IsDirectIface() bool { return t.Kind_&KindDirectIface != 0 } func (t *Type) GcSlice(begin, end uintptr) []byte { return unsafe.Slice(t.GCData, int(end))[begin:] } // 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 (t *UncommonType) Methods() []Method { if t.Mcount == 0 { return nil } return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.mcount > 0"))[:t.Mcount:t.Mcount] } func (t *UncommonType) ExportedMethods() []Method { if t.Xcount == 0 { return nil } return (*[1 << 16]Method)(addChecked(unsafe.Pointer(t), uintptr(t.Moff), "t.xcount > 0"))[:t.Xcount:t.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(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer { return unsafe.Pointer(uintptr(p) + x) } // 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 (t *Type) Len() int { if t.Kind() == Array { return int((*ArrayType)(unsafe.Pointer(t)).Len) } return 0 } func (t *Type) Common() *Type { return t } 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 (t *Type) ChanDir() ChanDir { if t.Kind() == Chan { ch := (*ChanType)(unsafe.Pointer(t)) return ch.Dir } return InvalidDir } // Uncommon returns a pointer to T's "uncommon" data if there is any, otherwise nil func (t *Type) Uncommon() *UncommonType { if t.TFlag&TFlagUncommon == 0 { return nil } switch t.Kind() { case Struct: return &(*structTypeUncommon)(unsafe.Pointer(t)).u case Pointer: type u struct { PtrType u UncommonType } return &(*u)(unsafe.Pointer(t)).u case Func: type u struct { FuncType u UncommonType } return &(*u)(unsafe.Pointer(t)).u case Slice: type u struct { SliceType u UncommonType } return &(*u)(unsafe.Pointer(t)).u case Array: type u struct { ArrayType u UncommonType } return &(*u)(unsafe.Pointer(t)).u case Chan: type u struct { ChanType u UncommonType } return &(*u)(unsafe.Pointer(t)).u case Map: type u struct { MapType u UncommonType } return &(*u)(unsafe.Pointer(t)).u case Interface: type u struct { InterfaceType u UncommonType } return &(*u)(unsafe.Pointer(t)).u default: type u struct { Type u UncommonType } return &(*u)(unsafe.Pointer(t)).u } } // Elem returns the element type for t if t is an array, channel, map, pointer, or slice, otherwise nil. func (t *Type) Elem() *Type { switch t.Kind() { case Array: tt := (*ArrayType)(unsafe.Pointer(t)) return tt.Elem case Chan: tt := (*ChanType)(unsafe.Pointer(t)) return tt.Elem case Map: tt := (*MapType)(unsafe.Pointer(t)) return tt.Elem case Pointer: tt := (*PtrType)(unsafe.Pointer(t)) return tt.Elem case Slice: tt := (*SliceType)(unsafe.Pointer(t)) return tt.Elem } return nil } // StructType returns t cast to a *StructType, or nil if its tag does not match. func (t *Type) StructType() *StructType { if t.Kind() != Struct { return nil } return (*StructType)(unsafe.Pointer(t)) } // MapType returns t cast to a *MapType, or nil if its tag does not match. func (t *Type) MapType() *MapType { if t.Kind() != Map { return nil } return (*MapType)(unsafe.Pointer(t)) } // ArrayType returns t cast to a *ArrayType, or nil if its tag does not match. func (t *Type) ArrayType() *ArrayType { if t.Kind() != Array { return nil } return (*ArrayType)(unsafe.Pointer(t)) } // FuncType returns t cast to a *FuncType, or nil if its tag does not match. func (t *Type) FuncType() *FuncType { if t.Kind() != Func { return nil } return (*FuncType)(unsafe.Pointer(t)) } // InterfaceType returns t cast to a *InterfaceType, or nil if its tag does not match. func (t *Type) InterfaceType() *InterfaceType { if t.Kind() != Interface { return nil } return (*InterfaceType)(unsafe.Pointer(t)) } // Size returns the size of data with type t. func (t *Type) Size() uintptr { return t.Size_ } // Align returns the alignment of data with type t. func (t *Type) Align() int { return int(t.Align_) } func (t *Type) FieldAlign() int { return int(t.FieldAlign_) } type InterfaceType struct { Type PkgPath Name // import path Methods []Imethod // sorted by hash } func (t *Type) ExportedMethods() []Method { ut := t.Uncommon() if ut == nil { return nil } return ut.ExportedMethods() } func (t *Type) NumMethod() int { if t.Kind() == Interface { tt := (*InterfaceType)(unsafe.Pointer(t)) return tt.NumMethod() } return len(t.ExportedMethods()) } // NumMethod returns the number of interface methods in the type's method set. func (t *InterfaceType) NumMethod() int { return len(t.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 (mt *MapType) IndirectKey() bool { // store ptr to key instead of key itself return mt.Flags&1 != 0 } func (mt *MapType) IndirectElem() bool { // store ptr to elem instead of elem itself return mt.Flags&2 != 0 } func (mt *MapType) ReflexiveKey() bool { // true if k==k for all keys return mt.Flags&4 != 0 } func (mt *MapType) NeedKeyUpdate() bool { // true if we need to update key on an overwrite return mt.Flags&8 != 0 } func (mt *MapType) HashMightPanic() bool { // true if hash function might panic return mt.Flags&16 != 0 } func (t *Type) Key() *Type { if t.Kind() == Map { return (*MapType)(unsafe.Pointer(t)).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 (t *FuncType) In(i int) *Type { return t.InSlice()[i] } func (t *FuncType) NumIn() int { return int(t.InCount) } func (t *FuncType) NumOut() int { return int(t.OutCount & (1<<15 - 1)) } func (t *FuncType) Out(i int) *Type { return (t.OutSlice()[i]) } func (t *FuncType) InSlice() []*Type { uadd := unsafe.Sizeof(*t) if t.TFlag&TFlagUncommon != 0 { uadd += unsafe.Sizeof(UncommonType{}) } if t.InCount == 0 { return nil } return (*[1 << 16]*Type)(addChecked(unsafe.Pointer(t), uadd, "t.inCount > 0"))[:t.InCount:t.InCount] } func (t *FuncType) OutSlice() []*Type { outCount := uint16(t.NumOut()) if outCount == 0 { return nil } uadd := unsafe.Sizeof(*t) if t.TFlag&TFlagUncommon != 0 { uadd += unsafe.Sizeof(UncommonType{}) } return (*[1 << 17]*Type)(addChecked(unsafe.Pointer(t), uadd, "outCount > 0"))[t.InCount : t.InCount+outCount : t.InCount+outCount] } func (t *FuncType) IsVariadic() bool { return t.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 (f *StructField) Embedded() bool { return f.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 (n Name) DataChecked(off int, whySafe string) *byte { return (*byte)(addChecked(unsafe.Pointer(n.Bytes), uintptr(off), whySafe)) } // 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 (n Name) Data(off int) *byte { return (*byte)(addChecked(unsafe.Pointer(n.Bytes), uintptr(off), "the runtime doesn't need to give you a reason")) } // IsExported returns "is n exported?" func (n Name) IsExported() bool { return (*n.Bytes)&(1<<0) != 0 } // HasTag returns true iff there is tag data following this name func (n Name) HasTag() bool { return (*n.Bytes)&(1<<1) != 0 } // IsEmbedded returns true iff n is embedded (an anonymous field). func (n Name) IsEmbedded() bool { return (*n.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 (n Name) ReadVarint(off int) (int, int) { v := 0 for i := 0; ; i++ { x := *n.DataChecked(off+i, "read varint") v += int(x&0x7f) << (7 * i) if x&0x80 == 0 { return i + 1, v } } } // IsBlank indicates whether n is "_". func (n Name) IsBlank() bool { if n.Bytes == nil { return false } _, l := n.ReadVarint(1) return l == 1 && *n.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(buf []byte, n int) int { for i := 0; ; i++ { b := byte(n & 0x7f) n >>= 7 if n == 0 { buf[i] = b return i + 1 } buf[i] = b | 0x80 } } // Name returns the tag string for n, or empty if there is none. func (n Name) Name() string { if n.Bytes == nil { return "" } i, l := n.ReadVarint(1) return unsafe.String(n.DataChecked(1+i, "non-empty string"), l) } // Tag returns the tag string for n, or empty if there is none. func (n Name) Tag() string { if !n.HasTag() { return "" } i, l := n.ReadVarint(1) i2, l2 := n.ReadVarint(1 + i + l) return unsafe.String(n.DataChecked(1+i+l+i2, "non-empty string"), l2) } func NewName(n, tag string, exported, embedded bool) Name { if len(n) >= 1<<29 { panic("abi.NewName: name too long: " + n[:1024] + "...") } if len(tag) >= 1<<29 { panic("abi.NewName: tag too long: " + tag[:1024] + "...") } var nameLen [10]byte var tagLen [10]byte nameLenLen := writeVarint(nameLen[:], len(n)) tagLenLen := writeVarint(tagLen[:], len(tag)) var bits byte l := 1 + nameLenLen + len(n) if exported { bits |= 1 << 0 } if len(tag) > 0 { l += tagLenLen + len(tag) bits |= 1 << 1 } if embedded { bits |= 1 << 3 } b := make([]byte, l) b[0] = bits copy(b[1:], nameLen[:nameLenLen]) copy(b[1+nameLenLen:], n) if len(tag) > 0 { tb := b[1+nameLenLen+len(n):] copy(tb, tagLen[:tagLenLen]) copy(tb[tagLenLen:], tag) } return Name{Bytes: &b[0]} }