swiftui_native/conformance.rs
1//! Synthesising Swift protocol conformances at run time.
2//!
3//! Some SwiftUI modifiers are generic over a protocol whose conformances a
4//! *client* is expected to declare — `PreferenceKey` above all. There is no
5//! Swift source here to declare one, and the framework exports only its own
6//! internal conformances, so the way to reach those modifiers is to build the
7//! conformance the compiler would have emitted.
8//!
9//! That is less daunting than it sounds, because of how Swift compiles
10//! generics: a generic function receives the witness table as an ordinary
11//! argument. Only a dynamic cast consults the global conformance table. So a
12//! witness table built here and passed straight to `View.preference(key:value:)`
13//! is used exactly as a compiler-emitted one would be — no registration
14//! required.
15//!
16//! # What a witness table is
17//!
18//! One word per protocol requirement, in the order the protocol descriptor
19//! lists them, behind one word pointing at the conformance descriptor:
20//!
21//! ```text
22//! [0] conformance descriptor
23//! [1] witness for requirement 0
24//! [2] witness for requirement 1
25//! ...
26//! ```
27//!
28//! A requirement is a function pointer, except for an associated type, whose
29//! slot holds the type's metadata directly. (The runtime treats a slot with
30//! bit 0 set as a mangled name to resolve instead; storing metadata avoids
31//! needing a generic environment at all.)
32//!
33//! Requirements that carry a *default implementation* — the protocol descriptor
34//! records one per requirement — need no witness from us: the default is
35//! copied in. For `PreferenceKey` that leaves three to supply: the associated
36//! `Value`, `defaultValue`, and `reduce(value:nextValue:)`.
37//!
38//! # Three things that are not optional
39//!
40//! * The descriptor must point at the witness table through its
41//! **witness table pattern**. A conformance *lookup* does not hand back a
42//! table it was given; it materialises one from the pattern, and a null
43//! pattern makes `swift_conformsToProtocol` report no conformance at all.
44//! * The conformance must be **registered**. Passing the table as a generic
45//! argument is not enough: building a generic type that constrains a
46//! parameter to the protocol makes the runtime look the conformance up.
47//! * The conforming type must not be a **standard library** type. Registering
48//! a conformance for `Swift.Int` leaves the lookup still reporting none;
49//! a framework type registers and resolves normally.
50//!
51//! None of the three fails loudly. Each surfaces as a null metadata response
52//! several calls later.
53
54use core::ffi::c_void;
55
56/// A relative pointer: a 32-bit signed offset from its own address.
57///
58/// Swift uses these throughout its metadata so that descriptors can live in
59/// read-only memory with no relocations. Null is encoded as a zero offset,
60/// which is unambiguous because no descriptor points at itself.
61unsafe fn relative(at: *const i32) -> *const u8 {
62 unsafe {
63 if *at == 0 {
64 core::ptr::null()
65 } else {
66 (at as *const u8).offset(*at as isize)
67 }
68 }
69}
70
71/// A Swift protocol descriptor, read only as far as this needs.
72pub struct Protocol {
73 descriptor: *const u32,
74 requirements: *const u32,
75 count: usize,
76}
77
78impl Protocol {
79 /// Read the descriptor `symbol` names.
80 ///
81 /// # Safety
82 /// `symbol` must be a protocol descriptor (`...Mp`).
83 pub unsafe fn from_symbol(symbol: &'static core::ffi::CStr) -> Self {
84 unsafe {
85 let p = crate::resolve::sym(symbol) as *const u32;
86 assert!(!p.is_null(), "protocol descriptor not found: {symbol:?}");
87 // ContextDescriptor{flags, parent}, name, numSignatureRequirements,
88 // numRequirements, associatedTypeNames, then the signature
89 // requirements (twelve bytes each) and finally the requirements.
90 let signature = *p.add(3) as usize;
91 let count = *p.add(4) as usize;
92 let requirements = (p.add(6) as *const u8).add(12 * signature) as *const u32;
93 Self { descriptor: p, requirements, count }
94 }
95 }
96
97 /// How many requirements the protocol has, and so how wide a witness table
98 /// for it must be.
99 pub fn requirement_count(&self) -> usize {
100 self.count
101 }
102
103 /// Print each requirement's kind and whether it has a default.
104 ///
105 /// Only for working out what a protocol needs; nothing depends on it.
106 pub fn describe(&self) {
107 unsafe {
108 for i in 0..self.count {
109 let flags = *self.requirements.add(2 * i);
110 let kind = flags & 0x0f;
111 let name = match kind {
112 0 => "base protocol", 1 => "method", 2 => "init", 3 => "getter",
113 4 => "setter", 5 => "read coroutine", 6 => "modify coroutine",
114 7 => "associated type", 8 => "associated conformance",
115 _ => "?",
116 };
117 let dflt = self.default_witness(i);
118 println!(
119 " [{i}] {name}{} {}",
120 if flags & 0x10 != 0 { " (instance)" } else { "" },
121 if dflt.is_null() { "— must supply" } else { "— has a default" },
122 );
123 }
124 }
125 }
126
127 /// The default implementation of requirement `i`, if it has one.
128 /// Whether requirement `i` has a default, and so need not be supplied.
129 ///
130 /// The ratio of defaults to requirements is what decides whether a
131 /// conformance is worth synthesising: `View` has one requirement and
132 /// several defaults, `Layout` has many of each.
133 pub fn has_default(&self, i: usize) -> bool {
134 !unsafe { self.default_witness(i) }.is_null()
135 }
136
137 unsafe fn default_witness(&self, i: usize) -> *const u8 {
138 unsafe { relative(self.requirements.add(2 * i + 1) as *const i32) }
139 }
140}
141
142/// What to put in one slot of a witness table.
143#[derive(Clone, Copy)]
144pub enum Witness {
145 /// Use the default implementation the protocol descriptor records.
146 ///
147 /// # Panics
148 /// At build time, if the requirement has no default.
149 Default,
150 /// A function implementing the requirement.
151 Function(*const c_void),
152 /// Metadata for an associated type, stored directly.
153 ///
154 /// This is the form to use. SwiftUI's own tables instead hold a pointer to
155 /// a mangled name with bit 0 set — the runtime accepts either — but a name
156 /// written here fails to demangle, because the blob the runtime expects is
157 /// not a plain string: it carries symbolic references, which are relative
158 /// pointers into the emitting image. Metadata needs no such context.
159 Metadata(*const c_void),
160}
161
162/// A conformance descriptor, laid out the way the compiler emits one.
163///
164/// Both references are *indirect*: each 32-bit offset addresses a neighbouring
165/// pointer-sized slot rather than the target. A direct offset could not reach —
166/// this is heap memory and the protocol descriptor is in the shared cache,
167/// further apart than a signed 32-bit offset spans.
168///
169/// The two fields signal that indirection differently, which is easy to get
170/// wrong. The protocol reference is a *relative indirectable* pointer, where
171/// bit 0 of the offset means "indirect". The type reference is a plain offset,
172/// and its kind lives in `flags` instead — `TypeReferenceKind` occupies bits
173/// three to five, and kind 1 is `IndirectTypeDescriptor`. It also points at a
174/// *nominal type descriptor*, not at type metadata.
175#[repr(C)]
176struct ConformanceDescriptor {
177 protocol_ref: i32,
178 type_ref: i32,
179 witness_table_pattern: i32,
180 flags: u32,
181 // The slots the relative pointers above address.
182 protocol_slot: *const c_void,
183 type_slot: *const c_void,
184}
185
186/// What a conformance record says its conforming type *is*.
187///
188/// The kind is a two-bit field at bit 3 of the conformance flags, and the four
189/// values are the four things a type reference can be. This crate needs two of
190/// them: a Swift struct minted at run time is named by its nominal descriptor,
191/// and an Objective-C class minted at run time has no nominal descriptor at all
192/// — its identity is the class object.
193#[derive(Clone, Copy)]
194pub enum Conformer {
195 /// A nominal type descriptor (`…Mn`): a struct, an enum, or a Swift class.
196 TypeDescriptor(*const c_void),
197 /// An Objective-C class object, as `objc_allocateClassPair` returns.
198 ///
199 /// The route by which a class conforms to a Swift protocol without there
200 /// being a Swift class: `Observation.Observable` requires `AnyObject`, and
201 /// an `NSObject` subclass is one.
202 ObjCClass(*mut c_void),
203}
204
205impl Conformer {
206 /// The pointer that goes in the type slot.
207 fn slot_value(self) -> *const c_void {
208 match self {
209 Self::TypeDescriptor(d) => d,
210 Self::ObjCClass(c) => c as *const c_void,
211 }
212 }
213
214 /// The `TypeReferenceKind` bits for the conformance flags.
215 fn flags(self) -> u32 {
216 match self {
217 // `IndirectTypeDescriptor` — the slot holds the descriptor.
218 Self::TypeDescriptor(_) => 1 << 3,
219 // `IndirectObjCClass` — the slot holds the class object.
220 Self::ObjCClass(_) => 3 << 3,
221 }
222 }
223}
224
225/// A conformance *record*: one relative pointer to a descriptor.
226///
227/// This is what a compiled binary's `__swift5_proto` section is made of, and
228/// what [`register`] hands the runtime.
229#[repr(C)]
230struct ConformanceRecord {
231 descriptor: i32,
232}
233
234/// Record, descriptor and witness table in one allocation.
235///
236/// Everything here is wired together with *32-bit* relative offsets, which is
237/// the whole reason the three cannot be three separate `Box`es: the allocator
238/// is free to place them more than two gigabytes apart, and the offset then
239/// silently truncates. The failure is not at registration — it is later, and
240/// somewhere else entirely, because `swift_conformsToProtocol` walks *every*
241/// registered record on any lookup it has not cached. One bad record turns an
242/// unrelated protocol query into a wild read.
243///
244/// Laid out as words so the tail can carry a witness table of any length:
245///
246/// | word | contents |
247/// |------|---------------------------------------------|
248/// | 0 | the record — a relative pointer to word 1 |
249/// | 1–2 | the descriptor's four 32-bit fields |
250/// | 3 | the protocol slot the first field addresses |
251/// | 4 | the type slot the second field addresses |
252/// | 5… | the witness table |
253const RECORD_WORD: usize = 0;
254const DESCRIPTOR_WORD: usize = 1;
255const PROTOCOL_SLOT_WORD: usize = 3;
256const TYPE_SLOT_WORD: usize = 4;
257const TABLE_WORD: usize = 5;
258
259/// A 32-bit relative offset from `at` to `to`, checked rather than truncated.
260unsafe fn offset(to: *const u8, at: *const u8) -> i32 {
261 let delta = to as isize - at as isize;
262 i32::try_from(delta).expect("relative pointer out of range")
263}
264
265/// A synthesized conformance, kept alive for the process's lifetime.
266pub struct Conformance {
267 table: *const c_void,
268 descriptor: *const c_void,
269}
270
271impl Conformance {
272 /// The conformance descriptor the runtime was handed.
273 ///
274 /// Only of interest when checking that a lookup found *this* conformance
275 /// rather than one the framework already had.
276 pub fn descriptor(&self) -> *const c_void {
277 self.descriptor
278 }
279
280 /// The witness table, ready to pass as a generic argument.
281 pub fn witness_table(&self) -> *const c_void {
282 self.table
283 }
284
285 /// Fill a witness in after the fact.
286 ///
287 /// For a conformance that cannot be complete when it is built. `Shape`
288 /// requires `View`, and a shape's `View.Body` is
289 /// `_ShapeView<Self, ForegroundStyle>` — whose metadata needs `Self: Shape`.
290 /// Neither table can be finished before the other exists, so the `Shape` one
291 /// is registered with its `View` slot empty, the metadata is instantiated
292 /// against it, and the slot is filled here. Swift resolves the same knot with
293 /// lazy witness tables; this is the same trick with the laziness written out.
294 ///
295 /// # Safety
296 /// `slot` must be within the protocol's requirement count, `witness` must be
297 /// the right kind of thing for it, and nothing may have *used* the slot yet —
298 /// a witness table the runtime has already cached a lookup for is still
299 /// readable, so writing to it is only safe before anything dispatches through
300 /// that entry.
301 pub unsafe fn patch(&self, slot: usize, witness: *const c_void) {
302 unsafe {
303 let words = self.table as *mut *const c_void;
304 *words.add(slot + 1) = witness;
305 }
306 }
307}
308
309/// Tell the runtime the conformance exists.
310///
311/// Passing the witness table as a generic argument is *not* enough. Building a
312/// generic type that constrains a parameter to the protocol — here
313/// `_PreferenceWritingModifier<K>` — makes the runtime look the conformance up
314/// rather than take the caller's word for it, and an unregistered one fails
315/// with a null metadata response and no diagnostic.
316unsafe fn register(record: *const ConformanceRecord) {
317 unsafe extern "C" {
318 fn swift_registerProtocolConformances(begin: *const c_void, end: *const c_void);
319 }
320 unsafe {
321 let begin = record as *const c_void;
322 let end = (record as *const u8).add(core::mem::size_of::<ConformanceRecord>());
323 swift_registerProtocolConformances(begin, end as *const c_void);
324 }
325}
326
327/// Build a witness table for `conforming_type: protocol`.
328///
329/// The conformance is leaked on purpose: a witness table handed to SwiftUI
330/// outlives any Rust scope, and there is no moment at which freeing it would
331/// be safe.
332///
333/// `type_descriptor` is the conforming type's *nominal type descriptor*
334/// (`...Mn`), not its metadata.
335///
336/// # Safety
337/// Every `Witness::Function` must have the signature the corresponding
338/// requirement expects, under Swift's witness-method convention — `Self`'s
339/// metadata arrives in x20 and an indirect result through x8. Getting one
340/// wrong corrupts memory inside SwiftUI rather than failing here.
341pub unsafe fn synthesize(
342 protocol: &Protocol,
343 type_descriptor: *const c_void,
344 witnesses: &[Witness],
345) -> Conformance {
346 unsafe { synthesize_for(protocol, Conformer::TypeDescriptor(type_descriptor), witnesses) }
347}
348
349/// [`synthesize`] for a conforming type that is not named by a nominal
350/// descriptor.
351///
352/// # Safety
353/// As [`synthesize`], and `conformer` must describe the type the witnesses were
354/// written for.
355pub unsafe fn synthesize_for(
356 protocol: &Protocol,
357 conformer: Conformer,
358 witnesses: &[Witness],
359) -> Conformance {
360 unsafe {
361 assert_eq!(
362 witnesses.len(),
363 protocol.count,
364 "a witness table needs one entry per requirement",
365 );
366
367 // One allocation, because every link below is a 32-bit relative offset
368 // and separate allocations can land out of range of one another.
369 let words = TABLE_WORD + 1 + protocol.count;
370 let block = Box::leak(vec![0u64; words].into_boxed_slice());
371 let base = block.as_mut_ptr();
372
373 // The witness table: its own conformance descriptor, then one word per
374 // requirement. A conformance *lookup* does not hand back a table it was
375 // given — it materialises one from this pattern, so a null pattern makes
376 // `swift_conformsToProtocol` report no conformance at all, which
377 // surfaces much later as a null metadata response.
378 let table = base.add(TABLE_WORD);
379 for (i, w) in witnesses.iter().enumerate() {
380 let word = match *w {
381 Witness::Default => {
382 let d = protocol.default_witness(i);
383 assert!(!d.is_null(), "requirement {i} has no default implementation");
384 d as u64
385 }
386 Witness::Function(f) => f as u64,
387 Witness::Metadata(m) => m as u64,
388 };
389 *table.add(1 + i) = word;
390 }
391
392 let descriptor = base.add(DESCRIPTOR_WORD) as *mut ConformanceDescriptor;
393 *base.add(PROTOCOL_SLOT_WORD) = protocol.descriptor as u64;
394 *base.add(TYPE_SLOT_WORD) = conformer.slot_value() as u64;
395 *table = descriptor as u64;
396
397 let d = descriptor as *const u8;
398 let protocol_slot = base.add(PROTOCOL_SLOT_WORD) as *const u8;
399 let type_slot = base.add(TYPE_SLOT_WORD) as *const u8;
400 // Each offset is measured from the field that holds it. Bit 0 of the
401 // protocol reference means "indirect"; the type reference signals the
402 // same thing through `flags` instead.
403 (*descriptor).protocol_ref = offset(protocol_slot, d) | 1;
404 (*descriptor).type_ref = offset(type_slot, d.add(4));
405 (*descriptor).witness_table_pattern = offset(table as *const u8, d.add(8));
406 (*descriptor).flags = conformer.flags();
407
408 let record = base.add(RECORD_WORD) as *mut ConformanceRecord;
409 (*record).descriptor = offset(d, record as *const u8);
410
411 let conformance = Conformance {
412 table: table as *const c_void,
413 descriptor: descriptor as *const c_void,
414 };
415 register(record);
416 conformance
417 }
418}
419
420// ── A `PreferenceKey` whose `Value` is `Swift.Int` ──────────────────────────
421//
422// The three requirements without defaults, written out. `Self`'s metadata
423// arrives in x20 and is ignored: a preference key is never instantiated, it is
424// only ever named.
425
426// `static var defaultValue: Value { get }` returns through x8, which a Rust
427// signature cannot name, so the entry point moves it into x0 first.
428core::arch::global_asm!(
429 ".p2align 2",
430 ".globl _rswift_pref_default_value",
431 "_rswift_pref_default_value:",
432 "mov x0, x8",
433 "b _rswift_pref_default_value_impl",
434);
435
436#[unsafe(no_mangle)]
437unsafe extern "C" fn rswift_pref_default_value_impl(out: *mut i64) {
438 unsafe { *out = 0 };
439}
440
441// `static func reduce(value: inout Value, nextValue: () -> Value)`.
442//
443// `Value` is an associated type, so every mention of it is abstract from the
444// callee's side: `value` arrives by address, and `nextValue` returns *through
445// x8* rather than in x0. Reading x0 instead yields whatever the closure left
446// there, which for a single preference writer is often the right answer by
447// accident and stops being one as soon as the tree has two.
448#[unsafe(no_mangle)]
449unsafe extern "C" fn rswift_pref_reduce(
450 value: *mut i64,
451 next: *const c_void,
452 context: *mut c_void,
453) {
454 unsafe {
455 let mut produced: i64 = 0;
456 core::arch::asm!("blr {f}", f = in(reg) next,
457 in("x8") &mut produced as *mut i64, in("x20") context,
458 lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
459 lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
460 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
461 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
462 lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
463 );
464 *value = produced;
465 }
466}
467
468unsafe extern "C" {
469 #[link_name = "rswift_pref_default_value"]
470 fn pref_default_value();
471}
472
473/// A `PreferenceKey` conformance whose `Value` is `Swift.Int`.
474///
475/// A preference key is an identity, never a value: SwiftUI instantiates the
476/// `Value`, not the key. So the key can be any nominal type, and this borrows
477/// `SwiftUI.Spacer` rather than synthesizing a type descriptor of its own.
478///
479/// Not a stdlib type, though — `Swift.Int` is the obvious candidate and it does
480/// not work. Registering a conformance for it leaves
481/// `swift_conformsToProtocol` still reporting none, presumably because the
482/// runtime resolves stdlib types by a path that does not consult registered
483/// records. A framework type registers and resolves normally.
484pub fn preference_key() -> &'static Conformance {
485 use std::sync::OnceLock;
486 static CACHE: OnceLock<Conformance> = OnceLock::new();
487 CACHE.get_or_init(|| unsafe {
488 let protocol = Protocol::from_symbol(c"$s7SwiftUI13PreferenceKeyMp");
489 assert_eq!(protocol.requirement_count(), 5, "PreferenceKey changed shape");
490 synthesize(
491 &protocol,
492 crate::resolve::spacer_descriptor(),
493 &[
494 Witness::Metadata(crate::resolve::int_meta()),
495 Witness::Function(pref_default_value as *const c_void),
496 Witness::Function(rswift_pref_reduce as *const c_void),
497 Witness::Default,
498 Witness::Default,
499 ],
500 )
501 })
502}
503
504// `Conformance` is only ever read, and the memory it points at is leaked.
505unsafe impl Send for Conformance {}
506unsafe impl Sync for Conformance {}
507
508/// A `LayoutValueKey` conformance whose `Value` is `Swift.Int`.
509///
510/// A second protocol through the same machinery, which is the point: nothing
511/// above is specific to `PreferenceKey`. `LayoutValueKey` asks for two
512/// witnesses rather than three — an associated `Value` and a `defaultValue` —
513/// and the key type is borrowed the same way.
514pub fn layout_value_key() -> &'static Conformance {
515 use std::sync::OnceLock;
516 static CACHE: OnceLock<Conformance> = OnceLock::new();
517 CACHE.get_or_init(|| unsafe {
518 let protocol = Protocol::from_symbol(c"$s7SwiftUI14LayoutValueKeyMp");
519 assert_eq!(protocol.requirement_count(), 2, "LayoutValueKey changed shape");
520 synthesize(
521 &protocol,
522 crate::resolve::divider_descriptor(),
523 &[
524 Witness::Metadata(crate::resolve::int_meta()),
525 Witness::Function(pref_default_value as *const c_void),
526 ],
527 )
528 })
529}
530
531core::arch::global_asm!(
532 ".p2align 2",
533 ".globl _rswift_stub_out_witness",
534 "_rswift_stub_out_witness:",
535 "str xzr, [x8]",
536 "ret",
537);
538
539unsafe extern "C" {
540 #[link_name = "rswift_stub_out_witness"]
541 fn stub_out_witness();
542}
543
544/// A `PresentationSizing` conformance for `SwiftUI.EmptyView`.
545///
546/// The cheapest conformance there is: every one of the protocol's three
547/// requirements carries a default implementation, so nothing has to be written
548/// — the table is the defaults and a descriptor.
549pub fn presentation_sizing() -> &'static Conformance {
550 use std::sync::OnceLock;
551 static CACHE: OnceLock<Conformance> = OnceLock::new();
552 CACHE.get_or_init(|| unsafe {
553 let protocol = Protocol::from_symbol(c"$s7SwiftUI18PresentationSizingMp");
554 let witnesses = vec![Witness::Default; protocol.requirement_count()];
555 synthesize(&protocol, crate::resolve::empty_view_descriptor(), &witnesses)
556 })
557}
558
559/// A `PreviewContext` conformance for `SwiftUI.EmptyView`.
560///
561/// Its one requirement is a subscript getter that SwiftUI does not call while
562/// the modifier is being applied, so the witness writes a null and returns.
563pub fn preview_context() -> &'static Conformance {
564 use std::sync::OnceLock;
565 static CACHE: OnceLock<Conformance> = OnceLock::new();
566 CACHE.get_or_init(|| unsafe {
567 let protocol = Protocol::from_symbol(c"$s7SwiftUI14PreviewContextMp");
568 synthesize(
569 &protocol,
570 crate::resolve::empty_view_descriptor(),
571 &[Witness::Function(stub_out_witness as *const c_void)],
572 )
573 })
574}
575
576/// `draw(layout:in:)` — a renderer that draws nothing.
577///
578/// `Text.Layout` arrives by address in x0, the `GraphicsContext` `inout` in x1,
579/// and `Self` in x20 under the witness-method convention.
580///
581/// This used to be empty, on the grounds that `GraphicsContext.draw` is
582/// `@_alwaysEmitIntoClient` and so unreachable. That is not true of all of its
583/// overloads: `draw(_ text: Text, at: CGPoint, anchor: UnitPoint)` is exported.
584/// Drawing nothing did compile and did match the reference — both sides drew
585/// nothing — but it made the case worthless as a *visual* check, which is the
586/// only kind a renderer is subject to. The parity suite listed it under FLAT
587/// for exactly that reason.
588///
589/// The layout is ignored: what this draws is a `Text` of its own, which is
590/// enough to put pixels on the screen in a position the comparison can see.
591/// Following the layout would need `Text.Layout`'s `Collection` conformance,
592/// its lines and their runs.
593unsafe extern "C" fn text_renderer_draw(_layout: *const c_void, ctx: *mut c_void) {
594 unsafe {
595 let t = crate::views::text_value("Fx");
596 // `ctx` is the `inout` pointer, and `draw` does not want it. A
597 // `GraphicsContext` is a struct of one class reference, and the method
598 // takes that reference by value: the IR loads `%0`'s storage and passes
599 // the result. Handing over the address of the context instead faults.
600 let storage = *(ctx as *const *const c_void);
601 // Text in x0-x3, the point in d0/d1, the anchor in d2/d3, and the
602 // context in x4 — not x20. `.topLeading` is (0, 0).
603 core::arch::asm!("blr {f}", f = in(reg) crate::resolve::graphics_draw_text(),
604 in("x0") t[0], in("x1") t[1], in("x2") t[2], in("x3") t[3],
605 in("x4") storage,
606 in("d0") 0.0f64, in("d1") 0.0f64, in("d2") 0.0f64, in("d3") 0.0f64,
607 lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
608 lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
609 lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
610 lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
611 clobber_abi("C"),
612 );
613 }
614}
615
616/// A `TextRenderer` conformance on `SwiftUI.EdgeInsets`.
617///
618/// `TextRenderer` refines `Animatable`, so the witness table's first slot is not
619/// a function but another *witness table* — the conforming type's `Animatable`
620/// one. Nothing here can synthesize that: `Animatable` wants an associated type,
621/// a getter, a setter and a `modify` coroutine. So the host type is chosen for
622/// already having the conformance. `EdgeInsets` is the smallest public SwiftUI
623/// type that exports all three of a metadata accessor, a nominal type descriptor
624/// and an `Animatable` witness table.
625pub fn text_renderer() -> &'static Conformance {
626 use std::sync::OnceLock;
627 static CACHE: OnceLock<Conformance> = OnceLock::new();
628 CACHE.get_or_init(|| unsafe {
629 let protocol = Protocol::from_symbol(c"$s7SwiftUI12TextRendererMp");
630 assert_eq!(protocol.requirement_count(), 4, "TextRenderer changed shape");
631 synthesize(
632 &protocol,
633 crate::resolve::edge_insets_descriptor(),
634 &[
635 Witness::Metadata(crate::resolve::edge_insets_animatable_wt()),
636 Witness::Function(text_renderer_draw as *const c_void),
637 Witness::Default,
638 Witness::Default,
639 ],
640 )
641 })
642}
643
644// A key path's `id` witness returns the associated `ID` through x8 under the
645// witness-method convention, and `Self` arrives in x20 — neither of which a
646// plain `extern "C"` signature can name. The body is small enough to write
647// outright: store the constant and return.
648core::arch::global_asm!(
649 ".p2align 2",
650 ".globl _rswift_identifiable_id",
651 "_rswift_identifiable_id:",
652 "mov x0, #0",
653 "str x0, [x8]",
654 "ret",
655);
656
657unsafe extern "C" {
658 #[link_name = "rswift_identifiable_id"]
659 fn identifiable_id();
660}
661
662/// An `Identifiable` conformance on `SwiftUI.EdgeInsets`, with `ID == Int`.
663///
664/// Three requirements: the associated `ID`, its `Hashable` conformance, and the
665/// `id` getter. Unlike `TextRenderer` none of them needs a base protocol, so the
666/// host type only has to export a nominal type descriptor — but `EdgeInsets` is
667/// reused anyway, because the parity reference has to name the same type and a
668/// public one keeps that possible.
669///
670/// Every instance reports the same id. That is what `[EdgeInsets]` can mean here
671/// — the values are indistinguishable, so distinct ids would be a fiction.
672pub fn identifiable() -> &'static Conformance {
673 use std::sync::OnceLock;
674 static CACHE: OnceLock<Conformance> = OnceLock::new();
675 CACHE.get_or_init(|| unsafe {
676 let protocol = Protocol::from_symbol(c"$ss12IdentifiableMp");
677 assert_eq!(protocol.requirement_count(), 3, "Identifiable changed shape");
678 synthesize(
679 &protocol,
680 crate::resolve::edge_insets_descriptor(),
681 &[
682 Witness::Metadata(crate::resolve::int_hashable_wt()),
683 Witness::Metadata(crate::resolve::int_meta()),
684 Witness::Function(identifiable_id as *const c_void),
685 ],
686 )
687 })
688}
689
690/// An `Identifiable` conformance on `Swift.String`, with `ID == Int`.
691///
692/// `dragContainer` wants an item that is *both* `Transferable` and
693/// `Identifiable`. `String` already has the first and no public SwiftUI type has
694/// either, so the second is synthesized onto it — the standard library exports a
695/// nominal type descriptor for `String` like any other type, and a conformance
696/// record registered from here is as real as one a Swift module declares.
697pub fn string_identifiable() -> &'static Conformance {
698 use std::sync::OnceLock;
699 static CACHE: OnceLock<Conformance> = OnceLock::new();
700 CACHE.get_or_init(|| unsafe {
701 let protocol = Protocol::from_symbol(c"$ss12IdentifiableMp");
702 synthesize(
703 &protocol,
704 crate::resolve::string_descriptor(),
705 &[
706 Witness::Metadata(crate::resolve::int_hashable_wt()),
707 Witness::Metadata(crate::resolve::int_meta()),
708 Witness::Function(identifiable_id as *const c_void),
709 ],
710 )
711 })
712}
713
714/// An `Observable` conformance on `Foundation.JSONDecoder`.
715///
716/// `Observation.Observable` has **no requirements at all** — the macro that
717/// usually applies it supplies everything through extensions — so the witness
718/// table is nothing but its own descriptor. What is scarce is not the
719/// conformance but the *class*: `focusedValue` wants `AnyObject & Observable`,
720/// and a parity reference has to be able to name the same type. `JSONDecoder`
721/// is the only public Swift class reachable here that exports a nominal type
722/// descriptor, which is what registering a conformance needs.
723pub fn observable() -> &'static Conformance {
724 use std::sync::OnceLock;
725 static CACHE: OnceLock<Conformance> = OnceLock::new();
726 CACHE.get_or_init(|| unsafe {
727 let protocol = Protocol::from_symbol(c"$s11Observation10ObservableMp");
728 assert_eq!(protocol.requirement_count(), 0, "Observable changed shape");
729 synthesize(&protocol, crate::resolve::json_decoder_descriptor(), &[])
730 })
731}
732
733crate::thunk::runtime_thunk! {
734 /// `ObservableObject.objectWillChange`, as the compiler writes it.
735 ///
736 /// The IR for a retroactive conformance is four instructions long:
737 ///
738 /// ```llvm
739 /// define ... void @"...objectWillChange...TW"(ptr noalias sret(ptr) %0,
740 /// ptr swiftself %1, ptr %Self, ptr %SelfWitnessTable) {
741 /// %2 = call ... @"$s10Foundation11JSONDecoderCMa"(i64 0)
742 /// %3 = extractvalue %swift.metadata_response %2, 0
743 /// %4 = call ... @"...Combine default..."(ptr %3, ptr %SelfWitnessTable,
744 /// ptr swiftself %1)
745 /// store ptr %4, ptr %0
746 /// }
747 /// ```
748 ///
749 /// Every argument the default wants is already in the register it wants it
750 /// in — `Self` in x0, the witness table in x1, the instance in x20 — so
751 /// the only work is keeping x8 alive across the call and storing the
752 /// result through it. (The compiler re-derives `Self` from the metadata
753 /// accessor instead of forwarding x0; for a concrete conformance the two
754 /// are the same pointer.)
755 ///
756 /// The publisher comes back at +1 and is stored without a retain, which is
757 /// what the compiler emits: the caller owns what the getter returns.
758 fn object_will_change = "rswift_object_will_change" via RSWIFT_OBJECT_WILL_CHANGE {
759 prologue: [
760 "stp x29, x30, [sp, #-32]!",
761 "mov x29, sp",
762 "str x8, [sp, #16]",
763 ],
764 branch: "blr",
765 epilogue: [
766 "ldr x8, [sp, #16]",
767 "str x0, [x8]",
768 "ldp x29, x30, [sp], #32",
769 "ret",
770 ],
771 }
772}
773
774/// An `ObservableObject` conformance on `Foundation.JSONDecoder`.
775///
776/// Three requirements, and the protocol has defaults for two of them: the
777/// associated `ObjectWillChangePublisher` and its `Publisher` conformance both
778/// come from the constrained extension where that type is
779/// `ObservableObjectPublisher`. Only the getter has to be supplied, and it
780/// forwards to Combine's own default — so what this builds is the same table
781/// `extension JSONDecoder: ObservableObject {}` compiles to, which is what lets
782/// the parity reference declare exactly that and dump the same value.
783///
784/// The object this hangs on used to be `ImageRenderer<AnyView>`, the one class
785/// in SwiftUI that already conforms. That worked, but nothing could check it:
786/// reflecting into a live `ImageRenderer` faults, so the three modifiers taking
787/// an `ObservableObject` had no parity case at all. A `JSONDecoder` dumps in
788/// two lines.
789pub fn observable_object() -> &'static Conformance {
790 use std::sync::OnceLock;
791 static CACHE: OnceLock<Conformance> = OnceLock::new();
792 CACHE.get_or_init(|| unsafe {
793 let protocol = Protocol::from_symbol(c"$s7Combine16ObservableObjectMp");
794 assert_eq!(protocol.requirement_count(), 3, "ObservableObject changed shape");
795 RSWIFT_OBJECT_WILL_CHANGE = crate::resolve::observable_object_will_change();
796 synthesize(
797 &protocol,
798 crate::resolve::json_decoder_descriptor(),
799 &[
800 // ObjectWillChangePublisher: Publisher
801 Witness::Default,
802 // ObjectWillChangePublisher == ObservableObjectPublisher
803 Witness::Default,
804 Witness::Function(object_will_change as *const c_void),
805 ],
806 )
807 })
808}
809
810/// `(Double) -> NSString` — how an axis describes a value aloud.
811unsafe extern "C" fn describe_axis_value(_block: *mut c_void, value: f64) -> *mut c_void {
812 unsafe { crate::objc::nsstring(&format!("{value}")) }
813}
814
815/// `makeChartDescriptor()` — a chart with one numeric axis and no series.
816///
817/// Built through the Objective-C runtime, because `AXChartDescriptor` is an
818/// Objective-C class: an axis, then the chart. The axis initialiser takes a
819/// *block* for turning a value into speech, which is the only place this crate
820/// hands Objective-C a callback rather than the other way round — see
821/// [`crate::objc::GlobalBlock`].
822///
823/// The result is returned `+1`, so a fresh one is built each call rather than
824/// a cached one handed out repeatedly.
825unsafe extern "C" fn make_chart_descriptor() -> *mut c_void {
826 use crate::objc;
827 unsafe {
828 static DESCRIPTOR: objc::BlockDescriptor = objc::BlockDescriptor::empty();
829 let block = objc::GlobalBlock::new(describe_axis_value as *const c_void, &DESCRIPTOR);
830 let block = &block as *const objc::GlobalBlock as *mut c_void;
831
832 type AxisInit = unsafe extern "C" fn(
833 *mut c_void, *const c_void, *mut c_void, f64, f64, *mut c_void, *mut c_void,
834 ) -> *mut c_void;
835 let axis_init: AxisInit = core::mem::transmute(objc::msg_send_addr());
836 let axis = axis_init(
837 objc::send(objc::class(c"OBJC_CLASS_$_AXNumericDataAxisDescriptor"), objc::sel(c"alloc")),
838 objc::sel(c"initWithTitle:lowerBound:upperBound:gridlinePositions:valueDescriptionProvider:"),
839 objc::nsstring("Value"),
840 0.0,
841 1.0,
842 core::ptr::null_mut(),
843 block,
844 );
845
846 type ChartInit = unsafe extern "C" fn(
847 *mut c_void, *const c_void, *mut c_void, *mut c_void, *mut c_void, *mut c_void,
848 *mut c_void,
849 ) -> *mut c_void;
850 let chart_init: ChartInit = core::mem::transmute(objc::msg_send_addr());
851 let empty = objc::send(objc::class(c"OBJC_CLASS_$_NSArray"), objc::sel(c"array"));
852 chart_init(
853 objc::send(objc::class(c"OBJC_CLASS_$_AXChartDescriptor"), objc::sel(c"alloc")),
854 objc::sel(c"initWithTitle:summary:xAxisDescriptor:yAxisDescriptor:series:"),
855 objc::nsstring("Chart"),
856 core::ptr::null_mut(),
857 axis,
858 core::ptr::null_mut(),
859 empty,
860 )
861 }
862}
863
864/// An `AXChartDescriptorRepresentable` conformance on `SwiftUI.EdgeInsets`.
865///
866/// Two requirements, one of which has a default; the one that does not is
867/// `makeChartDescriptor()`, and VoiceOver is the only thing that ever calls it.
868/// The modifier stores the *representable*, so what a parity run compares is
869/// the `EdgeInsets` — the descriptor is built on demand and never appears in
870/// the value.
871pub fn chart_descriptor_representable() -> &'static Conformance {
872 use std::sync::OnceLock;
873 static CACHE: OnceLock<Conformance> = OnceLock::new();
874 CACHE.get_or_init(|| unsafe {
875 let protocol = Protocol::from_symbol(c"$s7SwiftUI30AXChartDescriptorRepresentableMp");
876 assert_eq!(protocol.requirement_count(), 2, "AXChartDescriptorRepresentable changed shape");
877 synthesize(
878 &protocol,
879 crate::resolve::edge_insets_descriptor(),
880 &[Witness::Function(make_chart_descriptor as *const c_void), Witness::Default],
881 )
882 })
883}