swiftui-native 0.0.1

SwiftUI from pure Rust — no Swift bridge, no shim, only swift-runtime calls
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
//! Rust functions Swift can call back into.

use core::ffi::c_void;

/// Context for a Swift `() -> T` closure implemented in Rust.
///
/// Swift treats a closure context as a reference-counted heap object. A
/// non-escaping closure never touches the count, but an escaping one — a
/// Button's action, say — is stored by the receiver, which retains it now and
/// releases it later. Handing Swift a plain `Box` means it eventually calls
/// `swift_release` on memory that is not a Swift object, and the process dies
/// with SIGBUS long after the call that caused it.
///
/// So the first two words mirror a Swift object header and carry the refcount
/// pattern of a known immortal object, which makes retain and release no-ops.
#[repr(C)]
pub struct ClosureCtx {
    isa: *const c_void,
    refcount: u64,
    body: unsafe extern "C" fn(*mut u8, *mut c_void),
    payload: *mut c_void,
}

/// The refcount word Swift uses for statically allocated, never-freed objects.
pub(crate) const IMMORTAL_REFCOUNT: u64 = 0x8000_0004_ffff_ffff;

/// Metadata of a known-immortal runtime object, reused as a plausible `isa`.
pub(crate) fn immortal_isa() -> *const c_void {
    // Resolved once: every `ClosureCtx` asks for this, and there are as many of
    // those as there are closures handed to SwiftUI.
    static ISA: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
    *ISA.get_or_init(|| {
        unsafe extern "C" {
            fn dlsym(handle: *mut c_void, symbol: *const core::ffi::c_char) -> *mut c_void;
        }
        let obj = unsafe { dlsym((-2isize) as *mut c_void, c"_swiftEmptyArrayStorage".as_ptr()) };
        if obj.is_null() {
            0
        } else {
            unsafe { *(obj as *const *const c_void) as usize }
        }
    }) as *const c_void
}

impl ClosureCtx {
    /// `body` receives `(out_ptr, payload)` and must initialise `*out_ptr` with
    /// a `+1` value (or ignore it, for a closure returning `Void`).
    pub fn new(
        body: unsafe extern "C" fn(*mut u8, *mut c_void),
        payload: *mut c_void,
    ) -> Self {
        Self { isa: immortal_isa(), refcount: IMMORTAL_REFCOUNT, body, payload }
    }
}

// Swift passes a closure's indirect (`@out`) result in x8 and the closure
// context in x20 — neither of which is reachable from a plain `extern "C"` fn
// signature. This thunk moves them into the C argument registers and tail-calls
// the dispatcher, so closure bodies can be ordinary Rust functions.
core::arch::global_asm!(
    ".p2align 2",
    ".globl _rswift_swift_closure_thunk",
    "_rswift_swift_closure_thunk:",
    "mov x0, x8",
    "mov x1, x20",
    "b _rswift_swift_closure_dispatch",
);

unsafe extern "C" {
    /// Address to hand to Swift as the closure function pointer.
    #[link_name = "rswift_swift_closure_thunk"]
    pub fn swift_closure_thunk();
}

#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_swift_closure_dispatch(out: *mut u8, ctx: *mut ClosureCtx) {
    let ctx = unsafe { &*ctx };
    unsafe { (ctx.body)(out, ctx.payload) };
}

// A closure that takes one argument, such as `ForEach`'s `(Int) -> Content`.
// The argument arrives in x0, the indirect result in x8 and the context in x20,
// so all three are shuffled into the C argument registers. The moves are ordered
// so x0 is read before it is overwritten.
core::arch::global_asm!(
    ".p2align 2",
    ".globl _rswift_index_closure_thunk",
    "_rswift_index_closure_thunk:",
    "mov x2, x20",
    "mov x1, x0",
    "mov x0, x8",
    "b _rswift_index_closure_dispatch",
);

unsafe extern "C" {
    /// Address to hand to Swift for a one-argument closure.
    #[link_name = "rswift_index_closure_thunk"]
    pub fn index_closure_thunk();
}

/// The context an `@isolated(any)` async closure carries.
///
/// Such a closure is not two words of function and context — the *isolation* it
/// runs on has to travel with it, and Swift puts it in the context: a heap box
/// of an object header followed by an `(any Actor)?`. All-zero is `nil`, which
/// means nonisolated, which is what a closure written `{ }` with no actor
/// context gets.
///
/// The refcount is the immortal pattern for the same reason [`ClosureCtx`] uses
/// it: SwiftUI stores the closure and releases it later, and this is not a real
/// Swift object to be freed.
#[repr(C)]
pub struct IsolatedContext {
    isa: *const c_void,
    refcount: u64,
    isolation: [u64; 2],
}

