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}