swiftui_native/abi/metadata.rs
1//! Type metadata, witness tables and layout queries.
2
3use super::*;
4use core::ffi::c_void;
5
6/// Get the View witness table for a type from the runtime.
7/// # Safety
8/// `meta` must be type metadata the Swift runtime produced.
9pub unsafe fn get_view_wt(meta: *const c_void) -> *const c_void {
10 unsafe { get_view_wt_for_proto(meta, resolve::view_proto()) }
11}
12
13/// Get a protocol witness table for a type via `swift_conformsToProtocol`.
14///
15/// Cached: a conformance lookup scans the registered conformance records, which
16/// is not something to repeat on every view construction.
17/// # Safety
18/// `meta` must be type metadata and `proto` a protocol descriptor, both
19/// produced by the Swift runtime.
20pub unsafe fn get_view_wt_for_proto(meta: *const c_void, proto: *const c_void) -> *const c_void {
21 // Measured in a release build: the runtime call costs 27.1 ns and a hit on
22 // this map costs 7.5, so the cache earns its keep. It did not look that way
23 // in a debug build, where an un-inlined `HashMap` made the cache the more
24 // expensive half — the reason the lookup was briefly uncached.
25 //
26 // What was genuinely wrong was underneath: `conforms_to_protocol` used to
27 // `dlsym` on every call, and a `dlsym` is 172 ns.
28 use std::collections::HashMap;
29 use std::sync::{Mutex, OnceLock};
30 static CACHE: OnceLock<Mutex<HashMap<(usize, usize), usize>>> = OnceLock::new();
31 let cache = CACHE.get_or_init(|| Mutex::new(HashMap::new()));
32 let key = (meta as usize, proto as usize);
33 if let Ok(map) = cache.lock() {
34 if let Some(&hit) = map.get(&key) {
35 return hit as *const c_void;
36 }
37 }
38 let wt = unsafe { conforms_to_protocol(meta, proto) };
39 if !wt.is_null() {
40 if let Ok(mut map) = cache.lock() {
41 map.insert(key, wt as usize);
42 }
43 }
44 wt
45}
46
47unsafe fn conforms_to_protocol(meta: *const c_void, proto: *const c_void) -> *const c_void {
48 // Resolved once. This used to `dlsym` on every call, which cost 190 ns of
49 // the 220 the lookup took — six times the runtime call it was fetching.
50 static SYM: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
51 unsafe {
52 let f = *SYM.get_or_init(|| {
53 libc::dlsym((-2isize) as *mut c_void, c"swift_conformsToProtocol".as_ptr()) as usize
54 });
55 debug_assert!(f != 0, "swift_conformsToProtocol");
56 type ConformsFn = unsafe extern "C" fn(*const c_void, *const c_void) -> *const c_void;
57 let f: ConformsFn = core::mem::transmute(f as *const c_void);
58 f(meta, proto)
59 }
60}
61
62// ═══════════════════════════════════════════════════════════════════════════
63// Generic metadata instantiation
64// ═══════════════════════════════════════════════════════════════════════════
65
66unsafe extern "C" {
67 /// `swift_getTupleTypeMetadata` — builds metadata for an arbitrary tuple.
68 /// `flags` low 16 bits = element count.
69 fn swift_getTupleTypeMetadata(
70 request: usize,
71 flags: usize,
72 elements: *const *const c_void,
73 labels: *const u8,
74 proposed_witnesses: *const c_void,
75 ) -> *const c_void;
76}
77
78/// Call a metadata accessor that takes no key arguments (a non-generic but
79/// resilient type, whose layout is only known at runtime).
80/// # Safety
81/// `accessor` must be a metadata accessor taking no key arguments. One that
82/// takes them reads its arguments from registers this does not set.
83pub unsafe fn metadata0(accessor: *const c_void) -> *const c_void {
84 unsafe {
85 type Acc = unsafe extern "C" fn(usize) -> *const c_void;
86 let f: Acc = core::mem::transmute(accessor);
87 f(0)
88 }
89}
90
91/// Call a generic type's metadata accessor with one key argument.
92///
93/// # Get the count right, or use a mangled name
94///
95/// An accessor takes one word per generic parameter **and one per conformance
96/// requirement**: `TupleView<T>` wants one, but `ImageRenderer<Content>` —
97/// whose parameter is constrained to `View` — wants two, the metadata and the
98/// witness table.
99///
100/// Passing too few does not fail. It returns metadata built from whatever was
101/// in the register, caches it, and the *next* lookup of that type dies instead,
102/// in `checkTransitiveCompleteness`, with nothing to connect it to the call
103/// that caused it. Zero-filling the unused registers does not help either: the
104/// answer is then deterministically wrong rather than randomly wrong. This has
105/// cost four separate debugging sessions in this crate.
106///
107/// So prefer [`resolve_type_by_mangled_name`] whenever the type is known:
108/// the runtime reads the requirements out of the descriptor itself and cannot
109/// get the count wrong. The accessor is for arguments that are only known at
110/// run time — a `TupleView` over a tuple built from a slice, an `Optional` of a
111/// dynamic element — and both of those happen to be unconstrained.
112/// # Safety
113/// `accessor` must be a metadata accessor whose generic signature is exactly
114/// one unconstrained parameter, and `a0` metadata for it. A constrained one
115/// also wants a witness table — see [`generic_metadata2`].
116pub unsafe fn generic_metadata1(accessor: *const c_void, a0: *const c_void) -> *const c_void {
117 unsafe {
118 type Acc = unsafe extern "C" fn(usize, *const c_void) -> *const c_void;
119 let f: Acc = core::mem::transmute(accessor);
120 f(0, a0)
121 }
122}
123
124/// Call a generic type's metadata accessor with two key arguments.
125///
126/// Two words, which is either two unconstrained parameters
127/// (`ModifiedContent<Content, Modifier>`) or one parameter and its witness
128/// table (`ScrollView<Content>` where `Content: View`). See
129/// [`generic_metadata1`] for what happens when the count is wrong.
130///
131/// Use for `Type<P: SomeProtocol>` — the second argument is the `P: SomeProtocol`
132/// witness table, which the runtime stores alongside the metadata and which the
133/// type's own methods load back out of it. Omitting it yields metadata that
134/// looks correct (right name, right generic argument) but whose conformance slot
135/// is garbage.
136/// # Safety
137/// `accessor` must be a metadata accessor taking one parameter and one
138/// conformance, `a0` metadata for the parameter and `a1` the witness table for
139/// the conformance. Passing them the other way round builds metadata that
140/// looks right and whose conformance slot is not.
141pub unsafe fn generic_metadata2(
142 accessor: *const c_void,
143 a0: *const c_void,
144 a1: *const c_void,
145) -> *const c_void {
146 unsafe {
147 type Acc = unsafe extern "C" fn(usize, *const c_void, *const c_void) -> *const c_void;
148 let f: Acc = core::mem::transmute(accessor);
149 f(0, a0, a1)
150 }
151}
152
153/// Build metadata for a tuple of `elements`.
154/// # Safety
155/// Every element of `elements` must be type metadata.
156pub unsafe fn tuple_metadata(elements: &[*const c_void]) -> *const c_void {
157 unsafe {
158 swift_getTupleTypeMetadata(
159 0,
160 elements.len(),
161 elements.as_ptr(),
162 core::ptr::null(),
163 core::ptr::null(),
164 )
165 }
166}
167
168/// Instantiate a generic type that exports no metadata accessor.
169///
170/// `_OverlayModifier`, `GeometryReader` and friends have no `…Ma` symbol in
171/// either framework, so there is nothing to call. What they do have is an
172/// instantiation reachable by mangled name, and every metadata record carries a
173/// pointer to the nominal type descriptor it was built from — the second word
174/// for a struct. Read that off a known instantiation and the runtime will build
175/// any other from it.
176///
177/// `args` is the generic argument vector: one word per parameter, then one per
178/// conformance requirement, in declaration order.
179///
180/// # Safety
181/// `known` must be struct metadata for some instantiation of the type wanted,
182/// and `args` must match that type's generic signature.
183/// Instantiate a generic type from its nominal descriptor directly.
184///
185/// [`generic_metadata_from`] reads the descriptor off an existing instantiation,
186/// which needs one to exist. A type whose every initialiser is `@inlinable` —
187/// `StrokeShapeView`, which Swift builds in line — has no instantiation to read
188/// from and no function to call, but it does export its descriptor.
189///
190/// # Safety
191/// `descriptor` must be a nominal type descriptor and `args` must match its
192/// generic signature.
193pub unsafe fn generic_metadata_from_descriptor(
194 descriptor: *const c_void,
195 args: &[*const c_void],
196) -> *const c_void {
197 unsafe {
198 type Get = unsafe extern "C" fn(usize, *const *const c_void, *const c_void) -> *const c_void;
199 static SYM: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
200 let f = *SYM.get_or_init(|| {
201 libc::dlsym((-2isize) as *mut c_void, c"swift_getGenericMetadata".as_ptr()) as usize
202 });
203 assert!(f != 0, "swift_getGenericMetadata");
204 let get: Get = core::mem::transmute(f as *const c_void);
205 get(0, args.as_ptr(), descriptor)
206 }
207}
208
209/// # Safety
210/// `known` must be struct metadata for some instantiation of the type wanted,
211/// and `args` must match that type's generic signature — one word per
212/// parameter, then one per conformance requirement, in declaration order.
213pub unsafe fn generic_metadata_from(
214 known: *const c_void,
215 args: &[*const c_void],
216) -> *const c_void {
217 unsafe {
218 let descriptor = *(known as *const *const c_void).add(1);
219 type Get = unsafe extern "C" fn(usize, *const *const c_void, *const c_void) -> *const c_void;
220 static SYM: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
221 let f = *SYM.get_or_init(|| {
222 libc::dlsym((-2isize) as *mut c_void, c"swift_getGenericMetadata".as_ptr()) as usize
223 });
224 assert!(f != 0, "swift_getGenericMetadata");
225 let get: Get = core::mem::transmute(f as *const c_void);
226 get(0, args.as_ptr(), descriptor)
227 }
228}
229
230/// Where each element of a tuple sits inside it.
231///
232/// A tuple's metadata carries the layout the runtime chose, which is the only
233/// way to know it: the elements are not simply packed, and their order in
234/// memory is not something a caller may assume. The record is
235///
236/// ```text
237/// 0 kind
238/// 8 numElements
239/// 16 labels
240/// 24 [ { type, offset }, ... ] // two words per element
241/// ```
242///
243/// so element `i`'s offset is the second word of the `i`th pair.
244///
245/// # Safety
246/// `metadata` must be tuple metadata with at least `count` elements.
247pub unsafe fn tuple_element_offsets(metadata: *const c_void, count: usize) -> Vec<usize> {
248 unsafe {
249 debug_assert_eq!(
250 *(metadata as *const usize).add(1),
251 count,
252 "tuple metadata has a different number of elements than asked for",
253 );
254 let elements = (metadata as *const u8).add(24) as *const usize;
255 (0..count).map(|i| *elements.add(i * 2 + 1)).collect()
256 }
257}
258
259/// Size in bytes of a type, from its value witness table.
260/// # Safety
261/// `metadata` must be type metadata, so that it has a value witness table.
262pub unsafe fn type_size(metadata: *const c_void) -> usize {
263 unsafe {
264 (*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata)).size
265 }
266}
267
268/// Stride in bytes of a type — the spacing between consecutive array elements,
269/// which is not the same as its size once alignment padding is involved.
270/// # Safety
271/// As [`type_size`].
272pub unsafe fn type_stride(metadata: *const c_void) -> usize {
273 unsafe {
274 (*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata)).stride
275 }
276}
277
278/// How many bytes to allocate for a value of this type.
279///
280/// Always the larger of size and stride, and never zero. Sizing a buffer by
281/// `size` alone is a trap: a callee may write the type's trailing padding, which
282/// overruns the allocation. The damage is silent — it corrupts the heap and
283/// surfaces much later as an allocator abort in an unrelated call.
284/// # Safety
285/// As [`type_size`].
286pub unsafe fn type_bytes(metadata: *const c_void) -> usize {
287 unsafe { type_stride(metadata).max(type_size(metadata)).max(1) }
288}
289
290/// Destroy a value in place, through its type's value witness.
291///
292/// The runtime hands back `+1` values — `swift_getAtKeyPath` writes an owned
293/// copy into its `sret` buffer — and for a type holding references that is a
294/// retain the caller inherits. Dropping the buffer without this leaks it, and
295/// the leak is invisible: an `Int` key path behaves identically, so only a
296/// reference-holding value type shows the difference.
297///
298/// # Safety
299/// `value` must address an initialised value of the type `metadata` describes,
300/// and must not be read afterwards.
301pub unsafe fn type_destroy(metadata: *const c_void, value: *mut u8) {
302 unsafe {
303 let vwt = swift_runtime_sys::SwiftABI::get_value_witness_table(metadata);
304 ((*vwt).destroy)(value as *mut _, metadata as *const _);
305 }
306}
307
308/// Build a `Swift.Array` of `count` elements from raw bytes laid out at the
309/// element type's stride.
310/// # Safety
311/// `elem_meta` must be type metadata, and `bytes` must hold `count`
312/// initialised values of it laid out at its stride. The array takes ownership
313/// of what is copied in, so those values must be `+1`.
314pub unsafe fn swift_array(elem_meta: *const c_void, count: usize, bytes: &[u8]) -> u64 {
315 let stride = unsafe { type_stride(elem_meta) };
316 debug_assert!(bytes.len() >= stride * count);
317 let (array, buf): (u64, u64);
318 unsafe {
319 core::arch::asm!("blr {f}", f = in(reg) resolve::array_alloc_uninit(),
320 in("x0") count as u64,
321 in("x1") elem_meta,
322 lateout("x0") array, lateout("x1") buf,
323 lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
324 lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
325 lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
326 lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
327 lateout("lr") _, lateout("x20") _, lateout("x21") _, clobber_abi("C"),
328 );
329 core::ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, stride * count);
330 }
331 array
332}
333
334/// Build a Swift `Set` holding `count` elements laid out in `bytes`.
335///
336/// Through `Set.init(arrayLiteral:)`, which takes the variadic parameter Swift
337/// already lowers to an `Array` — so the one argument is an array word and no
338/// per-element insertion is needed. `Set.insert` is the obvious route and the
339/// wrong one: it is `mutating`, so `self` is an `inout` pointer in x20 while the
340/// element is `__owned` and indirect, and it returns a tuple with a generic
341/// member through x8. Every arrangement of that traps.
342///
343/// `Set<Element>` is `@frozen` around a single-word `_Variant` whatever its
344/// element, so it comes back in x0 rather than through an indirect result.
345///
346/// # Safety
347/// `bytes` must hold `count` initialised values of the type `elem_meta`
348/// describes, and `hashable_wt` must be that type's `Hashable` conformance.
349pub unsafe fn swift_set(
350 elem_meta: *const c_void,
351 hashable_wt: *const c_void,
352 count: usize,
353 bytes: &[u8],
354) -> u64 {
355 unsafe {
356 let array = swift_array(elem_meta, count, bytes);
357 let set_meta = generic_metadata2(resolve::set_ma(), elem_meta, hashable_wt);
358 let out: u64;
359 core::arch::asm!("blr {f}", f = in(reg) resolve::set_array_literal_init(),
360 inlateout("x0") array => out,
361 in("x1") elem_meta,
362 in("x2") hashable_wt,
363 in("x20") set_meta,
364 lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
365 lateout("x7") _, lateout("x8") _, lateout("x9") _, lateout("x10") _,
366 lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
367 lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
368 lateout("x21") _, clobber_abi("C"),
369 );
370 out
371 }
372}
373
374/// How many elements a Swift `Set` holds.
375///
376/// # Safety
377/// `set` must be a `Set` of the type `elem_meta` describes.
378pub unsafe fn set_count(set: u64, elem_meta: *const c_void, hashable_wt: *const c_void) -> usize {
379 let count: i64;
380 unsafe {
381 core::arch::asm!("blr {f}", f = in(reg) resolve::set_count_getter(),
382 inlateout("x0") set => count,
383 in("x1") elem_meta,
384 in("x2") hashable_wt,
385 lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
386 lateout("x7") _, lateout("x8") _, lateout("x9") _, lateout("x10") _,
387 lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
388 lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
389 lateout("x20") _, lateout("x21") _, clobber_abi("C"),
390 );
391 }
392 count.max(0) as usize
393}
394
395/// Copy a Swift value into uninitialised memory, at `+1`.
396///
397/// A `memcpy` is right only for a trivial type. Anything holding a reference
398/// needs the retain that `initializeWithCopy` does — and a closure returns its
399/// result *owned*, so handing back a borrowed copy renders correctly and then
400/// kills the process when the receiver releases something nobody retained.
401///
402/// # Safety
403/// `dest` must have room for the type `metadata` describes, and `src` must be
404/// an initialised value of it.
405pub unsafe fn copy_value(dest: *mut u8, src: *const u8, metadata: *const c_void) {
406 unsafe {
407 let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata);
408 (vwt.initialize_with_copy)(dest as *mut c_void, src as *mut c_void, metadata);
409 }
410}
411
412/// The elements and count of a Swift `Array`.
413///
414/// An array is one word pointing at `_ContiguousArrayStorage`: an object
415/// header, then the count, the capacity, and finally the elements. Reading it
416/// is how a Rust callback sees what SwiftUI handed it — building one with
417/// [`swift_array`] is only half of the traffic.
418///
419/// # Safety
420/// `array` must be a Swift `Array` whose elements have the given stride.
421pub unsafe fn array_parts(array: u64) -> (*const u8, usize) {
422 unsafe {
423 let base = array as *const u8;
424 let count = *(base.add(16) as *const usize);
425 (base.add(32), count)
426 }
427}
428
429/// How many key arguments a type's metadata accessor expects.
430///
431/// This is the number [`generic_metadata1`] and [`generic_metadata2`] have to
432/// match, and the runtime keeps it in the type's context descriptor, where it
433/// cannot be miscounted by hand: one word per generic parameter, plus one per
434/// conformance requirement. `State<Value>` reports 1; `ImageRenderer<Content>`,
435/// whose parameter is constrained to `View`, reports 2.
436///
437/// Takes metadata for *any* instantiation of the type — the count is a property
438/// of the generic type, not of the arguments. Returns `None` for a type that is
439/// not generic, and for metadata whose shape this does not know how to walk
440/// (anything that is not a struct, enum, or class).
441///
442/// Reading it costs a handful of loads, so it is cheap enough to assert on. The
443/// test `generic_metadata_accessors_match_the_arity_the_runtime_wants` uses it as the
444/// oracle for every accessor this crate calls with a statically known type.
445/// # Safety
446/// `metadata` must be type metadata for a nominal type.
447pub unsafe fn generic_key_argument_count(metadata: *const c_void) -> Option<u16> {
448 if metadata.is_null() {
449 return None;
450 }
451 unsafe {
452 let words = metadata as *const usize;
453 // Word 0 is the kind for a struct or enum and the isa for a class, and
454 // the kinds are all small: anything above the reserved range is a
455 // pointer, so the metadata is a class and keeps its descriptor at +64.
456 let descriptor = if words.read() > 0x7ff {
457 words.byte_add(64).read()
458 } else {
459 words.add(1).read()
460 } as *const u8;
461 if descriptor.is_null() {
462 return None;
463 }
464
465 let flags = (descriptor as *const u32).read();
466 // Bit 7 of the flags says the descriptor carries a generic signature.
467 // Without one there is no header to read and no accessor to get wrong.
468 if flags & 0x80 == 0 {
469 return None;
470 }
471
472 // A type context descriptor is 20 bytes, and each kind adds its own
473 // fields before the trailing objects: two words for a struct or an
474 // enum, six for a class.
475 let base = match flags & 0x1f {
476 0x10 => 44, // class
477 0x11 | 0x12 => 28, // struct, enum
478 _ => return None,
479 };
480 // The generic header comes first among the trailing objects, behind two
481 // relative pointers: the instantiation cache and the default pattern.
482 // Then: parameters, requirements, key arguments, flags.
483 let header = descriptor.add(base + 8) as *const u16;
484 Some(header.add(2).read())
485 }
486}
487
488/// Where a struct keeps each of its stored properties.
489///
490/// Composing a value by hand needs its field offsets, and padding each field to
491/// eight is a guess that happens to hold for the simple cases. The metadata
492/// knows: a struct descriptor ends with the number of fields and the offset —
493/// *in words* — of the field-offset vector inside the metadata record, and that
494/// vector is one `u32` per field.
495///
496/// ```text
497/// descriptor + 0 flags
498/// + 4 parent (relative)
499/// + 8 name (relative)
500/// + 12 access function (relative)
501/// + 16 field types (relative)
502/// + 20 numFields
503/// + 24 fieldOffsetVectorOffset <- in words from the metadata
504/// ```
505///
506/// Returns `None` when the type keeps no field-offset vector, which is what a
507/// zero offset means — a struct with nothing stored, or one whose layout the
508/// runtime did not record.
509///
510/// # Safety
511/// `meta` must be struct metadata.
512pub unsafe fn struct_field_offsets(meta: *const c_void) -> Option<Vec<usize>> {
513 unsafe {
514 let descriptor = *(meta as *const *const c_void).add(1);
515 if descriptor.is_null() {
516 return None;
517 }
518 let words = descriptor as *const u32;
519 let num_fields = *words.add(5) as usize;
520 let vector_at = *words.add(6) as usize;
521 if num_fields == 0 || vector_at == 0 {
522 return None;
523 }
524 let vector = (meta as *const u32).add(vector_at * 2);
525 Some((0..num_fields).map(|i| *vector.add(i) as usize).collect())
526 }
527}
528
529
530/// The mangled name of whatever `meta` describes.
531///
532/// A type built from an *opaque* return — `Circle.inset(by:)` returns
533/// `some InsettableShape` — has no name at the call site to build a
534/// [`crate::mangle::Type`] from, so anything that has to spell the type it
535/// appears inside was stuck with the shape it started as. The runtime knows the
536/// name: `swift_getMangledTypeName` is what `_mangledTypeName` calls.
537///
538/// The `$s` prefix is stripped, because that is the form this crate's manglers
539/// pass around.
540///
541/// # Safety
542/// `meta` must be type metadata.
543pub unsafe fn mangled_type_name(meta: *const c_void) -> Option<Vec<u8>> {
544 /// The runtime returns a pointer-and-length pair, which comes back in
545 /// x0/x1 — a `#[repr(C)]` struct rather than a tuple, so the declaration is
546 /// FFI-safe and says what the registers are.
547 #[repr(C)]
548 struct NamePair {
549 data: *const u8,
550 length: usize,
551 }
552 unsafe extern "C" {
553 fn swift_getMangledTypeName(meta: *const c_void) -> NamePair;
554 }
555 unsafe {
556 if meta.is_null() {
557 return None;
558 }
559 let NamePair { data: p, length: n } = swift_getMangledTypeName(meta);
560 if p.is_null() || n == 0 {
561 return None;
562 }
563 let raw = core::slice::from_raw_parts(p, n);
564 Some(raw.strip_prefix(b"$s").unwrap_or(raw).to_vec())
565 }
566}
567
568
569/// The metadata of `meta`'s `index`th stored field.
570///
571/// [`struct_field_offsets`] says *where* each field is; this says *what* it is,
572/// which is what reading a nested layout needs. `StrokeShapeView` has one field,
573/// a `ModifiedContent`, whose two fields are the stroked shape and a
574/// `_BackgroundModifier` — so the offsets that matter are a sum down three
575/// levels, and only the first is the type's own.
576///
577/// The alternative is composing the nested type's mangled name and resolving
578/// that, which needs every intermediate type to have a name worth writing.
579/// `_BackgroundModifier` exports no metadata accessor, so it has none. Asking
580/// the field record instead works whatever the type is called.
581///
582/// Field records store the type as a mangled name that may contain *symbolic
583/// references* — a tag byte followed by four bytes of relative pointer, which
584/// can include a zero — so the length is found by walking rather than by
585/// `strlen`. The name is resolved in the outer type's generic context, which is
586/// how a field whose type mentions `A` or `B` resolves to what this particular
587/// instantiation put there.
588///
589/// # Safety
590/// `meta` must be struct metadata.
591pub unsafe fn field_type_metadata(meta: *const c_void, index: usize) -> *const c_void {
592 unsafe extern "C" {
593 fn swift_getTypeByMangledNameInContext(
594 name: *const u8,
595 length: usize,
596 context: *const c_void,
597 generic_args: *const *const c_void,
598 ) -> *const c_void;
599 }
600 unsafe {
601 let descriptor = *(meta as *const *const u8).add(1);
602 if descriptor.is_null() {
603 return core::ptr::null();
604 }
605 // Struct descriptor +16 is a relative pointer to the field descriptor.
606 let at = descriptor.add(16);
607 let rel = at.cast::<i32>().read_unaligned();
608 if rel == 0 {
609 return core::ptr::null();
610 }
611 let fields = at.offset(rel as isize);
612 let record_size = fields.add(10).cast::<u16>().read_unaligned() as usize;
613 let count = fields.add(12).cast::<u32>().read_unaligned() as usize;
614 if index >= count || record_size == 0 {
615 return core::ptr::null();
616 }
617 let record = fields.add(16 + index * record_size);
618 let name_at = record.add(4);
619 let name_rel = name_at.cast::<i32>().read_unaligned();
620 if name_rel == 0 {
621 return core::ptr::null();
622 }
623 let name = name_at.offset(name_rel as isize);
624
625 // Walk to the terminating NUL, stepping over each symbolic reference's
626 // four payload bytes.
627 let mut len = 0usize;
628 loop {
629 let b = *name.add(len);
630 match b {
631 0 => break,
632 0x01..=0x17 => len += 5,
633 _ => len += 1,
634 }
635 }
636
637 // A struct's generic arguments begin right after the kind word and the
638 // descriptor pointer.
639 let generic_args = (meta as *const *const c_void).add(2);
640 swift_getTypeByMangledNameInContext(name, len, descriptor as *const c_void, generic_args)
641 }
642}
643
644/// The tag of a payload enum case, by name.
645///
646/// A resilient enum's cases are not numbered anywhere this crate can read from
647/// the outside — except in reflection, where the field descriptor lists them in
648/// tag order: payload cases first, then the empty ones. So the tag is the case's
649/// index in that list, and looking it up by name is exact where counting cases
650/// in the documentation is guesswork.
651///
652/// This exists because the alternative kept being "write the number down".
653/// `SymbolEffectConfiguration.Effect` has fifteen cases whose order is not the
654/// order the documentation lists them in, and a wrong tag builds a plausible
655/// value of a different case — the kind of mistake that renders, and renders
656/// wrongly.
657///
658/// Returns `None` if the type has no field descriptor or no such case.
659///
660/// # Safety
661/// `meta` must be enum metadata.
662pub unsafe fn enum_case_index(meta: *const c_void, case: &str) -> Option<usize> {
663 unsafe {
664 let descriptor = *(meta as *const *const u8).add(1);
665 if descriptor.is_null() {
666 return None;
667 }
668 // Enum descriptor +16 is the relative pointer to the field descriptor,
669 // the same slot a struct's is in.
670 let at = descriptor.add(16);
671 let rel = at.cast::<i32>().read_unaligned();
672 if rel == 0 {
673 return None;
674 }
675 let fields = at.offset(rel as isize);
676 let record_size = fields.add(10).cast::<u16>().read_unaligned() as usize;
677 let count = fields.add(12).cast::<u32>().read_unaligned() as usize;
678 if record_size == 0 {
679 return None;
680 }
681 for i in 0..count {
682 // A record is { flags: u32, mangledTypeName: rel32, fieldName: rel32 };
683 // the *name* is the third word, where `field_type_metadata` reads
684 // the second.
685 let record = fields.add(16 + i * record_size);
686 let name_at = record.add(8);
687 let name_rel = name_at.cast::<i32>().read_unaligned();
688 if name_rel == 0 {
689 continue;
690 }
691 let name = name_at.offset(name_rel as isize);
692 let mut len = 0usize;
693 while *name.add(len) != 0 {
694 len += 1;
695 }
696 if core::str::from_utf8(core::slice::from_raw_parts(name, len)) == Ok(case) {
697 return Some(i);
698 }
699 }
700 None
701 }
702}