Skip to main content

swiftui_native/
state.rs

1//! A Swift view type synthesised at runtime, with real `@State` storage.
2//!
3//! Everything else in this crate hands SwiftUI an `AnyView`, which erases the
4//! identity SwiftUI needs in order to see a *change* rather than a replacement.
5//! That is why an external mutation never animated, why `withAnimation` had
6//! nothing to interpolate, and why redisplay had to be driven by polling.
7//!
8//! This module removes that constraint by building an actual Swift struct at
9//! runtime:
10//!
11//! ```text
12//!   struct RustView: View {
13//!       @State var value: Int
14//!       var body: some View { <built by a Rust closure> }
15//!   }
16//! ```
17//!
18//! which means synthesising, by hand:
19//!
20//! * a nominal type descriptor with one field;
21//! * **reflection field metadata** — a field descriptor naming the field and its
22//!   type as `SwiftUI.State<Swift.Int>`. This is the part that matters: SwiftUI
23//!   discovers `DynamicProperty` storage by walking a type's reflection records,
24//!   so without it `@State` is simply never initialised;
25//! * struct metadata carrying a field offset vector;
26//! * a `View` conformance with all seven witnesses, three of which are the
27//!   underscored statics that point at SwiftUI's own protocol-extension defaults;
28//! * a registered protocol conformance record, so the runtime can find it.
29//!
30//! The struct has exactly one stored property, so its layout is identical to
31//! `State<Int>` — which lets it borrow that type's value witness table instead
32//! of needing one synthesised too.
33//!
34//! This works: SwiftUI installs the `@State` property, evaluates the body, and
35//! re-renders when the value changes — dependency tracking, not polling.
36//!
37//! # Two things that were not obvious
38//!
39//! **A value witness table cannot be borrowed.** The first attempt reused
40//! `State<Int>`'s, reasoning that a single-field struct is layout-identical to
41//! its field — which the sizes confirm. But Swift *shares* one value witness
42//! table between layout-compatible types, so `State<Int>`'s witnesses are
43//! literally `SwiftUI._AnimationView`'s outlined functions. They are written for
44//! that type and fault on any other. `value_witness_table` synthesises a
45//! correct one instead.
46//!
47//! **Property accessors take the struct's own metadata.** `State<Value>`'s
48//! `wrappedValue` getter wants `State<Int>.Type`, not `Int.Type`. Passing the
49//! generic argument instead compiles, runs, and faults inside
50//! `State.getValue(forReading:)`.
51//!
52//! Compare [`crate::views::timeline`], which also lets Rust state drive the
53//! interface but re-renders every frame regardless of whether anything changed.
54
55use crate::abi;
56use crate::resolve;
57use crate::views::ViewHandle;
58use core::ffi::c_void;
59use crate::mangle;
60
61/// The Rust closure that produces the body, given the current state value.
62// The registry is shared, and SwiftUI calls the body from the main thread,
63// so the builder has to be `Send`.
64type BodyBuilder = Box<dyn Fn(i64) -> ViewHandle + Send>;
65
66/// One entry per synthesised view instance.
67struct Instance {
68    builder: BodyBuilder,
69    /// The live `Binding<Int>` projected out of SwiftUI's own storage, captured
70    /// the first time the body runs. Writing through it is what invalidates.
71    binding: Option<Vec<u8>>,
72}
73
74/// Every live instance, keyed by the id carried in the view's second field.
75///
76/// The body witness is one `extern "C"` function shared by every instance, so
77/// it needs some way to tell them apart. Giving the struct a second stored
78/// property — a plain `Int` — and looking the instance up by it is what allows
79/// more than one of these to exist at a time.
80static INSTANCES: std::sync::Mutex<Option<std::collections::HashMap<i64, Instance>>> =
81    std::sync::Mutex::new(None);
82static NEXT_ID: std::sync::atomic::AtomicI64 = std::sync::atomic::AtomicI64::new(1);
83
84fn with_instances<R>(f: impl FnOnce(&mut std::collections::HashMap<i64, Instance>) -> R) -> R {
85    let mut guard = INSTANCES.lock().expect("instance registry poisoned");
86    f(guard.get_or_insert_with(std::collections::HashMap::new))
87}
88
89// `View.body`'s witness receives the indirect result in x8 and `self` in x20.
90core::arch::global_asm!(
91    ".p2align 2",
92    ".globl _rswift_view_body_thunk",
93    "_rswift_view_body_thunk:",
94    "mov x1, x20",
95    "mov x0, x8",
96    "b _rswift_view_body_getter",
97);
98
99unsafe extern "C" {
100    #[link_name = "rswift_view_body_thunk"]
101    fn view_body_thunk();
102}
103
104/// `body` — read the state SwiftUI is holding, then build the tree from Rust.
105#[unsafe(no_mangle)]
106unsafe extern "C" fn rswift_view_body_getter(out: *mut u8, this: *const u8) {
107    unsafe {
108        // Field 0 is the `State<Int>`; field 1 is the instance id.
109        let id = *(this.add(ID_OFFSET) as *const i64);
110
111        // The location box is live by the time SwiftUI evaluates the body. If it
112        // is not, the reflection metadata failed and reading would fault.
113        let value = if *(this.add(8) as *const u64) == 0 {
114            0
115        } else {
116            state_wrapped_value(this)
117        };
118
119        // Capture the projected Binding once; it is only obtainable from inside
120        // a body evaluation, because SwiftUI owns the storage.
121        let binding = state_projected_binding(this);
122        let view = with_instances(|map| {
123            let inst = map.get_mut(&id)?;
124            if inst.binding.is_none() {
125                inst.binding = Some(binding);
126            }
127            Some((inst.builder)(value))
128        });
129
130        let view = view.unwrap_or_else(crate::views::empty_view);
131        *(out as *mut u64) = view.into_anyview_word();
132    }
133}
134
135/// `State<Int>.wrappedValue` getter.
136unsafe fn state_wrapped_value(state: *const u8) -> i64 {
137    let mut out: i64 = 0;
138    unsafe {
139        core::arch::asm!("blr {f}", f = in(reg) resolve::state_wrapped_get(),
140            in("x8") &mut out as *mut i64,
141            in("x20") state,
142            // Property accessors on a generic struct take the *struct's* own
143            // metadata, not the generic argument's.
144            in("x0") abi::generic_metadata1(resolve::state_ma(), resolve::int_meta()),
145            lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
146            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
147            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
148            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
149            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
150        );
151    }
152    out
153}
154
155/// `State<Int>.projectedValue` getter, giving a `Binding<Int>`.
156unsafe fn state_projected_binding(state: *const u8) -> Vec<u8> {
157    unsafe {
158        let meta = abi::resolve_type_by_mangled_name(mangle::bound("7SwiftUI7BindingV", &[mangle::INT]).as_bytes());
159        assert!(!meta.is_null(), "Binding<Int> metadata");
160        let mut out = vec![0u8; abi::type_bytes(meta)];
161        core::arch::asm!("blr {f}", f = in(reg) resolve::state_projected_get(),
162            in("x8") out.as_mut_ptr(),
163            in("x20") state,
164            in("x0") abi::generic_metadata1(resolve::state_ma(), resolve::int_meta()),
165            lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
166            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
167            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
168            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
169            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
170        );
171        out
172    }
173}
174
175/// Write a new value through the captured `Binding`, which is what makes
176/// SwiftUI invalidate and re-run the body.
177///
178/// Returns false if the body has not run yet, so there is nothing to write to.
179pub fn set_state(id: i64, value: i64) -> bool {
180    let binding = with_instances(|map| map.get(&id).and_then(|i| i.binding.clone()));
181    let Some(mut binding) = binding else { return false };
182    unsafe {
183        let meta = abi::resolve_type_by_mangled_name(mangle::bound("7SwiftUI7BindingV", &[mangle::INT]).as_bytes());
184        let mut v = value;
185        core::arch::asm!("blr {f}", f = in(reg) resolve::binding_wrapped_set(),
186            in("x0") &mut v as *mut i64,
187            in("x1") meta,
188            in("x20") binding.as_mut_ptr(),
189            lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
190            lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
191            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
192            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
193            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
194        );
195    }
196    true
197}
198
199/// A runtime-built Swift view type with a real `@State`.
200///
201/// SwiftUI installs the storage itself: it walks the type's reflection field
202/// records looking for a stored property whose type conforms to
203/// `DynamicProperty`, finds the `State<Int>`, and attaches a location box. That
204/// is what makes [`set`](Self::set) invalidate the view and re-run the body,
205/// and what lets [`set_animated`](Self::set_animated) interpolate — a `@State`
206/// change is a change to a view SwiftUI already knows, unlike a root swap.
207///
208/// The catch is how quietly that walk fails. If the field's mangled type name
209/// does not demangle, the runtime reports the field as an empty tuple, SwiftUI
210/// finds no `DynamicProperty`, and everything still *works* — the body runs
211/// once, writes report success — except nothing ever updates. See the layout
212/// assertions in `build_type`.
213///
214/// # Several at once
215///
216/// The body witness is one `extern "C"` function with no room for an instance
217/// parameter, so the struct carries a second stored field holding an id. The
218/// witness reads it and looks the right builder up, which is what lets more
219/// than one `StatefulView` be live at a time.
220pub struct StatefulView {
221    /// Metadata for the synthesised struct.
222    pub metadata: *const c_void,
223    /// Its `View` witness table.
224    pub witness_table: *const c_void,
225    /// An instance, ready to hand to a hosting view.
226    value: Vec<u8>,
227    /// Identifies this instance to the shared body witness.
228    pub id: i64,
229}
230
231impl StatefulView {
232    /// Build the type and one instance of it.
233    ///
234    /// `builder` receives the current state value every time SwiftUI evaluates
235    /// the body.
236    ///
237    /// Hosting the result works: `examples/parity`'s `state_rebuild` check
238    /// hosts one, writes to it three times, and asserts the body saw each new
239    /// value. An earlier note here said it faulted in a value witness — that was
240    /// true of the first attempt, which borrowed `State<Int>`'s witness table;
241    /// the module docs explain why that cannot work and what replaced it.
242    pub fn new(initial: i64, builder: impl Fn(i64) -> ViewHandle + Send + 'static) -> Self {
243        unsafe {
244            let id = NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
245            with_instances(|map| {
246                map.insert(id, Instance { builder: Box::new(builder), binding: None })
247            });
248            let (metadata, witness_table) = build_type();
249            debug_assert_eq!(
250                field_offset(metadata, 1),
251                ID_OFFSET,
252                "the id moved; the body witness reads it at a fixed offset"
253            );
254
255            // The instance is a `State<Int>`, since that is the only field.
256            //
257            // `State<Int>` is the value inline followed by empty storage — the
258            // box is created by SwiftUI when it initialises the property, not by
259            // us — so it is built directly rather than through
260            // `State.init(wrappedValue:)`.
261            let mut value = vec![0u8; instance_bytes(metadata)];
262            value[..8].copy_from_slice(&initial.to_le_bytes());
263            value[ID_OFFSET..ID_OFFSET + 8].copy_from_slice(&id.to_le_bytes());
264
265            Self { metadata, witness_table, value, id }
266        }
267    }
268
269    /// Write a new value, re-running the body.
270    pub fn set(&self, value: i64) -> bool {
271        set_state(self.id, value)
272    }
273
274    /// Write a new value inside `withAnimation`, so SwiftUI interpolates.
275    pub fn set_animated(&self, value: i64) {
276        set_state_animated(self.id, value);
277    }
278
279    /// As [`set_animated`](Self::set_animated), with the animation spelled out —
280    /// `withAnimation(.easeInOut(duration: 1)) { ... }` and the like.
281    pub fn set_animated_with(&self, value: i64, animation: u64) {
282        set_state_animated_with(self.id, value, animation);
283    }
284
285    /// Wrap the instance in an `AnyView` so it can be hosted like any other view.
286    pub fn as_view(&self) -> ViewHandle {
287        unsafe { copy_as_view(&self.value, self.metadata, self.witness_table) }
288    }
289}
290
291// ── Value witnesses ──────────────────────────────────────────────────────
292//
293// The synthesised struct is one `State<Int>`: an `Int` at offset 0 and an
294// optional `AnyLocation<Int>` class reference at offset 8. That is enough to
295// write the witnesses directly, which is the only correct option — a value
296// witness table cannot be borrowed from another type, because Swift shares
297// tables between layout-compatible types and the functions in them are outlined
298// for whichever type they were emitted from.
299
300/// Field 1: the instance id, so one shared body witness can tell instances apart.
301const ID_OFFSET: usize = 16;
302
303
304/// Emit the descriptors, reflection metadata, conformance and witness table.
305///
306/// One allocation holds everything, so every 32-bit self-relative offset is
307/// trivially in range./// One stored property of a synthesised struct.
308struct FieldSpec {
309    /// The field's type, mangled and spelled out rather than given as a
310    /// symbolic reference — there is no image for the runtime to resolve a
311    /// reference against, and a name that fails to demangle is reported as an
312    /// empty tuple rather than as an error.
313    mangled_type: &'static [u8],
314    name: &'static [u8],
315    /// The field type's metadata.
316    ///
317    /// Offsets are computed from this rather than written out by the caller,
318    /// and so are the struct's value witnesses: each one dispatches to *this*
319    /// type's own witnesses for its slice of the value. That is what lets a
320    /// field be something other than a single refcounted word — an
321    /// `Environment<T>` is nine bytes with an enum inside it, and no
322    /// hand-written retain/release describes that correctly.
323    meta: *const c_void,
324}
325
326/// What [`emit_type`] worked out about a synthesised struct, kept so the value
327/// witnesses can find it again.
328struct TypeLayout {
329    /// `(field metadata, byte offset)`, in declaration order. The metadata is
330    /// held as a `usize` so the registry is `Send`; these point into the
331    /// Swift runtime and outlive everything here.
332    fields: Vec<(usize, usize)>,
333    size: usize,
334}
335
336// The metadata pointer is the key; the witnesses receive it as their last
337// argument. Registered after `emit_type` returns, which is safe because nothing
338// can copy a value of the type before it exists.
339static LAYOUTS: std::sync::Mutex<Option<std::collections::HashMap<usize, TypeLayout>>> =
340    std::sync::Mutex::new(None);
341
342fn with_layouts<R>(f: impl FnOnce(&mut std::collections::HashMap<usize, TypeLayout>) -> R) -> R {
343    let mut g = LAYOUTS.lock().expect("layout registry poisoned");
344    f(g.get_or_insert_with(std::collections::HashMap::new))
345}
346
347unsafe fn field_vwt(
348    meta: *const c_void,
349) -> *const swift_runtime_sys::SwiftABI::ValueWitnessTable {
350    unsafe { swift_runtime_sys::SwiftABI::get_value_witness_table(meta) }
351}
352
353/// Run `op` for every field of the type `meta` describes.
354///
355/// Returns false when the layout is unknown, which should not happen and means
356/// the caller must not touch the bytes.
357unsafe fn for_each_field(
358    meta: *const c_void,
359    mut op: impl FnMut(*const c_void, usize),
360) -> bool {
361    let fields = with_layouts(|m| m.get(&(meta as usize)).map(|l| l.fields.clone()));
362    match fields {
363        Some(fields) => {
364            for (fm, off) in fields {
365                op(fm as *const c_void, off);
366            }
367            true
368        }
369        None => false,
370    }
371}
372
373/// How many bytes an instance of the synthesised type takes, as laid out.
374unsafe fn instance_bytes(meta: *const c_void) -> usize {
375    let n = with_layouts(|m| m.get(&(meta as usize)).map_or(0, |l| l.size));
376    assert!(n > 0, "no layout registered for this synthesised type");
377    n
378}
379
380// Each witness walks the fields and calls that field type's own witness on its
381// slice. Swift shares one value witness table between layout-compatible types,
382// so these functions may well be outlined for some unrelated type — which is
383// fine here and was not when the whole struct borrowed one, because each is now
384// applied to exactly the type it was written for.
385
386unsafe extern "C" fn fw_destroy(value: *mut u8, meta: *const c_void) {
387    unsafe {
388        for_each_field(meta, |fm, off| {
389            ((*field_vwt(fm)).destroy)(value.add(off) as *mut c_void, fm);
390        });
391    }
392}
393
394unsafe extern "C" fn fw_init_with_copy(
395    dest: *mut u8,
396    src: *mut u8,
397    meta: *const c_void,
398) -> *mut u8 {
399    unsafe {
400        for_each_field(meta, |fm, off| {
401            ((*field_vwt(fm)).initialize_with_copy)(
402                dest.add(off) as *mut c_void,
403                src.add(off) as *mut c_void,
404                fm,
405            );
406        });
407    }
408    dest
409}
410
411unsafe extern "C" fn fw_assign_with_copy(
412    dest: *mut u8,
413    src: *mut u8,
414    meta: *const c_void,
415) -> *mut u8 {
416    unsafe {
417        for_each_field(meta, |fm, off| {
418            ((*field_vwt(fm)).assign_with_copy)(
419                dest.add(off) as *mut c_void,
420                src.add(off) as *mut c_void,
421                fm,
422            );
423        });
424    }
425    dest
426}
427
428unsafe extern "C" fn fw_init_with_take(
429    dest: *mut u8,
430    src: *mut u8,
431    meta: *const c_void,
432) -> *mut u8 {
433    unsafe {
434        for_each_field(meta, |fm, off| {
435            ((*field_vwt(fm)).initialize_with_take)(
436                dest.add(off) as *mut c_void,
437                src.add(off) as *mut c_void,
438                fm,
439            );
440        });
441    }
442    dest
443}
444
445unsafe extern "C" fn fw_assign_with_take(
446    dest: *mut u8,
447    src: *mut u8,
448    meta: *const c_void,
449) -> *mut u8 {
450    unsafe {
451        for_each_field(meta, |fm, off| {
452            ((*field_vwt(fm)).assign_with_take)(
453                dest.add(off) as *mut c_void,
454                src.add(off) as *mut c_void,
455                fm,
456            );
457        });
458    }
459    dest
460}
461
462/// The struct is larger than the three-word inline buffer in every case built
463/// here, so a buffer copy is a value copy.
464unsafe extern "C" fn fw_init_buffer(dest: *mut u8, src: *mut u8, meta: *const c_void) -> *mut u8 {
465    unsafe { fw_init_with_copy(dest, src, meta) }
466}
467
468/// No extra inhabitants are advertised, so there is never a spare case to read.
469unsafe extern "C" fn fw_get_tag(_v: *const u8, _n: u32, _m: *const c_void) -> u32 {
470    0
471}
472
473unsafe extern "C" fn fw_store_tag(_v: *mut u8, _w: u32, _n: u32, _m: *const c_void) {}
474
475/// A value witness table for a struct of `size` bytes at `align`, dispatching
476/// per field through [`LAYOUTS`].
477fn delegating_vwt(size: usize, align: usize) -> *const c_void {
478    unsafe {
479        let vwt = libc::malloc(96) as *mut u8;
480        assert!(!vwt.is_null());
481        core::ptr::write_bytes(vwt, 0, 96);
482        let f = vwt as *mut *const c_void;
483        *f.add(0) = fw_init_buffer as *const c_void;
484        *f.add(1) = fw_destroy as *const c_void;
485        *f.add(2) = fw_init_with_copy as *const c_void;
486        *f.add(3) = fw_assign_with_copy as *const c_void;
487        *f.add(4) = fw_init_with_take as *const c_void;
488        *f.add(5) = fw_assign_with_take as *const c_void;
489        *f.add(6) = fw_get_tag as *const c_void;
490        *f.add(7) = fw_store_tag as *const c_void;
491        *(vwt.add(64) as *mut usize) = size;
492        *(vwt.add(72) as *mut usize) = size;
493        // alignment mask | IsNonPOD — a synthesised view always holds storage
494        // SwiftUI installed, so it is never plain data.
495        *(vwt.add(80) as *mut u32) = 0x0001_0000 | (align as u32 - 1);
496        *(vwt.add(84) as *mut u32) = 0; // no extra inhabitants
497        vwt as *const c_void
498    }
499}
500
501/// Hand out a `+1` copy of a synthesised instance, for hosting.
502///
503/// [`ViewHandle::wrap`] takes ownership of a value the caller has just built,
504/// and destroys it when the handle drops. A bitwise copy of a value the owning
505/// struct still holds is therefore not a view to hand out: the handle's destroy
506/// releases fields that are still in use, and the next read of one faults
507/// somewhere else entirely. `@State` hid this for a long time, because SwiftUI
508/// installs its box in its *own* copy and ours stays null, so destroying it
509/// does nothing; an `@AppStorage` or `@Environment` field holds a real
510/// reference from the moment it is built.
511///
512/// So make a real copy first — through the type's own witnesses, which retain
513/// each field — and hand that over. `wrap`'s memcpy of an owned value into its
514/// own buffer is then a move, which is what the plain `Vec` being dropped
515/// without a destroy amounts to.
516unsafe fn copy_as_view(
517    value: &[u8],
518    metadata: *const c_void,
519    witness_table: *const c_void,
520) -> ViewHandle {
521    unsafe {
522        let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata);
523        let mut owned = vec![0u8; abi::type_bytes(metadata)];
524        (vwt.initialize_with_copy)(
525            owned.as_mut_ptr() as *mut c_void,
526            value.as_ptr() as *mut c_void,
527            metadata,
528        );
529        ViewHandle::wrap(owned.as_ptr() as *const c_void, metadata, witness_table)
530    }
531}
532
533/// Release what a synthesised instance holds, when its owner goes away.
534unsafe fn destroy_instance(value: &mut [u8], metadata: *const c_void) {
535    unsafe {
536        let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata);
537        if vwt.size > 0 {
538            (vwt.destroy)(value.as_mut_ptr() as *mut c_void, metadata);
539        }
540    }
541}
542
543/// The byte offset of field `i`, read back out of the metadata.
544///
545/// The body witnesses reach the instance id at a fixed offset, because a
546/// protocol witness gets `self` and nothing else. This is what lets that
547/// constant be checked against the layout rather than assumed.
548unsafe fn field_offset(metadata: *const c_void, i: usize) -> usize {
549    unsafe { *((metadata as *const u8).add(16 + 4 * i) as *const u32) as usize }
550}
551
552/// Emit the descriptors, reflection metadata, conformance and witness table for
553/// a struct with `fields`, and register the conformance.
554///
555/// Both synthesised types come through here. They differ only in their fields,
556/// their value witnesses and which `body` they point at — everything else about
557/// a minimal `View` conformance is the same, and having written it twice is how
558/// the two would drift.
559///
560/// One allocation holds everything, so every 32-bit self-relative offset is in
561/// range. That also means a slot starting too early silently overwrites the
562/// string before it, and that failure is close to invisible: the runtime
563/// reports the field as an empty tuple, SwiftUI finds no `DynamicProperty`, and
564/// everything still *works* except that nothing ever updates. Hence the
565/// assertions rather than trust.
566unsafe fn emit_type(
567    type_name: &[u8],
568    self_mangled: &[u8],
569    fields: &[FieldSpec],
570    body: *const c_void,
571) -> (*const c_void, *const c_void) {
572    const MODULE_NAME: &[u8] = b"rswift\0";
573    const MODULE: usize = 0;
574    const MODULE_NAME_OFF: usize = 16;
575    const STRUCT: usize = 32;
576    const TYPE_NAME_OFF: usize = 64;
577    // 128, not 80. The type name lives between `TYPE_NAME_OFF` and here, and
578    // sixteen bytes was enough for `RustLayout` and `RustApp` and not for
579    // `RustContainerKey0` — a synthesised name long enough to be descriptive is
580    // not an unreasonable thing to ask for, and everything after this offset is
581    // computed from it, so widening costs sixty-four unused bytes per type and
582    // nothing else.
583    const FIELD_DESC: usize = 128;
584
585    let n = fields.len();
586    assert!(n > 0, "a synthesised struct needs at least one field");
587
588    // Lay the fields out the way Swift would: each at its own alignment, the
589    // struct at the widest of them. Computing this beats writing offsets out
590    // by hand, which is how a field type of a different size silently
591    // overlaps its neighbour.
592    let mut offsets = Vec::with_capacity(n);
593    let mut at = 0usize;
594    let mut align = 1usize;
595    for f in fields {
596        let vw = unsafe { &*field_vwt(f.meta) };
597        let a = vw.flags.alignment().max(1);
598        align = align.max(a);
599        at = (at + a - 1) & !(a - 1);
600        offsets.push(at);
601        at += vw.stride.max(vw.size).max(1);
602    }
603    let size = ((at + align - 1) & !(align - 1)).max(1);
604    let vwt = delegating_vwt(size, align);
605    assert!(MODULE_NAME_OFF + MODULE_NAME.len() <= STRUCT);
606    assert!(
607        TYPE_NAME_OFF + type_name.len() <= FIELD_DESC,
608        "the synthesised type name {:?} needs {} bytes and there are {}",
609        core::str::from_utf8(type_name).unwrap_or("<not utf-8>"),
610        type_name.len(),
611        FIELD_DESC - TYPE_NAME_OFF
612    );
613
614    // Strings go end to end after the field records.
615    let field_desc_end = FIELD_DESC + 16 + 12 * n;
616    let mut strings: Vec<(usize, &[u8])> = Vec::with_capacity(2 * n + 3);
617    strings.push((MODULE_NAME_OFF, MODULE_NAME));
618    strings.push((TYPE_NAME_OFF, type_name));
619    let mut at = field_desc_end;
620    let mut field_string_offsets = Vec::with_capacity(n);
621    for f in fields {
622        let t = at;
623        strings.push((at, f.mangled_type));
624        at += f.mangled_type.len();
625        let name = at;
626        strings.push((at, f.name));
627        at += f.name.len();
628        field_string_offsets.push((t, name));
629    }
630    let self_mangled_off = at;
631    strings.push((at, self_mangled));
632    at += self_mangled.len();
633
634    let align8 = |x: usize| (x + 7) & !7;
635    let proto_slot = align8(at);
636    let conf = proto_slot + 16;
637    let record = conf + 24;
638    let witness = record + 8;
639    let total = witness + 7 * 8 + 8;
640
641    let block = unsafe { libc::malloc(total) } as *mut u8;
642    assert!(!block.is_null());
643    unsafe {
644        core::ptr::write_bytes(block, 0, total);
645        for (off, bytes) in strings {
646            core::ptr::copy_nonoverlapping(bytes.as_ptr(), block.add(off), bytes.len());
647        }
648    }
649
650    unsafe fn rel(field: *const u8, target: *const u8) -> i32 {
651        i32::try_from(target as isize - field as isize).expect("relative pointer out of range")
652    }
653
654    unsafe {
655        // Module context descriptor.
656        let m = block.add(MODULE);
657        *(m as *mut u32) = 0x40; // Kind = Module, IsUnique
658        *(m.add(4) as *mut i32) = 0;
659        *(m.add(8) as *mut i32) = rel(m.add(8), block.add(MODULE_NAME_OFF));
660
661        // Struct descriptor.
662        let d = block.add(STRUCT);
663        *(d as *mut u32) = 0x51; // Kind = Struct, IsUnique
664        *(d.add(4) as *mut i32) = rel(d.add(4), m);
665        *(d.add(8) as *mut i32) = rel(d.add(8), block.add(TYPE_NAME_OFF));
666        *(d.add(12) as *mut i32) = 0; // no metadata access function
667        *(d.add(16) as *mut i32) = rel(d.add(16), block.add(FIELD_DESC));
668        *(d.add(20) as *mut u32) = n as u32; // NumFields
669        *(d.add(24) as *mut u32) = 2; // FieldOffsetVectorOffset, in words
670
671        // Field descriptor. This is what SwiftUI walks to find the
672        // `DynamicProperty` storage it has to install.
673        let f = block.add(FIELD_DESC);
674        *(f as *mut i32) = rel(f, block.add(self_mangled_off));
675        *(f.add(4) as *mut i32) = 0; // no superclass
676        *(f.add(8) as *mut u16) = 0; // Kind = Struct
677        *(f.add(10) as *mut u16) = 12; // FieldRecordSize
678        *(f.add(12) as *mut u32) = n as u32;
679        for (i, (type_off, name_off)) in field_string_offsets.iter().enumerate() {
680            let r = f.add(16 + 12 * i);
681            *(r as *mut u32) = 0x2; // IsVar
682            *(r.add(4) as *mut i32) = rel(r.add(4), block.add(*type_off));
683            *(r.add(8) as *mut i32) = rel(r.add(8), block.add(*name_off));
684        }
685
686        // Metadata: [VWT][Kind][Descriptor][field offsets], one u32 per field.
687        let meta_bytes = 24 + 4 * n;
688        let meta = libc::malloc(meta_bytes) as *mut *const c_void;
689        assert!(!meta.is_null());
690        core::ptr::write_bytes(meta as *mut u8, 0, meta_bytes);
691        *meta.add(0) = vwt;
692        *(meta.add(1) as *mut usize) = 0x200; // Kind = Struct
693        *meta.add(2) = d as *const c_void;
694        for (i, off) in offsets.iter().enumerate() {
695            *((meta as *mut u8).add(24 + 4 * i) as *mut u32) = *off as u32;
696        }
697        let metadata = (meta as *const u8).add(8) as *const c_void;
698
699        // The witnesses find the layout by metadata pointer. Registered before
700        // the type escapes, so nothing can copy a value of it first.
701        with_layouts(|m| {
702            m.insert(
703                metadata as usize,
704                TypeLayout {
705                    fields: fields
706                        .iter()
707                        .zip(&offsets)
708                        .map(|(f, off)| (f.meta as usize, *off))
709                        .collect(),
710                    size,
711                },
712            )
713        });
714
715        // View witness table, in the order a real conformance uses:
716        //   [0] conformance descriptor
717        //   [1] associated conformance  Body: View
718        //   [2] associated type         Body
719        //   [3] _makeView          (SwiftUI's default)
720        //   [4] _makeViewList      (SwiftUI's default)
721        //   [5] _viewListCount     (SwiftUI's default)
722        //   [6] body getter
723        let wt = block.add(witness) as *mut *const c_void;
724        *wt.add(0) = block.add(conf) as *const c_void;
725        *wt.add(1) = resolve::anyview_wt();
726        *wt.add(2) = resolve::anyview_meta();
727        *wt.add(3) = resolve::view_make_view_default();
728        *wt.add(4) = resolve::view_make_view_list_default();
729        *wt.add(5) = resolve::view_list_count_default();
730        *wt.add(6) = body;
731
732        // Protocol conformance descriptor, in the simple form: a direct witness
733        // table pattern, no conditional requirements, no resilient witnesses.
734        *(block.add(proto_slot) as *mut *const c_void) = resolve::view_proto();
735        let c = block.add(conf);
736        *(c as *mut i32) = rel(c, block.add(proto_slot)) | 1; // indirectable
737        *(c.add(4) as *mut i32) = rel(c.add(4), d);
738        *(c.add(8) as *mut i32) = rel(c.add(8), block.add(witness));
739        *(c.add(12) as *mut u32) = 0;
740
741        let rec = block.add(record);
742        *(rec as *mut i32) = rel(rec, c);
743        type Register = unsafe extern "C" fn(*const u8, *const u8);
744        let reg = libc::dlsym(
745            (-2isize) as *mut c_void,
746            c"swift_registerProtocolConformances".as_ptr(),
747        );
748        assert!(!reg.is_null(), "swift_registerProtocolConformances not found");
749        let reg: Register = core::mem::transmute(reg);
750        reg(rec, rec.add(4));
751
752        (metadata, wt as *const c_void)
753    }
754}
755
756/// A nominal struct with one `Int` field, and nothing else.
757///
758/// [`emit_type`] also builds a `View` conformance, because everything it was
759/// written for is a view. A `Layout` is not: what it needs is a *named type*
760/// to be the `Self` of its own conformance, so that `_LayoutRoot<Self>` reads
761/// as something rather than as a borrowed `Spacer`.
762///
763/// Returns the type's nominal descriptor and its metadata. The `View`
764/// conformance the emitter registers alongside is unused and harmless — it
765/// describes a type nothing ever asks to render.
766///
767/// # Safety
768/// `type_name` and `self_mangled` must agree, and must not collide with
769/// another synthesised type.
770pub(crate) unsafe fn emit_int_carrier(
771    type_name: &'static [u8],
772    self_mangled: &'static [u8],
773) -> (*const c_void, *const c_void) {
774    unsafe {
775        let (metadata, _) = emit_type(
776            type_name,
777            self_mangled,
778            &[FieldSpec { mangled_type: b"Si\0", name: b"index\0", meta: resolve::int_meta() }],
779            stub_body as *const c_void,
780        );
781        let descriptor = *(metadata as *const *const c_void).add(1);
782        (descriptor, metadata)
783    }
784}
785
786/// A `body` that produces nothing, for a synthesised type that is not a view.
787unsafe extern "C" fn stub_body(out: *mut u8, _payload: *mut c_void) {
788    unsafe { *(out as *mut u64) = 0 };
789}
790
791/// The `@State` type: `struct RustView { @State var value: Int; var id: Int }`.
792unsafe fn build_type() -> (*const c_void, *const c_void) {
793    unsafe {
794        emit_type(
795            b"RustView\0",
796            b"6rswift8RustViewV\0",
797            &[
798                FieldSpec {
799                    mangled_type: b"7SwiftUI5StateVySiG\0",
800                    name: b"_value\0",
801                    meta: abi::generic_metadata1(resolve::state_ma(), resolve::int_meta()),
802                },
803                FieldSpec { mangled_type: b"Si\0", name: b"id\0", meta: resolve::int_meta() },
804            ],
805            view_body_thunk as *const c_void,
806        )
807    }
808}
809
810// `withAnimation` is not yet wired.
811//
812// The blocker it used to have is gone: a `@State` change on this type *is* a
813// change to a view SwiftUI already knows, so a transaction would apply and the
814// value would interpolate — unlike a root-view swap, which had nothing to
815// interpolate between. What remains is only the calling convention of
816// `withAnimation<R>(_: Animation?, _ body: () throws -> R) rethrows -> R`,
817// whose generic return and `swifterror` register my first attempt got wrong.
818
819/// The value a pending animated write should install.
820static PENDING_WRITE: std::sync::Mutex<(i64, i64)> = std::sync::Mutex::new((0, 0));
821
822/// The `() throws -> ()` body handed to `withAnimation`.
823unsafe extern "C" fn animated_write(_out: *mut u8, _payload: *mut c_void) {
824    let (id, value) = *PENDING_WRITE.lock().expect("pending write poisoned");
825    set_state(id, value);
826}
827
828/// Write a new value inside `withAnimation`, so SwiftUI interpolates.
829///
830/// This is what a stable view identity buys. Replacing a hosting view's root
831/// handed SwiftUI a new tree with nothing to interpolate between; a `@State`
832/// change on a type SwiftUI already knows is a change to an *existing* view, so
833/// the transaction applies.
834/// The application's root `@State`, built once and redrawn on demand.
835///
836/// Every application that owns its state in Rust needs the same three things: a
837/// `StatefulView` at the root, a way to rebuild from current state, and a way to
838/// ask for that from whatever thread a callback happens to run on. [`StatefulView`]
839/// itself holds raw metadata pointers and so is neither `Send` nor `Sync` — it is
840/// main-thread-only, correctly — which would otherwise push every caller into the
841/// same `thread_local` dance.
842///
843/// The view lives on the thread that first asked for it. The `id` is a number,
844/// and that is all [`refresh`] and [`refresh_animated`] need.
845mod root {
846    use super::{set_state_animated_with, StatefulView};
847    use crate::views::ViewHandle;
848    use std::sync::atomic::{AtomicI64, Ordering};
849
850    pub(super) static ID: AtomicI64 = AtomicI64::new(-1);
851    static GENERATION: AtomicI64 = AtomicI64::new(1);
852
853    thread_local! {
854        static VIEW: std::cell::OnceCell<StatefulView> = const { std::cell::OnceCell::new() };
855    }
856
857    pub(super) fn view(builder: fn(i64) -> ViewHandle) -> ViewHandle {
858        VIEW.with(|cell| {
859            let sv = cell.get_or_init(|| {
860                let sv = StatefulView::new(GENERATION.load(Ordering::Relaxed), builder);
861                ID.store(sv.id, Ordering::Release);
862                sv
863            });
864            sv.as_view()
865        })
866    }
867
868    pub(super) fn bump(animation: Option<u64>) {
869        let id = ID.load(Ordering::Acquire);
870        if id < 0 {
871            return;
872        }
873        let n = GENERATION.fetch_add(1, Ordering::Relaxed) + 1;
874        match animation {
875            Some(a) => set_state_animated_with(id, n, a),
876            None => {
877                super::set_state(id, n);
878            }
879        }
880    }
881}
882
883/// Build the application's root view, or return the one already built.
884///
885/// `builder` receives the current generation, which most callers ignore: the
886/// point is that it runs again whenever [`refresh`] is called, and reads
887/// whatever Rust state holds at that moment.
888///
889/// Must be called on the main thread.
890pub fn root_view(builder: fn(i64) -> ViewHandle) -> ViewHandle {
891    root::view(builder)
892}
893
894/// Re-run the root's body, so the interface reflects current Rust state.
895///
896/// Safe to call from any thread; SwiftUI cannot observe a Rust value on its own,
897/// so this is how it is told that one changed.
898pub fn refresh() {
899    root::bump(None);
900}
901
902/// As [`refresh`], inside `withAnimation` — see [`ease_in_out`].
903pub fn refresh_animated(animation: u64) {
904    root::bump(Some(animation));
905}
906
907/// `Animation.easeInOut(duration:)`, as one reference.
908///
909/// An `Animation` is a single class reference, so it comes back in x0 rather
910/// than through a buffer — which is what lets it be passed around as a `u64`.
911pub fn ease_in_out(duration: f64) -> u64 {
912    unsafe {
913        let meta = abi::metadata0(resolve::animation_ma());
914        let out: u64;
915        core::arch::asm!("blr {f}", f = in(reg) resolve::animation_ease_in_out(),
916            in("d0") duration,
917            in("x20") meta,
918            lateout("x0") out,
919            lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
920            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
921            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
922            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
923            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
924        );
925        out
926    }
927}
928
929/// As [`set_state_animated`], with the animation spelled out.
930pub fn set_state_animated_with(id: i64, value: i64, animation: u64) {
931    set_animated_inner(id, value, Some(animation))
932}
933
934pub fn set_state_animated(id: i64, value: i64) {
935    set_animated_inner(id, value, None)
936}
937
938fn set_animated_inner(id: i64, value: i64, animation: Option<u64>) {
939    *PENDING_WRITE.lock().expect("pending write poisoned") = (id, value);
940    unsafe {
941        // `Animation?` is eight bytes — a single reference — so it goes in a
942        // register by value, not through a buffer.
943        let anim_meta = abi::metadata0(resolve::animation_ma());
944        let anim = if let Some(a) = animation { a } else {
945            let out: u64;
946            core::arch::asm!("blr {f}", f = in(reg) resolve::animation_default(),
947                in("x20") anim_meta,
948                lateout("x0") out,
949                lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
950                lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
951                lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
952                lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
953                lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
954            );
955            out
956        };
957
958        let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
959            animated_write,
960            core::ptr::null_mut(),
961        ))) as *mut abi::ClosureCtx;
962
963        // withAnimation<R>(_: Animation?, _ body: () throws -> R) rethrows -> R
964        //   x0 = Animation? by value, x1 = closure fn, x2 = closure ctx,
965        //   x3 = R.Type, x8 = @out R, x21 = swifterror
966        let mut out = [0u8; 8];
967        core::arch::asm!("blr {f}", f = in(reg) resolve::with_animation(),
968            in("x0") anim,
969            in("x1") abi::swift_closure_thunk as *const c_void,
970            in("x2") ctx,
971            in("x3") resolve::sym(c"$sytN"),
972            in("x8") out.as_mut_ptr(),
973            inout("x21") 0u64 => _,
974            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
975            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
976            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
977            lateout("x17") _, lateout("lr") _, lateout("x20") _,
978            clobber_abi("C"),
979        );
980    }
981}
982
983// ═══════════════════════════════════════════════════════════════════════════
984// `@AppStorage`: read the compiler, not the crash
985// ═══════════════════════════════════════════════════════════════════════════
986//
987// This was blocked for a long time on a wrong diagnosis. Calling
988// `AppStorage.wrappedValue` faulted inside
989// `UserDefaultLocation.canonicalLocation`, which reads like a property that was
990// never installed, so the hunt went after installation: a real `App` and
991// `Scene` instead of a hosting view, `.defaultAppStorage(.standard)` supplying
992// the store, raising the write on the main run loop rather than a spawned
993// thread, checking that `swift_conformsToProtocol` finds a `DynamicProperty`
994// witness. All of it came back negative, because none of it was the problem.
995//
996// The answer was in one line of LLVM IR from `swiftc -emit-ir` on a four-line
997// Swift view:
998//
999// ```llvm
1000//   declare swiftcc void @"…AppStorageV12wrappedValuexvg"(
1001//       ptr noalias sret, ptr, ptr)                    ; x8 = out, x0 = self, x1 = Value.Type
1002//   declare swiftcc void @"…StateV12wrappedValuexvg"(
1003//       ptr noalias sret, ptr, ptr noalias swiftself)  ; x8 = out, x0 = Value.Type, x20 = self
1004// ```
1005//
1006// `State`'s accessor takes `swiftself`; `AppStorage`'s does not. So its `self`
1007// is the eight-byte wrapper passed *by value in x0* — the `UserDefaultLocation`
1008// reference itself, not a pointer to the field — and the metadata in x1 is the
1009// *wrapped* type's, `Bool.Type`, not `AppStorage<Bool>.Type`. Modelling it on
1010// `State` gets both wrong at once, and the fault it produces points at
1011// SwiftUI's internals rather than at the call.
1012//
1013// The setter is `(value @in, self, Value.Type)` in x0/x1/x2, and Swift retains
1014// `self` around each call, which [`app_storage_get`] and [`app_storage_set`]
1015// below do too.
1016//
1017// The lesson is cheap to state: four conventions guessed and measured cost far
1018// more than one `-emit-ir` would have. Reflection was never wrong — plain
1019// spelled-out field type names work where Swift emits symbolic references, and
1020// SwiftUI installs the property either way.
1021
1022// ── `@AppStorage` ────────────────────────────────────────────────────────
1023
1024/// Field 0 is the `AppStorage<T>` — one `UserDefaultLocation` reference,
1025/// whatever `T` is, so the struct's layout does not depend on the value type.
1026/// Checked against the computed layout in [`StoredView::new`].
1027const STORED_ID_OFFSET: usize = 8;
1028
1029/// A type `@AppStorage` can hold.
1030///
1031/// The four `AppStorage` takes without a `RawRepresentable` detour. The
1032/// accessors are generic over `Value` and so are shared — only the initialiser
1033/// differs per type, and it differs in ways worth having read rather than
1034/// guessed. From `swiftc -emit-ir`:
1035///
1036/// ```llvm
1037///   AppStorage<Bool>.init   (i1,     i64, ptr, i64)        ; x0=value, x1/x2=key, x3=store
1038///   AppStorage<Int>.init    (i64,    i64, ptr, i64)        ; x0=value, x1/x2=key, x3=store
1039///   AppStorage<Double>.init (double, i64, ptr, i64)        ; d0=value, x0/x1=key, x2=store
1040///   AppStorage<String>.init (i64, ptr, i64, ptr, i64)      ; x0/x1=value, x2/x3=key, x4=store
1041/// ```
1042///
1043/// `Double` is the one that breaks the pattern: its value travels in a *float*
1044/// register, so the key and the store shift down one.
1045pub trait StoredValue: Clone + Send + 'static {
1046    /// `Value.Type` — what the accessors want in x1, not `AppStorage<Value>`'s.
1047    fn metadata() -> *const c_void;
1048    /// The mangled `AppStorage<Self>`, NUL-terminated, for the field record.
1049    fn field_type() -> &'static [u8];
1050    /// `Self`'s own mangling, for naming `Binding<Self>`.
1051    fn mangled() -> &'static str;
1052    /// How many bytes the accessors read and write.
1053    fn buffer_bytes() -> usize;
1054    /// Build the wrapper, returning its `UserDefaultLocation`.
1055    ///
1056    /// # Safety
1057    /// Calls into SwiftUI; `key` must be a valid string.
1058    unsafe fn init_storage(&self, key: &str) -> u64;
1059    /// Read `Self` out of an accessor's `@out` buffer, consuming whatever the
1060    /// getter handed over.
1061    ///
1062    /// # Safety
1063    /// `buf` must be exactly [`buffer_bytes`](Self::buffer_bytes) of a valid
1064    /// `Self`, owned at +1.
1065    unsafe fn decode(buf: &[u8]) -> Self;
1066    /// Lay `Self` out for the setter's `@in` argument.
1067    fn encode(&self) -> Vec<u8>;
1068}
1069
1070impl StoredValue for bool {
1071    fn metadata() -> *const c_void {
1072        resolve::bool_meta()
1073    }
1074    fn field_type() -> &'static [u8] {
1075        b"7SwiftUI10AppStorageVySbG\0"
1076    }
1077    fn mangled() -> &'static str {
1078        mangle::BOOL
1079    }
1080    fn buffer_bytes() -> usize {
1081        1
1082    }
1083    unsafe fn init_storage(&self, key: &str) -> u64 {
1084        unsafe { app_storage_init_word(resolve::app_storage_bool_init(), u64::from(*self), key) }
1085    }
1086    unsafe fn decode(buf: &[u8]) -> Self {
1087        buf[0] != 0
1088    }
1089    fn encode(&self) -> Vec<u8> {
1090        vec![u8::from(*self)]
1091    }
1092}
1093
1094impl StoredValue for i64 {
1095    fn metadata() -> *const c_void {
1096        resolve::int_meta()
1097    }
1098    fn field_type() -> &'static [u8] {
1099        b"7SwiftUI10AppStorageVySiG\0"
1100    }
1101    fn mangled() -> &'static str {
1102        mangle::INT
1103    }
1104    fn buffer_bytes() -> usize {
1105        8
1106    }
1107    unsafe fn init_storage(&self, key: &str) -> u64 {
1108        unsafe { app_storage_init_word(resolve::app_storage_int_init(), *self as u64, key) }
1109    }
1110    unsafe fn decode(buf: &[u8]) -> Self {
1111        i64::from_le_bytes(buf[..8].try_into().expect("Int"))
1112    }
1113    fn encode(&self) -> Vec<u8> {
1114        self.to_le_bytes().to_vec()
1115    }
1116}
1117
1118impl StoredValue for f64 {
1119    fn metadata() -> *const c_void {
1120        resolve::double_meta()
1121    }
1122    fn field_type() -> &'static [u8] {
1123        b"7SwiftUI10AppStorageVySdG\0"
1124    }
1125    fn mangled() -> &'static str {
1126        mangle::DOUBLE
1127    }
1128    fn buffer_bytes() -> usize {
1129        8
1130    }
1131    unsafe fn init_storage(&self, key: &str) -> u64 {
1132        unsafe {
1133            // The value is in d0, so the key starts at x0 rather than x1.
1134            let k = abi::swift_string(key);
1135            let out: u64;
1136            core::arch::asm!("blr {f}", f = in(reg) resolve::app_storage_double_init(),
1137                in("d0") *self,
1138                inlateout("x0") u64::from_le_bytes(k[..8].try_into().expect("key lo")) => out,
1139                in("x1") u64::from_le_bytes(k[8..].try_into().expect("key hi")),
1140                in("x2") 0u64, // store: nil
1141                lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
1142                lateout("x7") _, lateout("x8") _, lateout("x9") _, lateout("x10") _,
1143                lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
1144                lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
1145                lateout("x20") _, lateout("x21") _, clobber_abi("C"),
1146            );
1147            assert!(out != 0, "AppStorage<Double>.init returned nothing");
1148            out
1149        }
1150    }
1151    unsafe fn decode(buf: &[u8]) -> Self {
1152        f64::from_le_bytes(buf[..8].try_into().expect("Double"))
1153    }
1154    fn encode(&self) -> Vec<u8> {
1155        self.to_le_bytes().to_vec()
1156    }
1157}
1158
1159impl StoredValue for String {
1160    fn metadata() -> *const c_void {
1161        resolve::string_meta()
1162    }
1163    fn field_type() -> &'static [u8] {
1164        b"7SwiftUI10AppStorageVySSG\0"
1165    }
1166    fn mangled() -> &'static str {
1167        mangle::STRING
1168    }
1169    fn buffer_bytes() -> usize {
1170        16
1171    }
1172    unsafe fn init_storage(&self, key: &str) -> u64 {
1173        unsafe {
1174            let v = abi::swift_string(self);
1175            let k = abi::swift_string(key);
1176            let out: u64;
1177            core::arch::asm!("blr {f}", f = in(reg) resolve::app_storage_string_init(),
1178                inlateout("x0") u64::from_le_bytes(v[..8].try_into().expect("v lo")) => out,
1179                in("x1") u64::from_le_bytes(v[8..].try_into().expect("v hi")),
1180                in("x2") u64::from_le_bytes(k[..8].try_into().expect("k lo")),
1181                in("x3") u64::from_le_bytes(k[8..].try_into().expect("k hi")),
1182                in("x4") 0u64, // store: nil
1183                lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
1184                lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1185                lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1186                lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1187                clobber_abi("C"),
1188            );
1189            assert!(out != 0, "AppStorage<String>.init returned nothing");
1190            out
1191        }
1192    }
1193    unsafe fn decode(buf: &[u8]) -> Self {
1194        unsafe {
1195            let bytes: [u8; 16] = buf[..16].try_into().expect("String");
1196            let s = abi::read_swift_string(bytes);
1197            // The getter handed over an owned `String`. Small ones are inline
1198            // and this is a no-op on the tagged word; a long one's storage
1199            // would otherwise leak.
1200            swift_runtime_sys::BridgeObject::swift_bridgeObjectRelease(
1201                u64::from_le_bytes(bytes[8..].try_into().expect("bridge word")) as *mut c_void,
1202            );
1203            s
1204        }
1205    }
1206    fn encode(&self) -> Vec<u8> {
1207        unsafe { abi::swift_string(self).to_vec() }
1208    }
1209}
1210
1211/// The three initialisers whose value is one integer-shaped word: x0 = value,
1212/// x1/x2 = key, x3 = store.
1213unsafe fn app_storage_init_word(func: *const c_void, value: u64, key: &str) -> u64 {
1214    unsafe {
1215        let k = abi::swift_string(key);
1216        let out: u64;
1217        core::arch::asm!("blr {f}", f = in(reg) func,
1218            inlateout("x0") value => out,
1219            in("x1") u64::from_le_bytes(k[..8].try_into().expect("key lo")),
1220            in("x2") u64::from_le_bytes(k[8..].try_into().expect("key hi")),
1221            in("x3") 0u64, // store: nil — UserDefaults.standard
1222            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
1223            lateout("x8") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
1224            lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
1225            lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x20") _,
1226            lateout("x21") _, clobber_abi("C"),
1227        );
1228        assert!(out != 0, "AppStorage.init returned nothing");
1229        out
1230    }
1231}
1232
1233// `AppStorage`'s accessors do *not* take `swiftself`, which is what separates
1234// them from `State`'s and what made every earlier attempt fault. Swift emits:
1235//
1236// ```llvm
1237//   declare swiftcc void @AppStorage.wrappedValue.getter(
1238//       ptr noalias sret, ptr, ptr)                   ; x8 = out, x0 = self, x1 = Value.Type
1239//   declare swiftcc void @State.wrappedValue.getter(
1240//       ptr noalias sret, ptr, ptr noalias swiftself) ; x8 = out, x0 = Value.Type, x20 = self
1241// ```
1242//
1243// So `self` is the eight-byte wrapper passed *by value* in x0 — the
1244// `UserDefaultLocation` reference itself, not a pointer to the field — and the
1245// metadata is the *wrapped* type's, not `AppStorage<Value>`'s.
1246
1247/// `AppStorage<T>.wrappedValue` getter.
1248unsafe fn app_storage_get<T: StoredValue>(location: u64) -> T {
1249    unsafe {
1250        let mut buf = vec![0u8; T::buffer_bytes()];
1251        // Swift retains around the call, `self` being `@guaranteed`.
1252        swift_runtime_sys::RuntimeRaw::swift_retain(location as *mut c_void);
1253        core::arch::asm!("blr {f}", f = in(reg) resolve::app_storage_wrapped_get(),
1254            in("x8") buf.as_mut_ptr(),
1255            in("x0") location,
1256            in("x1") T::metadata(),
1257            lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
1258            lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
1259            lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
1260            lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
1261            lateout("x20") _, lateout("x21") _, clobber_abi("C"),
1262        );
1263        swift_runtime_sys::RuntimeRaw::swift_release(location as *mut c_void);
1264        T::decode(&buf)
1265    }
1266}
1267
1268/// `AppStorage<T>.wrappedValue` setter — `(value @in, self, Value.Type)`.
1269unsafe fn app_storage_set<T: StoredValue>(location: u64, value: &T) {
1270    unsafe {
1271        let mut v = value.encode();
1272        swift_runtime_sys::RuntimeRaw::swift_retain(location as *mut c_void);
1273        core::arch::asm!("blr {f}", f = in(reg) resolve::app_storage_wrapped_set(),
1274            in("x0") v.as_mut_ptr(),
1275            in("x1") location,
1276            in("x2") T::metadata(),
1277            lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
1278            lateout("x7") _, lateout("x8") _, lateout("x9") _, lateout("x10") _,
1279            lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
1280            lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
1281            lateout("x20") _, lateout("x21") _, clobber_abi("C"),
1282        );
1283        swift_runtime_sys::RuntimeRaw::swift_release(location as *mut c_void);
1284    }
1285}
1286
1287/// `AppStorage<T>.projectedValue` — the `Binding<T>` Swift writes as `$flag`.
1288/// Same shape as the getter.
1289unsafe fn app_storage_projected<T: StoredValue>(location: u64) -> Vec<u8> {
1290    unsafe {
1291        let meta = abi::resolve_type_by_mangled_name(
1292            mangle::bound("7SwiftUI7BindingV", &[T::mangled()]).as_bytes(),
1293        );
1294        assert!(!meta.is_null(), "Binding metadata");
1295        let mut out = vec![0u8; abi::type_bytes(meta)];
1296        swift_runtime_sys::RuntimeRaw::swift_retain(location as *mut c_void);
1297        core::arch::asm!("blr {f}", f = in(reg) resolve::app_storage_projected_get(),
1298            in("x8") out.as_mut_ptr(),
1299            in("x0") location,
1300            in("x1") T::metadata(),
1301            lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
1302            lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
1303            lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
1304            lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
1305            lateout("x20") _, lateout("x21") _, clobber_abi("C"),
1306        );
1307        swift_runtime_sys::RuntimeRaw::swift_release(location as *mut c_void);
1308        out
1309    }
1310}
1311
1312// ── The synthesised type ─────────────────────────────────────────────────
1313
1314struct StoredInstance {
1315    /// Takes the location and produces the body. The value type is closed over
1316    /// here rather than carried, so one registry serves every `T`.
1317    render: Box<dyn Fn(u64) -> ViewHandle + Send>,
1318    runs: usize,
1319}
1320
1321static STORED: std::sync::Mutex<Option<std::collections::HashMap<i64, StoredInstance>>> =
1322    std::sync::Mutex::new(None);
1323
1324fn with_stored<R>(f: impl FnOnce(&mut std::collections::HashMap<i64, StoredInstance>) -> R) -> R {
1325    let mut g = STORED.lock().expect("stored registry poisoned");
1326    f(g.get_or_insert_with(std::collections::HashMap::new))
1327}
1328
1329core::arch::global_asm!(
1330    ".p2align 2",
1331    ".globl _rswift_stored_body_thunk",
1332    "_rswift_stored_body_thunk:",
1333    "mov x1, x20",
1334    "mov x0, x8",
1335    "b _rswift_stored_body_getter",
1336);
1337
1338unsafe extern "C" {
1339    #[link_name = "rswift_stored_body_thunk"]
1340    fn stored_body_thunk();
1341}
1342
1343#[unsafe(no_mangle)]
1344unsafe extern "C" fn rswift_stored_body_getter(out: *mut u8, this: *const u8) {
1345    unsafe {
1346        let id = *(this.add(STORED_ID_OFFSET) as *const i64);
1347        let location = *(this as *const u64);
1348        let view = with_stored(|m| {
1349            let inst = m.get_mut(&id)?;
1350            inst.runs += 1;
1351            Some((inst.render)(location))
1352        });
1353        *(out as *mut u64) = view.unwrap_or_else(crate::views::empty_view).into_anyview_word();
1354    }
1355}
1356
1357
1358/// A runtime-built Swift view type with a real `@AppStorage`.
1359///
1360/// ```text
1361///   struct RustStored: View {
1362///       @AppStorage("key") var flag: T
1363///       var id: Int
1364///       var body: some View { <built by a Rust closure> }
1365///   }
1366/// ```
1367///
1368/// SwiftUI installs the property itself, by walking the reflection field
1369/// records — the same route `@State` takes in [`StatefulView`]. The body reads
1370/// the value *through the wrapper*, which is what records the dependency, so a
1371/// later [`set`](Self::set) both persists to `UserDefaults` and re-runs the
1372/// body.
1373///
1374/// `T` is one of [`StoredValue`]: `bool`, `i64`, `f64` or `String`.
1375///
1376/// Compare [`crate::views::BoundState::persistent`], which reaches the same
1377/// store without SwiftUI's involvement — the right tool when the state is
1378/// Rust's rather than a view's.
1379pub struct StoredView<T: StoredValue> {
1380    pub metadata: *const c_void,
1381    pub witness_table: *const c_void,
1382    value: Vec<u8>,
1383    pub id: i64,
1384    key: String,
1385    marker: core::marker::PhantomData<fn() -> T>,
1386}
1387
1388impl<T: StoredValue> StoredView<T> {
1389    /// Build the type and one instance, backed by `key` in `UserDefaults`.
1390    pub fn new(key: &str, default: T, builder: impl Fn(T) -> ViewHandle + Send + 'static) -> Self {
1391        unsafe {
1392            let id = NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1393            with_stored(|m| {
1394                m.insert(
1395                    id,
1396                    StoredInstance {
1397                        render: Box::new(move |loc| builder(app_storage_get::<T>(loc))),
1398                        runs: 0,
1399                    },
1400                )
1401            });
1402            let (metadata, witness_table) = emit_type(
1403                b"RustStored\0",
1404                b"6rswift10RustStoredV\0",
1405                &[
1406                    FieldSpec {
1407                        mangled_type: T::field_type(),
1408                        name: b"_flag\0",
1409                        meta: abi::resolve_type_by_mangled_name(
1410                            &T::field_type()[..T::field_type().len() - 1],
1411                        ),
1412                    },
1413                    FieldSpec { mangled_type: b"Si\0", name: b"id\0", meta: resolve::int_meta() },
1414                ],
1415                stored_body_thunk as *const c_void,
1416            );
1417            debug_assert_eq!(
1418                field_offset(metadata, 1),
1419                STORED_ID_OFFSET,
1420                "the id moved; the body witness reads it at a fixed offset"
1421            );
1422            // Unlike `@State`, the field cannot start as zero: the wrapper
1423            // carries the key, and only its initialiser builds the
1424            // `UserDefaultLocation` that holds it.
1425            let mut value = vec![0u8; instance_bytes(metadata)];
1426            value[..8].copy_from_slice(&default.init_storage(key).to_le_bytes());
1427            value[STORED_ID_OFFSET..STORED_ID_OFFSET + 8].copy_from_slice(&id.to_le_bytes());
1428            Self {
1429                metadata,
1430                witness_table,
1431                value,
1432                id,
1433                key: key.to_owned(),
1434                marker: core::marker::PhantomData,
1435            }
1436        }
1437    }
1438
1439    fn location(&self) -> u64 {
1440        u64::from_le_bytes(self.value[..8].try_into().expect("location"))
1441    }
1442
1443    /// The key this is stored under.
1444    pub fn key(&self) -> &str {
1445        &self.key
1446    }
1447
1448    /// The current value, read through the wrapper.
1449    pub fn get(&self) -> T {
1450        unsafe { app_storage_get::<T>(self.location()) }
1451    }
1452
1453    /// Write, which persists and re-runs the body.
1454    pub fn set(&self, value: T) {
1455        unsafe { app_storage_set::<T>(self.location(), &value) }
1456    }
1457
1458    /// How many times SwiftUI has evaluated the body.
1459    pub fn runs(&self) -> usize {
1460        with_stored(|m| m.get(&self.id).map_or(0, |i| i.runs))
1461    }
1462
1463    /// The `Binding<T>` that Swift spells `$flag`.
1464    ///
1465    /// Hand this to a control and the control *is* the stored value: changing
1466    /// it writes through `UserDefaults` and re-runs the body, with no Rust
1467    /// state in between. See [`crate::views::toggle_app_storage`] and
1468    /// [`crate::views::slider_app_storage`].
1469    pub fn binding(&self) -> Vec<u8> {
1470        unsafe { app_storage_projected::<T>(self.location()) }
1471    }
1472
1473    /// Wrap the instance so it can be hosted like any other view.
1474    pub fn as_view(&self) -> ViewHandle {
1475        unsafe { copy_as_view(&self.value, self.metadata, self.witness_table) }
1476    }
1477}
1478
1479// ── `@Environment` ───────────────────────────────────────────────────────
1480//
1481// The other half of the environment: `.environment(_:_:)` writes a value into
1482// scope, and this reads one back out, from Rust, inside the body.
1483//
1484// Shaped unlike `@AppStorage` in two ways that matter. Its `wrappedValue`
1485// getter *does* take `swiftself` and wants the wrapper's own metadata —
1486// `(x8 = out, x0 = Environment<V>.Type, x20 = self by address)` — and
1487// `Environment<V>` is not one word: `Environment<Bool>` is nine bytes, a
1488// `Content` enum plus a tag. That second point is why the value witnesses had
1489// to stop assuming a single refcounted field before this could work at all.
1490//
1491// Its initialiser takes only the key path, `(x8 = out, x0 = KeyPath)`,
1492// recovering `V` from the path at run time.
1493
1494/// One `\EnvironmentValues` property, as a type.
1495///
1496/// Each implementation names a property and says how to read its value back.
1497/// Adding another is three resolve entries and one impl.
1498pub trait EnvKey: Copy + 'static {
1499    /// What the property holds.
1500    type Value: Clone + Send + 'static;
1501    /// The mangled `Environment<Value>`, NUL-terminated, for the field record.
1502    fn field_type() -> &'static [u8];
1503    /// The mangled `Value` on its own.
1504    ///
1505    /// `field_type` is what the reflection record needs; this is what a
1506    /// modifier generic over `V` needs, and the two are different manglings of
1507    /// different types.
1508    fn value_type() -> &'static [u8];
1509    /// `\EnvironmentValues.<property>`, built once.
1510    fn key_path() -> u64;
1511    /// How many bytes the getter writes.
1512    fn buffer_bytes() -> usize;
1513    /// Read `Value` out of the getter's `@out` buffer.
1514    ///
1515    /// # Safety
1516    /// `buf` must be [`buffer_bytes`](Self::buffer_bytes) of a valid `Value`.
1517    unsafe fn decode(buf: &[u8]) -> Self::Value;
1518    /// The tag a `Value` is stored as — `decode`'s inverse.
1519    ///
1520    /// Reading the environment only ever needed one direction. Writing one —
1521    /// `transformEnvironment` hands a handler the current value and stores
1522    /// whatever it returns — needs the other, and a key that can do one but
1523    /// not the other is half a key.
1524    fn encode(value: &Self::Value) -> u8;
1525}
1526
1527/// Declare an `@Environment` property.
1528///
1529/// Everything a property needs is the same shape every time: a marker type, the
1530/// mangled `Environment<V>` for the field record, the three symbols behind
1531/// `\EnvironmentValues.<name>`, and how to read `V` back out of a buffer. This
1532/// says it once, so adding a property is the eight lines below rather than
1533/// fifty.
1534macro_rules! env_key {
1535    ($(
1536        $(#[$meta:meta])*
1537        $name:ident {
1538            value: $value:ty,
1539            field: $field:expr,
1540            value_type: $value_type:expr,
1541            symbols: ($getter:ident, $setter:ident, $descriptor:ident),
1542            bytes: $bytes:expr,
1543            decode: |$buf:ident| $decode:expr,
1544            encode: |$enc:ident| $encode:expr,
1545        }
1546    )*) => {$(
1547        $(#[$meta])*
1548        #[derive(Clone, Copy)]
1549        pub struct $name;
1550
1551        impl EnvKey for $name {
1552            type Value = $value;
1553            fn field_type() -> &'static [u8] {
1554                $field
1555            }
1556            fn value_type() -> &'static [u8] {
1557                $value_type
1558            }
1559            fn key_path() -> u64 {
1560                use std::sync::atomic::{AtomicU64, Ordering};
1561                static CACHE: AtomicU64 = AtomicU64::new(0);
1562                static mut SLOT: crate::keypath::Slot = crate::keypath::Slot::new();
1563                let hit = CACHE.load(Ordering::Relaxed);
1564                if hit != 0 {
1565                    return hit;
1566                }
1567                unsafe {
1568                    let kp = crate::keypath::build(
1569                        &mut *(&raw mut SLOT),
1570                        crate::keypath::Property {
1571                            getter: resolve::$getter(),
1572                            setter: resolve::$setter(),
1573                            descriptor: resolve::$descriptor(),
1574                            root: b"7SwiftUI17EnvironmentValuesV",
1575                            value: $value_type,
1576                        },
1577                        crate::keypath::next_thunks(),
1578                    );
1579                    CACHE.store(kp, Ordering::Relaxed);
1580                    kp
1581                }
1582            }
1583            fn buffer_bytes() -> usize {
1584                $bytes
1585            }
1586            unsafe fn decode($buf: &[u8]) -> Self::Value {
1587                $decode
1588            }
1589            fn encode($enc: &Self::Value) -> u8 {
1590                $encode
1591            }
1592        }
1593    )*};
1594}
1595
1596env_key! {
1597    /// `\EnvironmentValues.colorScheme` — whether the interface is light or dark.
1598    ColorSchemeKey {
1599        value: crate::views::ColorScheme,
1600        field: b"7SwiftUI11EnvironmentVyAA11ColorSchemeOG\0",
1601        value_type: b"7SwiftUI11ColorSchemeO",
1602        symbols: (env_color_scheme_getter, env_color_scheme_setter, env_color_scheme_descriptor),
1603        bytes: 1,
1604        decode: |b| match b[0] {
1605            0 => crate::views::ColorScheme::Light,
1606            _ => crate::views::ColorScheme::Dark,
1607        },
1608        encode: |v| match v {
1609            crate::views::ColorScheme::Light => 0,
1610            crate::views::ColorScheme::Dark => 1,
1611        },
1612    }
1613
1614    /// `\EnvironmentValues.isEnabled` — whether controls in scope accept input.
1615    IsEnabledKey {
1616        value: bool,
1617        field: b"7SwiftUI11EnvironmentVySbG\0",
1618        value_type: b"Sb",
1619        symbols: (env_is_enabled_getter, env_is_enabled_setter, env_is_enabled_descriptor),
1620        bytes: 1,
1621        decode: |b| b[0] != 0,
1622        encode: |v| u8::from(*v),
1623    }
1624
1625    /// `\EnvironmentValues.layoutDirection` — which way text and layout run.
1626    LayoutDirectionKey {
1627        value: crate::views::LayoutDirection,
1628        field: b"7SwiftUI11EnvironmentVyAA15LayoutDirectionOG\0",
1629        value_type: b"7SwiftUI15LayoutDirectionO",
1630        symbols: (
1631            env_layout_direction_getter,
1632            env_layout_direction_setter,
1633            env_layout_direction_descriptor
1634        ),
1635        bytes: 1,
1636        decode: |b| match b[0] {
1637            0 => crate::views::LayoutDirection::LeftToRight,
1638            _ => crate::views::LayoutDirection::RightToLeft,
1639        },
1640        encode: |v| match v {
1641            crate::views::LayoutDirection::LeftToRight => 0,
1642            crate::views::LayoutDirection::RightToLeft => 1,
1643        },
1644    }
1645
1646    /// `\EnvironmentValues.dynamicTypeSize` — the reader's chosen text size.
1647    DynamicTypeSizeKey {
1648        value: crate::views::DynamicTypeSize,
1649        field: b"7SwiftUI11EnvironmentVyAA15DynamicTypeSizeOG\0",
1650        value_type: b"7SwiftUI15DynamicTypeSizeO",
1651        symbols: (
1652            env_dynamic_type_size_getter,
1653            env_dynamic_type_size_setter,
1654            env_dynamic_type_size_descriptor
1655        ),
1656        bytes: 1,
1657        decode: |b| {
1658            use crate::views::DynamicTypeSize::*;
1659            [
1660                XSmall, Small, Medium, Large, XLarge, XxLarge, XxxLarge,
1661                Accessibility1, Accessibility2, Accessibility3, Accessibility4,
1662                Accessibility5,
1663            ][(b[0] as usize).min(11)]
1664        },
1665        encode: |v| {
1666            use crate::views::DynamicTypeSize::*;
1667            [
1668                XSmall, Small, Medium, Large, XLarge, XxLarge, XxxLarge,
1669                Accessibility1, Accessibility2, Accessibility3, Accessibility4,
1670                Accessibility5,
1671            ]
1672            .iter()
1673            .position(|c| c == v)
1674            .expect("every DynamicTypeSize is in the table") as u8
1675        },
1676    }
1677
1678    /// `\EnvironmentValues.controlSize` — how large controls in scope draw.
1679    ControlSizeKey {
1680        value: crate::views::ControlSize,
1681        field: b"7SwiftUI11EnvironmentVyAA11ControlSizeOG\0",
1682        value_type: b"7SwiftUI11ControlSizeO",
1683        symbols: (env_control_size_getter, env_control_size_setter, env_control_size_descriptor),
1684        bytes: 1,
1685        decode: |b| {
1686            use crate::views::ControlSize::*;
1687            [Mini, Small, Regular, Large, ExtraLarge][(b[0] as usize).min(4)]
1688        },
1689        encode: |v| {
1690            use crate::views::ControlSize::*;
1691            [Mini, Small, Regular, Large, ExtraLarge]
1692                .iter()
1693                .position(|c| c == v)
1694                .expect("every ControlSize is in the table") as u8
1695        },
1696    }
1697
1698    /// `\EnvironmentValues.isLuminanceReduced` — whether the display is dimmed,
1699    /// as on an always-on screen.
1700    IsLuminanceReducedKey {
1701        value: bool,
1702        field: b"7SwiftUI11EnvironmentVySbG\0",
1703        value_type: b"Sb",
1704        symbols: (
1705            env_luminance_reduced_getter,
1706            env_luminance_reduced_setter,
1707            env_luminance_reduced_descriptor
1708        ),
1709        bytes: 1,
1710        decode: |b| b[0] != 0,
1711        encode: |v| u8::from(*v),
1712    }
1713}
1714
1715/// `Environment<V>.init(_ keyPath:)` — `(x8 = out, x0 = KeyPath)`.
1716///
1717/// The initialiser *consumes* the key path. Swift's own call site pairs it with
1718/// a fresh `swift_getKeyPath`, which returns `+1`, and never releases
1719/// afterwards. The key path here is built once and cached, so every call after
1720/// the first was spending a reference it did not own — which showed up not as a
1721/// crash at the call but as a trap inside `Environment.init` much later, once
1722/// the count reached zero and the object had been freed. Retaining first is
1723/// what keeps the cached one alive.
1724unsafe fn environment_init(key_path: u64, bytes: usize) -> Vec<u8> {
1725    unsafe {
1726        swift_runtime_sys::RuntimeRaw::swift_retain(key_path as *mut c_void);
1727        let mut out = vec![0u8; bytes];
1728        core::arch::asm!("blr {f}", f = in(reg) resolve::environment_init(),
1729            in("x8") out.as_mut_ptr(),
1730            in("x0") key_path,
1731            lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
1732            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
1733            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
1734            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
1735            lateout("lr") _, lateout("x20") _, lateout("x21") _, clobber_abi("C"),
1736        );
1737        out
1738    }
1739}
1740
1741/// `Environment<V>.wrappedValue` getter — takes `swiftself` and the wrapper's
1742/// own metadata, unlike `AppStorage`'s.
1743unsafe fn environment_get<K: EnvKey>(field: *const u8, env_meta: *const c_void) -> K::Value {
1744    unsafe {
1745        let mut buf = vec![0u8; K::buffer_bytes()];
1746        core::arch::asm!("blr {f}", f = in(reg) resolve::environment_wrapped_get(),
1747            in("x8") buf.as_mut_ptr(),
1748            in("x0") env_meta,
1749            in("x20") field,
1750            lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
1751            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
1752            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
1753            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
1754            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
1755        );
1756        K::decode(&buf)
1757    }
1758}
1759
1760struct EnvInstance {
1761    render: Box<dyn Fn(*const u8, *const c_void) -> ViewHandle + Send>,
1762    /// Where this view's `Environment<V>` sits, and the metadata its getter
1763    /// wants. Per instance, because `Environment<V>` is not the same width for
1764    /// every `V` — holding either globally would make two live views of
1765    /// different key types read each other's layout.
1766    env_offset: usize,
1767    env_meta: usize,
1768    runs: usize,
1769}
1770
1771static ENVS: std::sync::Mutex<Option<std::collections::HashMap<i64, EnvInstance>>> =
1772    std::sync::Mutex::new(None);
1773
1774fn with_envs<R>(f: impl FnOnce(&mut std::collections::HashMap<i64, EnvInstance>) -> R) -> R {
1775    let mut g = ENVS.lock().expect("environment registry poisoned");
1776    f(g.get_or_insert_with(std::collections::HashMap::new))
1777}
1778
1779core::arch::global_asm!(
1780    ".p2align 2",
1781    ".globl _rswift_env_body_thunk",
1782    "_rswift_env_body_thunk:",
1783    "mov x1, x20",
1784    "mov x0, x8",
1785    "b _rswift_env_body_getter",
1786);
1787
1788unsafe extern "C" {
1789    #[link_name = "rswift_env_body_thunk"]
1790    fn env_body_thunk();
1791}
1792
1793#[unsafe(no_mangle)]
1794unsafe extern "C" fn rswift_env_body_getter(out: *mut u8, this: *const u8) {
1795    unsafe {
1796        // The id is field 0, so it is at offset 0 whatever `V` is — which is
1797        // the point of putting it first here rather than last as the other two
1798        // synthesised types do. Everything width-dependent is then looked up
1799        // per instance.
1800        let id = *(this as *const i64);
1801        let view = with_envs(|m| {
1802            let inst = m.get_mut(&id)?;
1803            inst.runs += 1;
1804            let (off, meta) = (inst.env_offset, inst.env_meta as *const c_void);
1805            Some((inst.render)(this.add(off), meta))
1806        });
1807        *(out as *mut u64) = view.unwrap_or_else(crate::views::empty_view).into_anyview_word();
1808    }
1809}
1810
1811/// A runtime-built Swift view type with a real `@Environment`.
1812///
1813/// ```text
1814///   struct RustEnv: View {
1815///       @Environment(\.colorScheme) var value
1816///       var id: Int
1817///       var body: some View { <built by a Rust closure> }
1818///   }
1819/// ```
1820///
1821/// The body receives whatever SwiftUI has in scope — so a view built this way
1822/// follows the system appearance, or the nearest `.environment(...)`, without
1823/// Rust being told about the change.
1824pub struct EnvironmentView<K: EnvKey> {
1825    pub metadata: *const c_void,
1826    pub witness_table: *const c_void,
1827    value: Vec<u8>,
1828    pub id: i64,
1829    marker: core::marker::PhantomData<fn() -> K>,
1830}
1831
1832impl<K: EnvKey> EnvironmentView<K> {
1833    /// Build the type and one instance.
1834    pub fn new(builder: impl Fn(K::Value) -> ViewHandle + Send + 'static) -> Self {
1835        unsafe {
1836            let id = NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1837            with_envs(|m| {
1838                m.insert(
1839                    id,
1840                    EnvInstance {
1841                        render: Box::new(move |field, meta| {
1842                            builder(environment_get::<K>(field, meta))
1843                        }),
1844                        env_offset: 0,
1845                        env_meta: 0,
1846                        runs: 0,
1847                    },
1848                )
1849            });
1850            let field_meta = abi::resolve_type_by_mangled_name(
1851                &K::field_type()[..K::field_type().len() - 1],
1852            );
1853            assert!(!field_meta.is_null(), "Environment<V> metadata");
1854            let (metadata, witness_table) = emit_type(
1855                b"RustEnv\0",
1856                b"6rswift7RustEnvV\0",
1857                &[
1858                    FieldSpec { mangled_type: b"Si\0", name: b"id\0", meta: resolve::int_meta() },
1859                    FieldSpec {
1860                        mangled_type: K::field_type(),
1861                        name: b"_value\0",
1862                        meta: field_meta,
1863                    },
1864                ],
1865                env_body_thunk as *const c_void,
1866            );
1867            let env_off = field_offset(metadata, 1);
1868            debug_assert_eq!(field_offset(metadata, 0), 0, "the id must be field 0");
1869            with_envs(|m| {
1870                if let Some(inst) = m.get_mut(&id) {
1871                    inst.env_offset = env_off;
1872                    inst.env_meta = field_meta as usize;
1873                }
1874            });
1875
1876            // The field has to start as a real `Environment` built from the key
1877            // path — that is where the property learns which value to read.
1878            let mut value = vec![0u8; instance_bytes(metadata)];
1879            let env = environment_init(K::key_path(), abi::type_bytes(field_meta));
1880            value[..8].copy_from_slice(&id.to_le_bytes());
1881            value[env_off..env_off + env.len()].copy_from_slice(&env);
1882            Self { metadata, witness_table, value, id, marker: core::marker::PhantomData }
1883        }
1884    }
1885
1886    /// How many times SwiftUI has evaluated the body.
1887    pub fn runs(&self) -> usize {
1888        with_envs(|m| m.get(&self.id).map_or(0, |i| i.runs))
1889    }
1890
1891    /// Wrap the instance so it can be hosted like any other view.
1892    pub fn as_view(&self) -> ViewHandle {
1893        unsafe { copy_as_view(&self.value, self.metadata, self.witness_table) }
1894    }
1895}
1896
1897/// A type `@State` can hold.
1898///
1899/// `State<V>` is the value followed by the location box SwiftUI installs, so
1900/// its width follows `V`: sixteen bytes for the three scalars, twenty-four for
1901/// a `String`. The box is therefore the *last* word rather than the one at
1902/// offset eight, which is what an earlier `Int`-only reading of it assumed.
1903pub trait StateValue: StoredValue {
1904    /// The mangled `State<Self>`, NUL-terminated, for the field record.
1905    fn state_field_type() -> &'static [u8];
1906}
1907
1908impl StateValue for bool {
1909    fn state_field_type() -> &'static [u8] {
1910        b"7SwiftUI5StateVySbG\0"
1911    }
1912}
1913impl StateValue for i64 {
1914    fn state_field_type() -> &'static [u8] {
1915        b"7SwiftUI5StateVySiG\0"
1916    }
1917}
1918impl StateValue for f64 {
1919    fn state_field_type() -> &'static [u8] {
1920        b"7SwiftUI5StateVySdG\0"
1921    }
1922}
1923impl StateValue for String {
1924    fn state_field_type() -> &'static [u8] {
1925        b"7SwiftUI5StateVySSG\0"
1926    }
1927}
1928
1929/// Where the location box sits inside a `State<V>`: the last word.
1930unsafe fn state_box_offset(state_meta: *const c_void) -> usize {
1931    unsafe { abi::type_size(state_meta).saturating_sub(8) }
1932}
1933
1934/// `State<V>.wrappedValue` getter — `(x8 = out, x0 = State<V>.Type,
1935/// x20 = self by address)`.
1936unsafe fn state_value_of<T: StateValue>(field: *const u8, state_meta: *const c_void) -> T {
1937    unsafe {
1938        let mut buf = vec![0u8; T::buffer_bytes()];
1939        core::arch::asm!("blr {f}", f = in(reg) resolve::state_wrapped_get(),
1940            in("x8") buf.as_mut_ptr(),
1941            in("x0") state_meta,
1942            in("x20") field,
1943            lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
1944            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
1945            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
1946            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
1947            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
1948        );
1949        T::decode(&buf)
1950    }
1951}
1952
1953
1954/// The `Binding<V>` bytes a `State<V>` projects, without needing `V` at the
1955/// call site — the body witness iterates slots and has only metadata.
1956unsafe fn state_binding_bytes(
1957    field: *const u8,
1958    state_meta: *const c_void,
1959    bytes: usize,
1960) -> Vec<u8> {
1961    unsafe {
1962        let mut out = vec![0u8; bytes];
1963        core::arch::asm!("blr {f}", f = in(reg) resolve::state_projected_get(),
1964            in("x8") out.as_mut_ptr(),
1965            in("x0") state_meta,
1966            in("x20") field,
1967            lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
1968            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
1969            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
1970            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
1971            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
1972        );
1973        out
1974    }
1975}
1976
1977unsafe fn binding_meta<T: StoredValue>() -> *const c_void {
1978    unsafe {
1979        let m = abi::resolve_type_by_mangled_name(
1980            mangle::bound("7SwiftUI7BindingV", &[T::mangled()]).as_bytes(),
1981        );
1982        assert!(!m.is_null(), "Binding metadata");
1983        m
1984    }
1985}
1986
1987/// `Binding<V>.wrappedValue` setter — `(x0 = value @in, x1 = Binding<V>.Type,
1988/// x20 = self by address)`.
1989unsafe fn binding_set_of<T: StoredValue>(binding: &mut [u8], value: &T) {
1990    unsafe {
1991        let mut v = value.encode();
1992        core::arch::asm!("blr {f}", f = in(reg) resolve::binding_wrapped_set(),
1993            in("x0") v.as_mut_ptr(),
1994            in("x1") binding_meta::<T>(),
1995            in("x20") binding.as_mut_ptr(),
1996            lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
1997            lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
1998            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
1999            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
2000            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
2001        );
2002    }
2003}
2004
2005// ── Composing property wrappers ──────────────────────────────────────────
2006//
2007// [`StatefulView`], [`StoredView`] and [`EnvironmentView`] each synthesise a
2008// type with exactly one wrapper in it, which is enough to prove each one works
2009// and not enough to write a screen with. A real view wants `@State` *and*
2010// `@Environment` *and* whatever it persists, in the same struct.
2011//
2012// Nothing in the machinery objected — [`emit_type`] has laid out arbitrary
2013// fields and delegated their value witnesses since the layout work — so what
2014// was missing was only a way to say it. This is that: build the field list,
2015// and read the values back by index inside the body.
2016
2017/// The field name for composed property `i`.
2018///
2019/// Reflection field names have to outlive the type, and the type outlives
2020/// everything here, so the name is leaked rather than drawn from a fixed table
2021/// — which is what capped composition at eight properties for no reason beyond
2022/// how many names had been written out.
2023fn prop_name(i: usize) -> &'static [u8] {
2024    // A property wrapper's stored property is spelled with a leading
2025    // underscore, as `_flag` is for `@AppStorage("…") var flag`.
2026    Box::leak(format!("_p{i}\0").into_bytes().into_boxed_slice())
2027}
2028
2029#[derive(Clone, Copy, PartialEq, Eq)]
2030enum PropKind {
2031    State,
2032    Stored,
2033    Env,
2034    Focus,
2035}
2036
2037struct PropSpec {
2038    field_type: &'static [u8],
2039    meta: *const c_void,
2040    /// For a `@State`, how wide its `Binding<V>` projection is. The builder
2041    /// knows `V`; the body witness, which captures the projection, does not.
2042    binding_bytes: usize,
2043    /// The field's initial bytes. `@State` starts zeroed and SwiftUI fills it
2044    /// in; the other two carry what they were built from.
2045    init: Vec<u8>,
2046    kind: PropKind,
2047}
2048
2049/// Where a property ended up, and what is needed to read it.
2050#[derive(Clone, Copy)]
2051struct PropSlot {
2052    offset: usize,
2053    meta: usize,
2054    binding_bytes: usize,
2055    kind: PropKind,
2056}
2057
2058/// A `@State` property of a composed view.
2059///
2060/// Handed out by [`ComposedBuilder::state`] and passed back to read or write
2061/// it. Carrying the type makes the index unspellable and the kind a matter for
2062/// the compiler: `p.state(count)` cannot name a property that is not there,
2063/// cannot read an `@AppStorage` as `@State`, and cannot read an `i64` one as a
2064/// `String`. All three were runtime assertions when properties were addressed
2065/// by number.
2066#[derive(Debug)]
2067pub struct StateProp<T> {
2068    index: usize,
2069    marker: core::marker::PhantomData<fn() -> T>,
2070}
2071
2072/// An `@AppStorage` property of a composed view.
2073#[derive(Debug)]
2074pub struct StoredProp<T> {
2075    index: usize,
2076    marker: core::marker::PhantomData<fn() -> T>,
2077}
2078
2079/// An `@Environment` property of a composed view.
2080#[derive(Debug)]
2081pub struct EnvProp<K> {
2082    index: usize,
2083    marker: core::marker::PhantomData<fn() -> K>,
2084}
2085
2086/// A `@FocusState` property of a composed view.
2087#[derive(Clone, Copy, Debug)]
2088pub struct FocusProp {
2089    index: usize,
2090}
2091
2092/// The properties of a composed view, read inside its body.
2093///
2094/// Indices are the order the properties were added. Reading the wrong type at
2095/// an index is a programming error the same way an out-of-range index is, and
2096/// is caught the same way — by an assertion, not by a fault inside SwiftUI.
2097pub struct Props {
2098    this: *const u8,
2099    slots: Vec<PropSlot>,
2100}
2101
2102impl Props {
2103    fn slot(&self, i: usize, want: PropKind, what: &str) -> PropSlot {
2104        let s = *self.slots.get(i).unwrap_or_else(|| {
2105            panic!("no property at index {i}; the view has {}", self.slots.len())
2106        });
2107        assert!(s.kind == want, "property {i} is not {what}");
2108        s
2109    }
2110
2111    /// The `@State` at index `i`.
2112    ///
2113    /// Returns `T`'s default before SwiftUI has installed the property — the
2114    /// box is the last word of a `State<V>`, and a zero one means reading
2115    /// would fault rather than answer.
2116    pub fn state<T: StateValue + Default>(&self, prop: StateProp<T>) -> T {
2117        let s = self.slot(prop.index, PropKind::State, "@State");
2118        unsafe {
2119            let meta = s.meta as *const c_void;
2120            if *(self.this.add(s.offset + state_box_offset(meta)) as *const u64) == 0 {
2121                T::default()
2122            } else {
2123                state_value_of::<T>(self.this.add(s.offset), meta)
2124            }
2125        }
2126    }
2127
2128    /// The `@AppStorage` at index `i`.
2129    pub fn stored<T: StoredValue>(&self, prop: StoredProp<T>) -> T {
2130        let s = self.slot(prop.index, PropKind::Stored, "@AppStorage");
2131        unsafe { app_storage_get::<T>(*(self.this.add(s.offset) as *const u64)) }
2132    }
2133
2134    /// Whether the `@FocusState` at index `i` currently has focus.
2135    pub fn focus(&self, prop: FocusProp) -> bool {
2136        let s = self.slot(prop.index, PropKind::Focus, "@FocusState");
2137        unsafe { focus_get(self.this.add(s.offset), s.meta as *const c_void) }
2138    }
2139
2140    /// The three words `.focused(_:)` wants, for the `@FocusState` at index
2141    /// `i`. Pass it to [`crate::views::focused_on`] on the field it belongs to.
2142    pub fn focus_projection(&self, prop: FocusProp) -> [u64; 3] {
2143        let s = self.slot(prop.index, PropKind::Focus, "@FocusState");
2144        unsafe { focus_projection(self.this.add(s.offset), s.meta as *const c_void) }
2145    }
2146
2147    /// The `@Environment` value at index `i`.
2148    pub fn env<K: EnvKey>(&self, prop: EnvProp<K>) -> K::Value {
2149        let s = self.slot(prop.index, PropKind::Env, "@Environment");
2150        unsafe { environment_get::<K>(self.this.add(s.offset), s.meta as *const c_void) }
2151    }
2152}
2153
2154struct ComposedInstance {
2155    body: Box<dyn Fn(&Props) -> ViewHandle + Send>,
2156    slots: Vec<PropSlot>,
2157    /// One captured `Binding<Int>` per `@State`, by property index. Only
2158    /// obtainable from inside a body evaluation, as in [`StatefulView`].
2159    bindings: std::collections::HashMap<usize, Vec<u8>>,
2160    /// One captured `FocusState.Binding` per `@FocusState`, by property index.
2161    /// As with the state bindings, only obtainable while the body runs.
2162    focus: std::collections::HashMap<usize, [u64; 3]>,
2163    runs: usize,
2164}
2165
2166static COMPOSED: std::sync::Mutex<Option<std::collections::HashMap<i64, ComposedInstance>>> =
2167    std::sync::Mutex::new(None);
2168
2169fn with_composed<R>(
2170    f: impl FnOnce(&mut std::collections::HashMap<i64, ComposedInstance>) -> R,
2171) -> R {
2172    let mut g = COMPOSED.lock().expect("composed registry poisoned");
2173    f(g.get_or_insert_with(std::collections::HashMap::new))
2174}
2175
2176core::arch::global_asm!(
2177    ".p2align 2",
2178    ".globl _rswift_composed_body_thunk",
2179    "_rswift_composed_body_thunk:",
2180    "mov x1, x20",
2181    "mov x0, x8",
2182    "b _rswift_composed_body_getter",
2183);
2184
2185unsafe extern "C" {
2186    #[link_name = "rswift_composed_body_thunk"]
2187    fn composed_body_thunk();
2188}
2189
2190#[unsafe(no_mangle)]
2191unsafe extern "C" fn rswift_composed_body_getter(out: *mut u8, this: *const u8) {
2192    unsafe {
2193        // The id is field 0, so it is at offset 0 whatever the properties are.
2194        let id = *(this as *const i64);
2195        let view = with_composed(|m| {
2196            let inst = m.get_mut(&id)?;
2197            inst.runs += 1;
2198            // Capture each `@State`'s binding the first time through; there is
2199            // nowhere else it can be had.
2200            for (i, s) in inst.slots.iter().enumerate() {
2201                if s.kind == PropKind::State && !inst.bindings.contains_key(&i) {
2202                    let meta = s.meta as *const c_void;
2203                    if *(this.add(s.offset + state_box_offset(meta)) as *const u64) != 0 {
2204                        // The projection is `Binding<V>`, whose width follows
2205                        // `V`; taking it generically is why the slot carries
2206                        // the field's metadata.
2207                        inst.bindings
2208                            .insert(i, state_binding_bytes(this.add(s.offset), meta, s.binding_bytes));
2209                    }
2210                }
2211            }
2212            for (i, s) in inst.slots.iter().enumerate() {
2213                if s.kind == PropKind::Focus && !inst.focus.contains_key(&i) {
2214                    inst.focus
2215                        .insert(i, focus_projection(this.add(s.offset), s.meta as *const c_void));
2216                }
2217            }
2218            let props = Props { this, slots: inst.slots.clone() };
2219            Some((inst.body)(&props))
2220        });
2221        *(out as *mut u64) = view.unwrap_or_else(crate::views::empty_view).into_anyview_word();
2222    }
2223}
2224
2225/// Collects the properties a composed view will have.
2226///
2227/// See [`ComposedView`].
2228pub struct ComposedBuilder {
2229    specs: Vec<PropSpec>,
2230}
2231
2232impl Default for ComposedBuilder {
2233    fn default() -> Self {
2234        Self::new()
2235    }
2236}
2237
2238impl ComposedBuilder {
2239    pub fn new() -> Self {
2240        Self { specs: Vec::new() }
2241    }
2242
2243    /// Add a `@State var: T`, starting at `initial`.
2244    ///
2245    /// `T` is one of [`StateValue`]: `bool`, `i64`, `f64` or `String`.
2246    pub fn state<T: StateValue>(&mut self, initial: T) -> StateProp<T> {
2247        unsafe {
2248            let ft = T::state_field_type();
2249            let meta = abi::resolve_type_by_mangled_name(&ft[..ft.len() - 1]);
2250            assert!(!meta.is_null(), "State<T> metadata");
2251            // The value inline, then the location box SwiftUI installs.
2252            let mut init = vec![0u8; abi::type_bytes(meta)];
2253            let bytes = initial.encode();
2254            init[..bytes.len()].copy_from_slice(&bytes);
2255            self.specs.push(PropSpec {
2256                field_type: ft,
2257                meta,
2258                binding_bytes: abi::type_bytes(binding_meta::<T>()),
2259                init,
2260                kind: PropKind::State,
2261            });
2262            StateProp { index: self.specs.len() - 1, marker: core::marker::PhantomData }
2263        }
2264    }
2265
2266    /// Add an `@AppStorage(key) var: T`.
2267    pub fn app_storage<T: StoredValue>(&mut self, key: &str, default: T) -> StoredProp<T> {
2268        unsafe {
2269            let ft = T::field_type();
2270            let meta = abi::resolve_type_by_mangled_name(&ft[..ft.len() - 1]);
2271            assert!(!meta.is_null(), "AppStorage<T> metadata");
2272            self.specs.push(PropSpec {
2273                field_type: ft,
2274                meta,
2275                binding_bytes: 0,
2276                init: default.init_storage(key).to_le_bytes().to_vec(),
2277                kind: PropKind::Stored,
2278            });
2279            StoredProp { index: self.specs.len() - 1, marker: core::marker::PhantomData }
2280        }
2281    }
2282
2283    /// Add a `@FocusState var: Bool`.
2284    ///
2285    /// Attach it to a field with [`crate::views::focused_on`]; then
2286    /// [`Props::focus`] reports whether that field has focus and
2287    /// [`ComposedView::set_focus`] moves it.
2288    pub fn focus_state(&mut self) -> FocusProp {
2289        unsafe {
2290            let meta = focus_state_meta();
2291            self.specs.push(PropSpec {
2292                field_type: FOCUS_STATE_BOOL,
2293                meta,
2294                binding_bytes: 0,
2295                init: focus_state_init(meta),
2296                kind: PropKind::Focus,
2297            });
2298            FocusProp { index: self.specs.len() - 1 }
2299        }
2300    }
2301
2302    /// Add an `@Environment(\.…) var`.
2303    pub fn environment<K: EnvKey>(&mut self) -> EnvProp<K> {
2304        unsafe {
2305            let ft = K::field_type();
2306            let meta = abi::resolve_type_by_mangled_name(&ft[..ft.len() - 1]);
2307            assert!(!meta.is_null(), "Environment<V> metadata");
2308            self.specs.push(PropSpec {
2309                field_type: ft,
2310                meta,
2311                binding_bytes: 0,
2312                init: environment_init(K::key_path(), abi::type_bytes(meta)),
2313                kind: PropKind::Env,
2314            });
2315            EnvProp { index: self.specs.len() - 1, marker: core::marker::PhantomData }
2316        }
2317    }
2318
2319    /// Synthesise the type and one instance, with `body` as its `body`.
2320    pub fn build(self, body: impl Fn(&Props) -> ViewHandle + Send + 'static) -> ComposedView {
2321        assert!(!self.specs.is_empty(), "a composed view needs at least one property");
2322        unsafe {
2323            let id = NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2324
2325            // Field 0 is the id, so the body witness can find it without
2326            // knowing the shape of anything after it.
2327            let mut fields = vec![FieldSpec {
2328                mangled_type: b"Si\0",
2329                name: b"id\0",
2330                meta: resolve::int_meta(),
2331            }];
2332            for (i, spec) in self.specs.iter().enumerate() {
2333                fields.push(FieldSpec {
2334                    mangled_type: spec.field_type,
2335                    name: prop_name(i),
2336                    meta: spec.meta,
2337                });
2338            }
2339            let (metadata, witness_table) = emit_type(
2340                b"RustComposed\0",
2341                b"6rswift12RustComposedV\0",
2342                &fields,
2343                composed_body_thunk as *const c_void,
2344            );
2345            debug_assert_eq!(field_offset(metadata, 0), 0, "the id must be field 0");
2346
2347            let mut value = vec![0u8; instance_bytes(metadata)];
2348            value[..8].copy_from_slice(&id.to_le_bytes());
2349            let mut slots = Vec::with_capacity(self.specs.len());
2350            for (i, spec) in self.specs.iter().enumerate() {
2351                let off = field_offset(metadata, i + 1);
2352                value[off..off + spec.init.len()].copy_from_slice(&spec.init);
2353                slots.push(PropSlot {
2354                    offset: off,
2355                    meta: spec.meta as usize,
2356                    binding_bytes: spec.binding_bytes,
2357                    kind: spec.kind,
2358                });
2359            }
2360
2361            with_composed(|m| {
2362                m.insert(
2363                    id,
2364                    ComposedInstance {
2365                        body: Box::new(body),
2366                        slots: slots.clone(),
2367                        bindings: std::collections::HashMap::new(),
2368                        focus: std::collections::HashMap::new(),
2369                        runs: 0,
2370                    },
2371                )
2372            });
2373            ComposedView { metadata, witness_table, value, id, slots }
2374        }
2375    }
2376}
2377
2378/// A runtime-built Swift view type with any mix of property wrappers.
2379///
2380/// ```no_run
2381/// use swiftui_native::state::{ComposedBuilder, ColorSchemeKey};
2382/// use swiftui_native::views;
2383///
2384/// let mut b = ComposedBuilder::new();
2385/// let count = b.state(0_i64);
2386/// let dark = b.app_storage::<bool>("dark", false);
2387/// let scheme = b.environment::<ColorSchemeKey>();
2388/// let v = b.build(move |p| {
2389///     views::text(&format!("{} {} {:?}", p.state(count), p.stored(dark), p.env(scheme)))
2390/// });
2391/// v.set_state(count, 1);
2392/// ```
2393///
2394/// Reading a property as the wrong kind, or at the wrong type, does not
2395/// compile — which is what the handles buy over addressing properties by
2396/// number:
2397///
2398/// ```compile_fail
2399/// use swiftui_native::state::ComposedBuilder;
2400/// use swiftui_native::views;
2401///
2402/// let mut b = ComposedBuilder::new();
2403/// let count = b.state(0_i64);
2404/// let v = b.build(|_| views::text("x"));
2405/// let _: bool = v.stored(count); // a `@State` is not a `StoredProp`
2406/// ```
2407///
2408/// Each of [`StatefulView`], [`StoredView`] and [`EnvironmentView`] is the
2409/// one-property case of this; they are kept because they are simpler to use
2410/// when one is all that is wanted.
2411pub struct ComposedView {
2412    pub metadata: *const c_void,
2413    pub witness_table: *const c_void,
2414    value: Vec<u8>,
2415    pub id: i64,
2416    slots: Vec<PropSlot>,
2417}
2418
2419impl ComposedView {
2420    /// Write the `@State` at index `i`, re-running the body.
2421    ///
2422    /// Returns false before the first body evaluation, when there is no
2423    /// binding to write through yet — the same contract as
2424    /// [`StatefulView::set`].
2425    pub fn set_state<T: StateValue>(&self, prop: StateProp<T>, value: T) -> bool {
2426        let i = prop.index;
2427        let binding = with_composed(|m| m.get(&self.id).and_then(|c| c.bindings.get(&i).cloned()));
2428        let Some(mut binding) = binding else { return false };
2429        unsafe { binding_set_of::<T>(&mut binding, &value) };
2430        true
2431    }
2432
2433    fn stored_location(&self, i: usize) -> u64 {
2434        let s = self.slots[i];
2435        u64::from_le_bytes(self.value[s.offset..s.offset + 8].try_into().expect("location"))
2436    }
2437
2438    /// Read an `@AppStorage` from outside the body.
2439    pub fn stored<T: StoredValue>(&self, prop: StoredProp<T>) -> T {
2440        unsafe { app_storage_get::<T>(self.stored_location(prop.index)) }
2441    }
2442
2443    /// Write an `@AppStorage`, which persists and re-runs the body.
2444    pub fn set_stored<T: StoredValue>(&self, prop: StoredProp<T>, value: T) {
2445        unsafe { app_storage_set::<T>(self.stored_location(prop.index), &value) }
2446    }
2447
2448    /// Move focus to — or away from — the `@FocusState` at index `i`.
2449    ///
2450    /// Writing `true` focuses the field the property is attached to, which is
2451    /// what `@FocusState var focused: Bool; focused = true` does in Swift.
2452    /// Returns false before the first body evaluation, when there is no
2453    /// installed property to write to yet.
2454    pub fn set_focus(&self, prop: FocusProp, value: bool) -> bool {
2455        let i = prop.index;
2456        let proj = with_composed(|m| m.get(&self.id).and_then(|c| c.focus.get(&i).copied()));
2457        let Some(mut proj) = proj else { return false };
2458        unsafe { focus_binding_set(&mut proj, value) };
2459        true
2460    }
2461
2462    /// Whether the `@FocusState` at index `i` has focus, read from outside the
2463    /// body.
2464    pub fn focus(&self, prop: FocusProp) -> bool {
2465        let i = prop.index;
2466        let proj = with_composed(|m| m.get(&self.id).and_then(|c| c.focus.get(&i).copied()));
2467        match proj {
2468            Some(p) => unsafe { focus_binding_get(&p) },
2469            None => false,
2470        }
2471    }
2472
2473
2474
2475    /// How many times SwiftUI has evaluated the body.
2476    pub fn runs(&self) -> usize {
2477        with_composed(|m| m.get(&self.id).map_or(0, |c| c.runs))
2478    }
2479
2480    /// Wrap the instance so it can be hosted like any other view.
2481    pub fn as_view(&self) -> ViewHandle {
2482        unsafe { copy_as_view(&self.value, self.metadata, self.witness_table) }
2483    }
2484}
2485
2486impl Drop for StatefulView {
2487    fn drop(&mut self) {
2488        unsafe { destroy_instance(&mut self.value, self.metadata) };
2489    }
2490}
2491
2492impl<T: StoredValue> Drop for StoredView<T> {
2493    fn drop(&mut self) {
2494        unsafe { destroy_instance(&mut self.value, self.metadata) };
2495    }
2496}
2497
2498impl<K: EnvKey> Drop for EnvironmentView<K> {
2499    fn drop(&mut self) {
2500        unsafe { destroy_instance(&mut self.value, self.metadata) };
2501    }
2502}
2503
2504impl Drop for ComposedView {
2505    fn drop(&mut self) {
2506        unsafe { destroy_instance(&mut self.value, self.metadata) };
2507    }
2508}
2509
2510// ── `@FocusState` ────────────────────────────────────────────────────────
2511//
2512// [`crate::views::focused`] already applies `.focused(_:)`, but with a
2513// `FocusState` built on the spot and thrown away: the modifier is there and
2514// nothing can read or move focus through it. Installed as a property it
2515// becomes real — Rust can ask whether the field is focused and put focus into
2516// it.
2517//
2518// Its accessors take `swiftself` and the wrapper's own metadata, like
2519// `@State`'s and unlike `@AppStorage`'s:
2520//
2521// ```llvm
2522//   declare swiftcc void @FocusState.wrappedValue.getter(
2523//       ptr noalias sret, ptr, ptr noalias swiftself)   ; x8=out, x0=FocusState<V>.Type, x20=self
2524// ```
2525//
2526// `FocusState<Bool>` is seventeen bytes at a stride of twenty-four, and the
2527// `Binding` it projects is the same — three words once exploded, which is how
2528// `.focused(_:)` takes it.
2529
2530/// The mangled `FocusState<Bool>`, NUL-terminated.
2531const FOCUS_STATE_BOOL: &[u8] = b"7SwiftUI10FocusStateVySbG\0";
2532
2533unsafe fn focus_state_meta() -> *const c_void {
2534    unsafe {
2535        let m = abi::resolve_type_by_mangled_name(&FOCUS_STATE_BOOL[..FOCUS_STATE_BOOL.len() - 1]);
2536        assert!(!m.is_null(), "FocusState<Bool> metadata");
2537        m
2538    }
2539}
2540
2541/// `FocusState<Bool>.init()`.
2542///
2543/// Written through x8 with the metadata in x20, not returned in registers —
2544/// see [`crate::views::focus_binding`], which found that out the hard way.
2545unsafe fn focus_state_init(meta: *const c_void) -> Vec<u8> {
2546    unsafe {
2547        let mut out = vec![0u8; abi::type_bytes(meta)];
2548        core::arch::asm!("blr {f}", f = in(reg) resolve::focus_state_bool_init(),
2549            in("x8") out.as_mut_ptr(),
2550            in("x20") meta,
2551            lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
2552            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
2553            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
2554            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
2555            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
2556        );
2557        out
2558    }
2559}
2560
2561/// `FocusState<Bool>.wrappedValue` getter.
2562unsafe fn focus_get(field: *const u8, meta: *const c_void) -> bool {
2563    let mut out: u8 = 0;
2564    unsafe {
2565        core::arch::asm!("blr {f}", f = in(reg) resolve::focus_state_wrapped_get(),
2566            in("x8") &mut out as *mut u8,
2567            in("x0") meta,
2568            in("x20") field,
2569            lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
2570            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
2571            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
2572            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
2573            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
2574        );
2575    }
2576    out != 0
2577}
2578
2579
2580unsafe fn focus_binding_meta() -> *const c_void {
2581    unsafe {
2582        let m = abi::resolve_type_by_mangled_name(b"7SwiftUI10FocusStateV7BindingVySb_G");
2583        assert!(!m.is_null(), "FocusState<Bool>.Binding metadata");
2584        m
2585    }
2586}
2587
2588/// `FocusState<Bool>.Binding.wrappedValue` setter — `(x0 = value @in,
2589/// x1 = Binding.Type, x20 = self by address)`.
2590///
2591/// Writing the *property* only touches this process's copy; SwiftUI installs
2592/// its own and never looks at ours. The projection captured while the body ran
2593/// is the handle on the installed one, which is the same arrangement `@State`
2594/// uses.
2595unsafe fn focus_binding_set(projection: &mut [u64; 3], value: bool) {
2596    unsafe {
2597        let mut v = u8::from(value);
2598        core::arch::asm!("blr {f}", f = in(reg) resolve::focus_binding_wrapped_set(),
2599            in("x0") &mut v as *mut u8,
2600            in("x1") focus_binding_meta(),
2601            in("x20") projection.as_mut_ptr(),
2602            lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
2603            lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
2604            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
2605            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
2606            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
2607        );
2608    }
2609}
2610
2611/// `FocusState<Bool>.Binding.wrappedValue` getter.
2612unsafe fn focus_binding_get(projection: &[u64; 3]) -> bool {
2613    let mut out: u8 = 0;
2614    unsafe {
2615        core::arch::asm!("blr {f}", f = in(reg) resolve::focus_binding_wrapped_get(),
2616            in("x8") &mut out as *mut u8,
2617            in("x0") focus_binding_meta(),
2618            in("x20") projection.as_ptr(),
2619            lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
2620            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
2621            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
2622            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
2623            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
2624        );
2625    }
2626    out != 0
2627}
2628
2629/// `FocusState<Bool>.projectedValue`, as the three words `.focused(_:)` wants.
2630unsafe fn focus_projection(field: *const u8, meta: *const c_void) -> [u64; 3] {
2631    unsafe {
2632        let mut out = [0u64; 3];
2633        core::arch::asm!("blr {f}", f = in(reg) resolve::focus_state_projected(),
2634            in("x8") out.as_mut_ptr(),
2635            in("x0") meta,
2636            in("x20") field,
2637            lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
2638            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
2639            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
2640            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
2641            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
2642        );
2643        out
2644    }
2645}
2646
2647// A handle is an index and a marker; `PhantomData<fn() -> T>` is `Copy`
2648// whatever `T` is. `#[derive(Copy)]` would add a `T: Copy` bound that nothing
2649// needs, which is what stopped a `StateProp<String>` being read twice inside a
2650// `Fn` body.
2651impl<T> Clone for StateProp<T> {
2652    fn clone(&self) -> Self {
2653        *self
2654    }
2655}
2656impl<T> Copy for StateProp<T> {}
2657impl<T> Clone for StoredProp<T> {
2658    fn clone(&self) -> Self {
2659        *self
2660    }
2661}
2662impl<T> Copy for StoredProp<T> {}
2663impl<K> Clone for EnvProp<K> {
2664    fn clone(&self) -> Self {
2665        *self
2666    }
2667}
2668impl<K> Copy for EnvProp<K> {}