swiftui-native 0.0.1

SwiftUI from pure Rust — no Swift bridge, no shim, only swift-runtime calls
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
//! Type metadata, witness tables and layout queries.

use super::*;
use core::ffi::c_void;

/// Get the View witness table for a type from the runtime.
/// # Safety
/// `meta` must be type metadata the Swift runtime produced.
pub unsafe fn get_view_wt(meta: *const c_void) -> *const c_void {
    unsafe { get_view_wt_for_proto(meta, resolve::view_proto()) }
}

/// Get a protocol witness table for a type via `swift_conformsToProtocol`.
///
/// Cached: a conformance lookup scans the registered conformance records, which
/// is not something to repeat on every view construction.
/// # Safety
/// `meta` must be type metadata and `proto` a protocol descriptor, both
/// produced by the Swift runtime.
pub unsafe fn get_view_wt_for_proto(meta: *const c_void, proto: *const c_void) -> *const c_void {
    // Measured in a release build: the runtime call costs 27.1 ns and a hit on
    // this map costs 7.5, so the cache earns its keep. It did not look that way
    // in a debug build, where an un-inlined `HashMap` made the cache the more
    // expensive half — the reason the lookup was briefly uncached.
    //
    // What was genuinely wrong was underneath: `conforms_to_protocol` used to
    // `dlsym` on every call, and a `dlsym` is 172 ns.
    use std::collections::HashMap;
    use std::sync::{Mutex, OnceLock};
    static CACHE: OnceLock<Mutex<HashMap<(usize, usize), usize>>> = OnceLock::new();
    let cache = CACHE.get_or_init(|| Mutex::new(HashMap::new()));
    let key = (meta as usize, proto as usize);
    if let Ok(map) = cache.lock() {
        if let Some(&hit) = map.get(&key) {
            return hit as *const c_void;
        }
    }
    let wt = unsafe { conforms_to_protocol(meta, proto) };
    if !wt.is_null() {
        if let Ok(mut map) = cache.lock() {
            map.insert(key, wt as usize);
        }
    }
    wt
}

unsafe fn conforms_to_protocol(meta: *const c_void, proto: *const c_void) -> *const c_void {
    // Resolved once. This used to `dlsym` on every call, which cost 190 ns of
    // the 220 the lookup took — six times the runtime call it was fetching.
    static SYM: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
    unsafe {
        let f = *SYM.get_or_init(|| {
            libc::dlsym((-2isize) as *mut c_void, c"swift_conformsToProtocol".as_ptr()) as usize
        });
        debug_assert!(f != 0, "swift_conformsToProtocol");
        type ConformsFn = unsafe extern "C" fn(*const c_void, *const c_void) -> *const c_void;
        let f: ConformsFn = core::mem::transmute(f as *const c_void);
        f(meta, proto)
    }
}

// ═══════════════════════════════════════════════════════════════════════════
// Generic metadata instantiation
// ═══════════════════════════════════════════════════════════════════════════

unsafe extern "C" {
    /// `swift_getTupleTypeMetadata` — builds metadata for an arbitrary tuple.
    /// `flags` low 16 bits = element count.
    fn swift_getTupleTypeMetadata(
        request: usize,
        flags: usize,
        elements: *const *const c_void,
        labels: *const u8,
        proposed_witnesses: *const c_void,
    ) -> *const c_void;
}

/// Call a metadata accessor that takes no key arguments (a non-generic but
/// resilient type, whose layout is only known at runtime).
/// # Safety
/// `accessor` must be a metadata accessor taking no key arguments. One that
/// takes them reads its arguments from registers this does not set.
pub unsafe fn metadata0(accessor: *const c_void) -> *const c_void {
    unsafe {
        type Acc = unsafe extern "C" fn(usize) -> *const c_void;
        let f: Acc = core::mem::transmute(accessor);
        f(0)
    }
}

