diff options
author | David Chase <drchase@google.com> | 2023-01-20 16:41:57 -0500 |
---|---|---|
committer | David Chase <drchase@google.com> | 2023-05-05 14:59:28 +0000 |
commit | bdc6ae579aa86d21183c612c8c37916f397afaa8 (patch) | |
tree | cc63ff843ab5a7f3c196c8fa2c74924bfee61d93 /src/internal | |
parent | dace96b9a12905b34af609eedaa6b43e30e7cdb1 (diff) | |
download | go-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.go | 167 | ||||
-rw-r--r-- | src/internal/abi/type.go | 712 | ||||
-rw-r--r-- | src/internal/abi/unsafestring_go119.go | 32 | ||||
-rw-r--r-- | src/internal/abi/unsafestring_go120.go | 18 | ||||
-rw-r--r-- | src/internal/reflectlite/swapper.go | 2 | ||||
-rw-r--r-- | src/internal/reflectlite/type.go | 95 | ||||
-rw-r--r-- | src/internal/reflectlite/value.go | 2 |
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 { |