impl IsolatedContext {
    /// A context carrying no isolation.
    pub fn nonisolated() -> Self {
        Self { isa: immortal_isa(), refcount: IMMORTAL_REFCOUNT, isolation: [0; 2] }
    }
}

/// Context for a Swift `(Int) -> T` closure implemented in Rust.
#[repr(C)]
pub struct IndexClosureCtx {
    isa: *const c_void,
    refcount: u64,
    body: unsafe extern "C" fn(*mut u8, i64, *mut c_void),
    payload: *mut c_void,
}

impl IndexClosureCtx {
    pub fn new(
        body: unsafe extern "C" fn(*mut u8, i64, *mut c_void),
        payload: *mut c_void,
    ) -> Self {
        Self { isa: immortal_isa(), refcount: IMMORTAL_REFCOUNT, body, payload }
    }
}

#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_index_closure_dispatch(
    out: *mut u8,
    index: i64,
    ctx: *mut IndexClosureCtx,
) {
    let ctx = unsafe { &*ctx };
    unsafe { (ctx.body)(out, index, ctx.payload) };
}

// A closure that *consumes* one argument, such as a `Binding` setter's
// `(Value) -> ()`. The value arrives by pointer in x0 and the context in x20;
// only the context needs moving, so x0 passes straight through.
core::arch::global_asm!(
    ".p2align 2",
    ".globl _rswift_value_closure_thunk",
    "_rswift_value_closure_thunk:",
    "mov x1, x20",
    "b _rswift_value_closure_dispatch",
);

unsafe extern "C" {
    /// Address to hand to Swift for a one-argument, no-result closure.
    #[link_name = "rswift_value_closure_thunk"]
    pub fn value_closure_thunk();
}

/// Context for a Swift `(Value) -> ()` closure implemented in Rust.
#[repr(C)]
pub struct ValueClosureCtx {
    isa: *const c_void,
    refcount: u64,
    body: unsafe extern "C" fn(*const u8, *mut c_void),
    payload: *mut c_void,
}

impl ValueClosureCtx {
    pub fn new(
        body: unsafe extern "C" fn(*const u8, *mut c_void),
        payload: *mut c_void,
    ) -> Self {
        Self { isa: immortal_isa(), refcount: IMMORTAL_REFCOUNT, body, payload }
    }
}

#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_value_closure_dispatch(value: *const u8, ctx: *mut ValueClosureCtx) {
    let ctx = unsafe { &*ctx };
    unsafe { (ctx.body)(value, ctx.payload) };
}

// A closure taking two arguments and returning nothing, such as `onChange`'s
// `(V, V) -> ()`. Both values arrive by pointer in x0 and x1 (V is generic, so
// they are indirect); only the context needs moving.
core::arch::global_asm!(
    ".p2align 2",
    ".globl _rswift_pair_closure_thunk",
    "_rswift_pair_closure_thunk:",
    "mov x2, x20",
    "b _rswift_pair_closure_dispatch",
);

unsafe extern "C" {
    /// Address to hand to Swift for a two-argument, no-result closure.
    #[link_name = "rswift_pair_closure_thunk"]
    pub fn pair_closure_thunk();
}

/// Context for a Swift `(A, A) -> ()` closure implemented in Rust.
#[repr(C)]
pub struct PairClosureCtx {
    isa: *const c_void,
    refcount: u64,
    body: unsafe extern "C" fn(*const u8, *const u8, *mut c_void),
    payload: *mut c_void,
}

impl PairClosureCtx {
    pub fn new(
        body: unsafe extern "C" fn(*const u8, *const u8, *mut c_void),
        payload: *mut c_void,
    ) -> Self {
        Self { isa: immortal_isa(), refcount: IMMORTAL_REFCOUNT, body, payload }
    }
}

#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_pair_closure_dispatch(
    a: *const u8,
    b: *const u8,
    ctx: *mut PairClosureCtx,
) {
    let ctx = unsafe { &*ctx };
    unsafe { (ctx.body)(a, b, ctx.payload) };
}

// A closure taking one argument that may be up to three registers wide, such
// as `(HoverPhase) -> ()`. A frozen enum of seventeen bytes arrives *in* x0-x2;
// a resilient one of a single byte arrives as a pointer in x0. One thunk covers
// both, because forwarding registers the callee ignores costs nothing — while
// the narrow `value_closure_thunk` cannot, since it overwrites x1 with the
// context.
core::arch::global_asm!(
    ".p2align 2",
    ".globl _rswift_wide_closure_thunk",
    "_rswift_wide_closure_thunk:",
    "mov x3, x20",
    "b _rswift_wide_closure_dispatch",
);