/// Call a generic type's metadata accessor with one key argument.
///
/// # Get the count right, or use a mangled name
///
/// An accessor takes one word per generic parameter **and one per conformance
/// requirement**: `TupleView<T>` wants one, but `ImageRenderer<Content>` —
/// whose parameter is constrained to `View` — wants two, the metadata and the
/// witness table.
///
/// Passing too few does not fail. It returns metadata built from whatever was
/// in the register, caches it, and the *next* lookup of that type dies instead,
/// in `checkTransitiveCompleteness`, with nothing to connect it to the call
/// that caused it. Zero-filling the unused registers does not help either: the
/// answer is then deterministically wrong rather than randomly wrong. This has
/// cost four separate debugging sessions in this crate.
///
/// So prefer [`resolve_type_by_mangled_name`] whenever the type is known:
/// the runtime reads the requirements out of the descriptor itself and cannot
/// get the count wrong. The accessor is for arguments that are only known at
/// run time — a `TupleView` over a tuple built from a slice, an `Optional` of a
/// dynamic element — and both of those happen to be unconstrained.
/// # Safety
/// `accessor` must be a metadata accessor whose generic signature is exactly
/// one unconstrained parameter, and `a0` metadata for it. A constrained one
/// also wants a witness table — see [`generic_metadata2`].
pub unsafe fn generic_metadata1(accessor: *const c_void, a0: *const c_void) -> *const c_void {
    unsafe {
        type Acc = unsafe extern "C" fn(usize, *const c_void) -> *const c_void;
        let f: Acc = core::mem::transmute(accessor);
        f(0, a0)
    }
}

/// Call a generic type's metadata accessor with two key arguments.
///
/// Two words, which is either two unconstrained parameters
/// (`ModifiedContent<Content, Modifier>`) or one parameter and its witness
/// table (`ScrollView<Content>` where `Content: View`). See
/// [`generic_metadata1`] for what happens when the count is wrong.
///
/// Use for `Type<P: SomeProtocol>` — the second argument is the `P: SomeProtocol`
/// witness table, which the runtime stores alongside the metadata and which the
/// type's own methods load back out of it. Omitting it yields metadata that
/// looks correct (right name, right generic argument) but whose conformance slot
/// is garbage.
/// # Safety
/// `accessor` must be a metadata accessor taking one parameter and one
/// conformance, `a0` metadata for the parameter and `a1` the witness table for
/// the conformance. Passing them the other way round builds metadata that
/// looks right and whose conformance slot is not.
pub unsafe fn generic_metadata2(
    accessor: *const c_void,
    a0: *const c_void,
    a1: *const c_void,
) -> *const c_void {
    unsafe {
        type Acc = unsafe extern "C" fn(usize, *const c_void, *const c_void) -> *const c_void;
        let f: Acc = core::mem::transmute(accessor);
        f(0, a0, a1)
    }
}

/// Build metadata for a tuple of `elements`.
/// # Safety
/// Every element of `elements` must be type metadata.
pub unsafe fn tuple_metadata(elements: &[*const c_void]) -> *const c_void {
    unsafe {
        swift_getTupleTypeMetadata(
            0,
            elements.len(),
            elements.as_ptr(),
            core::ptr::null(),
            core::ptr::null(),
        )
    }
}

