| // 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. |
| // |
| // Type is also referenced implicitly |
| // (in the form of expressions involving constants and arch.PtrSize) |
| // in cmd/compile/internal/reflectdata/reflect.go |
| // and cmd/link/internal/ld/decodesym.go |
| // (e.g. data[2*arch.PtrSize+4] references the TFlag field) |
| // unsafe.OffsetOf(Type{}.TFlag) cannot be used directly in those |
| // places because it varies with cross compilation and experiments. |
| 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 |
| ) |
| |
| // 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 unsafeSliceFor(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 unsafeStringFor(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 unsafeStringFor(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]} |
| } |