unsafe extern "C" {
    /// Address to hand to Swift for a one-argument, no-result closure whose
    /// argument may not fit in a single register.
    #[link_name = "rswift_wide_closure_thunk"]
    pub fn wide_closure_thunk();
}

/// Context for a Swift `(V) -> ()` closure where `V` may span three registers.
#[repr(C)]
pub struct WideClosureCtx {
    isa: *const c_void,
    refcount: u64,
    body: unsafe extern "C" fn(u64, u64, u64, *mut c_void),
    payload: *mut c_void,
}

impl WideClosureCtx {
    pub fn new(
        body: unsafe extern "C" fn(u64, u64, u64, *mut c_void),
        payload: *mut c_void,
    ) -> Self {
        Self { isa: immortal_isa(), refcount: IMMORTAL_REFCOUNT, body, payload }
    }
}

#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_wide_closure_dispatch(
    a0: u64,
    a1: u64,
    a2: u64,
    ctx: *mut WideClosureCtx,
) {
    let ctx = unsafe { &*ctx };
    unsafe { (ctx.body)(a0, a1, a2, ctx.payload) };
}

/// Which case of an enum a value holds.
///
/// The inverse of [`super::inject_enum_tag`], and the first of the three enum value
/// witnesses rather than the third.
///
/// # Safety
/// `value` must point at a valid value of the enum `metadata` describes.
pub unsafe fn read_enum_tag(value: *const u8, metadata: *const c_void) -> u32 {
    unsafe {
        const ENUM_WITNESSES: usize = 8 * 8 + 8 + 8 + 4 + 4;
        let vwt = *(metadata.cast::<*const u8>().offset(-1));
        let get = *(vwt.add(ENUM_WITNESSES) as *const *const c_void);
        type Get = unsafe extern "C" fn(*const c_void, *const c_void) -> u32;
        let f: Get = core::mem::transmute(get);
        f(value as *const c_void, metadata)
    }
}

/// Resolve a Swift type from its mangled name string.
///
/// Results are cached. Demangling walks the name, instantiates generic metadata
/// and consults the runtime's caches, which costs on the order of a microsecond
/// — and the same handful of names are resolved on *every* view construction, so
/// uncached it dominates the cost of building a tree.
/// Resolve a *fixed* mangled name, caching in one word at the call site.
///
/// This is what the Swift compiler emits for the same job: a global per site,
/// a relaxed load, a null check, and the slow path taken once. The shared map
/// behind [`resolve_type_by_mangled_name`] has to hash the whole mangled string
/// under a mutex — 266 ns against the 416 the runtime call costs, so it earns
/// its keep, but a site that always asks for the same type should not be paying
/// either.
///
/// Only for names that cannot vary between calls. A site whose name is computed
/// would cache the first answer and hand it back for every later one.
#[macro_export]
macro_rules! type_by_name {
    ($bytes:expr) => {{
        static SITE: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(0);
        let hit = SITE.load(::std::sync::atomic::Ordering::Relaxed);
        if hit != 0 {
            hit as *const ::core::ffi::c_void
        } else {
            let m = $crate::abi::resolve_type_by_mangled_name($bytes);
            SITE.store(m as usize, ::std::sync::atomic::Ordering::Relaxed);
            m
        }
    }};
}

/// # Safety
/// `mangled` must be a well-formed mangled type name without its `$s` prefix.
/// The runtime parses it, and a malformed one is not merely a null answer.
pub unsafe fn resolve_type_by_mangled_name(mangled: &[u8]) -> *const c_void {
    use std::collections::HashMap;
    use std::sync::{Mutex, OnceLock};
    static CACHE: OnceLock<Mutex<HashMap<Vec<u8>, usize>>> = OnceLock::new();
    let cache = CACHE.get_or_init(|| Mutex::new(HashMap::new()));

    if let Ok(map) = cache.lock() {
        if let Some(&hit) = map.get(mangled) {
            return hit as *const c_void;
        }
    }
    let resolved = unsafe {
        swift_runtime_sys::RuntimeRaw::swift_getTypeByMangledNameInEnvironment(
            mangled.as_ptr(),
            mangled.len(),
            core::ptr::null(),
            0,
        )
    };
    if !resolved.is_null() {
        if let Ok(mut map) = cache.lock() {
            map.insert(mangled.to_vec(), resolved as usize);
        }
    }
    resolved
}