/// Instantiate a generic type that exports no metadata accessor.
///
/// `_OverlayModifier`, `GeometryReader` and friends have no `…Ma` symbol in
/// either framework, so there is nothing to call. What they do have is an
/// instantiation reachable by mangled name, and every metadata record carries a
/// pointer to the nominal type descriptor it was built from — the second word
/// for a struct. Read that off a known instantiation and the runtime will build
/// any other from it.
///
/// `args` is the generic argument vector: one word per parameter, then one per
/// conformance requirement, in declaration order.
///
/// # Safety
/// `known` must be struct metadata for some instantiation of the type wanted,
/// and `args` must match that type's generic signature.
/// Instantiate a generic type from its nominal descriptor directly.
///
/// [`generic_metadata_from`] reads the descriptor off an existing instantiation,
/// which needs one to exist. A type whose every initialiser is `@inlinable` —
/// `StrokeShapeView`, which Swift builds in line — has no instantiation to read
/// from and no function to call, but it does export its descriptor.
///
/// # Safety
/// `descriptor` must be a nominal type descriptor and `args` must match its
/// generic signature.
pub unsafe fn generic_metadata_from_descriptor(
    descriptor: *const c_void,
    args: &[*const c_void],
) -> *const c_void {
    unsafe {
        type Get = unsafe extern "C" fn(usize, *const *const c_void, *const c_void) -> *const c_void;
        static SYM: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
        let f = *SYM.get_or_init(|| {
            libc::dlsym((-2isize) as *mut c_void, c"swift_getGenericMetadata".as_ptr()) as usize
        });
        assert!(f != 0, "swift_getGenericMetadata");
        let get: Get = core::mem::transmute(f as *const c_void);
        get(0, args.as_ptr(), descriptor)
    }
}

/// # Safety
/// `known` must be struct metadata for some instantiation of the type wanted,
/// and `args` must match that type's generic signature — one word per
/// parameter, then one per conformance requirement, in declaration order.
pub unsafe fn generic_metadata_from(
    known: *const c_void,
    args: &[*const c_void],
) -> *const c_void {
    unsafe {
        let descriptor = *(known as *const *const c_void).add(1);
        type Get = unsafe extern "C" fn(usize, *const *const c_void, *const c_void) -> *const c_void;
        static SYM: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
        let f = *SYM.get_or_init(|| {
            libc::dlsym((-2isize) as *mut c_void, c"swift_getGenericMetadata".as_ptr()) as usize
        });
        assert!(f != 0, "swift_getGenericMetadata");
        let get: Get = core::mem::transmute(f as *const c_void);
        get(0, args.as_ptr(), descriptor)
    }
}

/// Where each element of a tuple sits inside it.
///
/// A tuple's metadata carries the layout the runtime chose, which is the only
/// way to know it: the elements are not simply packed, and their order in
/// memory is not something a caller may assume. The record is
///
/// ```text
///   0  kind
///   8  numElements
///  16  labels
///  24  [ { type, offset }, ... ]   // two words per element
/// ```
///
/// so element `i`'s offset is the second word of the `i`th pair.
///
/// # Safety
/// `metadata` must be tuple metadata with at least `count` elements.
pub unsafe fn tuple_element_offsets(metadata: *const c_void, count: usize) -> Vec<usize> {
    unsafe {
        debug_assert_eq!(
            *(metadata as *const usize).add(1),
            count,
            "tuple metadata has a different number of elements than asked for",
        );
        let elements = (metadata as *const u8).add(24) as *const usize;
        (0..count).map(|i| *elements.add(i * 2 + 1)).collect()
    }
}

/// Size in bytes of a type, from its value witness table.
/// # Safety
/// `metadata` must be type metadata, so that it has a value witness table.
pub unsafe fn type_size(metadata: *const c_void) -> usize {
    unsafe {
        (*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata)).size
    }
}

/// Stride in bytes of a type — the spacing between consecutive array elements,
/// which is not the same as its size once alignment padding is involved.
/// # Safety
/// As [`type_size`].
pub unsafe fn type_stride(metadata: *const c_void) -> usize {
    unsafe {
        (*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata)).stride
    }
}

/// How many bytes to allocate for a value of this type.
///
/// Always the larger of size and stride, and never zero. Sizing a buffer by
/// `size` alone is a trap: a callee may write the type's trailing padding, which
/// overruns the allocation. The damage is silent — it corrupts the heap and
/// surfaces much later as an allocator abort in an unrelated call.
/// # Safety
/// As [`type_size`].
pub unsafe fn type_bytes(metadata: *const c_void) -> usize {
    unsafe { type_stride(metadata).max(type_size(metadata)).max(1) }
}

