Skip to main content

swiftui_native/views/
focus.rs

1//! Key paths into SwiftUI's keyed value bags, built from scratch.
2//!
3//! Three of SwiftUI's protocols ask the *caller* to declare a key type and then
4//! reach the value through a key path: `FocusedValueKey`, `EnvironmentKey` and
5//! `ContainerValueKey`. All three need the same two things synthesised, and all
6//! three draw from the same pool of thunks and pattern slots in
7//! [`crate::keypath`], so they are here together.
8//!
9//! # Focused values — publishing a value the menu bar can read
10//!
11//! `.focusedSceneValue(\.someKey, value)` is how a window tells the commands
12//! above it what is selected, and it is the last entry in Apple's catalogue this
13//! crate could not build. Two things stood in the way, and both are interesting:
14//!
15//! 1. **The key.** `FocusedValues` is keyed by a type conforming to
16//!    `FocusedValueKey`, and the conformance is the caller's to write. So one is
17//!    synthesised at run time, the same way [`crate::conformance`] does for
18//!    `PreferenceKey` and `Layout`: a carrier type is minted, a witness table
19//!    holding its `Value` is built, and a conformance record is registered.
20//!
21//! 2. **The key path.** `WritableKeyPath<FocusedValues, T?>` is not a symbol,
22//!    and unlike `EnvironmentValues` there is no SwiftUI modifier that stores one
23//!    this crate could read back out — `FocusedValues` exports no properties at
24//!    all. So the key path *pattern* is emitted, with a settable computed
25//!    component whose getter and setter go through `FocusedValues`'s subscript.
26//!    That is exactly what `swiftc` emits for a client's own computed property on
27//!    `FocusedValues`, and the pattern here was read out of its IR rather than
28//!    assembled from the bit definitions.
29//!
30//! ```no_run
31//! # use swiftui_native::views;
32//! let key = views::focus_key("selectedRow");
33//! let v = views::focused_scene_value(&views::text("row"), &key, 7);
34//! ```
35//!
36//! # What is bound and what is not
37//!
38//! Publishing. *Reading* a focused value needs `@FocusedValue`, which is a
39//! `DynamicProperty` — a property wrapper SwiftUI instantiates itself, which is
40//! a different mechanism from anything here. The key's value type is `Int`,
41//! because that is what a key path's value type has to be fixed to at the moment
42//! the pattern is emitted.
43
44use super::{wrap_owned, ViewHandle};
45use crate::conformance::{synthesize, Conformance, Protocol, Witness};
46use crate::keypath;
47use crate::mangle::{self, swift_type};
48use crate::resolve;
49use core::ffi::c_void;
50use std::sync::{Mutex, OnceLock};
51
52/// A Swift type a synthesised key can hold.
53///
54/// The three keyed bags — `EnvironmentKey`, `FocusedValueKey`,
55/// `ContainerValueKey` — are generic over their value, and so are these. Each
56/// implementation is four things: the type's mangled name (for the key path's
57/// value type), its metadata (for the conformance's associated type), and the
58/// two directions of moving a value across.
59///
60/// # Ownership
61///
62/// `write` produces a `+1` value, because that is what every use here needs: an
63/// `@in` argument is *owned* by the callee, and a `defaultValue` witness is
64/// called again each time SwiftUI wants the default and destroys what it got. A
65/// `String` that handed back the same object twice without retaining would be
66/// over-released, which is why the default is kept as a closure that writes a
67/// fresh one rather than as bytes to copy.
68///
69/// `read` copies and takes nothing: it is reading a value somebody else owns.
70pub trait KeyValue: Clone + Send + Sync + 'static {
71    /// The mangled name of the Swift type, without the `$s`.
72    fn mangled() -> &'static str;
73
74    /// The type's metadata.
75    fn metadata() -> *const c_void;
76
77    /// Write a `+1` copy into a buffer of the type's stride.
78    ///
79    /// # Safety
80    /// `out` must have room for the type's stride.
81    unsafe fn write(&self, out: *mut u8);
82
83    /// Read a copy back, leaving the source owned by whoever owns it.
84    ///
85    /// # Safety
86    /// `from` must address an initialised value of this type.
87    unsafe fn read(from: *const u8) -> Self;
88}
89
90/// What a key's default may be written as.
91///
92/// `env_key("depth", 3)` and `env_key("greeting", "hello")` should both work, and
93/// `Into` cannot do it: `3.into()` with an `i64` target is ambiguous, because
94/// every integer width has a `From` into `i64`. This has one impl per
95/// destination, so the literal's type is decided by the key's.
96pub trait IntoKeyValue<T: KeyValue> {
97    /// The value itself.
98    fn into_key_value(self) -> T;
99}
100
101impl<T: KeyValue> IntoKeyValue<T> for T {
102    fn into_key_value(self) -> T {
103        self
104    }
105}
106
107impl IntoKeyValue<String> for &str {
108    fn into_key_value(self) -> String {
109        self.to_string()
110    }
111}
112
113impl KeyValue for i64 {
114    fn mangled() -> &'static str {
115        crate::mangle::INT
116    }
117    fn metadata() -> *const c_void {
118        resolve::int_meta()
119    }
120    unsafe fn write(&self, out: *mut u8) {
121        unsafe { *(out as *mut i64) = *self };
122    }
123    unsafe fn read(from: *const u8) -> Self {
124        unsafe { *(from as *const i64) }
125    }
126}
127
128impl KeyValue for f64 {
129    fn mangled() -> &'static str {
130        crate::mangle::DOUBLE
131    }
132    fn metadata() -> *const c_void {
133        resolve::double_meta()
134    }
135    unsafe fn write(&self, out: *mut u8) {
136        unsafe { *(out as *mut f64) = *self };
137    }
138    unsafe fn read(from: *const u8) -> Self {
139        unsafe { *(from as *const f64) }
140    }
141}
142
143impl KeyValue for bool {
144    fn mangled() -> &'static str {
145        crate::mangle::BOOL
146    }
147    fn metadata() -> *const c_void {
148        resolve::bool_meta()
149    }
150    unsafe fn write(&self, out: *mut u8) {
151        unsafe { *out = u8::from(*self) };
152    }
153    unsafe fn read(from: *const u8) -> Self {
154        unsafe { *from & 1 != 0 }
155    }
156}
157
158impl KeyValue for String {
159    fn mangled() -> &'static str {
160        crate::mangle::STRING
161    }
162    fn metadata() -> *const c_void {
163        resolve::string_meta()
164    }
165    unsafe fn write(&self, out: *mut u8) {
166        // A fresh `+1` string every time: see the note on the trait.
167        let bytes = unsafe { crate::abi::swift_string(self) };
168        unsafe { core::ptr::copy_nonoverlapping(bytes.as_ptr(), out, 16) };
169    }
170    unsafe fn read(from: *const u8) -> Self {
171        let mut bytes = [0u8; 16];
172        unsafe { core::ptr::copy_nonoverlapping(from, bytes.as_mut_ptr(), 16) };
173        unsafe { crate::abi::read_swift_string(bytes) }
174    }
175}
176
177/// Call a synthesised `defaultValue` witness and read what it wrote.
178///
179/// Through the witness table rather than the Rust function behind it, so this
180/// covers the whole path SwiftUI takes: the table slot, the thunk's `x8`/`x20`
181/// shuffle, the registry lookup, and the generic writer. Calling the Rust
182/// function directly would skip every part that has ever been wrong.
183///
184/// # Safety
185/// `table` must be a synthesised conformance whose requirement `slot` is a
186/// `static defaultValue` returning `T`, and `key_meta` the conforming type's
187/// metadata.
188unsafe fn read_default<T: KeyValue>(
189    table: *const c_void,
190    slot: usize,
191    key_meta: *const c_void,
192) -> T {
193    unsafe {
194        let witness = *(table as *const *const c_void).add(slot + 1);
195        let mut out = vec![0u8; crate::abi::type_bytes(T::metadata())];
196        core::arch::asm!("blr {f}", f = in(reg) witness,
197            in("x8") out.as_mut_ptr(),
198            in("x20") key_meta,
199            lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
200            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
201            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
202            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
203            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
204        );
205        let value = T::read(out.as_ptr());
206        crate::abi::type_destroy(T::metadata(), out.as_mut_ptr());
207        value
208    }
209}
210
211/// A registered focused-value key: a synthesised `FocusedValueKey` conformance
212/// and the `WritableKeyPath<FocusedValues, T?>` that reaches it.
213pub struct FocusKey<T: KeyValue = i64> {
214    /// Which of [`keypath::VALUE_SLOTS`] this key's pattern and thunks occupy.
215    ///
216    /// The thunks carry it as an immediate, so it is also how the accessors find
217    /// their way back to the conformance — which is why the registry is indexed
218    /// by *slot* rather than by registration order. Those were the same number
219    /// for as long as focused keys were the only thing drawing from the pool.
220    slot: usize,
221    key_path: u64,
222    value: core::marker::PhantomData<fn() -> T>,
223}
224
225impl<T: KeyValue> FocusKey<T> {
226    /// The `WritableKeyPath<FocusedValues, T?>` object, for driving directly.
227    ///
228    /// Exposed for `tests/native_tests.rs`, which pushes a value through the key
229    /// path and reads it back: whether the pattern *works* is not something a
230    /// render or a type comparison can answer, and a key path that built but
231    /// never ran would look identical from outside.
232    #[doc(hidden)]
233    pub fn key_path_for_test(&self) -> u64 {
234        self.key_path
235    }
236
237    /// The key type's metadata and its `FocusedValueKey` witness table.
238    #[doc(hidden)]
239    pub fn parts_for_test(&self) -> (usize, usize) {
240        let r = registry().lock().expect("focus key registry");
241        let e = r[self.slot].as_ref().expect("a registered focus key");
242        (e.key_meta, e.key_wt)
243    }
244}
245
246/// One registered key's parts, as the thunks need them.
247///
248/// Held as `usize` rather than pointers so the registry can be a `static`:
249/// metadata and witness tables are immortal, so a raw pointer across threads is
250/// safe here, but `*const c_void` is not `Send` and saying so with a wrapper
251/// would be more machinery than the two casts.
252struct Registered {
253    key_meta: usize,
254    key_wt: usize,
255    key_path: u64,
256}
257
258fn registry() -> &'static Mutex<Vec<Option<Registered>>> {
259    static R: OnceLock<Mutex<Vec<Option<Registered>>>> = OnceLock::new();
260    R.get_or_init(|| Mutex::new((0..keypath::VALUE_SLOTS).map(|_| None).collect()))
261}
262
263/// Names already registered, and the slot each took.
264fn named() -> &'static Mutex<Vec<(String, usize)>> {
265    static N: OnceLock<Mutex<Vec<(String, usize)>>> = OnceLock::new();
266    N.get_or_init(|| Mutex::new(Vec::new()))
267}
268
269/// Register a focused-value key, or return the one already registered for `name`.
270///
271/// The name is not passed to SwiftUI — it identifies the key on the Rust side, so
272/// that the publisher and any future reader agree on which of the slots they
273/// mean. What SwiftUI sees is a type, minted once per name.
274///
275/// # Panics
276/// Past [`keypath::VALUE_SLOTS`] distinct synthesised key paths — the pool is
277/// shared with [`env_key`](super::env_key), [`container_key`](super::container_key)
278/// and each named property of an observable model — since each needs its own
279/// thunk pair, and those are declared rather than generated.
280pub fn focus_key<T: KeyValue>(name: &str) -> FocusKey<T> {
281    let mut names = named().lock().expect("focus key names");
282    if let Some(&(_, slot)) = names.iter().find(|(n, _)| n == name) {
283        let key_path = registry().lock().expect("focus key registry")[slot]
284            .as_ref()
285            .expect("a registered focus key")
286            .key_path;
287        return FocusKey { slot, key_path, value: core::marker::PhantomData };
288    }
289
290    let slot = keypath::next_value_slot();
291    names.push((name.to_string(), slot));
292
293    unsafe {
294        // A distinct carrier type per key, because `FocusedValues` is keyed by
295        // the *type*: one type conforming for every key would make them all the
296        // same entry.
297        let type_name: &'static [u8] = leak_cstr(&format!("RustFocusKey{slot}"));
298        let mangled: &'static [u8] =
299            leak_cstr(&format!("6rswift{}RustFocusKey{slot}V", 12 + digits(slot)));
300        let (descriptor, key_meta) = crate::state::emit_int_carrier(type_name, mangled);
301
302        let protocol = Protocol::from_symbol(c"$s7SwiftUI15FocusedValueKeyMp");
303        assert_eq!(
304            protocol.requirement_count(),
305            1,
306            "FocusedValueKey changed shape — it used to be one associated type",
307        );
308        // The one requirement is `associatedtype Value`, and it has to agree with
309        // what the key path's value type is built as, below.
310        let conformance: &'static Conformance = Box::leak(Box::new(synthesize(
311            &protocol,
312            descriptor,
313            &[Witness::Metadata(T::metadata())],
314        )));
315        let key_wt = conformance.witness_table();
316
317        let (get, set, get_target, set_target) = keypath::value_thunks(slot);
318        *get_target = focus_get as *const c_void;
319        *set_target = focus_set as *const c_void;
320        // The conformance goes in *before* the pattern is built: instantiating a
321        // key path can call its own getter, and the getter reads this.
322        registry().lock().expect("focus key registry")[slot] = Some(Registered {
323            key_meta: key_meta as usize,
324            key_wt: key_wt as usize,
325            key_path: 0,
326        });
327        let key_path = keypath::build_writable_computed(
328            keypath::value_slot(slot),
329            swift_type!(SwiftUI.FocusedValues).as_bytes(),
330            mangle::optional(T::mangled()).as_bytes(),
331            get,
332            set,
333        );
334        // SwiftUI keeps the key path inside the modifier it is handed to, and
335        // this one is handed over again on every rebuild.
336        swift_runtime_sys::RuntimeRaw::swift_retain(key_path as *mut c_void);
337        registry().lock().expect("focus key registry")[slot]
338            .as_mut()
339            .expect("just registered")
340            .key_path = key_path;
341
342        FocusKey { slot, key_path, value: core::marker::PhantomData }
343    }
344}
345
346/// How many times the key path accessors have been entered.
347///
348/// The only thing about this mechanism a client can observe. Whether a value
349/// *survives* a round trip through a `FocusedValues` is not testable from
350/// outside SwiftUI: `FocusedValues()` is `internal`, so a client cannot make one
351/// — Swift itself cannot write that test — and one built here by allocating the
352/// stride is not the thing SwiftUI's focus machinery produces. What *is*
353/// observable, and what these count, is that `swift_getKeyPath` accepted the
354/// pattern and that both accessors are reached with the shuffles they expect.
355static GETS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
356static SETS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
357
358/// `(getter calls, setter calls)` so far.
359#[doc(hidden)]
360pub fn focus_accessor_counts() -> (usize, usize) {
361    use std::sync::atomic::Ordering::Relaxed;
362    (GETS.load(Relaxed), SETS.load(Relaxed))
363}
364
365/// How many decimal digits `n` takes, for the mangled name's length prefix.
366fn digits(n: usize) -> usize {
367    if n < 10 {
368        1
369    } else if n < 100 {
370        2
371    } else {
372        3
373    }
374}
375
376/// A NUL-terminated copy of `s`, leaked.
377///
378/// The type name and mangled name go into a runtime-minted type descriptor,
379/// which outlives every caller.
380fn leak_cstr(s: &str) -> &'static [u8] {
381    let mut bytes = s.as_bytes().to_vec();
382    bytes.push(0);
383    Box::leak(bytes.into_boxed_slice())
384}
385
386/// The key path's getter: read `values[Key.self]`.
387///
388/// # Safety
389/// Reached through one of [`keypath::focus_thunks`]'s getter thunks, which
390/// supplies the three arguments in order. `root` must address a live
391/// `FocusedValues`.
392unsafe extern "C" fn focus_get(out: *mut u8, root: *const c_void, index: u64) {
393    GETS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
394    let (meta, wt) = {
395        let r = registry().lock().expect("focus key registry");
396        let e = r[index as usize].as_ref().expect("a registered focus key");
397        (e.key_meta as *const c_void, e.key_wt as *const c_void)
398    };
399    unsafe {
400        // `FocusedValues.subscript(_:)`'s getter: x0 the metatype value — which
401        // for a struct is its metadata — x1 the generic argument, x2 the
402        // conformance, x8 the `Value?` result, x20 the values themselves.
403        core::arch::asm!("blr {f}", f = in(reg) resolve::focused_values_get(),
404            in("x0") meta,
405            in("x1") meta,
406            in("x2") wt,
407            in("x8") out,
408            in("x20") root,
409            lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
410            lateout("x7") _,
411            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
412            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
413            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
414        );
415    }
416}
417
418/// The key path's setter: write `values[Key.self] = value`.
419///
420/// # Safety
421/// As [`focus_get`]; `root` must address a live, mutable `FocusedValues`.
422unsafe extern "C" fn focus_set(value: *const u8, root: *mut c_void, index: u64) {
423    SETS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
424    let (meta, wt) = {
425        let r = registry().lock().expect("focus key registry");
426        let e = r[index as usize].as_ref().expect("a registered focus key");
427        (e.key_meta as *const c_void, e.key_wt as *const c_void)
428    };
429    unsafe {
430        // The setter takes the new `Value?` by address in x0, then the same three
431        // arguments, and the values as an `inout` in x20.
432        core::arch::asm!("blr {f}", f = in(reg) resolve::focused_values_set(),
433            in("x0") value,
434            in("x1") meta,
435            in("x2") meta,
436            in("x3") wt,
437            in("x20") root,
438            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
439            lateout("x8") _,
440            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
441            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
442            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
443        );
444    }
445}
446
447/// `.focusedSceneValue(keyPath, value)` — publish a value for the whole scene.
448///
449/// What a window tells the menu bar about its selection. The scene form is the
450/// one commands read; [`focused_value_keyed`] is the same thing scoped to
451/// whatever currently has focus.
452pub fn focused_scene_value<T: KeyValue>(
453    view: &ViewHandle,
454    key: &FocusKey<T>,
455    value: impl IntoKeyValue<T>,
456) -> ViewHandle {
457    unsafe {
458        publish(
459            view,
460            key,
461            value.into_key_value(),
462            resolve::focused_scene_value_fn(),
463            resolve::focused_scene_value_oret(),
464        )
465    }
466}
467
468/// `.focusedValue(keyPath, value)` — publish a value for the focused view.
469pub fn focused_value_keyed<T: KeyValue>(
470    view: &ViewHandle,
471    key: &FocusKey<T>,
472    value: impl IntoKeyValue<T>,
473) -> ViewHandle {
474    unsafe {
475        publish(
476            view,
477            key,
478            value.into_key_value(),
479            resolve::focused_value_kp_fn(),
480            resolve::focused_value_kp_oret(),
481        )
482    }
483}
484
485/// # Safety
486/// `f`/`oret` must be a matching `focusedValue`-shaped modifier and descriptor.
487unsafe fn publish<T: KeyValue>(
488    view: &ViewHandle,
489    key: &FocusKey<T>,
490    value: T,
491    f: *const c_void,
492    oret: *const c_void,
493) -> ViewHandle {
494    unsafe {
495        // `@in T`, consumed — so a `+1` copy, which is what `write` produces.
496        let mut boxed = vec![0u8; crate::abi::type_bytes(T::metadata())];
497        value.write(boxed.as_mut_ptr());
498        // The key path is *consumed* by the initialiser, as `Environment.init`
499        // and `ForEach.init(_:id:)` are — so a cached one needs a retain per
500        // call or it is freed under SwiftUI a few rebuilds later.
501        swift_runtime_sys::RuntimeRaw::swift_retain(key.key_path as *mut c_void);
502
503        // Three substitutions, not two: the opaque type is a function of the
504        // whole generic signature — `<Self: View, T>` — so `T`'s metadata sits
505        // between the receiver's and its conformance. The *call* does not pass
506        // `T.Type`, which is what made two look right; asking for the metadata
507        // with it missing reads the conformance out of past the end of the array.
508        let (meta, wt, size) =
509            super::opaque_result_type(oret, &[view.meta, T::metadata(), view.wt]);
510        let mut result = vec![0u8; size];
511        let this = view.value_ptr();
512        // x0 = the key path, x1 = @in the value, x2 = Self.Type,
513        // x3 = Self: View, x8 = @out, x20 = self.
514        //
515        // No `T.Type`: SwiftUI recovers the value type from the key path, which
516        // is why the argument list is shorter than the signature suggests.
517        core::arch::asm!("blr {f}", f = in(reg) f,
518            in("x0") key.key_path,
519            in("x1") boxed.as_mut_ptr(),
520            in("x2") view.meta,
521            in("x3") view.wt,
522            in("x8") result.as_mut_ptr(),
523            in("x20") this,
524            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
525            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
526            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
527            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
528        );
529        wrap_owned(result, meta, wt)
530    }
531}
532
533// ── Environment keys defined in Rust ──
534//
535// `.environment(\.myKey, value)` where `myKey` is the caller's own — the last
536// thing `EnvironmentValues` could not do from here. The pieces are the same as a
537// focused value's and reuse the same pool:
538//
539//   * an `EnvironmentKey` conformance, synthesised, whose `defaultValue` is a
540//     Rust function;
541//   * a `WritableKeyPath<EnvironmentValues, Int>` whose accessors go through
542//     `EnvironmentValues`'s `EnvironmentKey` subscript.
543//
544// The value is not optional here. An `EnvironmentKey` *has* a default, so the
545// subscript returns `Value`, and a reader always gets something — which is the
546// whole difference between an environment value and a focused one.
547
548/// A registered environment key: the conformance, the key path, and the default.
549///
550/// Generic over what it holds. The type is fixed when the key is made, which is
551/// what lets [`environment_value`] and
552/// [`EnvironmentValues::value_for`](crate::views::EnvironmentValues::value_for)
553/// be checked rather than trusted — reading a `String` key as an `Int` is a
554/// compile error here and a reinterpreted pointer in Swift.
555pub struct CustomEnvKey<T: KeyValue = i64> {
556    slot: usize,
557    key_path: u64,
558    value: core::marker::PhantomData<fn() -> T>,
559}
560
561impl<T: KeyValue> CustomEnvKey<T> {
562    /// What the synthesised `defaultValue` witness produces, called as SwiftUI
563    /// calls it.
564    ///
565    /// Reading it twice must give the same answer twice: the witness is called
566    /// afresh on every access and its result is destroyed by the caller, so a
567    /// default that handed back a shared object rather than a copy would be
568    /// over-released here rather than somewhere inside SwiftUI later.
569    #[doc(hidden)]
570    pub fn default_for_test(&self) -> T {
571        let (key_meta, key_wt) = {
572            let r = env_registry().lock().expect("environment key registry");
573            let e = r[self.slot].as_ref().expect("a registered environment key");
574            (e.key_meta as *const c_void, e.key_wt as *const c_void)
575        };
576        // Requirement 1 of `EnvironmentKey` — `Value` is 0, `defaultValue` is 1.
577        unsafe { read_default(key_wt, 1, key_meta) }
578    }
579
580    /// The `WritableKeyPath<EnvironmentValues, Int>` object.
581    #[doc(hidden)]
582    pub fn key_path_for_test(&self) -> u64 {
583        self.key_path
584    }
585}
586
587/// One registered environment key.
588///
589/// The default is a *writer* rather than bytes: `defaultValue` is called again
590/// every time SwiftUI wants it and destroys what it gets, so a `String` default
591/// has to be a fresh `+1` each time. Copying stored bytes would hand back the
592/// same object twice and over-release it.
593struct EnvRegistered {
594    key_meta: usize,
595    key_wt: usize,
596    key_path: u64,
597    write_default: Box<dyn Fn(*mut u8) + Send + Sync>,
598}
599
600fn env_registry() -> &'static Mutex<Vec<Option<EnvRegistered>>> {
601    static R: OnceLock<Mutex<Vec<Option<EnvRegistered>>>> = OnceLock::new();
602    R.get_or_init(|| Mutex::new((0..keypath::VALUE_SLOTS).map(|_| None).collect()))
603}
604
605/// Names already registered, and the slot each took.
606fn env_named() -> &'static Mutex<Vec<(String, usize)>> {
607    static N: OnceLock<Mutex<Vec<(String, usize)>>> = OnceLock::new();
608    N.get_or_init(|| Mutex::new(Vec::new()))
609}
610
611/// Which key a `defaultValue` getter is being asked for.
612///
613/// Unlike the key path accessors, this one needs no per-key thunk: a static
614/// property getter is handed `Self.Type` in x20, and `Self` *is* the key. So one
615/// thunk serves every key and the metadata is the lookup.
616fn env_slot_for(meta: *const c_void) -> usize {
617    let r = env_registry().lock().expect("environment key registry");
618    r.iter()
619        .position(|e| e.as_ref().is_some_and(|e| e.key_meta == meta as usize))
620        .unwrap_or_else(|| {
621            panic!("no environment key registered for {}", crate::diag::type_name(meta))
622        })
623}
624
625crate::thunk::runtime_thunk! {
626    /// `static defaultValue` — result through x8, `Self.Type` in x20.
627    fn env_default_thunk = "rswift_env_default" via RSWIFT_ENV_DEFAULT {
628        prologue: ["mov x1, x20", "mov x0, x8"],
629        branch: "br",
630        epilogue: [],
631    }
632}
633
634/// The `defaultValue` witness: write the registered default.
635///
636/// # Safety
637/// Reached through [`env_default_thunk`]; `out` must have room for an `Int` and
638/// `meta` must be a registered key's metadata.
639unsafe extern "C" fn env_default(out: *mut u8, meta: *const c_void) {
640    let slot = env_slot_for(meta);
641    let r = env_registry().lock().expect("environment key registry");
642    (r[slot].as_ref().expect("a registered environment key").write_default)(out);
643}
644
645/// The key path's getter: read `values[Key.self]`.
646///
647/// # Safety
648/// As [`focus_get`], for an environment key.
649unsafe extern "C" fn env_get(out: *mut u8, root: *const c_void, index: u64) {
650    let (meta, wt) = {
651        let r = env_registry().lock().expect("environment key registry");
652        let e = r[index as usize].as_ref().expect("a registered environment key");
653        (e.key_meta as *const c_void, e.key_wt as *const c_void)
654    };
655    unsafe {
656        core::arch::asm!("blr {f}", f = in(reg) resolve::environment_values_get(),
657            in("x0") meta,
658            in("x1") meta,
659            in("x2") wt,
660            in("x8") out,
661            in("x20") root,
662            lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
663            lateout("x7") _,
664            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
665            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
666            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
667        );
668    }
669}
670
671/// The key path's setter: write `values[Key.self] = value`.
672///
673/// # Safety
674/// As [`env_get`].
675unsafe extern "C" fn env_set(value: *const u8, root: *mut c_void, index: u64) {
676    let (meta, wt) = {
677        let r = env_registry().lock().expect("environment key registry");
678        let e = r[index as usize].as_ref().expect("a registered environment key");
679        (e.key_meta as *const c_void, e.key_wt as *const c_void)
680    };
681    unsafe {
682        core::arch::asm!("blr {f}", f = in(reg) resolve::environment_values_set(),
683            in("x0") value,
684            in("x1") meta,
685            in("x2") meta,
686            in("x3") wt,
687            in("x20") root,
688            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
689            lateout("x8") _,
690            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
691            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
692            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
693        );
694    }
695}
696
697/// Define an environment key, or return the one already defined for `name`.
698///
699/// `default` is what a view reads when nothing above it has written the key —
700/// SwiftUI asks for it through the synthesised `defaultValue` witness, so this is
701/// the real default and not a value written into a root somewhere.
702///
703/// # Panics
704/// Past [`keypath::VALUE_SLOTS`] synthesised key paths: the pool is shared with
705/// focused keys, container keys and observable properties.
706pub fn env_key<T: KeyValue>(name: &str, default: impl IntoKeyValue<T>) -> CustomEnvKey<T> {
707    let default = default.into_key_value();
708    let mut names = env_named().lock().expect("environment key names");
709    if let Some(&(_, slot)) = names.iter().find(|(n, _)| n == name) {
710        let key_path = env_registry().lock().expect("environment key registry")[slot]
711            .as_ref()
712            .expect("a registered environment key")
713            .key_path;
714        return CustomEnvKey { slot, key_path, value: core::marker::PhantomData };
715    }
716
717    let slot = keypath::next_value_slot();
718    names.push((name.to_string(), slot));
719
720    unsafe {
721        let type_name: &'static [u8] = leak_cstr(&format!("RustEnvKey{slot}"));
722        let mangled: &'static [u8] =
723            leak_cstr(&format!("6rswift{}RustEnvKey{slot}V", 10 + digits(slot)));
724        let (descriptor, key_meta) = crate::state::emit_int_carrier(type_name, mangled);
725
726        let protocol = Protocol::from_symbol(c"$s7SwiftUI14EnvironmentKeyMp");
727        assert_eq!(
728            protocol.requirement_count(),
729            3,
730            "EnvironmentKey changed shape — it used to be Value, defaultValue and \
731             one defaulted requirement",
732        );
733        let conformance: &'static Conformance = Box::leak(Box::new(synthesize(
734            &protocol,
735            descriptor,
736            &[
737                Witness::Metadata(T::metadata()),
738                Witness::Function(env_default_thunk as *const c_void),
739                Witness::Default,
740            ],
741        )));
742        let key_wt = conformance.witness_table();
743
744        let (get, set, get_target, set_target) = keypath::value_thunks(slot);
745        *get_target = env_get as *const c_void;
746        *set_target = env_set as *const c_void;
747        RSWIFT_ENV_DEFAULT = env_default as *const c_void;
748        // Registered before the pattern is built: instantiating a key path can
749        // call its own getter, and the getter reads this.
750        env_registry().lock().expect("environment key registry")[slot] = Some(EnvRegistered {
751            key_meta: key_meta as usize,
752            key_wt: key_wt as usize,
753            key_path: 0,
754            // SAFETY (the enclosing block's): `env_default` sizes `out` by this
755            // key's own value type, which is the `T` captured here.
756            write_default: Box::new(move |out| default.write(out)),
757        });
758
759        let key_path = keypath::build_writable_computed(
760            keypath::value_slot(slot),
761            swift_type!(SwiftUI.EnvironmentValues).as_bytes(),
762            T::mangled().as_bytes(),
763            get,
764            set,
765        );
766        swift_runtime_sys::RuntimeRaw::swift_retain(key_path as *mut c_void);
767        env_registry().lock().expect("environment key registry")[slot]
768            .as_mut()
769            .expect("just registered")
770            .key_path = key_path;
771
772        CustomEnvKey { slot, key_path, value: core::marker::PhantomData }
773    }
774}
775
776/// `.environment(\.key, value)` for a key defined here.
777pub fn environment_value<T: KeyValue>(
778    view: &ViewHandle,
779    key: &CustomEnvKey<T>,
780    value: impl IntoKeyValue<T>,
781) -> ViewHandle {
782    let value = value.into_key_value();
783    unsafe {
784        let _ = key.slot;
785        // `@in`, and owned by the callee — so a `+1` copy, which is what
786        // `KeyValue::write` produces.
787        let mut boxed = vec![0u8; crate::abi::type_bytes(T::metadata()).max(1)];
788        value.write(boxed.as_mut_ptr());
789        // Consumed by the modifier, as every key path argument in this crate is.
790        swift_runtime_sys::RuntimeRaw::swift_retain(key.key_path as *mut c_void);
791
792        let (meta, wt, size) = super::opaque_result_type(
793            resolve::environment_oret(),
794            &[view.meta, T::metadata(), view.wt],
795        );
796        let mut result = vec![0u8; size];
797        let this = view.value_ptr();
798        core::arch::asm!("blr {f}", f = in(reg) resolve::environment_fn(),
799            in("x0") key.key_path,
800            in("x1") boxed.as_mut_ptr(),
801            in("x2") view.meta,
802            in("x3") view.wt,
803            in("x8") result.as_mut_ptr(),
804            in("x20") this,
805            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
806            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
807            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
808            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
809        );
810        wrap_owned(result, meta, wt)
811    }
812}
813
814// ── Container values defined in Rust ──
815//
816// `.containerValue(\.myKey, value)` — how a row tells the container it is in
817// something about itself. The modifier is `@_alwaysEmitIntoClient`, so there is
818// no symbol for it; its body is
819// `modifier(_ContainerValueWritingModifier(keyPath:value:))`, and that
820// initialiser is client-emitted too.
821//
822// The type is `@frozen`, though, and its two stored properties are the whole of
823// it — so the value is built by writing the key path and the value into a buffer.
824// `@frozen` is exactly the promise that makes that legitimate: it says the layout
825// is API. Nothing else in this crate writes a layout down, and this is the one
826// declaration that permits it.
827
828/// A registered container-value key.
829pub struct CustomContainerKey<T: KeyValue = i64> {
830    slot: usize,
831    key_path: u64,
832    value: core::marker::PhantomData<fn() -> T>,
833}
834
835impl<T: KeyValue> CustomContainerKey<T> {
836    /// What the synthesised `defaultValue` witness produces. As
837    /// [`CustomEnvKey::default_for_test`].
838    #[doc(hidden)]
839    pub fn default_for_test(&self) -> T {
840        let (key_meta, key_wt) = {
841            let r = container_registry().lock().expect("container key registry");
842            let e = r[self.slot].as_ref().expect("a registered container key");
843            (e.key_meta as *const c_void, e.key_wt as *const c_void)
844        };
845        // Requirement 1 of `ContainerValueKey` — `Value`, then `defaultValue`.
846        unsafe { read_default(key_wt, 1, key_meta) }
847    }
848}
849
850/// One registered container-value key.
851///
852/// The default is a writer for the same reason [`EnvRegistered`]'s is.
853struct ContainerRegistered {
854    key_meta: usize,
855    key_wt: usize,
856    key_path: u64,
857    write_default: Box<dyn Fn(*mut u8) + Send + Sync>,
858}
859
860fn container_registry() -> &'static Mutex<Vec<Option<ContainerRegistered>>> {
861    static R: OnceLock<Mutex<Vec<Option<ContainerRegistered>>>> = OnceLock::new();
862    R.get_or_init(|| Mutex::new((0..keypath::VALUE_SLOTS).map(|_| None).collect()))
863}
864
865fn container_named() -> &'static Mutex<Vec<(String, usize)>> {
866    static N: OnceLock<Mutex<Vec<(String, usize)>>> = OnceLock::new();
867    N.get_or_init(|| Mutex::new(Vec::new()))
868}
869
870crate::thunk::runtime_thunk! {
871    /// `static defaultValue` for a container key — result in x8, `Self.Type` in x20.
872    fn container_default_thunk = "rswift_container_default" via RSWIFT_CONTAINER_DEFAULT {
873        prologue: ["mov x1, x20", "mov x0, x8"],
874        branch: "br",
875        epilogue: [],
876    }
877}
878
879/// The `defaultValue` witness for a container key.
880///
881/// # Safety
882/// Reached through [`container_default_thunk`].
883unsafe extern "C" fn container_default(out: *mut u8, meta: *const c_void) {
884    let r = container_registry().lock().expect("container key registry");
885    let entry = r
886        .iter()
887        .flatten()
888        .find(|e| e.key_meta == meta as usize)
889        .unwrap_or_else(|| {
890            panic!("no container key registered for {}", crate::diag::type_name(meta))
891        });
892    (entry.write_default)(out);
893}
894
895/// The key path's getter for a container key.
896///
897/// # Safety
898/// As [`focus_get`].
899unsafe extern "C" fn container_get(out: *mut u8, root: *const c_void, index: u64) {
900    let (meta, wt) = {
901        let r = container_registry().lock().expect("container key registry");
902        let e = r[index as usize].as_ref().expect("a registered container key");
903        (e.key_meta as *const c_void, e.key_wt as *const c_void)
904    };
905    unsafe {
906        core::arch::asm!("blr {f}", f = in(reg) resolve::container_values_get(),
907            in("x0") meta, in("x1") meta, in("x2") wt,
908            in("x8") out, in("x20") root,
909            lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
910            lateout("x7") _,
911            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
912            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
913            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
914        );
915    }
916}
917
918/// The key path's setter for a container key.
919///
920/// # Safety
921/// As [`container_get`].
922unsafe extern "C" fn container_set(value: *const u8, root: *mut c_void, index: u64) {
923    let (meta, wt) = {
924        let r = container_registry().lock().expect("container key registry");
925        let e = r[index as usize].as_ref().expect("a registered container key");
926        (e.key_meta as *const c_void, e.key_wt as *const c_void)
927    };
928    unsafe {
929        core::arch::asm!("blr {f}", f = in(reg) resolve::container_values_set(),
930            in("x0") value, in("x1") meta, in("x2") meta, in("x3") wt,
931            in("x20") root,
932            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
933            lateout("x8") _,
934            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
935            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
936            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
937        );
938    }
939}
940
941/// Define a container-value key, or return the one already defined for `name`.
942///
943/// # Panics
944/// Past [`keypath::VALUE_SLOTS`] synthesised key paths — the pool is shared with
945/// [`focus_key`], [`env_key`] and each named property of an observable model.
946pub fn container_key<T: KeyValue>(
947    name: &str,
948    default: impl IntoKeyValue<T>,
949) -> CustomContainerKey<T> {
950    let default = default.into_key_value();
951    let mut names = container_named().lock().expect("container key names");
952    if let Some(&(_, slot)) = names.iter().find(|(n, _)| n == name) {
953        let key_path = container_registry().lock().expect("container key registry")[slot]
954            .as_ref()
955            .expect("a registered container key")
956            .key_path;
957        return CustomContainerKey { slot, key_path, value: core::marker::PhantomData };
958    }
959
960    let slot = keypath::next_value_slot();
961    names.push((name.to_string(), slot));
962
963    unsafe {
964        let type_name: &'static [u8] = leak_cstr(&format!("RustContainerKey{slot}"));
965        let mangled: &'static [u8] =
966            leak_cstr(&format!("6rswift{}RustContainerKey{slot}V", 16 + digits(slot)));
967        let (descriptor, key_meta) = crate::state::emit_int_carrier(type_name, mangled);
968
969        let protocol = Protocol::from_symbol(c"$s7SwiftUI17ContainerValueKeyMp");
970        assert_eq!(
971            protocol.requirement_count(),
972            2,
973            "ContainerValueKey changed shape — it used to be Value and defaultValue",
974        );
975        let conformance: &'static Conformance = Box::leak(Box::new(synthesize(
976            &protocol,
977            descriptor,
978            &[
979                Witness::Metadata(T::metadata()),
980                Witness::Function(container_default_thunk as *const c_void),
981            ],
982        )));
983        let key_wt = conformance.witness_table();
984
985        let (get, set, get_target, set_target) = keypath::value_thunks(slot);
986        *get_target = container_get as *const c_void;
987        *set_target = container_set as *const c_void;
988        RSWIFT_CONTAINER_DEFAULT = container_default as *const c_void;
989        container_registry().lock().expect("container key registry")[slot] =
990            Some(ContainerRegistered {
991                key_meta: key_meta as usize,
992                key_wt: key_wt as usize,
993                key_path: 0,
994                // SAFETY (the enclosing block's): as `env_key`'s.
995                write_default: Box::new(move |out| default.write(out)),
996            });
997
998        let key_path = keypath::build_writable_computed(
999            keypath::value_slot(slot),
1000            swift_type!(SwiftUI.ContainerValues).as_bytes(),
1001            T::mangled().as_bytes(),
1002            get,
1003            set,
1004        );
1005        swift_runtime_sys::RuntimeRaw::swift_retain(key_path as *mut c_void);
1006        container_registry().lock().expect("container key registry")[slot]
1007            .as_mut()
1008            .expect("just registered")
1009            .key_path = key_path;
1010
1011        CustomContainerKey { slot, key_path, value: core::marker::PhantomData }
1012    }
1013}
1014
1015/// `.containerValue(\.key, value)` for a key defined here.
1016///
1017/// What the `@_alwaysEmitIntoClient` modifier's body does, since the modifier
1018/// itself exports no symbol: build a `_ContainerValueWritingModifier` and apply
1019/// it with `.modifier(_:)`.
1020pub fn container_value<T: KeyValue>(
1021    view: &ViewHandle,
1022    key: &CustomContainerKey<T>,
1023    value: impl IntoKeyValue<T>,
1024) -> ViewHandle {
1025    let value = value.into_key_value();
1026    unsafe {
1027        let _ = key.slot;
1028        let modifier_meta =
1029            crate::abi::generic_metadata1(resolve::container_write_ma(), T::metadata());
1030        assert!(
1031            !modifier_meta.is_null(),
1032            "_ContainerValueWritingModifier<{}> metadata",
1033            crate::diag::type_name(T::metadata()),
1034        );
1035
1036        // `@frozen public struct _ContainerValueWritingModifier<Value> { keyPath,
1037        // value }`. `@frozen` is the promise that those two fields, in that order,
1038        // *are* the layout — so the value is written rather than initialised,
1039        // because the initialiser is `@_alwaysEmitIntoClient` and exports nothing.
1040        // The field offsets are still asked for rather than assumed.
1041        let offsets = crate::abi::struct_field_offsets(modifier_meta)
1042            .expect("_ContainerValueWritingModifier has reflection metadata");
1043        assert_eq!(offsets.len(), 2, "_ContainerValueWritingModifier changed shape");
1044        let mut modifier = vec![0u8; crate::abi::type_bytes(modifier_meta)];
1045        // The modifier owns the key path it stores.
1046        swift_runtime_sys::RuntimeRaw::swift_retain(key.key_path as *mut c_void);
1047        modifier[offsets[0]..offsets[0] + 8].copy_from_slice(&key.key_path.to_le_bytes());
1048        // The stored field is the second one; the modifier owns what is written
1049        // there, so this is the `+1` copy and not a borrow of the argument.
1050        value.write(modifier.as_mut_ptr().add(offsets[1]));
1051
1052        let meta = crate::abi::generic_metadata2(
1053            resolve::modified_content_ma(),
1054            view.meta,
1055            modifier_meta,
1056        );
1057        assert!(!meta.is_null(), "ModifiedContent metadata");
1058        let wt = crate::abi::get_view_wt(meta);
1059        assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
1060
1061        let mut result = vec![0u8; crate::abi::type_bytes(meta)];
1062        let this = view.value_ptr();
1063        core::arch::asm!("blr {f}", f = in(reg) resolve::apply_view_modifier_fn(),
1064            in("x0") modifier.as_mut_ptr(),
1065            in("x1") view.meta,
1066            in("x2") modifier_meta,
1067            in("x3") view.wt,
1068            in("x8") result.as_mut_ptr(),
1069            in("x20") this,
1070            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
1071            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1072            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1073            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
1074        );
1075        wrap_owned(result, meta, wt)
1076    }
1077}