// A closure returning a single word — an `Array`, a class reference, a `Bool`.
// Swift returns a loadable one-word result in x0, not through x8, so this
// thunk moves only the context and lets the return value pass straight back.
core::arch::global_asm!(
    ".p2align 2",
    ".globl _rswift_word_closure_thunk",
    "_rswift_word_closure_thunk:",
    "mov x1, x0",
    "mov x0, x20",
    "b _rswift_word_closure_dispatch",
);

unsafe extern "C" {
    /// Address to hand to Swift for a closure returning one word.
    #[link_name = "rswift_word_closure_thunk"]
    pub fn word_closure_thunk();
}

/// Context for a Swift closure returning a single word.
///
/// `body` also receives whatever was in x0 on entry, so one context serves
/// both `() -> T` and `(U) -> T` where each of `T` and `U` is one word.
#[repr(C)]
pub struct WordClosureCtx {
    isa: *const c_void,
    refcount: u64,
    body: unsafe extern "C" fn(*mut c_void, u64) -> u64,
    payload: *mut c_void,
}

impl WordClosureCtx {
    pub fn new(
        body: unsafe extern "C" fn(*mut c_void, u64) -> u64,
        payload: *mut c_void,
    ) -> Self {
        Self { isa: immortal_isa(), refcount: IMMORTAL_REFCOUNT, body, payload }
    }
}

#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_word_closure_dispatch(ctx: *mut WordClosureCtx, arg: u64) -> u64 {
    let ctx = unsafe { &*ctx };
    unsafe { (ctx.body)(ctx.payload, arg) }
}

// A closure returning a `Double` — an alignment guide, a length. Swift returns
// it in d0, which no integer-returning thunk can carry back.
core::arch::global_asm!(
    ".p2align 2",
    ".globl _rswift_float_closure_thunk",
    "_rswift_float_closure_thunk:",
    "mov x1, x20",
    "b _rswift_float_closure_dispatch",
);

unsafe extern "C" {
    /// Address to hand to Swift for a closure returning a `Double`.
    #[link_name = "rswift_float_closure_thunk"]
    pub fn float_closure_thunk();
}

/// Context for a Swift closure returning a `Double`.
#[repr(C)]
pub struct FloatClosureCtx {
    isa: *const c_void,
    refcount: u64,
    body: unsafe extern "C" fn(u64, *mut c_void) -> f64,
    payload: *mut c_void,
}

impl FloatClosureCtx {
    pub fn new(
        body: unsafe extern "C" fn(u64, *mut c_void) -> f64,
        payload: *mut c_void,
    ) -> Self {
        Self { isa: immortal_isa(), refcount: IMMORTAL_REFCOUNT, body, payload }
    }
}

#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_float_closure_dispatch(arg: u64, ctx: *mut FloatClosureCtx) -> f64 {
    let ctx = unsafe { &*ctx };
    unsafe { (ctx.body)(arg, ctx.payload) }
}

// A closure returning *two* words. `draggable(_:)` takes an autoclosure
// `() -> T`, and the smallest useful `T` is `String` — sixteen bytes, which is
// loadable, so Swift returns it in x0/x1 rather than through x8. The
// single-word thunk next door cannot carry the second half back.
core::arch::global_asm!(
    ".p2align 2",
    ".globl _rswift_pair_return_closure_thunk",
    "_rswift_pair_return_closure_thunk:",
    "mov x0, x20",
    "b _rswift_pair_return_closure_dispatch",
);

unsafe extern "C" {
    /// Address to hand to Swift for a `() -> (word, word)` closure.
    #[link_name = "rswift_pair_return_closure_thunk"]
    pub fn pair_return_closure_thunk();
}

/// Two words, laid out so the ABI returns them in x0 and x1.
#[repr(C)]
pub struct Pair(pub u64, pub u64);

/// Context for a Swift closure returning two words.
#[repr(C)]
pub struct PairReturnClosureCtx {
    isa: *const c_void,
    refcount: u64,
    body: unsafe extern "C" fn(*mut c_void) -> Pair,
    payload: *mut c_void,
}

impl PairReturnClosureCtx {
    pub fn new(body: unsafe extern "C" fn(*mut c_void) -> Pair, payload: *mut c_void) -> Self {
        Self { isa: immortal_isa(), refcount: IMMORTAL_REFCOUNT, body, payload }
    }
}

#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_pair_return_closure_dispatch(ctx: *mut PairReturnClosureCtx) -> Pair {
    let ctx = unsafe { &*ctx };
    unsafe { (ctx.body)(ctx.payload) }
}