/// Destroy a value in place, through its type's value witness.
///
/// The runtime hands back `+1` values — `swift_getAtKeyPath` writes an owned
/// copy into its `sret` buffer — and for a type holding references that is a
/// retain the caller inherits. Dropping the buffer without this leaks it, and
/// the leak is invisible: an `Int` key path behaves identically, so only a
/// reference-holding value type shows the difference.
///
/// # Safety
/// `value` must address an initialised value of the type `metadata` describes,
/// and must not be read afterwards.
pub unsafe fn type_destroy(metadata: *const c_void, value: *mut u8) {
    unsafe {
        let vwt = swift_runtime_sys::SwiftABI::get_value_witness_table(metadata);
        ((*vwt).destroy)(value as *mut _, metadata as *const _);
    }
}

/// Build a `Swift.Array` of `count` elements from raw bytes laid out at the
/// element type's stride.
/// # Safety
/// `elem_meta` must be type metadata, and `bytes` must hold `count`
/// initialised values of it laid out at its stride. The array takes ownership
/// of what is copied in, so those values must be `+1`.
pub unsafe fn swift_array(elem_meta: *const c_void, count: usize, bytes: &[u8]) -> u64 {
    let stride = unsafe { type_stride(elem_meta) };
    debug_assert!(bytes.len() >= stride * count);
    let (array, buf): (u64, u64);
    unsafe {
        core::arch::asm!("blr {f}", f = in(reg) resolve::array_alloc_uninit(),
            in("x0") count as u64,
            in("x1") elem_meta,
            lateout("x0") array, lateout("x1") buf,
            lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
            lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
            lateout("lr") _, lateout("x20") _, lateout("x21") _, clobber_abi("C"),
        );
        core::ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, stride * count);
    }
    array
}

/// Build a Swift `Set` holding `count` elements laid out in `bytes`.
///
/// Through `Set.init(arrayLiteral:)`, which takes the variadic parameter Swift
/// already lowers to an `Array` — so the one argument is an array word and no
/// per-element insertion is needed. `Set.insert` is the obvious route and the
/// wrong one: it is `mutating`, so `self` is an `inout` pointer in x20 while the
/// element is `__owned` and indirect, and it returns a tuple with a generic
/// member through x8. Every arrangement of that traps.
///
/// `Set<Element>` is `@frozen` around a single-word `_Variant` whatever its
/// element, so it comes back in x0 rather than through an indirect result.
///
/// # Safety
/// `bytes` must hold `count` initialised values of the type `elem_meta`
/// describes, and `hashable_wt` must be that type's `Hashable` conformance.
pub unsafe fn swift_set(
    elem_meta: *const c_void,
    hashable_wt: *const c_void,
    count: usize,
    bytes: &[u8],
) -> u64 {
    unsafe {
        let array = swift_array(elem_meta, count, bytes);
        let set_meta = generic_metadata2(resolve::set_ma(), elem_meta, hashable_wt);
        let out: u64;
        core::arch::asm!("blr {f}", f = in(reg) resolve::set_array_literal_init(),
            inlateout("x0") array => out,
            in("x1") elem_meta,
            in("x2") hashable_wt,
            in("x20") set_meta,
            lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
            lateout("x7") _, lateout("x8") _, lateout("x9") _, lateout("x10") _,
            lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
            lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
            lateout("x21") _, clobber_abi("C"),
        );
        out
    }
}

/// How many elements a Swift `Set` holds.
///
/// # Safety
/// `set` must be a `Set` of the type `elem_meta` describes.
pub unsafe fn set_count(set: u64, elem_meta: *const c_void, hashable_wt: *const c_void) -> usize {
    let count: i64;
    unsafe {
        core::arch::asm!("blr {f}", f = in(reg) resolve::set_count_getter(),
            inlateout("x0") set => count,
            in("x1") elem_meta,
            in("x2") hashable_wt,
            lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
            lateout("x7") _, lateout("x8") _, lateout("x9") _, lateout("x10") _,
            lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
            lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
            lateout("x20") _, lateout("x21") _, clobber_abi("C"),
        );
    }
    count.max(0) as usize
}

