Skip to main content

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}