summaryrefslogtreecommitdiff
path: root/src/internal
diff options
context:
space:
mode:
authorDavid Chase <drchase@google.com>2023-01-20 16:41:57 -0500
committerDavid Chase <drchase@google.com>2023-05-05 14:59:28 +0000
commitbdc6ae579aa86d21183c612c8c37916f397afaa8 (patch)
treecc63ff843ab5a7f3c196c8fa2c74924bfee61d93 /src/internal
parentdace96b9a12905b34af609eedaa6b43e30e7cdb1 (diff)
downloadgo-git-bdc6ae579aa86d21183c612c8c37916f397afaa8.tar.gz
internal/abi: refactor (basic) type struct into one definition
This touches a lot of files, which is bad, but it is also good, since there's N copies of this information commoned into 1. The new files in internal/abi are copied from the end of the stack; ultimately this will all end up being used. Change-Id: Ia252c0055aaa72ca569411ef9f9e96e3d610889e Reviewed-on: https://go-review.googlesource.com/c/go/+/462995 TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Carlos Amedee <carlos@golang.org> Run-TryBot: David Chase <drchase@google.com> Reviewed-by: Keith Randall <khr@golang.org>
Diffstat (limited to 'src/internal')
-rw-r--r--src/internal/abi/compiletype.go167
-rw-r--r--src/internal/abi/type.go712
-rw-r--r--src/internal/abi/unsafestring_go119.go32
-rw-r--r--src/internal/abi/unsafestring_go120.go18
-rw-r--r--src/internal/reflectlite/swapper.go2
-rw-r--r--src/internal/reflectlite/type.go95
-rw-r--r--src/internal/reflectlite/value.go2
7 files changed, 952 insertions, 76 deletions
diff --git a/src/internal/abi/compiletype.go b/src/internal/abi/compiletype.go
new file mode 100644
index 0000000000..d92addec25
--- /dev/null
+++ b/src/internal/abi/compiletype.go
@@ -0,0 +1,167 @@
+// 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
+
+// These functions are the build-time version of the Go type data structures.
+
+// Their contents must be kept in sync with their definitions.
+// Because the host and target type sizes can differ, the compiler and
+// linker cannot use the host information that they might get from
+// either unsafe.Sizeof and Alignof, nor runtime, reflect, or reflectlite.
+
+// CommonSize returns sizeof(Type) for a compilation target with a given ptrSize
+func CommonSize(ptrSize int) int { return 4*ptrSize + 8 + 8 }
+
+// StructFieldSize returns sizeof(StructField) for a compilation target with a given ptrSize
+func StructFieldSize(ptrSize int) int { return 3 * ptrSize }
+
+// UncommonSize returns sizeof(UncommonType). This currently does not depend on ptrSize.
+// This exported function is in an internal package, so it may change to depend on ptrSize in the future.
+func UncommonSize() uint64 { return 4 + 2 + 2 + 4 + 4 }
+
+// IMethodSize returns sizeof(IMethod) for a compilation target with a given ptrSize
+func IMethodSize(ptrSize int) int { return 4 + 4 }
+
+// KindOff returns the offset of Type.Kind_ for a compilation target with a given ptrSize
+func KindOff(ptrSize int) int { return 2*ptrSize + 7 }
+
+// SizeOff returns the offset of Type.Size_ for a compilation target with a given ptrSize
+func SizeOff(ptrSize int) int { return 0 }
+
+// PtrBytes returns the offset of Type.PtrBytes for a compilation target with a given ptrSize
+func PtrBytesOff(ptrSize int) int { return ptrSize }
+
+// TFlagOff returns the offset of Type.TFlag for a compilation target with a given ptrSize
+func TFlagOff(ptrSize int) int { return 2*ptrSize + 4 }
+
+// Offset is for computing offsets of type data structures at compile/link time;
+// the target platform may not be the host platform. Its state includes the
+// current offset, necessary alignment for the sequence of types, and the size
+// of pointers and alignment of slices, interfaces, and strings (this is for tearing-
+// resistant access to these types, if/when that is supported).
+type Offset struct {
+ off uint64 // the current offset
+ align uint8 // the required alignmentof the container
+ ptrSize uint8 // the size of a pointer in bytes
+ sliceAlign uint8 // the alignment of slices (and interfaces and strings)
+}
+
+// NewOffset returns a new Offset with offset 0 and alignment 1.
+func NewOffset(ptrSize uint8, twoWordAlignSlices bool) Offset {
+ if twoWordAlignSlices {
+ return Offset{off: 0, align: 1, ptrSize: ptrSize, sliceAlign: 2 * ptrSize}
+ }
+ return Offset{off: 0, align: 1, ptrSize: ptrSize, sliceAlign: ptrSize}
+}
+
+func assertIsAPowerOfTwo(x uint8) {
+ if x == 0 {
+ panic("Zero is not a power of two")
+ }
+ if x&-x == x {
+ return
+ }
+ panic("Not a power of two")
+}
+
+// InitializedOffset returns a new Offset with specified offset, alignment, pointer size, and slice alignment.
+func InitializedOffset(off int, align uint8, ptrSize uint8, twoWordAlignSlices bool) Offset {
+ assertIsAPowerOfTwo(align)
+ o0 := NewOffset(ptrSize, twoWordAlignSlices)
+ o0.off = uint64(off)
+ o0.align = align
+ return o0
+}
+
+func (o Offset) align_(a uint8) Offset {
+ o.off = (o.off + uint64(a) - 1) & ^(uint64(a) - 1)
+ if o.align < a {
+ o.align = a
+ }
+ return o
+}
+
+// Align returns the offset obtained by aligning offset to a multiple of a.
+// a must be a power of two.
+func (o Offset) Align(a uint8) Offset {
+ assertIsAPowerOfTwo(a)
+ return o.align_(a)
+}
+
+// plus returns the offset obtained by appending a power-of-2-sized-and-aligned object to o.
+func (o Offset) plus(x uint64) Offset {
+ o = o.align_(uint8(x))
+ o.off += x
+ return o
+}
+
+// D8 returns the offset obtained by appending an 8-bit field to o.
+func (o Offset) D8() Offset {
+ return o.plus(1)
+}
+
+// D16 returns the offset obtained by appending a 16-bit field to o.
+func (o Offset) D16() Offset {
+ return o.plus(2)
+}
+
+// D32 returns the offset obtained by appending a 32-bit field to o.
+func (o Offset) D32() Offset {
+ return o.plus(4)
+}
+
+// D64 returns the offset obtained by appending a 64-bit field to o.
+func (o Offset) D64() Offset {
+ return o.plus(8)
+}
+
+// D64 returns the offset obtained by appending a pointer field to o.
+func (o Offset) P() Offset {
+ if o.ptrSize == 0 {
+ panic("This offset has no defined pointer size")
+ }
+ return o.plus(uint64(o.ptrSize))
+}
+
+// Slice returns the offset obtained by appending a slice field to o.
+func (o Offset) Slice() Offset {
+ o = o.align_(o.sliceAlign)
+ o.off += 3 * uint64(o.ptrSize)
+ // There's been discussion of whether slices should be 2-word aligned to allow
+ // use of aligned 2-word load/store to prevent tearing, this is future proofing.
+ // In general, for purposes of struct layout (and very likely default C layout
+ // compatibility) the "size" of a Go type is rounded up to its alignment.
+ return o.Align(o.sliceAlign)
+}
+
+// String returns the offset obtained by appending a string field to o.
+func (o Offset) String() Offset {
+ o = o.align_(o.sliceAlign)
+ o.off += 2 * uint64(o.ptrSize)
+ return o // We "know" it needs no further alignment
+}
+
+// Interface returns the offset obtained by appending an interface field to o.
+func (o Offset) Interface() Offset {
+ o = o.align_(o.sliceAlign)
+ o.off += 2 * uint64(o.ptrSize)
+ return o // We "know" it needs no further alignment
+}
+
+// Offset returns the struct-aligned offset (size) of o.
+// This is at least as large as the current internal offset; it may be larger.
+func (o Offset) Offset() uint64 {
+ return o.Align(o.align).off
+}
+
+func (o Offset) PlusUncommon() Offset {
+ o.off += UncommonSize()
+ return o
+}
+
+// CommonOffset returns the Offset to the data after the common portion of type data structures.
+func CommonOffset(ptrSize int, twoWordAlignSlices bool) Offset {
+ return InitializedOffset(CommonSize(ptrSize), uint8(ptrSize), uint8(ptrSize), twoWordAlignSlices)
+}
diff --git a/src/internal/abi/type.go b/src/internal/abi/type.go
new file mode 100644
index 0000000000..73988b6a2b
--- /dev/null
+++ b/src/internal/abi/type.go
@@ -0,0 +1,712 @@
+// 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() uintptr {
+ if t.Kind() == Array {
+ return (*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("reflect.nameFrom: name too long: " + n[:1024] + "...")
+ }
+ if len(tag) >= 1<<29 {
+ panic("reflect.nameFrom: 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]}
+}
diff --git a/src/internal/abi/unsafestring_go119.go b/src/internal/abi/unsafestring_go119.go
new file mode 100644
index 0000000000..a7103849a4
--- /dev/null
+++ b/src/internal/abi/unsafestring_go119.go
@@ -0,0 +1,32 @@
+// 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.
+
+//go:build !go1.20
+// +build !go1.20
+
+package abi
+
+import "unsafe"
+
+type (
+ stringHeader struct {
+ Data *byte
+ Len int
+ }
+ sliceHeader struct {
+ Data *byte
+ Len int
+ Cap int
+ }
+)
+
+func unsafeStringFor(b *byte, l int) string {
+ h := stringHeader{Data: b, Len: l}
+ return *(*string)(unsafe.Pointer(&h))
+}
+
+func unsafeSliceFor(b *byte, l int) []byte {
+ h := sliceHeader{Data: b, Len: l, Cap: l}
+ return *(*[]byte)(unsafe.Pointer(&h))
+}
diff --git a/src/internal/abi/unsafestring_go120.go b/src/internal/abi/unsafestring_go120.go
new file mode 100644
index 0000000000..93ff8eacc8
--- /dev/null
+++ b/src/internal/abi/unsafestring_go120.go
@@ -0,0 +1,18 @@
+// 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.
+
+//go:build go1.20
+// +build go1.20
+
+package abi
+
+import "unsafe"
+
+func unsafeStringFor(b *byte, l int) string {
+ return unsafe.String(b, l)
+}
+
+func unsafeSliceFor(b *byte, l int) []byte {
+ return unsafe.Slice(b, l)
+}
diff --git a/src/internal/reflectlite/swapper.go b/src/internal/reflectlite/swapper.go
index fc402bb38a..1bc1bae87b 100644
--- a/src/internal/reflectlite/swapper.go
+++ b/src/internal/reflectlite/swapper.go
@@ -33,7 +33,7 @@ func Swapper(slice any) func(i, j int) {
typ := v.Type().Elem().(*rtype)
size := typ.Size()
- hasPtr := typ.ptrdata != 0
+ hasPtr := typ.PtrBytes != 0
// Some common & small cases, without using memmove:
if hasPtr {
diff --git a/src/internal/reflectlite/type.go b/src/internal/reflectlite/type.go
index 43440b1126..e913fb6e10 100644
--- a/src/internal/reflectlite/type.go
+++ b/src/internal/reflectlite/type.go
@@ -3,10 +3,13 @@
// license that can be found in the LICENSE file.
// Package reflectlite implements lightweight version of reflect, not using
-// any package except for "runtime" and "unsafe".
+// any package except for "runtime", "unsafe", and "internal/abi"
package reflectlite
-import "unsafe"
+import (
+ "internal/abi"
+ "unsafe"
+)
// Type is the representation of a Go type.
//
@@ -106,63 +109,11 @@ const (
const Ptr = Pointer
-// tflag is used by an rtype to signal what extra type information is
-// available in the memory directly following the rtype value.
-//
-// tflag values must be kept in sync with copies in:
-//
-// cmd/compile/internal/reflectdata/reflect.go
-// cmd/link/internal/ld/decodesym.go
-// runtime/type.go
-type tflag uint8
+type nameOff = abi.NameOff
+type typeOff = abi.TypeOff
+type textOff = abi.TextOff
-const (
- // tflagUncommon means that there is a pointer, *uncommonType,
- // just beyond the outer type structure.
- //
- // For example, if t.Kind() == Struct and t.tflag&tflagUncommon != 0,
- // then t has uncommonType data and it can be accessed as:
- //
- // type tUncommon struct {
- // structType
- // u uncommonType
- // }
- // u := &(*tUncommon)(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
-)
-
-// rtype is the common implementation of most values.
-// It is embedded in other struct types.
-//
-// rtype must be kept in sync with ../runtime/type.go:/^type._type.
-type rtype struct {
- size uintptr
- ptrdata uintptr // number of 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 *byte // garbage collection data
- str nameOff // string form
- ptrToThis typeOff // type for pointer to this type, may be zero
-}
+type rtype abi.Type
// Method on non-interface type
type method struct {
@@ -446,10 +397,6 @@ func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer
// Implemented in the runtime package.
func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer
-type nameOff int32 // offset to a name
-type typeOff int32 // offset to an *rtype
-type textOff int32 // offset from top of text section
-
func (t *rtype) nameOff(off nameOff) name {
return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))}
}
@@ -459,7 +406,7 @@ func (t *rtype) typeOff(off typeOff) *rtype {
}
func (t *rtype) uncommon() *uncommonType {
- if t.tflag&tflagUncommon == 0 {
+ if t.TFlag&abi.TFlagUncommon == 0 {
return nil
}
switch t.Kind() {
@@ -517,18 +464,18 @@ func (t *rtype) uncommon() *uncommonType {
}
func (t *rtype) String() string {
- s := t.nameOff(t.str).name()
- if t.tflag&tflagExtraStar != 0 {
+ s := t.nameOff(t.Str).name()
+ if t.TFlag&abi.TFlagExtraStar != 0 {
return s[1:]
}
return s
}
-func (t *rtype) Size() uintptr { return t.size }
+func (t *rtype) Size() uintptr { return t.Size_ }
-func (t *rtype) Kind() Kind { return Kind(t.kind & kindMask) }
+func (t *rtype) Kind() Kind { return Kind(t.Kind_ & kindMask) }
-func (t *rtype) pointers() bool { return t.ptrdata != 0 }
+func (t *rtype) pointers() bool { return t.PtrBytes != 0 }
func (t *rtype) common() *rtype { return t }
@@ -549,7 +496,7 @@ func (t *rtype) NumMethod() int {
}
func (t *rtype) PkgPath() string {
- if t.tflag&tflagNamed == 0 {
+ if t.TFlag&abi.TFlagNamed == 0 {
return ""
}
ut := t.uncommon()
@@ -560,7 +507,7 @@ func (t *rtype) PkgPath() string {
}
func (t *rtype) hasName() bool {
- return t.tflag&tflagNamed != 0
+ return t.TFlag&abi.TFlagNamed != 0
}
func (t *rtype) Name() string {
@@ -669,7 +616,7 @@ func (t *rtype) Out(i int) Type {
func (t *funcType) in() []*rtype {
uadd := unsafe.Sizeof(*t)
- if t.tflag&tflagUncommon != 0 {
+ if t.TFlag&abi.TFlagUncommon != 0 {
uadd += unsafe.Sizeof(uncommonType{})
}
if t.inCount == 0 {
@@ -680,7 +627,7 @@ func (t *funcType) in() []*rtype {
func (t *funcType) out() []*rtype {
uadd := unsafe.Sizeof(*t)
- if t.tflag&tflagUncommon != 0 {
+ if t.TFlag&abi.TFlagUncommon != 0 {
uadd += unsafe.Sizeof(uncommonType{})
}
outCount := t.outCount & (1<<15 - 1)
@@ -730,7 +677,7 @@ func (t *rtype) AssignableTo(u Type) bool {
}
func (t *rtype) Comparable() bool {
- return t.equal != nil
+ return t.Equal != nil
}
// implements reports whether the type V implements the interface type T.
@@ -970,5 +917,5 @@ func toType(t *rtype) Type {
// ifaceIndir reports whether t is stored indirectly in an interface value.
func ifaceIndir(t *rtype) bool {
- return t.kind&kindDirectIface == 0
+ return t.Kind_&kindDirectIface == 0
}
diff --git a/src/internal/reflectlite/value.go b/src/internal/reflectlite/value.go
index b9bca3ab44..ca31889cfc 100644
--- a/src/internal/reflectlite/value.go
+++ b/src/internal/reflectlite/value.go
@@ -89,7 +89,7 @@ func (f flag) ro() flag {
// pointer returns the underlying pointer represented by v.
// v.Kind() must be Pointer, Map, Chan, Func, or UnsafePointer
func (v Value) pointer() unsafe.Pointer {
- if v.typ.size != goarch.PtrSize || !v.typ.pointers() {
+ if v.typ.Size_ != goarch.PtrSize || !v.typ.pointers() {
panic("can't call pointer on a non-pointer Value")
}
if v.flag&flagIndir != 0 {