/// Copy a Swift value into uninitialised memory, at `+1`.
///
/// A `memcpy` is right only for a trivial type. Anything holding a reference
/// needs the retain that `initializeWithCopy` does — and a closure returns its
/// result *owned*, so handing back a borrowed copy renders correctly and then
/// kills the process when the receiver releases something nobody retained.
///
/// # Safety
/// `dest` must have room for the type `metadata` describes, and `src` must be
/// an initialised value of it.
pub unsafe fn copy_value(dest: *mut u8, src: *const u8, metadata: *const c_void) {
    unsafe {
        let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata);
        (vwt.initialize_with_copy)(dest as *mut c_void, src as *mut c_void, metadata);
    }
}

/// The elements and count of a Swift `Array`.
///
/// An array is one word pointing at `_ContiguousArrayStorage`: an object
/// header, then the count, the capacity, and finally the elements. Reading it
/// is how a Rust callback sees what SwiftUI handed it — building one with
/// [`swift_array`] is only half of the traffic.
///
/// # Safety
/// `array` must be a Swift `Array` whose elements have the given stride.
pub unsafe fn array_parts(array: u64) -> (*const u8, usize) {
    unsafe {
        let base = array as *const u8;
        let count = *(base.add(16) as *const usize);
        (base.add(32), count)
    }
}

/// How many key arguments a type's metadata accessor expects.
///
/// This is the number [`generic_metadata1`] and [`generic_metadata2`] have to
/// match, and the runtime keeps it in the type's context descriptor, where it
/// cannot be miscounted by hand: one word per generic parameter, plus one per
/// conformance requirement. `State<Value>` reports 1; `ImageRenderer<Content>`,
/// whose parameter is constrained to `View`, reports 2.
///
/// Takes metadata for *any* instantiation of the type — the count is a property
/// of the generic type, not of the arguments. Returns `None` for a type that is
/// not generic, and for metadata whose shape this does not know how to walk
/// (anything that is not a struct, enum, or class).
///
/// Reading it costs a handful of loads, so it is cheap enough to assert on. The
/// test `generic_metadata_accessors_match_the_arity_the_runtime_wants` uses it as the
/// oracle for every accessor this crate calls with a statically known type.
/// # Safety
/// `metadata` must be type metadata for a nominal type.
pub unsafe fn generic_key_argument_count(metadata: *const c_void) -> Option<u16> {
    if metadata.is_null() {
        return None;
    }
    unsafe {
        let words = metadata as *const usize;
        // Word 0 is the kind for a struct or enum and the isa for a class, and
        // the kinds are all small: anything above the reserved range is a
        // pointer, so the metadata is a class and keeps its descriptor at +64.
        let descriptor = if words.read() > 0x7ff {
            words.byte_add(64).read()
        } else {
            words.add(1).read()
        } as *const u8;
        if descriptor.is_null() {
            return None;
        }

        let flags = (descriptor as *const u32).read();
        // Bit 7 of the flags says the descriptor carries a generic signature.
        // Without one there is no header to read and no accessor to get wrong.
        if flags & 0x80 == 0 {
            return None;
        }

        // A type context descriptor is 20 bytes, and each kind adds its own
        // fields before the trailing objects: two words for a struct or an
        // enum, six for a class.
        let base = match flags & 0x1f {
            0x10 => 44,          // class
            0x11 | 0x12 => 28,   // struct, enum
            _ => return None,
        };
        // The generic header comes first among the trailing objects, behind two
        // relative pointers: the instantiation cache and the default pattern.
        // Then: parameters, requirements, key arguments, flags.
        let header = descriptor.add(base + 8) as *const u16;
        Some(header.add(2).read())
    }
}

