typekind.mx raw
1 package runtime
2
3 // Minimal type introspection for interface equality and hashmap hashing.
4 // Replaces the reflectlite dependency in runtime.
5 // These constants and types mirror internal/reflectlite but are defined
6 // locally to break the runtime→reflectlite→runtime cycle.
7 //
8 // CRITICAL: struct layouts must exactly match those in
9 // internal/reflectlite/type.mx — the compiler generates type descriptors
10 // in that format.
11
12 import "unsafe"
13
14 type Kind uint8
15
16 const (
17 Invalid Kind = iota
18 Bool
19 Int
20 Int8
21 Int16
22 Int32
23 Int64
24 Uint
25 Uint8
26 Uint16
27 Uint32
28 Uint64
29 Uintptr
30 Float32
31 Float64
32 Complex64
33 Complex128
34 kindBytes // kind 17: unified string=[]byte
35 kindUnsafePointer
36 kindChan
37 kindInterface
38 kindPointer
39 kindSlice
40 kindArray
41 kindFunc
42 kindMap
43 kindStruct
44 )
45
46 const (
47 kindMask = 31
48 flagNamed = 32
49 flagComparable = 64
50 flagIsBinary = 128
51 )
52
53 // rawType is the base type descriptor. All type descriptors start with this.
54 type rawType struct {
55 meta uint8
56 }
57
58 // rtElemType: named, chan, slice, array, map — types with elem pointer.
59 // Layout: meta(1) pad(1) numMethod(2) pad(4) ptrTo(8) elem(8) = 24 bytes
60 type rtElemType struct {
61 rawType
62 numMethod uint16
63 ptrTo *rawType
64 elem *rawType
65 }
66
67 // rtPtrType: pointer types — no ptrTo field.
68 // Layout: meta(1) pad(1) numMethod(2) pad(4) elem(8) = 16 bytes
69 type rtPtrType struct {
70 rawType
71 numMethod uint16
72 elem *rawType
73 }
74
75 // rtArrayType: array types.
76 // Layout: meta(1) pad(1) numMethod(2) pad(4) ptrTo(8) elem(8) arrayLen(8) slicePtr(8) = 40 bytes
77 type rtArrayType struct {
78 rtElemType
79 arrayLen uintptr
80 slicePtr *rawType
81 }
82
83 // rtStructType: struct types. Does NOT have elem field — has pkgpath instead.
84 // Layout: meta(1) pad(1) numMethod(2) pad(4) ptrTo(8) pkgpath(8) size(4) numField(2) pad(2) fields(...)
85 type rtStructType struct {
86 rawType
87 numMethod uint16
88 ptrTo *rawType
89 pkgpath *byte
90 size uint32
91 numField uint16
92 fields [1]rtStructFieldDesc // flexible array member
93 }
94
95 // rtStructFieldDesc is a struct field descriptor.
96 type rtStructFieldDesc struct {
97 fieldType *rawType
98 data unsafe.Pointer
99 }
100
101 func (t *rawType) isNamed() bool {
102 if uintptr(unsafe.Pointer(t))&0b11 != 0 {
103 return false
104 }
105 return t.meta&flagNamed != 0
106 }
107
108 func (t *rawType) kind() Kind {
109 if t == nil {
110 return Invalid
111 }
112 if tag := uintptr(unsafe.Pointer(t)) & 0b11; tag != 0 {
113 return kindPointer
114 }
115 return Kind(t.meta & kindMask)
116 }
117
118 func (t *rawType) size() uintptr {
119 switch t.kind() {
120 case Bool, Int8, Uint8:
121 return 1
122 case Int16, Uint16:
123 return 2
124 case Int32, Uint32, Float32:
125 return 4
126 case Int64, Uint64, Float64:
127 return 8
128 case Int, Uint:
129 return unsafe.Sizeof(int(0))
130 case Uintptr:
131 return unsafe.Sizeof(uintptr(0))
132 case Complex64:
133 return 8
134 case Complex128:
135 return 16
136 case kindBytes:
137 return unsafe.Sizeof("")
138 case kindUnsafePointer, kindChan, kindMap, kindPointer:
139 return unsafe.Sizeof(uintptr(0))
140 case kindSlice:
141 return unsafe.Sizeof([]int{})
142 case kindInterface:
143 return unsafe.Sizeof(interface{}(nil))
144 case kindFunc:
145 var f func()
146 return unsafe.Sizeof(f)
147 case kindArray:
148 return t.elem().size() * uintptr(t.arrayLen())
149 case kindStruct:
150 return uintptr(t.structSize())
151 default:
152 return 0
153 }
154 }
155
156 // underlying returns the underlying type. For named types, follows the
157 // elem pointer in the namedType/elemType layout to the underlying type.
158 func (t *rawType) underlying() *rawType {
159 if t.isNamed() {
160 // namedType has same layout as elemType: elem is at the same offset.
161 return (*rtElemType)(unsafe.Pointer(t)).elem
162 }
163 return t
164 }
165
166 // elem returns the element type for pointer, chan, slice, array, map types.
167 func (t *rawType) elem() *rawType {
168 if tag := uintptr(unsafe.Pointer(t)) & 0b11; tag != 0 {
169 // Tagged pointer: peel one pointer level.
170 return (*rawType)(unsafe.Pointer(uintptr(unsafe.Pointer(t)) - 1))
171 }
172 u := t.underlying()
173 switch u.kind() {
174 case kindPointer:
175 return (*rtPtrType)(unsafe.Pointer(u)).elem
176 default:
177 return (*rtElemType)(unsafe.Pointer(u)).elem
178 }
179 }
180
181 func (t *rawType) arrayLen() int {
182 u := t.underlying()
183 return int((*rtArrayType)(unsafe.Pointer(u)).arrayLen)
184 }
185
186 func (t *rawType) structSize() uint32 {
187 u := t.underlying()
188 return (*rtStructType)(unsafe.Pointer(u)).size
189 }
190
191 func (t *rawType) numField() int {
192 u := t.underlying()
193 return int((*rtStructType)(unsafe.Pointer(u)).numField)
194 }
195
196 // structFieldType returns the type of the i-th field of a struct.
197 func (t *rawType) structFieldType(i int) *rawType {
198 u := t.underlying()
199 st := (*rtStructType)(unsafe.Pointer(u))
200 fd := (*rtStructFieldDesc)(unsafe.Add(
201 unsafe.Pointer(&st.fields[0]),
202 uintptr(i)*unsafe.Sizeof(rtStructFieldDesc{}),
203 ))
204 return fd.fieldType
205 }
206
207 // structFieldOffset returns the offset of the i-th field by reading the
208 // varint-encoded offset from the field descriptor's data pointer.
209 // Data format: flags(1 byte) + varint(offset) + name(null-term) + ...
210 func (t *rawType) structFieldOffset(i int) uintptr {
211 u := t.underlying()
212 st := (*rtStructType)(unsafe.Pointer(u))
213 fd := (*rtStructFieldDesc)(unsafe.Add(
214 unsafe.Pointer(&st.fields[0]),
215 uintptr(i)*unsafe.Sizeof(rtStructFieldDesc{}),
216 ))
217 p := (*uint8)(fd.data)
218 // skip flags byte
219 p = (*uint8)(unsafe.Add(unsafe.Pointer(p), 1))
220 // read varint-encoded offset
221 var offset uintptr
222 var shift uint
223 for {
224 b := *p
225 offset |= uintptr(b&0x7f) << shift
226 if b&0x80 == 0 {
227 break
228 }
229 shift += 7
230 p = (*uint8)(unsafe.Add(unsafe.Pointer(p), 1))
231 }
232 return offset
233 }
234