Skip to main content

swiftui_native/views/
mod.rs

1//! Building SwiftUI views from Rust, with no Swift source anywhere.
2//!
3//! Every view here is made by resolving a mangled symbol and calling it
4//! through the primitives in [`crate::abi`]. Results are wrapped in `AnyView`,
5//! which is a single class reference, so one [`ViewHandle`] can stand for any
6//! view regardless of the concrete type SwiftUI built.
7//!
8//! This module holds what the rest share — the handle, the helpers that apply
9//! a modifier, and the dump used to compare a value against Swift's — and the
10//! bindings themselves are grouped by what they build:
11//!
12//! * `primitives`, `typography`, `shapes`, `stacks`, `collections` — views
13//! * `layout`, `effects`, `materials`, `compositing`, `hover` — appearance
14//! * `controls`, `interaction`, `binding`, `closures` — behaviour
15//! * `navigation`, `presentation`, `toolbar`, `conditional`, `timeline`
16//! * `accessibility`, `namespace`, `generics`, `interop`
17//! * `generated`, `enums`, `styles`, `strings`, `plain`, `sets`,
18//!   `optionsets`, `concrete` — the long tail, grouped by argument shape
19//!
20//! The grouping is by *argument shape* rather than by feature in the last
21//! group on purpose: what makes a binding hard is almost never what the
22//! modifier does, it is whether its arguments travel in registers or by
23//! address.
24
25use crate::abi;
26use crate::resolve;
27use core::ffi::c_void;
28
29/// A SwiftUI view value, kept concrete.
30///
31/// This used to be one word: a retained `AnyView`, which every modifier both
32/// consumed and produced. That made ownership trivial — a retain and a release —
33/// and made every type this crate built a lie by one layer. Swift's
34/// `Text("Fx").padding(4)` is `ModifiedContent<Text, _PaddingLayout>`; erasing
35/// each step made it `ModifiedContent<AnyView, _PaddingLayout>`, and the
36/// documentation corpus could only ever match 18 of its 89 listings on type.
37///
38/// So the handle carries the value itself, with the metadata and `View` witness
39/// needed to hand it back to Swift. `AnyView` is not special here — it is simply
40/// one of the types a handle can hold, which is what [`Self::into_anyview`]
41/// produces for the places that genuinely need erasure: a `@ViewBuilder` closure
42/// returning heterogeneous branches, or a container's children.
43///
44/// Ownership is the value witness table's business now rather than the
45/// refcount's: `initialize_with_copy` to clone, `destroy` to drop. For a view
46/// that is a single class reference those are still a retain and a release; for
47/// one with several stored properties they do the right thing for each.
48pub struct ViewHandle {
49    /// The value, `stride` bytes of it.
50    bytes: Vec<u8>,
51    /// Its type.
52    pub(crate) meta: *const c_void,
53    /// Its `View` conformance.
54    pub(crate) wt: *const c_void,
55}
56
57impl ViewHandle {
58    /// Take ownership of a `+1` value the caller has just built.
59    ///
60    /// The bytes are moved, not copied: whoever produced them is handing over
61    /// the one reference they own, so the source must not be destroyed after.
62    /// That is what every modifier does with its result buffer.
63    pub(crate) unsafe fn wrap(
64        value_ptr: *const c_void,
65        meta: *const c_void,
66        wt: *const c_void,
67    ) -> Self {
68        unsafe {
69            debug_assert!(!meta.is_null() && !wt.is_null(), "view needs a type and a witness");
70            // Allocate the stride (`type_bytes` floors at 1 so the handle
71            // always has an address), but copy only the size. `Circle` and
72            // friends are empty structs — size 0, stride 1 — and their callers
73            // hand us an empty `Vec`, whose `as_ptr()` is u8's dangling `0x1`.
74            // Copying the stride would memmove a byte off that.
75            let n = abi::type_bytes(meta);
76            let copy = abi::type_size(meta);
77            let mut bytes = vec![0u8; n.max(1)];
78            if copy > 0 {
79                core::ptr::copy_nonoverlapping(value_ptr as *const u8, bytes.as_mut_ptr(), copy);
80            }
81            Self { bytes, meta, wt }
82        }
83    }
84
85    /// Take ownership of a buffer a callee has already written into.
86    ///
87    /// [`wrap`](Self::wrap) copies, because its caller may not own the bytes.
88    /// Most callers do: `call_modifier` allocates the `@out` buffer, hands it to
89    /// SwiftUI, and then has no further use for it. Copying that into a second
90    /// allocation of the same size is two allocations per modifier where one
91    /// will do.
92    ///
93    /// # Safety
94    /// `bytes` must hold an initialised, owned value of type `meta`, and be at
95    /// least its size.
96    pub(crate) unsafe fn adopt(
97        mut bytes: Vec<u8>,
98        meta: *const c_void,
99        wt: *const c_void,
100    ) -> Self {
101        unsafe {
102            debug_assert!(!meta.is_null() && !wt.is_null(), "view needs a type and a witness");
103            debug_assert!(bytes.len() >= abi::type_size(meta), "buffer smaller than its type");
104            // `wrap` allocates `type_bytes`, which floors at 1 so a handle always
105            // has an address; match that for an empty value.
106            if bytes.is_empty() {
107                bytes.push(0);
108            }
109            Self { bytes, meta, wt }
110        }
111    }
112
113    /// A handle over an `AnyView` that is already built and retained.
114    pub(crate) fn from_anyview(anyview: u64) -> Self {
115        debug_assert!(anyview != 0, "AnyView pointer is null");
116        Self {
117            bytes: anyview.to_le_bytes().to_vec(),
118            meta: resolve::anyview_meta(),
119            wt: resolve::anyview_wt(),
120        }
121    }
122
123    /// The value's address, for passing as `@in_guaranteed` self.
124    pub(crate) fn value_ptr(&self) -> *const c_void {
125        self.bytes.as_ptr() as *const c_void
126    }
127
128    /// Erase to `AnyView`, consuming this handle.
129    ///
130    /// The one place erasure still belongs: a closure that must return a single
131    /// type for branches of different ones, and a container's children.
132    pub(crate) fn into_anyview_word(self) -> u64 {
133        unsafe {
134            if self.meta == resolve::anyview_meta() {
135                // Already one. Take the word rather than boxing an AnyView in an
136                // AnyView, which Swift does not do either.
137                let w = u64::from_le_bytes(self.bytes[..8].try_into().expect("AnyView word"));
138                core::mem::forget(self);
139                return w;
140            }
141            let w = abi::anyview_wrap(self.value_ptr(), self.meta, self.wt);
142            // `AnyView.init` takes its argument `@in` — consumed. It is handed a
143            // bitwise copy of these bytes, so the box now owns the very
144            // references this handle holds. Letting the handle drop would
145            // release them a second time.
146            core::mem::forget(self);
147            w
148        }
149    }
150
151    /// Erase to `AnyView` without consuming, for callers that keep the original.
152    pub(crate) fn to_anyview_word(&self) -> u64 {
153        self.clone().into_anyview_word()
154    }
155
156    /// This view as an `AnyView`-typed handle.
157    pub fn into_anyview(self) -> Self {
158        if self.meta == resolve::anyview_meta() {
159            return self;
160        }
161        Self::from_anyview(self.into_anyview_word())
162    }
163
164    /// The raw `AnyView` word, for the render path and the ObjC bridge.
165    pub fn as_ptr(&self) -> *mut c_void {
166        self.to_anyview_word() as *mut c_void
167    }
168}
169
170impl Clone for ViewHandle {
171    fn clone(&self) -> Self {
172        unsafe {
173            let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(self.meta);
174            let mut bytes = vec![0u8; self.bytes.len()];
175            if vwt.size > 0 {
176                (vwt.initialize_with_copy)(
177                    bytes.as_mut_ptr() as *mut c_void,
178                    self.bytes.as_ptr() as *mut c_void,
179                    self.meta,
180                );
181            }
182            Self { bytes, meta: self.meta, wt: self.wt }
183        }
184    }
185}
186
187impl Drop for ViewHandle {
188    fn drop(&mut self) {
189        unsafe {
190            let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(self.meta);
191            if vwt.size > 0 {
192                (vwt.destroy)(self.bytes.as_mut_ptr() as *mut c_void, self.meta);
193            }
194        }
195    }
196}
197
198/// The Swift type of the view a handle wraps, as the runtime spells it.
199///
200/// An `AnyView` is one word: a reference to a box of the generic class
201/// `AnyViewStorage<V>`. The box's isa is that class's metadata, and the runtime
202/// prints a generic class with its arguments spelled out — so `V` comes back
203/// even though `AnyView` exists to erase it.
204///
205/// This is what makes a modifier that draws nothing checkable. `.accessibilityLabel`
206/// and `.focusable` produce no pixels and expose nothing through AppKit, but they
207/// do produce a *type* — and comparing that against the one Swift builds catches a
208/// modifier applied wrongly, applied twice, or not applied at all.
209///
210/// The handle carries its own metadata, so this is a read rather than an
211/// excavation. It used to reach through the `AnyView` box to the generic
212/// argument of its storage class, and strip the `AnyViewStorage<…>` wrapper back
213/// off — which was only ever a way of recovering the type the box had just
214/// erased.
215pub fn dynamic_type_name(view: &ViewHandle) -> String {
216    // A `ViewHandle` holds the metadata of the value it wraps — an invariant of
217    // every constructor — so this is the form safe code should use.
218    crate::diag::type_name(view.meta)
219}
220
221/// A full recursive description of the value a handle holds, as Swift's own
222/// `dump` writes it.
223///
224/// [`dynamic_type_name`] answers "is this the same type Swift builds"; this
225/// answers "is it the same *value*", which is the only thing that reaches a
226/// modifier's arguments. `.accessibilityLabel("Save")` and
227/// `.accessibilityLabel("Delete")` produce one type and one rendering, and
228/// differ here.
229///
230/// Calling `dump` rather than reimplementing it means both sides of a
231/// comparison are formatted by the same code, so the strings line up exactly.
232/// Addresses and dyld-relative context names are normalised out, since those
233/// legitimately differ between processes; see [`normalise_dump`].
234pub fn dump_value(view: &ViewHandle) -> String {
235    dump_value_to_depth(view, DUMP_MAX_DEPTH)
236}
237
238/// [`dump_value`] with an explicit depth limit.
239pub fn dump_value_to_depth(view: &ViewHandle, max_depth: u64) -> String {
240    // The value to reflect is the view itself, at its own type. Reflecting the
241    // `AnyView` that used to wrap it put one `AnyView(…)` layer in front of
242    // every dump, and one more for every step of the chain.
243    let out = unsafe {
244        crate::diag::dump_any(view.value_ptr() as *mut c_void, view.meta, max_depth)
245    };
246    normalise_dump(&out)
247}
248
249/// How deep [`dump_value`] descends: far enough to reach a modifier's stored
250/// arguments, bounded so a cyclic or huge tree cannot run away.
251const DUMP_MAX_DEPTH: u64 = 12;
252
253
254/// Remove the parts of a dump that legitimately differ between two runs.
255///
256/// Object addresses, the dyld-relative context a private type prints itself in,
257/// and `dump`'s own back-reference numbering for repeated objects.
258///
259/// A third is Combine's `pointerValue`, which is an address like any other —
260/// printed in *decimal*, so the rule for `0x…` does not reach it.
261///
262/// A fourth is `Predicate`, whose variables are numbered from a counter that
263/// runs for the life of the *process*. The reference binary makes other
264/// predicates before it reaches this one, so the same predicate is
265/// `Variable(0)` here and `Variable(9)` there — and `Variable(11)` in the next
266/// case along, which is what gives it away.
267///
268/// A fourth is `.task`, whose modifier stores a name SwiftUI synthesizes from
269/// the call site's `#file` and `#line` — `"View.task @ <file>:<line>"`. Rust has
270/// no equivalent of either, and the two sides are genuinely at different source
271/// locations, so what follows the `@` goes. The prefix stays, so a name of the
272/// wrong *shape* still fails.
273///
274/// A fourth is `CharacterSet`, which is a struct wrapping a *bridged*
275/// `NSCharacterSet`. Which concrete class backs it — a bitmap `NSCharacterSet`
276/// whose `description` lists every member, or a CF-bridged `__NSCFCharacterSet`
277/// that reflects as a superclass chain — depends on what else in the process has
278/// touched Foundation, so the same set reflects two ways in two processes. The
279/// set itself is identical; only its backing is not, so the backing goes.
280///
281/// Two more come from `AttributedString`, which a search field's prompt is made
282/// of: `Guts.version` counts every attributed string the *process* has built,
283/// and `_RopeVersion._value` is seeded from the process's random hash seed. Both
284/// differ between two runs of the same program, let alone between this one and
285/// the Swift reference, and neither says anything about the value. Everything
286/// left is a property of the value.
287pub fn normalise_dump(s: &str) -> String {
288    let bytes = s.as_bytes();
289    // Scanning by byte is safe because every pattern below is ASCII, but the
290    // output has to be rebuilt as bytes too: `dump` draws its tree with `▿`
291    // and friends, and pushing each byte as a `char` would turn one of those
292    // into three.
293    let mut out: Vec<u8> = Vec::with_capacity(bytes.len());
294    let mut i = 0;
295    while i < bytes.len() {
296        if bytes[i..].starts_with(b"0x") {
297            out.extend_from_slice(b"0x*");
298            i += 2;
299            while i < bytes.len() && (bytes[i] as char).is_ascii_hexdigit() {
300                i += 1;
301            }
302        } else if bytes[i..].starts_with(b"pointerValue: ") {
303            out.extend_from_slice(b"pointerValue: *");
304            i += "pointerValue: ".len();
305            while i < bytes.len() && bytes[i].is_ascii_digit() {
306                i += 1;
307            }
308        } else if let Some(prefix) = [
309            // `dump` writes the whole value inline on its first line, and some
310            // of SwiftUI's own descriptions embed it again further down, so the
311            // id has to be caught as text as well as on its own line. Both
312            // spellings appear: the type named in full, and the bare `ID(id:)`
313            // inside a hand-written description.
314            &b"Namespace.ID(id: "[..],
315            &b"namespace: ID(id: "[..],
316        ]
317        .into_iter()
318        .find(|p| bytes[i..].starts_with(p))
319        {
320            out.extend_from_slice(prefix);
321            i += prefix.len();
322            let start = i;
323            while i < bytes.len() && bytes[i].is_ascii_digit() {
324                i += 1;
325            }
326            let allocated = &bytes[start..i] != b"0";
327            out.extend_from_slice(if allocated { b"<allocated>" } else { b"0" });
328        } else if bytes[i..].starts_with(b"Variable(") {
329            out.extend_from_slice(b"Variable(*");
330            i += "Variable(".len();
331            while i < bytes.len() && bytes[i] != b')' {
332                i += 1;
333            }
334        } else if bytes[i..].starts_with(b"View.task @ ") {
335            out.extend_from_slice(b"View.task @ *");
336            i += "View.task @ ".len();
337            while i < bytes.len() && bytes[i] != b'"' {
338                i += 1;
339            }
340        } else if bytes[i..].starts_with(b"(unknown context at $") {
341            out.extend_from_slice(b"(unknown context)");
342            i += "(unknown context at $".len();
343            while i < bytes.len() && bytes[i] != b')' {
344                i += 1;
345            }
346            i += 1;
347        } else if bytes[i..].starts_with(b" #") {
348            out.extend_from_slice(b" #");
349            i += 2;
350            while i < bytes.len() && bytes[i].is_ascii_digit() {
351                i += 1;
352            }
353        } else {
354            out.push(bytes[i]);
355            i += 1;
356        }
357    }
358    let once = String::from_utf8_lossy(&out).into_owned();
359    // Line-oriented, because both fields are only non-deterministic under a
360    // particular parent: a bare `version:` elsewhere in a dump is real data.
361    let mut lines: Vec<String> = Vec::new();
362    let mut in_rope_version = false;
363    let mut in_variable_id = false;
364    let mut in_namespace_id = false;
365    let mut in_attributed_guts = false;
366    // How deep the `- immutable:` line was, while skipping a bridged
367    // `CharacterSet`'s backing. `None` when not skipping.
368    let mut skipping: Option<usize> = None;
369    for line in once.lines() {
370        let t = line.trim_start();
371        let indent = line.len() - t.len();
372        if let Some(depth) = skipping {
373            // The class chain is indented under it. The description's own
374            // embedded newlines are not part of the tree at all, so they carry
375            // no dump glyph — including the trailing back-reference marker,
376            // which lands at an indent of its own.
377            let glyph = t.starts_with('-') || t.starts_with('\u{25bf}') || t.starts_with('\u{25b9}');
378            if indent > depth || !glyph {
379                continue;
380            }
381            skipping = None;
382        }
383        if t.starts_with("- immutable: ") && t.contains("CharacterSet") {
384            lines.push(format!("{}- immutable: *", &line[..indent]));
385            skipping = Some(indent);
386            continue;
387        }
388        if in_variable_id && t.starts_with("- id: ") {
389            lines.push(format!("{}- id: *", &line[..indent]));
390            in_variable_id = false;
391            continue;
392        }
393        // A namespace id is minted by `Namespace.wrappedValue` from a counter
394        // that advances on every *read*, so two processes never agree on the
395        // number — but they do agree on whether there is one. Zero is what
396        // SwiftUI stores for a namespace no view graph installed, and reading
397        // `wrappedValue` never returns it, so keeping that case distinct is
398        // what makes these modifiers worth comparing at all: it is exactly the
399        // value this crate used to produce by calling the getter wrongly.
400        if in_namespace_id && t.starts_with("- id: ") {
401            let allocated = t["- id: ".len()..].trim() != "0";
402            let shown = if allocated { "<allocated>" } else { "0" };
403            lines.push(format!("{}- id: {shown}", &line[..indent]));
404            in_namespace_id = false;
405            continue;
406        }
407        if in_rope_version && t.starts_with("- _value: ") {
408            lines.push(format!("{}- _value: *", &line[..line.len() - t.len()]));
409        } else if in_attributed_guts && t.starts_with("- version: ") {
410            lines.push(format!("{}- version: *", &line[..line.len() - t.len()]));
411        } else {
412            lines.push(line.to_string());
413        }
414        in_rope_version = t.ends_with("CollectionsInternal._RopeVersion");
415        if t.ends_with("VariableID") {
416            in_variable_id = true;
417        }
418        if t.ends_with("SwiftUI.Namespace.ID") {
419            in_namespace_id = true;
420        }
421        if t.ends_with("Foundation.AttributedString.Guts #") {
422            in_attributed_guts = true;
423        } else if t.starts_with("- trackedRanges:") {
424            in_attributed_guts = false;
425        }
426    }
427    let mut joined = lines.join("\n");
428    if once.ends_with('\n') {
429        joined.push('\n');
430    }
431    joined
432}
433
434/// A `Text` value, as four words, rather than one wrapped in an `AnyView`.
435///
436/// A handful of modifiers take a `Text` as an *argument* — `.help(_:)`,
437/// `.navigationSubtitle(_:)` — and want the value itself, not an erased view.
438pub(crate) unsafe fn text_value(s: &str) -> [u64; 4] {
439    unsafe {
440        let ss = abi::swift_string(s);
441        let s0 = u64::from_le_bytes(ss[..8].try_into().expect("string lo"));
442        let s1 = u64::from_le_bytes(ss[8..].try_into().expect("string hi"));
443        let (k0, k1, k2, k3) = localized_string_key(s0, s1);
444        let (t0, t1, t2, t3): (u64, u64, u64, u64);
445        // `comment` spills past x7, so zero the stack the callee will read.
446        core::arch::asm!(
447            "sub sp, sp, #32",
448            "stp xzr, xzr, [sp]",
449            "stp xzr, xzr, [sp, #16]",
450            "blr {f}",
451            "add sp, sp, #32",
452            f = in(reg) resolve::text_lsk_init(),
453            inout("x0") k0 => t0, inout("x1") k1 => t1,
454            inout("x2") k2 => t2, inout("x3") k3 => t3,
455            in("x4") 0u64, in("x5") 0u64, in("x6") 0u64, in("x7") 0u64,
456            in("x20") resolve::text_meta(),
457            lateout("x8") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
458            lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
459            lateout("x16") _, lateout("x17") _, lateout("lr") _, clobber_abi("C"),
460        );
461        [t0, t1, t2, t3]
462    }
463}
464
465/// [`opaque_modifier`] with one `LocalizedStringKey`, which is four words wide.
466unsafe fn modifier_lsk(f: *const c_void, o: *const c_void, v: &ViewHandle, s: &str) -> ViewHandle {
467    unsafe {
468        let ss = abi::swift_string(s);
469        let s0 = u64::from_le_bytes(ss[..8].try_into().expect("string lo"));
470        let s1 = u64::from_le_bytes(ss[8..].try_into().expect("string hi"));
471        let (k0, k1, k2, k3) = localized_string_key(s0, s1);
472        opaque_modifier(f, o, v, &[k0, k1, k2, k3], [0.0; 4])
473    }
474}
475
476/// [`opaque_modifier`] with one `Text`, which is four words wide.
477unsafe fn modifier_text(f: *const c_void, o: *const c_void, v: &ViewHandle, s: &str) -> ViewHandle {
478    unsafe {
479        let t = text_value(s);
480        opaque_modifier(f, o, v, &t, [0.0; 4])
481    }
482}
483
484/// [`opaque_modifier`] with one `Bool?`.
485///
486/// A `Bool` has spare bits, but `Optional<Bool>` still travels as payload and
487/// tag in two registers rather than one.
488unsafe fn modifier_opt_bool(
489    f: *const c_void,
490    o: *const c_void,
491    v: &ViewHandle,
492    value: Option<bool>,
493) -> ViewHandle {
494    unsafe {
495        // One byte, not two words. A `Bool` uses two of the 256 values a byte
496        // can hold, so `Optional<Bool>` spends a third on `nil` instead of
497        // carrying a discriminator of its own — which is why the whole thing
498        // fits in a single register.
499        let mut buf = [0u8; 1];
500        if let Some(b) = value {
501            buf[0] = u8::from(b);
502        }
503        abi::store_optional_tag(&mut buf, resolve::bool_meta(), value.is_some());
504        opaque_modifier(f, o, v, &[u64::from(buf[0])], [0.0; 4])
505    }
506}
507
508/// [`opaque_modifier`] with one `Color?`, which is one word in one register.
509///
510/// `Color` is `@frozen` and wraps a single reference, so it is loadable and
511/// passed by value — and the optional needs no discriminator of its own,
512/// because a null reference is already `nil`. Passing the *address* of the word
513/// instead is what a resilient type would want, and it faults here.
514unsafe fn modifier_opt_color(
515    f: *const c_void,
516    o: *const c_void,
517    v: &ViewHandle,
518    value: Option<crate::Color>,
519) -> ViewHandle {
520    unsafe {
521        let word: u64 = match value {
522            Some(c) => color_value(c.r, c.g, c.b, c.a),
523            None => 0,
524        };
525        opaque_modifier(f, o, v, &[word], [0.0; 4])
526    }
527}
528
529/// The first three words of a `Binding`, which is how one travels in registers.
530///
531/// A `Binding` is seventeen bytes — a getter, a setter and a discriminator — so
532/// it is loadable and occupies three registers rather than being passed by
533/// pointer.
534unsafe fn binding_words(binding: &mut [u8]) -> [u64; 3] {
535    unsafe {
536        let p = binding.as_ptr();
537        [
538            (p as *const u64).read_unaligned(),
539            (p.add(8) as *const u64).read_unaligned(),
540            u64::from(*p.add(16)),
541        ]
542    }
543}
544
545/// Attach a `DragGesture` with an `onEnded` handler through a modifier that is
546/// generic over `Gesture` — `.highPriorityGesture`, `.simultaneousGesture`.
547///
548/// The same shape as [`style_modifier`]: the gesture's metadata and conformance
549/// ride alongside `Self`'s, with a `GestureMask` after the gesture itself.
550///
551/// # Safety
552/// `func` and `oret` must be a modifier taking `(some Gesture, GestureMask)`.
553unsafe fn gesture_variant(
554    func: *const c_void,
555    oret: *const c_void,
556    view: &ViewHandle,
557    minimum_distance: f64,
558    action: impl Fn() + 'static,
559) -> ViewHandle {
560    unsafe {
561        let g_meta = abi::metadata0(resolve::drag_gesture_ma());
562        let space_meta = abi::metadata0(resolve::local_space_ma());
563        let mut gesture = vec![0u8; abi::type_bytes(g_meta)];
564        let mut space = [0u8; 8];
565        core::arch::asm!("blr {f}", f = in(reg) resolve::drag_gesture_init(),
566            in("d0") minimum_distance,
567            in("x0") space.as_mut_ptr(),
568            in("x1") space_meta,
569            in("x2") resolve::local_space_wt(),
570            in("x8") gesture.as_mut_ptr(),
571            in("x20") g_meta,
572            lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
573            lateout("x7") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
574            lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
575            lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
576            clobber_abi("C"),
577        );
578
579        let g_wt = abi::get_view_wt_for_proto(g_meta, resolve::gesture_proto());
580        assert!(!g_wt.is_null(), "DragGesture: Gesture");
581        let ended_meta =
582            abi::generic_metadata2(resolve::ended_gesture_ma(), g_meta, g_wt);
583        let mut ended = vec![0u8; abi::type_bytes(ended_meta)];
584        let ctx = Box::leak(Box::new(abi::ValueClosureCtx::new(
585            invoke_value_action,
586            boxed_ctx(Box::new(action) as Box<dyn Fn()>),
587        ))) as *mut abi::ValueClosureCtx;
588        core::arch::asm!("blr {f}", f = in(reg) resolve::gesture_on_ended(),
589            in("x0") abi::value_closure_thunk as *const () as u64,
590            in("x1") ctx as u64,
591            in("x2") g_meta,
592            in("x3") g_wt,
593            in("x8") ended.as_mut_ptr(),
594            in("x20") gesture.as_mut_ptr(),
595            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
596            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
597            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
598            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
599        );
600        let ended_wt = abi::get_view_wt_for_proto(ended_meta, resolve::gesture_proto());
601        assert!(!ended_wt.is_null(), "_EndedGesture<DragGesture>: Gesture");
602
603        let subst = [view.meta, ended_meta, view.wt, ended_wt];
604        let (meta, wt, size) = opaque_result_type(oret, &subst);
605        let mut result = vec![0u8; size];
606        let this = view.value_ptr();
607        core::arch::asm!("blr {f}", f = in(reg) func,
608            in("x0") ended.as_mut_ptr(),
609            in("x1") 3u64, // GestureMask.all
610            in("x2") view.meta,
611            in("x3") ended_meta,
612            in("x4") view.wt,
613            in("x5") ended_wt,
614            in("x8") result.as_mut_ptr(),
615            in("x20") this,
616            lateout("x6") _, lateout("x7") _,
617            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
618            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
619            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
620        );
621        ViewHandle::wrap(result.as_ptr() as _, meta, wt)
622    }
623}
624
625/// Apply a `View` modifier that takes a value of some protocol — a *style*.
626///
627/// `.listStyle(_:)` and its two dozen siblings are generic over a protocol
628/// rather than taking a concrete type, so the call carries two extra arguments
629/// beyond the usual ones: the style's metadata and its conformance. The style
630/// itself is a resilient struct built by its own `init()`, which writes through
631/// the indirect-result register.
632///
633/// # Safety
634/// Every pointer must belong to the same modifier and style.
635unsafe fn style_modifier(
636    func: *const c_void,
637    oret: *const c_void,
638    view: &ViewHandle,
639    style_ma: *const c_void,
640    style_wt: *const c_void,
641    style_init: *const c_void,
642) -> ViewHandle {
643    unsafe {
644        // `style_ma` is the metadata *accessor*, not the metadata: the enums
645        // below hand back symbols, and one of them has to be called.
646        let style_meta = abi::metadata0(style_ma);
647        let mut style = vec![0u8; abi::type_bytes(style_meta)];
648        core::arch::asm!("blr {f}", f = in(reg) style_init,
649            in("x8") style.as_mut_ptr(),
650            in("x20") style_meta,
651            lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
652            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
653            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
654            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
655            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
656        );
657
658        // The opaque result depends on the style as well as on `Self`, so both
659        // types and both conformances substitute into the descriptor.
660        let subst = [view.meta, style_meta, view.wt, style_wt];
661        let (meta, wt, size) = opaque_result_type(oret, &subst);
662        let mut result = vec![0u8; size];
663        let this = view.value_ptr();
664        // x0 = @in S, x1 = Self.Type, x2 = S.Type,
665        // x3 = Self: View WT, x4 = S: <Style> WT, x8 = @out, x20 = self
666        core::arch::asm!("blr {f}", f = in(reg) func,
667            in("x0") style.as_mut_ptr(),
668            in("x1") view.meta,
669            in("x2") style_meta,
670            in("x3") view.wt,
671            in("x4") style_wt,
672            in("x8") result.as_mut_ptr(),
673            in("x20") this,
674            lateout("x5") _, lateout("x6") _, lateout("x7") _,
675            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
676            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
677            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
678        );
679        ViewHandle::wrap(result.as_ptr() as _, meta, wt)
680    }
681}
682
683/// Apply a `View` modifier that takes plain arguments and returns `some View`.
684///
685/// Nearly every modifier has this shape, and the reason it can be written once
686/// is the *opaque return type descriptor*: SwiftUI emits one beside each
687/// `-> some View` function, and `swift_getOpaqueTypeMetadata` turns it into the
688/// concrete `ModifiedContent<...>` at run time. Spelling those result types out
689/// by hand is what the older bindings do, and it is where they go wrong — a
690/// mangling that resolves to null, or to the wrong arity.
691///
692/// Integer and pointer arguments fill x0 upward, floats fill d0 upward, and
693/// `Self.Type` and the `Self: View` witness table follow the last integer.
694///
695/// # Safety
696/// `func` and `oret` must be the function and its descriptor, and the arguments
697/// must match its signature.
698unsafe fn opaque_modifier(
699    func: *const c_void,
700    oret: *const c_void,
701    view: &ViewHandle,
702    ints: &[u64],
703    floats: [f64; 4],
704) -> ViewHandle {
705    unsafe {
706        // The substitutions are the receiver's own. An opaque return is a
707        // *function* of `Self`, so with `AnyView` hardcoded here the result type
708        // described a call that was never made — right size only because every
709        // receiver was one word.
710        let subst = [view.meta, view.wt];
711        let (meta, wt, size) = opaque_result_type(oret, &subst);
712        let r = abi::call_modifier(func, view.value_ptr(), view.meta, view.wt, ints, floats, size);
713        ViewHandle::wrap(r.as_ptr() as _, meta, wt)
714    }
715}
716
717/// [`opaque_modifier`] with no arguments.
718unsafe fn modifier0(f: *const c_void, o: *const c_void, v: &ViewHandle) -> ViewHandle {
719    unsafe { opaque_modifier(f, o, v, &[], [0.0; 4]) }
720}
721
722/// [`opaque_modifier`] with one `CGFloat`/`Double`.
723unsafe fn modifier_f64(f: *const c_void, o: *const c_void, v: &ViewHandle, x: f64) -> ViewHandle {
724    unsafe { opaque_modifier(f, o, v, &[], [x, 0.0, 0.0, 0.0]) }
725}
726
727/// [`opaque_modifier`] with one `Bool`.
728unsafe fn modifier_bool(f: *const c_void, o: *const c_void, v: &ViewHandle, x: bool) -> ViewHandle {
729    unsafe { opaque_modifier(f, o, v, &[u64::from(x)], [0.0; 4]) }
730}
731
732/// [`opaque_modifier`] with one `Int`.
733unsafe fn modifier_i64(f: *const c_void, o: *const c_void, v: &ViewHandle, x: i64) -> ViewHandle {
734    unsafe { opaque_modifier(f, o, v, &[x as u64], [0.0; 4]) }
735}
736
737/// [`opaque_modifier`] with one `String`, which is two words wide.
738unsafe fn modifier_str(f: *const c_void, o: *const c_void, v: &ViewHandle, x: &str) -> ViewHandle {
739    unsafe {
740        let s = abi::swift_string(x);
741        let w0 = u64::from_le_bytes(s[..8].try_into().expect("string lo"));
742        let w1 = u64::from_le_bytes(s[8..].try_into().expect("string hi"));
743        opaque_modifier(f, o, v, &[w0, w1], [0.0; 4])
744    }
745}
746
747/// Retain a view and keep it alive for good, returning the raw pointer.
748///
749/// `handle.clone().0` looks like it does this, but does not: the clone is a
750/// temporary, so it is released again at the end of the statement and the net
751/// retain count is unchanged. Escaping closures — the ones SwiftUI stores and
752/// calls back into long after the builder returned — need the value to outlive
753/// the call, so the retain has to be one that nothing later undoes.
754/// [`ViewHandle::adopt`] with the same size check [`wrap_buf`] makes.
755///
756/// # Safety
757/// As [`wrap_buf`], and `buf` must be the buffer the callee wrote.
758pub(crate) unsafe fn wrap_owned(
759    buf: Vec<u8>,
760    meta: *const c_void,
761    wt: *const c_void,
762) -> ViewHandle {
763    unsafe {
764        let n = abi::type_size(meta);
765        assert!(buf.len() >= n, "{} needs {n} bytes, buffer was {}",
766            crate::diag::type_name(meta), buf.len());
767        ViewHandle::adopt(buf, meta, wt)
768    }
769}
770
771/// [`ViewHandle::wrap`] from a buffer the callee wrote into.
772///
773/// Every `@out` result in this crate is a `vec![0u8; n]` sized from a type we
774/// resolved separately. If the two ever disagree the callee has already run off
775/// the end of the allocation, and the failure surfaces later as heap corruption
776/// in whatever allocates next. Checking here names the guilty modifier instead.
777pub(crate) unsafe fn wrap_buf(
778    buf: &[u8],
779    meta: *const c_void,
780    wt: *const c_void,
781) -> ViewHandle {
782    unsafe {
783        // `type_size`, not `type_bytes`: the callee writes the value's size,
784        // and an empty struct like `Circle` has size 0 but stride 1, so a
785        // zero-length buffer for it is correct rather than an overflow.
786        let n = abi::type_size(meta);
787        assert!(buf.len() >= n, "{} needs {n} bytes, buffer was {}",
788            crate::diag::type_name(meta), buf.len());
789        ViewHandle::wrap(buf.as_ptr() as _, meta, wt)
790    }
791}
792
793fn leak_retained(view: &ViewHandle) -> u64 {
794    view.to_anyview_word()
795}
796
797unsafe impl Send for ViewHandle {}
798
799impl std::fmt::Debug for ViewHandle {
800    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
801        write!(f, "ViewHandle({})", dynamic_type_name(self))
802    }
803}
804
805
806// The mangled result-type constants are internal: nothing outside this
807// crate has a use for a `&[u8]` of Swift mangling.
808
809// ── Declaring the Swift types a modifier takes ──────────────────────────────
810//
811// Two shapes account for nearly every enum in this module, and writing either
812// by hand is fifteen lines of boilerplate around one line of fact. Worse, the
813// boilerplate hides the fact: what matters about `PointerStyle.link` is its
814// mangled symbol, and what matters about `Text.Case.uppercase` is its case
815// number. These macros leave only that.
816
817/// Declare a Rust enum mirroring a Swift type's `static` members.
818///
819/// The variants are read at run time through their property getters, so each
820/// one is named by its mangled symbol:
821///
822/// ```ignore
823/// swift_statics! {
824///     /// `SwiftUI.PointerStyle`.
825///     pub enum PointerStyle {
826///         Link = c"$s7SwiftUI12PointerStyleV4linkACvgZ",
827///     }
828/// }
829/// ```
830///
831/// Whether the getter returns in a register or through x8 is a property of the
832/// *type*, not of this declaration — see [`crate::abi::static_out`] and
833/// [`crate::abi::static_regs`], and the call site that picks between them.
834/// The Swift metadata a handle's value is described by.
835///
836/// The field is crate-private, and something outside — a test, a tool reading
837/// layouts — has no other way to ask.
838pub fn meta_of(view: &ViewHandle) -> *const c_void {
839    view.meta
840}
841
842/// Rebuild a known instantiation with one generic argument replaced.
843///
844/// The type a closure-built container reports is the type it was *constructed*
845/// with — Swift bakes the content type into the closure's own signature, so
846/// `GeometryReader { Text(…) }` is a `GeometryReader<Text>`. A Rust closure
847/// returning `ViewHandle` has no such type to read, so the container is built
848/// over `AnyView` and then rebuilt here over whatever the builder was found to
849/// produce.
850///
851/// `meta_at` and `wt_at` are indices into the generic argument vector —
852/// parameters first, then conformances — and they cannot be derived from each
853/// other, so the result is checked: a wrong pair would build some *other*
854/// instantiation rather than fail, and `None` is what the caller falls back on.
855///
856/// # Safety
857/// `known` must be struct metadata for an instantiation of the type wanted.
858pub(crate) unsafe fn substitute_content(
859    known: *const c_void,
860    args: usize,
861    meta_at: usize,
862    wt_at: usize,
863    content_meta: *const c_void,
864    content_wt: *const c_void,
865) -> Option<(*const c_void, *const c_void)> {
866    unsafe { substitute_args(known, args, &[(meta_at, content_meta)], &[(wt_at, content_wt)]) }
867}
868
869/// As [`substitute_content`], replacing any number of generic arguments.
870///
871/// A `Section` has three children and three type parameters, so one pair is not
872/// enough: a header of its own type and a body of its own type are two
873/// substitutions in one instantiation.
874///
875/// `params` and `witnesses` are both slots in the generic argument vector —
876/// parameters first, then conformances — and they are separate because only the
877/// parameters can be *checked*. The runtime canonicalises a conformance: the
878/// witness table in the instantiated metadata is the one it has already cached
879/// for that type, which is often not the pointer handed in. Checking those too
880/// made every caller fall back to boxing and pass anyway, because the references
881/// they were compared against had been written with `AnyView` in them.
882///
883/// # Safety
884/// `known` must be struct metadata for an instantiation of the type wanted, and
885/// `args` its generic argument count.
886pub(crate) unsafe fn substitute_args(
887    known: *const c_void,
888    args: usize,
889    params: &[(usize, *const c_void)],
890    witnesses: &[(usize, *const c_void)],
891) -> Option<(*const c_void, *const c_void)> {
892    unsafe {
893        let base = (known as *const *const c_void).add(2);
894        let mut vec: Vec<*const c_void> = (0..args).map(|i| *base.add(i)).collect();
895        for &(at, value) in params.iter().chain(witnesses) {
896            if at >= args {
897                return None;
898            }
899            vec[at] = value;
900        }
901        let meta = abi::generic_metadata_from(known, &vec);
902        if meta.is_null() {
903            return None;
904        }
905        let built = (meta as *const *const c_void).add(2);
906        if params.iter().any(|&(at, value)| *built.add(at) != value) {
907            return None;
908        }
909        let wt = abi::get_view_wt(meta);
910        if wt.is_null() { None } else { Some((meta, wt)) }
911    }
912}
913
914// `swift_statics!` used to live here. It moved to `crate::macros` so that
915// `crate::scene` can use it too: a `macro_rules!` is in scope only in the module
916// it is written in and that module's descendants, and `scene` is a sibling of
917// `views` rather than a child. Re-exporting it instead made every existing use
918// inside `views` ambiguous between the textual macro and the re-export.
919
920
921/// Declare a Rust enum mirroring a Swift enum's cases, by case number.
922///
923/// The numbers are not always the declaration order: Swift gives its *payload*
924/// cases the low tags and the empty ones follow, so an enum with two payload
925/// cases starts its empty ones at two. Spell out what the runtime reports
926/// rather than what the header reads like.
927macro_rules! swift_cases {
928    (
929        $(#[$meta:meta])*
930        $vis:vis enum $name:ident {
931            $($(#[$vmeta:meta])* $variant:ident = $tag:literal),* $(,)?
932        }
933    ) => {
934        $(#[$meta])*
935        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
936        #[repr(u32)]
937        $vis enum $name {
938            $($(#[$vmeta])* $variant = $tag),*
939        }
940
941        impl $name {
942            /// The case number Swift assigns this case.
943            pub(crate) fn tag(self) -> u32 {
944                self as u32
945            }
946        }
947
948    };
949}
950
951/// Declare modifiers that differ only in their arguments.
952///
953/// A binding whose arguments are all plain values is the same five lines every
954/// time: resolve the function, resolve the opaque return type descriptor
955/// beside it, and hand both to whichever helper knows that argument shape.
956/// Only the last part carries any information, so that is all this takes:
957///
958/// ```ignore
959/// modifiers! {
960///     /// Apply `.colorInvert()`.
961///     fn color_invert() = modifier0(color_invert_fn, color_invert_oret);
962///     /// Apply `.blur(radius:)`.
963///     fn blur(radius: f64) = modifier_f64(blur_fn, blur_oret, radius);
964/// }
965/// ```
966///
967/// The two symbols are named rather than derived from the function name: they
968/// are chosen *per overload*, and pairing one overload's function with
969/// another's descriptor resolves a result type of the wrong size, which
970/// corrupts the heap rather than failing.
971macro_rules! modifiers {
972    ($(
973        $(#[$meta:meta])*
974        fn $name:ident ( $($arg:ident : $ty:ty),* $(,)? )
975            = $apply:ident ( $func:ident, $oret:ident $(, $extra:expr)* $(,)? );
976    )*) => {$(
977        $(#[$meta])*
978        pub fn $name(view: &ViewHandle $(, $arg: $ty)*) -> ViewHandle {
979            unsafe { $apply(resolve::$func(), resolve::$oret(), view $(, $extra)*) }
980        }
981    )*};
982}
983
984
985
986mod accessibility;
987mod binding;
988mod catalogue;
989mod closures;
990mod dragdrop;
991mod collections;
992mod compositing;
993mod customlayout;
994mod customshape;
995mod customstyle;
996mod concrete;
997mod conditional;
998mod controls;
999mod effects;
1000mod enums;
1001mod generated;
1002mod focus;
1003mod gestures;
1004mod input;
1005mod generics;
1006mod hover;
1007mod interaction;
1008mod interop;
1009#[cfg(not(target_os = "macos"))]
1010mod ios;
1011mod layout;
1012mod materials;
1013mod namespace;
1014#[cfg(target_os = "macos")]
1015mod representable;
1016mod search;
1017mod navigation;
1018mod optionsets;
1019mod plain;
1020mod presentation;
1021mod primitives;
1022mod sets;
1023mod shapes;
1024mod stacks;
1025mod strings;
1026mod styles;
1027mod timeline;
1028mod toolbar;
1029pub(crate) mod types;
1030#[doc(hidden)]
1031pub use types::bench_result_type_paths;
1032mod typography;
1033
1034pub use accessibility::*;
1035pub use binding::*;
1036pub use catalogue::*;
1037pub use dragdrop::*;
1038pub use focus::*;
1039pub use input::*;
1040#[cfg(target_os = "macos")]
1041pub use representable::*;
1042pub use search::*;
1043pub use closures::*;
1044pub use collections::*;
1045pub use compositing::*;
1046pub use customlayout::*;
1047pub use customshape::*;
1048pub use customstyle::*;
1049pub use concrete::*;
1050pub use conditional::*;
1051pub use controls::*;
1052pub use effects::*;
1053pub use enums::*;
1054pub use generated::*;
1055pub use gestures::*;
1056pub use generics::*;
1057pub use hover::*;
1058pub use interaction::*;
1059pub use interop::*;
1060#[cfg(not(target_os = "macos"))]
1061pub use ios::*;
1062pub use layout::*;
1063pub use materials::*;
1064pub use namespace::*;
1065pub use navigation::*;
1066pub use optionsets::*;
1067pub use plain::*;
1068pub use presentation::*;
1069pub use primitives::*;
1070pub use sets::*;
1071pub use shapes::*;
1072pub use stacks::*;
1073pub use strings::*;
1074pub use styles::*;
1075pub use timeline::*;
1076pub use toolbar::*;
1077pub use typography::*;
1078
1079// The mangled result-type constants are internal: nothing outside this crate
1080// has a use for a `&[u8]` of Swift mangling.
1081pub(crate) use types::*;
1082
1083/// The handle as a raw `+1` `AnyView` word.
1084///
1085/// For call sites outside this crate that assemble a modifier call themselves.
1086/// A handle is no longer an `AnyView`, so this erases one on demand and the
1087/// caller owns it.
1088pub fn handle_word(view: &ViewHandle) -> u64 {
1089    view.to_anyview_word()
1090}
1091
1092/// Wrap a raw `AnyView` word back into a handle.
1093///
1094/// The inverse of [`handle_word`], for call sites outside this crate that
1095/// assemble a modifier call themselves.
1096pub fn wrap_word(anyview: u64) -> ViewHandle {
1097    ViewHandle::from_anyview(anyview)
1098}
1099
1100// ── Closure contexts ────────────────────────────────────────────────────────
1101
1102/// Leak `f` and return it as a Swift closure's context word.
1103///
1104/// Every callback in this crate used to store `f as *mut c_void` — the raw
1105/// address of a `fn` item — and transmute it back in the thunk. That works only
1106/// for a function pointer, which is precisely the closure that captures
1107/// nothing: a button could not be told which row it was on, an `.onAppear`
1108/// could not name what had appeared. The Rust equivalent of Swift's boxed
1109/// context is a double box, `Box<Box<dyn Fn…>>`: the outer one is a thin
1110/// pointer that fits a context word, the inner one carries the vtable.
1111///
1112/// The leak is deliberate and matches [`action_words`](closures::action_words):
1113/// SwiftUI holds an escaping closure for as long as the view lives, and calls a
1114/// non-escaping one before the modifier returns.
1115pub(crate) fn boxed_ctx<F: ?Sized>(f: Box<F>) -> *mut core::ffi::c_void {
1116    Box::into_raw(Box::new(f)) as *mut core::ffi::c_void
1117}
1118
1119/// Borrow back what [`boxed_ctx`] leaked.
1120///
1121/// # Safety
1122///
1123/// `payload` must have come from `boxed_ctx::<F>` with the same `F`.
1124pub(crate) unsafe fn boxed_ref<'a, F: ?Sized>(payload: *mut core::ffi::c_void) -> &'a F {
1125    unsafe { &*(payload as *const Box<F>) }
1126}