/// Where a struct keeps each of its stored properties.
///
/// Composing a value by hand needs its field offsets, and padding each field to
/// eight is a guess that happens to hold for the simple cases. The metadata
/// knows: a struct descriptor ends with the number of fields and the offset —
/// *in words* — of the field-offset vector inside the metadata record, and that
/// vector is one `u32` per field.
///
/// ```text
///   descriptor + 0   flags
///              + 4   parent          (relative)
///              + 8   name            (relative)
///              + 12  access function (relative)
///              + 16  field types     (relative)
///              + 20  numFields
///              + 24  fieldOffsetVectorOffset   <- in words from the metadata
/// ```
///
/// Returns `None` when the type keeps no field-offset vector, which is what a
/// zero offset means — a struct with nothing stored, or one whose layout the
/// runtime did not record.
///
/// # Safety
/// `meta` must be struct metadata.
pub unsafe fn struct_field_offsets(meta: *const c_void) -> Option<Vec<usize>> {
    unsafe {
        let descriptor = *(meta as *const *const c_void).add(1);
        if descriptor.is_null() {
            return None;
        }
        let words = descriptor as *const u32;
        let num_fields = *words.add(5) as usize;
        let vector_at = *words.add(6) as usize;
        if num_fields == 0 || vector_at == 0 {
            return None;
        }
        let vector = (meta as *const u32).add(vector_at * 2);
        Some((0..num_fields).map(|i| *vector.add(i) as usize).collect())
    }
}


/// The mangled name of whatever `meta` describes.
///
/// A type built from an *opaque* return — `Circle.inset(by:)` returns
/// `some InsettableShape` — has no name at the call site to build a
/// [`crate::mangle::Type`] from, so anything that has to spell the type it
/// appears inside was stuck with the shape it started as. The runtime knows the
/// name: `swift_getMangledTypeName` is what `_mangledTypeName` calls.
///
/// The `$s` prefix is stripped, because that is the form this crate's manglers
/// pass around.
///
/// # Safety
/// `meta` must be type metadata.
pub unsafe fn mangled_type_name(meta: *const c_void) -> Option<Vec<u8>> {
    /// The runtime returns a pointer-and-length pair, which comes back in
    /// x0/x1 — a `#[repr(C)]` struct rather than a tuple, so the declaration is
    /// FFI-safe and says what the registers are.
    #[repr(C)]
    struct NamePair {
        data: *const u8,
        length: usize,
    }
    unsafe extern "C" {
        fn swift_getMangledTypeName(meta: *const c_void) -> NamePair;
    }
    unsafe {
        if meta.is_null() {
            return None;
        }
        let NamePair { data: p, length: n } = swift_getMangledTypeName(meta);
        if p.is_null() || n == 0 {
            return None;
        }
        let raw = core::slice::from_raw_parts(p, n);
        Some(raw.strip_prefix(b"$s").unwrap_or(raw).to_vec())
    }
}


/// The metadata of `meta`'s `index`th stored field.
///
/// [`struct_field_offsets`] says *where* each field is; this says *what* it is,
/// which is what reading a nested layout needs. `StrokeShapeView` has one field,
/// a `ModifiedContent`, whose two fields are the stroked shape and a
/// `_BackgroundModifier` — so the offsets that matter are a sum down three
/// levels, and only the first is the type's own.
///
/// The alternative is composing the nested type's mangled name and resolving
/// that, which needs every intermediate type to have a name worth writing.
/// `_BackgroundModifier` exports no metadata accessor, so it has none. Asking
/// the field record instead works whatever the type is called.
///
/// Field records store the type as a mangled name that may contain *symbolic
/// references* — a tag byte followed by four bytes of relative pointer, which
/// can include a zero — so the length is found by walking rather than by
/// `strlen`. The name is resolved in the outer type's generic context, which is
/// how a field whose type mentions `A` or `B` resolves to what this particular
/// instantiation put there.
///
/// # Safety
/// `meta` must be struct metadata.
pub unsafe fn field_type_metadata(meta: *const c_void, index: usize) -> *const c_void {
    unsafe extern "C" {
        fn swift_getTypeByMangledNameInContext(
            name: *const u8,
            length: usize,
            context: *const c_void,
            generic_args: *const *const c_void,
        ) -> *const c_void;
    }
    unsafe {
        let descriptor = *(meta as *const *const u8).add(1);
        if descriptor.is_null() {
            return core::ptr::null();
        }
        // Struct descriptor +16 is a relative pointer to the field descriptor.
        let at = descriptor.add(16);
        let rel = at.cast::<i32>().read_unaligned();
        if rel == 0 {
            return core::ptr::null();
        }
        let fields = at.offset(rel as isize);
        let record_size = fields.add(10).cast::<u16>().read_unaligned() as usize;
        let count = fields.add(12).cast::<u32>().read_unaligned() as usize;
        if index >= count || record_size == 0 {
            return core::ptr::null();
        }
        let record = fields.add(16 + index * record_size);
        let name_at = record.add(4);
        let name_rel = name_at.cast::<i32>().read_unaligned();
        if name_rel == 0 {
            return core::ptr::null();
        }
        let name = name_at.offset(name_rel as isize);

        // Walk to the terminating NUL, stepping over each symbolic reference's
        // four payload bytes.
        let mut len = 0usize;
        loop {
            let b = *name.add(len);
            match b {
                0 => break,
                0x01..=0x17 => len += 5,
                _ => len += 1,
            }
        }

        // A struct's generic arguments begin right after the kind word and the
        // descriptor pointer.
        let generic_args = (meta as *const *const c_void).add(2);
        swift_getTypeByMangledNameInContext(name, len, descriptor as *const c_void, generic_args)
    }
}

/// The tag of a payload enum case, by name.
///
/// A resilient enum's cases are not numbered anywhere this crate can read from
/// the outside — except in reflection, where the field descriptor lists them in
/// tag order: payload cases first, then the empty ones. So the tag is the case's
/// index in that list, and looking it up by name is exact where counting cases
/// in the documentation is guesswork.
///
/// This exists because the alternative kept being "write the number down".
/// `SymbolEffectConfiguration.Effect` has fifteen cases whose order is not the
/// order the documentation lists them in, and a wrong tag builds a plausible
/// value of a different case — the kind of mistake that renders, and renders
/// wrongly.
///
/// Returns `None` if the type has no field descriptor or no such case.
///
/// # Safety
/// `meta` must be enum metadata.
pub unsafe fn enum_case_index(meta: *const c_void, case: &str) -> Option<usize> {
    unsafe {
        let descriptor = *(meta as *const *const u8).add(1);
        if descriptor.is_null() {
            return None;
        }
        // Enum descriptor +16 is the relative pointer to the field descriptor,
        // the same slot a struct's is in.
        let at = descriptor.add(16);
        let rel = at.cast::<i32>().read_unaligned();
        if rel == 0 {
            return None;
        }
        let fields = at.offset(rel as isize);
        let record_size = fields.add(10).cast::<u16>().read_unaligned() as usize;
        let count = fields.add(12).cast::<u32>().read_unaligned() as usize;
        if record_size == 0 {
            return None;
        }
        for i in 0..count {
            // A record is { flags: u32, mangledTypeName: rel32, fieldName: rel32 };
            // the *name* is the third word, where `field_type_metadata` reads
            // the second.
            let record = fields.add(16 + i * record_size);
            let name_at = record.add(8);
            let name_rel = name_at.cast::<i32>().read_unaligned();
            if name_rel == 0 {
                continue;
            }
            let name = name_at.offset(name_rel as isize);
            let mut len = 0usize;
            while *name.add(len) != 0 {
                len += 1;
            }
            if core::str::from_utf8(core::slice::from_raw_parts(name, len)) == Ok(case) {
                return Some(i);
            }
        }
        None
    }
}