use crate::abi;
use crate::resolve;
use crate::views::ViewHandle;
use core::ffi::c_void;
use crate::mangle;
type BodyBuilder = Box<dyn Fn(i64) -> ViewHandle + Send>;
struct Instance {
builder: BodyBuilder,
binding: Option<Vec<u8>>,
}
static INSTANCES: std::sync::Mutex<Option<std::collections::HashMap<i64, Instance>>> =
std::sync::Mutex::new(None);
static NEXT_ID: std::sync::atomic::AtomicI64 = std::sync::atomic::AtomicI64::new(1);
fn with_instances<R>(f: impl FnOnce(&mut std::collections::HashMap<i64, Instance>) -> R) -> R {
let mut guard = INSTANCES.lock().expect("instance registry poisoned");
f(guard.get_or_insert_with(std::collections::HashMap::new))
}
core::arch::global_asm!(
".p2align 2",
".globl _rswift_view_body_thunk",
"_rswift_view_body_thunk:",
"mov x1, x20",
"mov x0, x8",
"b _rswift_view_body_getter",
);
unsafe extern "C" {
#[link_name = "rswift_view_body_thunk"]
fn view_body_thunk();
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_view_body_getter(out: *mut u8, this: *const u8) {
unsafe {
let id = *(this.add(ID_OFFSET) as *const i64);
let value = if *(this.add(8) as *const u64) == 0 {
0
} else {
state_wrapped_value(this)
};
let binding = state_projected_binding(this);
let view = with_instances(|map| {
let inst = map.get_mut(&id)?;
if inst.binding.is_none() {
inst.binding = Some(binding);
}
Some((inst.builder)(value))
});
let view = view.unwrap_or_else(crate::views::empty_view);
*(out as *mut u64) = view.into_anyview_word();
}
}
unsafe fn state_wrapped_value(state: *const u8) -> i64 {
let mut out: i64 = 0;
unsafe {
core::arch::asm!("blr {f}", f = in(reg) resolve::state_wrapped_get(),
in("x8") &mut out as *mut i64,
in("x20") state,
in("x0") abi::generic_metadata1(resolve::state_ma(), resolve::int_meta()),
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
}
out
}
unsafe fn state_projected_binding(state: *const u8) -> Vec<u8> {
unsafe {
let meta = abi::resolve_type_by_mangled_name(mangle::bound("7SwiftUI7BindingV", &[mangle::INT]).as_bytes());
assert!(!meta.is_null(), "Binding<Int> metadata");
let mut out = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::state_projected_get(),
in("x8") out.as_mut_ptr(),
in("x20") state,
in("x0") abi::generic_metadata1(resolve::state_ma(), resolve::int_meta()),
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
out
}
}
pub fn set_state(id: i64, value: i64) -> bool {
let binding = with_instances(|map| map.get(&id).and_then(|i| i.binding.clone()));
let Some(mut binding) = binding else { return false };
unsafe {
let meta = abi::resolve_type_by_mangled_name(mangle::bound("7SwiftUI7BindingV", &[mangle::INT]).as_bytes());
let mut v = value;
core::arch::asm!("blr {f}", f = in(reg) resolve::binding_wrapped_set(),
in("x0") &mut v as *mut i64,
in("x1") meta,
in("x20") binding.as_mut_ptr(),
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
}
true
}
pub struct StatefulView {
pub metadata: *const c_void,
pub witness_table: *const c_void,
value: Vec<u8>,
pub id: i64,
}
impl StatefulView {
pub fn new(initial: i64, builder: impl Fn(i64) -> ViewHandle + Send + 'static) -> Self {
unsafe {
let id = NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
with_instances(|map| {
map.insert(id, Instance { builder: Box::new(builder), binding: None })
});
let (metadata, witness_table) = build_type();
debug_assert_eq!(
field_offset(metadata, 1),
ID_OFFSET,
"the id moved; the body witness reads it at a fixed offset"
);
let mut value = vec![0u8; instance_bytes(metadata)];
value[..8].copy_from_slice(&initial.to_le_bytes());
value[ID_OFFSET..ID_OFFSET + 8].copy_from_slice(&id.to_le_bytes());
Self { metadata, witness_table, value, id }
}
}
pub fn set(&self, value: i64) -> bool {
set_state(self.id, value)
}
pub fn set_animated(&self, value: i64) {
set_state_animated(self.id, value);
}
pub fn set_animated_with(&self, value: i64, animation: u64) {
set_state_animated_with(self.id, value, animation);
}
pub fn as_view(&self) -> ViewHandle {
unsafe { copy_as_view(&self.value, self.metadata, self.witness_table) }
}
}
const ID_OFFSET: usize = 16;
struct FieldSpec {
mangled_type: &'static [u8],
name: &'static [u8],
meta: *const c_void,
}
struct TypeLayout {
fields: Vec<(usize, usize)>,
size: usize,
}
static LAYOUTS: std::sync::Mutex<Option<std::collections::HashMap<usize, TypeLayout>>> =
std::sync::Mutex::new(None);
fn with_layouts<R>(f: impl FnOnce(&mut std::collections::HashMap<usize, TypeLayout>) -> R) -> R {
let mut g = LAYOUTS.lock().expect("layout registry poisoned");
f(g.get_or_insert_with(std::collections::HashMap::new))
}
unsafe fn field_vwt(
meta: *const c_void,
) -> *const swift_runtime_sys::SwiftABI::ValueWitnessTable {
unsafe { swift_runtime_sys::SwiftABI::get_value_witness_table(meta) }
}
unsafe fn for_each_field(
meta: *const c_void,
mut op: impl FnMut(*const c_void, usize),
) -> bool {
let fields = with_layouts(|m| m.get(&(meta as usize)).map(|l| l.fields.clone()));
match fields {
Some(fields) => {
for (fm, off) in fields {
op(fm as *const c_void, off);
}
true
}
None => false,
}
}
unsafe fn instance_bytes(meta: *const c_void) -> usize {
let n = with_layouts(|m| m.get(&(meta as usize)).map_or(0, |l| l.size));
assert!(n > 0, "no layout registered for this synthesised type");
n
}
unsafe extern "C" fn fw_destroy(value: *mut u8, meta: *const c_void) {
unsafe {
for_each_field(meta, |fm, off| {
((*field_vwt(fm)).destroy)(value.add(off) as *mut c_void, fm);
});
}
}
unsafe extern "C" fn fw_init_with_copy(
dest: *mut u8,
src: *mut u8,
meta: *const c_void,
) -> *mut u8 {
unsafe {
for_each_field(meta, |fm, off| {
((*field_vwt(fm)).initialize_with_copy)(
dest.add(off) as *mut c_void,
src.add(off) as *mut c_void,
fm,
);
});
}
dest
}
unsafe extern "C" fn fw_assign_with_copy(
dest: *mut u8,
src: *mut u8,
meta: *const c_void,
) -> *mut u8 {
unsafe {
for_each_field(meta, |fm, off| {
((*field_vwt(fm)).assign_with_copy)(
dest.add(off) as *mut c_void,
src.add(off) as *mut c_void,
fm,
);
});
}
dest
}
unsafe extern "C" fn fw_init_with_take(
dest: *mut u8,
src: *mut u8,
meta: *const c_void,
) -> *mut u8 {
unsafe {
for_each_field(meta, |fm, off| {
((*field_vwt(fm)).initialize_with_take)(
dest.add(off) as *mut c_void,
src.add(off) as *mut c_void,
fm,
);
});
}
dest
}
unsafe extern "C" fn fw_assign_with_take(
dest: *mut u8,
src: *mut u8,
meta: *const c_void,
) -> *mut u8 {
unsafe {
for_each_field(meta, |fm, off| {
((*field_vwt(fm)).assign_with_take)(
dest.add(off) as *mut c_void,
src.add(off) as *mut c_void,
fm,
);
});
}
dest
}
unsafe extern "C" fn fw_init_buffer(dest: *mut u8, src: *mut u8, meta: *const c_void) -> *mut u8 {
unsafe { fw_init_with_copy(dest, src, meta) }
}
unsafe extern "C" fn fw_get_tag(_v: *const u8, _n: u32, _m: *const c_void) -> u32 {
0
}
unsafe extern "C" fn fw_store_tag(_v: *mut u8, _w: u32, _n: u32, _m: *const c_void) {}
fn delegating_vwt(size: usize, align: usize) -> *const c_void {
unsafe {
let vwt = libc::malloc(96) as *mut u8;
assert!(!vwt.is_null());
core::ptr::write_bytes(vwt, 0, 96);
let f = vwt as *mut *const c_void;
*f.add(0) = fw_init_buffer as *const c_void;
*f.add(1) = fw_destroy as *const c_void;
*f.add(2) = fw_init_with_copy as *const c_void;
*f.add(3) = fw_assign_with_copy as *const c_void;
*f.add(4) = fw_init_with_take as *const c_void;
*f.add(5) = fw_assign_with_take as *const c_void;
*f.add(6) = fw_get_tag as *const c_void;
*f.add(7) = fw_store_tag as *const c_void;
*(vwt.add(64) as *mut usize) = size;
*(vwt.add(72) as *mut usize) = size;
*(vwt.add(80) as *mut u32) = 0x0001_0000 | (align as u32 - 1);
*(vwt.add(84) as *mut u32) = 0; vwt as *const c_void
}
}
unsafe fn copy_as_view(
value: &[u8],
metadata: *const c_void,
witness_table: *const c_void,
) -> ViewHandle {
unsafe {
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata);
let mut owned = vec![0u8; abi::type_bytes(metadata)];
(vwt.initialize_with_copy)(
owned.as_mut_ptr() as *mut c_void,
value.as_ptr() as *mut c_void,
metadata,
);
ViewHandle::wrap(owned.as_ptr() as *const c_void, metadata, witness_table)
}
}
unsafe fn destroy_instance(value: &mut [u8], metadata: *const c_void) {
unsafe {
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(metadata);
if vwt.size > 0 {
(vwt.destroy)(value.as_mut_ptr() as *mut c_void, metadata);
}
}
}
unsafe fn field_offset(metadata: *const c_void, i: usize) -> usize {
unsafe { *((metadata as *const u8).add(16 + 4 * i) as *const u32) as usize }
}
unsafe fn emit_type(
type_name: &[u8],
self_mangled: &[u8],
fields: &[FieldSpec],
body: *const c_void,
) -> (*const c_void, *const c_void) {
const MODULE_NAME: &[u8] = b"rswift\0";
const MODULE: usize = 0;
const MODULE_NAME_OFF: usize = 16;
const STRUCT: usize = 32;
const TYPE_NAME_OFF: usize = 64;
const FIELD_DESC: usize = 128;
let n = fields.len();
assert!(n > 0, "a synthesised struct needs at least one field");
let mut offsets = Vec::with_capacity(n);
let mut at = 0usize;
let mut align = 1usize;
for f in fields {
let vw = unsafe { &*field_vwt(f.meta) };
let a = vw.flags.alignment().max(1);
align = align.max(a);
at = (at + a - 1) & !(a - 1);
offsets.push(at);
at += vw.stride.max(vw.size).max(1);
}
let size = ((at + align - 1) & !(align - 1)).max(1);
let vwt = delegating_vwt(size, align);
assert!(MODULE_NAME_OFF + MODULE_NAME.len() <= STRUCT);
assert!(
TYPE_NAME_OFF + type_name.len() <= FIELD_DESC,
"the synthesised type name {:?} needs {} bytes and there are {}",
core::str::from_utf8(type_name).unwrap_or("<not utf-8>"),
type_name.len(),
FIELD_DESC - TYPE_NAME_OFF
);
let field_desc_end = FIELD_DESC + 16 + 12 * n;
let mut strings: Vec<(usize, &[u8])> = Vec::with_capacity(2 * n + 3);
strings.push((MODULE_NAME_OFF, MODULE_NAME));
strings.push((TYPE_NAME_OFF, type_name));
let mut at = field_desc_end;
let mut field_string_offsets = Vec::with_capacity(n);
for f in fields {
let t = at;
strings.push((at, f.mangled_type));
at += f.mangled_type.len();
let name = at;
strings.push((at, f.name));
at += f.name.len();
field_string_offsets.push((t, name));
}
let self_mangled_off = at;
strings.push((at, self_mangled));
at += self_mangled.len();
let align8 = |x: usize| (x + 7) & !7;
let proto_slot = align8(at);
let conf = proto_slot + 16;
let record = conf + 24;
let witness = record + 8;
let total = witness + 7 * 8 + 8;
let block = unsafe { libc::malloc(total) } as *mut u8;
assert!(!block.is_null());
unsafe {
core::ptr::write_bytes(block, 0, total);
for (off, bytes) in strings {
core::ptr::copy_nonoverlapping(bytes.as_ptr(), block.add(off), bytes.len());
}
}
unsafe fn rel(field: *const u8, target: *const u8) -> i32 {
i32::try_from(target as isize - field as isize).expect("relative pointer out of range")
}
unsafe {
let m = block.add(MODULE);
*(m as *mut u32) = 0x40; *(m.add(4) as *mut i32) = 0;
*(m.add(8) as *mut i32) = rel(m.add(8), block.add(MODULE_NAME_OFF));
let d = block.add(STRUCT);
*(d as *mut u32) = 0x51; *(d.add(4) as *mut i32) = rel(d.add(4), m);
*(d.add(8) as *mut i32) = rel(d.add(8), block.add(TYPE_NAME_OFF));
*(d.add(12) as *mut i32) = 0; *(d.add(16) as *mut i32) = rel(d.add(16), block.add(FIELD_DESC));
*(d.add(20) as *mut u32) = n as u32; *(d.add(24) as *mut u32) = 2;
let f = block.add(FIELD_DESC);
*(f as *mut i32) = rel(f, block.add(self_mangled_off));
*(f.add(4) as *mut i32) = 0; *(f.add(8) as *mut u16) = 0; *(f.add(10) as *mut u16) = 12; *(f.add(12) as *mut u32) = n as u32;
for (i, (type_off, name_off)) in field_string_offsets.iter().enumerate() {
let r = f.add(16 + 12 * i);
*(r as *mut u32) = 0x2; *(r.add(4) as *mut i32) = rel(r.add(4), block.add(*type_off));
*(r.add(8) as *mut i32) = rel(r.add(8), block.add(*name_off));
}
let meta_bytes = 24 + 4 * n;
let meta = libc::malloc(meta_bytes) as *mut *const c_void;
assert!(!meta.is_null());
core::ptr::write_bytes(meta as *mut u8, 0, meta_bytes);
*meta.add(0) = vwt;
*(meta.add(1) as *mut usize) = 0x200; *meta.add(2) = d as *const c_void;
for (i, off) in offsets.iter().enumerate() {
*((meta as *mut u8).add(24 + 4 * i) as *mut u32) = *off as u32;
}
let metadata = (meta as *const u8).add(8) as *const c_void;
with_layouts(|m| {
m.insert(
metadata as usize,
TypeLayout {
fields: fields
.iter()
.zip(&offsets)
.map(|(f, off)| (f.meta as usize, *off))
.collect(),
size,
},
)
});
let wt = block.add(witness) as *mut *const c_void;
*wt.add(0) = block.add(conf) as *const c_void;
*wt.add(1) = resolve::anyview_wt();
*wt.add(2) = resolve::anyview_meta();
*wt.add(3) = resolve::view_make_view_default();
*wt.add(4) = resolve::view_make_view_list_default();
*wt.add(5) = resolve::view_list_count_default();
*wt.add(6) = body;
*(block.add(proto_slot) as *mut *const c_void) = resolve::view_proto();
let c = block.add(conf);
*(c as *mut i32) = rel(c, block.add(proto_slot)) | 1; *(c.add(4) as *mut i32) = rel(c.add(4), d);
*(c.add(8) as *mut i32) = rel(c.add(8), block.add(witness));
*(c.add(12) as *mut u32) = 0;
let rec = block.add(record);
*(rec as *mut i32) = rel(rec, c);
type Register = unsafe extern "C" fn(*const u8, *const u8);
let reg = libc::dlsym(
(-2isize) as *mut c_void,
c"swift_registerProtocolConformances".as_ptr(),
);
assert!(!reg.is_null(), "swift_registerProtocolConformances not found");
let reg: Register = core::mem::transmute(reg);
reg(rec, rec.add(4));
(metadata, wt as *const c_void)
}
}
pub(crate) unsafe fn emit_int_carrier(
type_name: &'static [u8],
self_mangled: &'static [u8],
) -> (*const c_void, *const c_void) {
unsafe {
let (metadata, _) = emit_type(
type_name,
self_mangled,
&[FieldSpec { mangled_type: b"Si\0", name: b"index\0", meta: resolve::int_meta() }],
stub_body as *const c_void,
);
let descriptor = *(metadata as *const *const c_void).add(1);
(descriptor, metadata)
}
}
unsafe extern "C" fn stub_body(out: *mut u8, _payload: *mut c_void) {
unsafe { *(out as *mut u64) = 0 };
}
unsafe fn build_type() -> (*const c_void, *const c_void) {
unsafe {
emit_type(
b"RustView\0",
b"6rswift8RustViewV\0",
&[
FieldSpec {
mangled_type: b"7SwiftUI5StateVySiG\0",
name: b"_value\0",
meta: abi::generic_metadata1(resolve::state_ma(), resolve::int_meta()),
},
FieldSpec { mangled_type: b"Si\0", name: b"id\0", meta: resolve::int_meta() },
],
view_body_thunk as *const c_void,
)
}
}
static PENDING_WRITE: std::sync::Mutex<(i64, i64)> = std::sync::Mutex::new((0, 0));
unsafe extern "C" fn animated_write(_out: *mut u8, _payload: *mut c_void) {
let (id, value) = *PENDING_WRITE.lock().expect("pending write poisoned");
set_state(id, value);
}
mod root {
use super::{set_state_animated_with, StatefulView};
use crate::views::ViewHandle;
use std::sync::atomic::{AtomicI64, Ordering};
pub(super) static ID: AtomicI64 = AtomicI64::new(-1);
static GENERATION: AtomicI64 = AtomicI64::new(1);
thread_local! {
static VIEW: std::cell::OnceCell<StatefulView> = const { std::cell::OnceCell::new() };
}
pub(super) fn view(builder: fn(i64) -> ViewHandle) -> ViewHandle {
VIEW.with(|cell| {
let sv = cell.get_or_init(|| {
let sv = StatefulView::new(GENERATION.load(Ordering::Relaxed), builder);
ID.store(sv.id, Ordering::Release);
sv
});
sv.as_view()
})
}
pub(super) fn bump(animation: Option<u64>) {
let id = ID.load(Ordering::Acquire);
if id < 0 {
return;
}
let n = GENERATION.fetch_add(1, Ordering::Relaxed) + 1;
match animation {
Some(a) => set_state_animated_with(id, n, a),
None => {
super::set_state(id, n);
}
}
}
}
pub fn root_view(builder: fn(i64) -> ViewHandle) -> ViewHandle {
root::view(builder)
}
pub fn refresh() {
root::bump(None);
}
pub fn refresh_animated(animation: u64) {
root::bump(Some(animation));
}
pub fn ease_in_out(duration: f64) -> u64 {
unsafe {
let meta = abi::metadata0(resolve::animation_ma());
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::animation_ease_in_out(),
in("d0") duration,
in("x20") meta,
lateout("x0") out,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
out
}
}
pub fn set_state_animated_with(id: i64, value: i64, animation: u64) {
set_animated_inner(id, value, Some(animation))
}
pub fn set_state_animated(id: i64, value: i64) {
set_animated_inner(id, value, None)
}
fn set_animated_inner(id: i64, value: i64, animation: Option<u64>) {
*PENDING_WRITE.lock().expect("pending write poisoned") = (id, value);
unsafe {
let anim_meta = abi::metadata0(resolve::animation_ma());
let anim = if let Some(a) = animation { a } else {
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::animation_default(),
in("x20") anim_meta,
lateout("x0") out,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
out
};
let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
animated_write,
core::ptr::null_mut(),
))) as *mut abi::ClosureCtx;
let mut out = [0u8; 8];
core::arch::asm!("blr {f}", f = in(reg) resolve::with_animation(),
in("x0") anim,
in("x1") abi::swift_closure_thunk as *const c_void,
in("x2") ctx,
in("x3") resolve::sym(c"$sytN"),
in("x8") out.as_mut_ptr(),
inout("x21") 0u64 => _,
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x20") _,
clobber_abi("C"),
);
}
}
const STORED_ID_OFFSET: usize = 8;
pub trait StoredValue: Clone + Send + 'static {
fn metadata() -> *const c_void;
fn field_type() -> &'static [u8];
fn mangled() -> &'static str;
fn buffer_bytes() -> usize;
unsafe fn init_storage(&self, key: &str) -> u64;
unsafe fn decode(buf: &[u8]) -> Self;
fn encode(&self) -> Vec<u8>;
}
impl StoredValue for bool {
fn metadata() -> *const c_void {
resolve::bool_meta()
}
fn field_type() -> &'static [u8] {
b"7SwiftUI10AppStorageVySbG\0"
}
fn mangled() -> &'static str {
mangle::BOOL
}
fn buffer_bytes() -> usize {
1
}
unsafe fn init_storage(&self, key: &str) -> u64 {
unsafe { app_storage_init_word(resolve::app_storage_bool_init(), u64::from(*self), key) }
}
unsafe fn decode(buf: &[u8]) -> Self {
buf[0] != 0
}
fn encode(&self) -> Vec<u8> {
vec![u8::from(*self)]
}
}
impl StoredValue for i64 {
fn metadata() -> *const c_void {
resolve::int_meta()
}
fn field_type() -> &'static [u8] {
b"7SwiftUI10AppStorageVySiG\0"
}
fn mangled() -> &'static str {
mangle::INT
}
fn buffer_bytes() -> usize {
8
}
unsafe fn init_storage(&self, key: &str) -> u64 {
unsafe { app_storage_init_word(resolve::app_storage_int_init(), *self as u64, key) }
}
unsafe fn decode(buf: &[u8]) -> Self {
i64::from_le_bytes(buf[..8].try_into().expect("Int"))
}
fn encode(&self) -> Vec<u8> {
self.to_le_bytes().to_vec()
}
}
impl StoredValue for f64 {
fn metadata() -> *const c_void {
resolve::double_meta()
}
fn field_type() -> &'static [u8] {
b"7SwiftUI10AppStorageVySdG\0"
}
fn mangled() -> &'static str {
mangle::DOUBLE
}
fn buffer_bytes() -> usize {
8
}
unsafe fn init_storage(&self, key: &str) -> u64 {
unsafe {
let k = abi::swift_string(key);
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::app_storage_double_init(),
in("d0") *self,
inlateout("x0") u64::from_le_bytes(k[..8].try_into().expect("key lo")) => out,
in("x1") u64::from_le_bytes(k[8..].try_into().expect("key hi")),
in("x2") 0u64, lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
lateout("x7") _, lateout("x8") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x20") _, lateout("x21") _, clobber_abi("C"),
);
assert!(out != 0, "AppStorage<Double>.init returned nothing");
out
}
}
unsafe fn decode(buf: &[u8]) -> Self {
f64::from_le_bytes(buf[..8].try_into().expect("Double"))
}
fn encode(&self) -> Vec<u8> {
self.to_le_bytes().to_vec()
}
}
impl StoredValue for String {
fn metadata() -> *const c_void {
resolve::string_meta()
}
fn field_type() -> &'static [u8] {
b"7SwiftUI10AppStorageVySSG\0"
}
fn mangled() -> &'static str {
mangle::STRING
}
fn buffer_bytes() -> usize {
16
}
unsafe fn init_storage(&self, key: &str) -> u64 {
unsafe {
let v = abi::swift_string(self);
let k = abi::swift_string(key);
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::app_storage_string_init(),
inlateout("x0") u64::from_le_bytes(v[..8].try_into().expect("v lo")) => out,
in("x1") u64::from_le_bytes(v[8..].try_into().expect("v hi")),
in("x2") u64::from_le_bytes(k[..8].try_into().expect("k lo")),
in("x3") u64::from_le_bytes(k[8..].try_into().expect("k hi")),
in("x4") 0u64, lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
clobber_abi("C"),
);
assert!(out != 0, "AppStorage<String>.init returned nothing");
out
}
}
unsafe fn decode(buf: &[u8]) -> Self {
unsafe {
let bytes: [u8; 16] = buf[..16].try_into().expect("String");
let s = abi::read_swift_string(bytes);
swift_runtime_sys::BridgeObject::swift_bridgeObjectRelease(
u64::from_le_bytes(bytes[8..].try_into().expect("bridge word")) as *mut c_void,
);
s
}
}
fn encode(&self) -> Vec<u8> {
unsafe { abi::swift_string(self).to_vec() }
}
}
unsafe fn app_storage_init_word(func: *const c_void, value: u64, key: &str) -> u64 {
unsafe {
let k = abi::swift_string(key);
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) func,
inlateout("x0") value => out,
in("x1") u64::from_le_bytes(k[..8].try_into().expect("key lo")),
in("x2") u64::from_le_bytes(k[8..].try_into().expect("key hi")),
in("x3") 0u64, lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x8") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x20") _,
lateout("x21") _, clobber_abi("C"),
);
assert!(out != 0, "AppStorage.init returned nothing");
out
}
}
unsafe fn app_storage_get<T: StoredValue>(location: u64) -> T {
unsafe {
let mut buf = vec![0u8; T::buffer_bytes()];
swift_runtime_sys::RuntimeRaw::swift_retain(location as *mut c_void);
core::arch::asm!("blr {f}", f = in(reg) resolve::app_storage_wrapped_get(),
in("x8") buf.as_mut_ptr(),
in("x0") location,
in("x1") T::metadata(),
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x20") _, lateout("x21") _, clobber_abi("C"),
);
swift_runtime_sys::RuntimeRaw::swift_release(location as *mut c_void);
T::decode(&buf)
}
}
unsafe fn app_storage_set<T: StoredValue>(location: u64, value: &T) {
unsafe {
let mut v = value.encode();
swift_runtime_sys::RuntimeRaw::swift_retain(location as *mut c_void);
core::arch::asm!("blr {f}", f = in(reg) resolve::app_storage_wrapped_set(),
in("x0") v.as_mut_ptr(),
in("x1") location,
in("x2") T::metadata(),
lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
lateout("x7") _, lateout("x8") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x20") _, lateout("x21") _, clobber_abi("C"),
);
swift_runtime_sys::RuntimeRaw::swift_release(location as *mut c_void);
}
}
unsafe fn app_storage_projected<T: StoredValue>(location: u64) -> Vec<u8> {
unsafe {
let meta = abi::resolve_type_by_mangled_name(
mangle::bound("7SwiftUI7BindingV", &[T::mangled()]).as_bytes(),
);
assert!(!meta.is_null(), "Binding metadata");
let mut out = vec![0u8; abi::type_bytes(meta)];
swift_runtime_sys::RuntimeRaw::swift_retain(location as *mut c_void);
core::arch::asm!("blr {f}", f = in(reg) resolve::app_storage_projected_get(),
in("x8") out.as_mut_ptr(),
in("x0") location,
in("x1") T::metadata(),
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x20") _, lateout("x21") _, clobber_abi("C"),
);
swift_runtime_sys::RuntimeRaw::swift_release(location as *mut c_void);
out
}
}
struct StoredInstance {
render: Box<dyn Fn(u64) -> ViewHandle + Send>,
runs: usize,
}
static STORED: std::sync::Mutex<Option<std::collections::HashMap<i64, StoredInstance>>> =
std::sync::Mutex::new(None);
fn with_stored<R>(f: impl FnOnce(&mut std::collections::HashMap<i64, StoredInstance>) -> R) -> R {
let mut g = STORED.lock().expect("stored registry poisoned");
f(g.get_or_insert_with(std::collections::HashMap::new))
}
core::arch::global_asm!(
".p2align 2",
".globl _rswift_stored_body_thunk",
"_rswift_stored_body_thunk:",
"mov x1, x20",
"mov x0, x8",
"b _rswift_stored_body_getter",
);
unsafe extern "C" {
#[link_name = "rswift_stored_body_thunk"]
fn stored_body_thunk();
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_stored_body_getter(out: *mut u8, this: *const u8) {
unsafe {
let id = *(this.add(STORED_ID_OFFSET) as *const i64);
let location = *(this as *const u64);
let view = with_stored(|m| {
let inst = m.get_mut(&id)?;
inst.runs += 1;
Some((inst.render)(location))
});
*(out as *mut u64) = view.unwrap_or_else(crate::views::empty_view).into_anyview_word();
}
}
pub struct StoredView<T: StoredValue> {
pub metadata: *const c_void,
pub witness_table: *const c_void,
value: Vec<u8>,
pub id: i64,
key: String,
marker: core::marker::PhantomData<fn() -> T>,
}
impl<T: StoredValue> StoredView<T> {
pub fn new(key: &str, default: T, builder: impl Fn(T) -> ViewHandle + Send + 'static) -> Self {
unsafe {
let id = NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
with_stored(|m| {
m.insert(
id,
StoredInstance {
render: Box::new(move |loc| builder(app_storage_get::<T>(loc))),
runs: 0,
},
)
});
let (metadata, witness_table) = emit_type(
b"RustStored\0",
b"6rswift10RustStoredV\0",
&[
FieldSpec {
mangled_type: T::field_type(),
name: b"_flag\0",
meta: abi::resolve_type_by_mangled_name(
&T::field_type()[..T::field_type().len() - 1],
),
},
FieldSpec { mangled_type: b"Si\0", name: b"id\0", meta: resolve::int_meta() },
],
stored_body_thunk as *const c_void,
);
debug_assert_eq!(
field_offset(metadata, 1),
STORED_ID_OFFSET,
"the id moved; the body witness reads it at a fixed offset"
);
let mut value = vec![0u8; instance_bytes(metadata)];
value[..8].copy_from_slice(&default.init_storage(key).to_le_bytes());
value[STORED_ID_OFFSET..STORED_ID_OFFSET + 8].copy_from_slice(&id.to_le_bytes());
Self {
metadata,
witness_table,
value,
id,
key: key.to_owned(),
marker: core::marker::PhantomData,
}
}
}
fn location(&self) -> u64 {
u64::from_le_bytes(self.value[..8].try_into().expect("location"))
}
pub fn key(&self) -> &str {
&self.key
}
pub fn get(&self) -> T {
unsafe { app_storage_get::<T>(self.location()) }
}
pub fn set(&self, value: T) {
unsafe { app_storage_set::<T>(self.location(), &value) }
}
pub fn runs(&self) -> usize {
with_stored(|m| m.get(&self.id).map_or(0, |i| i.runs))
}
pub fn binding(&self) -> Vec<u8> {
unsafe { app_storage_projected::<T>(self.location()) }
}
pub fn as_view(&self) -> ViewHandle {
unsafe { copy_as_view(&self.value, self.metadata, self.witness_table) }
}
}
pub trait EnvKey: Copy + 'static {
type Value: Clone + Send + 'static;
fn field_type() -> &'static [u8];
fn value_type() -> &'static [u8];
fn key_path() -> u64;
fn buffer_bytes() -> usize;
unsafe fn decode(buf: &[u8]) -> Self::Value;
fn encode(value: &Self::Value) -> u8;
}
macro_rules! env_key {
($(
$(#[$meta:meta])*
$name:ident {
value: $value:ty,
field: $field:expr,
value_type: $value_type:expr,
symbols: ($getter:ident, $setter:ident, $descriptor:ident),
bytes: $bytes:expr,
decode: |$buf:ident| $decode:expr,
encode: |$enc:ident| $encode:expr,
}
)*) => {$(
$(#[$meta])*
#[derive(Clone, Copy)]
pub struct $name;
impl EnvKey for $name {
type Value = $value;
fn field_type() -> &'static [u8] {
$field
}
fn value_type() -> &'static [u8] {
$value_type
}
fn key_path() -> u64 {
use std::sync::atomic::{AtomicU64, Ordering};
static CACHE: AtomicU64 = AtomicU64::new(0);
static mut SLOT: crate::keypath::Slot = crate::keypath::Slot::new();
let hit = CACHE.load(Ordering::Relaxed);
if hit != 0 {
return hit;
}
unsafe {
let kp = crate::keypath::build(
&mut *(&raw mut SLOT),
crate::keypath::Property {
getter: resolve::$getter(),
setter: resolve::$setter(),
descriptor: resolve::$descriptor(),
root: b"7SwiftUI17EnvironmentValuesV",
value: $value_type,
},
crate::keypath::next_thunks(),
);
CACHE.store(kp, Ordering::Relaxed);
kp
}
}
fn buffer_bytes() -> usize {
$bytes
}
unsafe fn decode($buf: &[u8]) -> Self::Value {
$decode
}
fn encode($enc: &Self::Value) -> u8 {
$encode
}
}
)*};
}
env_key! {
ColorSchemeKey {
value: crate::views::ColorScheme,
field: b"7SwiftUI11EnvironmentVyAA11ColorSchemeOG\0",
value_type: b"7SwiftUI11ColorSchemeO",
symbols: (env_color_scheme_getter, env_color_scheme_setter, env_color_scheme_descriptor),
bytes: 1,
decode: |b| match b[0] {
0 => crate::views::ColorScheme::Light,
_ => crate::views::ColorScheme::Dark,
},
encode: |v| match v {
crate::views::ColorScheme::Light => 0,
crate::views::ColorScheme::Dark => 1,
},
}
IsEnabledKey {
value: bool,
field: b"7SwiftUI11EnvironmentVySbG\0",
value_type: b"Sb",
symbols: (env_is_enabled_getter, env_is_enabled_setter, env_is_enabled_descriptor),
bytes: 1,
decode: |b| b[0] != 0,
encode: |v| u8::from(*v),
}
LayoutDirectionKey {
value: crate::views::LayoutDirection,
field: b"7SwiftUI11EnvironmentVyAA15LayoutDirectionOG\0",
value_type: b"7SwiftUI15LayoutDirectionO",
symbols: (
env_layout_direction_getter,
env_layout_direction_setter,
env_layout_direction_descriptor
),
bytes: 1,
decode: |b| match b[0] {
0 => crate::views::LayoutDirection::LeftToRight,
_ => crate::views::LayoutDirection::RightToLeft,
},
encode: |v| match v {
crate::views::LayoutDirection::LeftToRight => 0,
crate::views::LayoutDirection::RightToLeft => 1,
},
}
DynamicTypeSizeKey {
value: crate::views::DynamicTypeSize,
field: b"7SwiftUI11EnvironmentVyAA15DynamicTypeSizeOG\0",
value_type: b"7SwiftUI15DynamicTypeSizeO",
symbols: (
env_dynamic_type_size_getter,
env_dynamic_type_size_setter,
env_dynamic_type_size_descriptor
),
bytes: 1,
decode: |b| {
use crate::views::DynamicTypeSize::*;
[
XSmall, Small, Medium, Large, XLarge, XxLarge, XxxLarge,
Accessibility1, Accessibility2, Accessibility3, Accessibility4,
Accessibility5,
][(b[0] as usize).min(11)]
},
encode: |v| {
use crate::views::DynamicTypeSize::*;
[
XSmall, Small, Medium, Large, XLarge, XxLarge, XxxLarge,
Accessibility1, Accessibility2, Accessibility3, Accessibility4,
Accessibility5,
]
.iter()
.position(|c| c == v)
.expect("every DynamicTypeSize is in the table") as u8
},
}
ControlSizeKey {
value: crate::views::ControlSize,
field: b"7SwiftUI11EnvironmentVyAA11ControlSizeOG\0",
value_type: b"7SwiftUI11ControlSizeO",
symbols: (env_control_size_getter, env_control_size_setter, env_control_size_descriptor),
bytes: 1,
decode: |b| {
use crate::views::ControlSize::*;
[Mini, Small, Regular, Large, ExtraLarge][(b[0] as usize).min(4)]
},
encode: |v| {
use crate::views::ControlSize::*;
[Mini, Small, Regular, Large, ExtraLarge]
.iter()
.position(|c| c == v)
.expect("every ControlSize is in the table") as u8
},
}
IsLuminanceReducedKey {
value: bool,
field: b"7SwiftUI11EnvironmentVySbG\0",
value_type: b"Sb",
symbols: (
env_luminance_reduced_getter,
env_luminance_reduced_setter,
env_luminance_reduced_descriptor
),
bytes: 1,
decode: |b| b[0] != 0,
encode: |v| u8::from(*v),
}
}
unsafe fn environment_init(key_path: u64, bytes: usize) -> Vec<u8> {
unsafe {
swift_runtime_sys::RuntimeRaw::swift_retain(key_path as *mut c_void);
let mut out = vec![0u8; bytes];
core::arch::asm!("blr {f}", f = in(reg) resolve::environment_init(),
in("x8") out.as_mut_ptr(),
in("x0") key_path,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x20") _, lateout("x21") _, clobber_abi("C"),
);
out
}
}
unsafe fn environment_get<K: EnvKey>(field: *const u8, env_meta: *const c_void) -> K::Value {
unsafe {
let mut buf = vec![0u8; K::buffer_bytes()];
core::arch::asm!("blr {f}", f = in(reg) resolve::environment_wrapped_get(),
in("x8") buf.as_mut_ptr(),
in("x0") env_meta,
in("x20") field,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
K::decode(&buf)
}
}
struct EnvInstance {
render: Box<dyn Fn(*const u8, *const c_void) -> ViewHandle + Send>,
env_offset: usize,
env_meta: usize,
runs: usize,
}
static ENVS: std::sync::Mutex<Option<std::collections::HashMap<i64, EnvInstance>>> =
std::sync::Mutex::new(None);
fn with_envs<R>(f: impl FnOnce(&mut std::collections::HashMap<i64, EnvInstance>) -> R) -> R {
let mut g = ENVS.lock().expect("environment registry poisoned");
f(g.get_or_insert_with(std::collections::HashMap::new))
}
core::arch::global_asm!(
".p2align 2",
".globl _rswift_env_body_thunk",
"_rswift_env_body_thunk:",
"mov x1, x20",
"mov x0, x8",
"b _rswift_env_body_getter",
);
unsafe extern "C" {
#[link_name = "rswift_env_body_thunk"]
fn env_body_thunk();
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_env_body_getter(out: *mut u8, this: *const u8) {
unsafe {
let id = *(this as *const i64);
let view = with_envs(|m| {
let inst = m.get_mut(&id)?;
inst.runs += 1;
let (off, meta) = (inst.env_offset, inst.env_meta as *const c_void);
Some((inst.render)(this.add(off), meta))
});
*(out as *mut u64) = view.unwrap_or_else(crate::views::empty_view).into_anyview_word();
}
}
pub struct EnvironmentView<K: EnvKey> {
pub metadata: *const c_void,
pub witness_table: *const c_void,
value: Vec<u8>,
pub id: i64,
marker: core::marker::PhantomData<fn() -> K>,
}
impl<K: EnvKey> EnvironmentView<K> {
pub fn new(builder: impl Fn(K::Value) -> ViewHandle + Send + 'static) -> Self {
unsafe {
let id = NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
with_envs(|m| {
m.insert(
id,
EnvInstance {
render: Box::new(move |field, meta| {
builder(environment_get::<K>(field, meta))
}),
env_offset: 0,
env_meta: 0,
runs: 0,
},
)
});
let field_meta = abi::resolve_type_by_mangled_name(
&K::field_type()[..K::field_type().len() - 1],
);
assert!(!field_meta.is_null(), "Environment<V> metadata");
let (metadata, witness_table) = emit_type(
b"RustEnv\0",
b"6rswift7RustEnvV\0",
&[
FieldSpec { mangled_type: b"Si\0", name: b"id\0", meta: resolve::int_meta() },
FieldSpec {
mangled_type: K::field_type(),
name: b"_value\0",
meta: field_meta,
},
],
env_body_thunk as *const c_void,
);
let env_off = field_offset(metadata, 1);
debug_assert_eq!(field_offset(metadata, 0), 0, "the id must be field 0");
with_envs(|m| {
if let Some(inst) = m.get_mut(&id) {
inst.env_offset = env_off;
inst.env_meta = field_meta as usize;
}
});
let mut value = vec![0u8; instance_bytes(metadata)];
let env = environment_init(K::key_path(), abi::type_bytes(field_meta));
value[..8].copy_from_slice(&id.to_le_bytes());
value[env_off..env_off + env.len()].copy_from_slice(&env);
Self { metadata, witness_table, value, id, marker: core::marker::PhantomData }
}
}
pub fn runs(&self) -> usize {
with_envs(|m| m.get(&self.id).map_or(0, |i| i.runs))
}
pub fn as_view(&self) -> ViewHandle {
unsafe { copy_as_view(&self.value, self.metadata, self.witness_table) }
}
}
pub trait StateValue: StoredValue {
fn state_field_type() -> &'static [u8];
}
impl StateValue for bool {
fn state_field_type() -> &'static [u8] {
b"7SwiftUI5StateVySbG\0"
}
}
impl StateValue for i64 {
fn state_field_type() -> &'static [u8] {
b"7SwiftUI5StateVySiG\0"
}
}
impl StateValue for f64 {
fn state_field_type() -> &'static [u8] {
b"7SwiftUI5StateVySdG\0"
}
}
impl StateValue for String {
fn state_field_type() -> &'static [u8] {
b"7SwiftUI5StateVySSG\0"
}
}
unsafe fn state_box_offset(state_meta: *const c_void) -> usize {
unsafe { abi::type_size(state_meta).saturating_sub(8) }
}
unsafe fn state_value_of<T: StateValue>(field: *const u8, state_meta: *const c_void) -> T {
unsafe {
let mut buf = vec![0u8; T::buffer_bytes()];
core::arch::asm!("blr {f}", f = in(reg) resolve::state_wrapped_get(),
in("x8") buf.as_mut_ptr(),
in("x0") state_meta,
in("x20") field,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
T::decode(&buf)
}
}
unsafe fn state_binding_bytes(
field: *const u8,
state_meta: *const c_void,
bytes: usize,
) -> Vec<u8> {
unsafe {
let mut out = vec![0u8; bytes];
core::arch::asm!("blr {f}", f = in(reg) resolve::state_projected_get(),
in("x8") out.as_mut_ptr(),
in("x0") state_meta,
in("x20") field,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
out
}
}
unsafe fn binding_meta<T: StoredValue>() -> *const c_void {
unsafe {
let m = abi::resolve_type_by_mangled_name(
mangle::bound("7SwiftUI7BindingV", &[T::mangled()]).as_bytes(),
);
assert!(!m.is_null(), "Binding metadata");
m
}
}
unsafe fn binding_set_of<T: StoredValue>(binding: &mut [u8], value: &T) {
unsafe {
let mut v = value.encode();
core::arch::asm!("blr {f}", f = in(reg) resolve::binding_wrapped_set(),
in("x0") v.as_mut_ptr(),
in("x1") binding_meta::<T>(),
in("x20") binding.as_mut_ptr(),
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
}
}
fn prop_name(i: usize) -> &'static [u8] {
Box::leak(format!("_p{i}\0").into_bytes().into_boxed_slice())
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum PropKind {
State,
Stored,
Env,
Focus,
}
struct PropSpec {
field_type: &'static [u8],
meta: *const c_void,
binding_bytes: usize,
init: Vec<u8>,
kind: PropKind,
}
#[derive(Clone, Copy)]
struct PropSlot {
offset: usize,
meta: usize,
binding_bytes: usize,
kind: PropKind,
}
#[derive(Debug)]
pub struct StateProp<T> {
index: usize,
marker: core::marker::PhantomData<fn() -> T>,
}
#[derive(Debug)]
pub struct StoredProp<T> {
index: usize,
marker: core::marker::PhantomData<fn() -> T>,
}
#[derive(Debug)]
pub struct EnvProp<K> {
index: usize,
marker: core::marker::PhantomData<fn() -> K>,
}
#[derive(Clone, Copy, Debug)]
pub struct FocusProp {
index: usize,
}
pub struct Props {
this: *const u8,
slots: Vec<PropSlot>,
}
impl Props {
fn slot(&self, i: usize, want: PropKind, what: &str) -> PropSlot {
let s = *self.slots.get(i).unwrap_or_else(|| {
panic!("no property at index {i}; the view has {}", self.slots.len())
});
assert!(s.kind == want, "property {i} is not {what}");
s
}
pub fn state<T: StateValue + Default>(&self, prop: StateProp<T>) -> T {
let s = self.slot(prop.index, PropKind::State, "@State");
unsafe {
let meta = s.meta as *const c_void;
if *(self.this.add(s.offset + state_box_offset(meta)) as *const u64) == 0 {
T::default()
} else {
state_value_of::<T>(self.this.add(s.offset), meta)
}
}
}
pub fn stored<T: StoredValue>(&self, prop: StoredProp<T>) -> T {
let s = self.slot(prop.index, PropKind::Stored, "@AppStorage");
unsafe { app_storage_get::<T>(*(self.this.add(s.offset) as *const u64)) }
}
pub fn focus(&self, prop: FocusProp) -> bool {
let s = self.slot(prop.index, PropKind::Focus, "@FocusState");
unsafe { focus_get(self.this.add(s.offset), s.meta as *const c_void) }
}
pub fn focus_projection(&self, prop: FocusProp) -> [u64; 3] {
let s = self.slot(prop.index, PropKind::Focus, "@FocusState");
unsafe { focus_projection(self.this.add(s.offset), s.meta as *const c_void) }
}
pub fn env<K: EnvKey>(&self, prop: EnvProp<K>) -> K::Value {
let s = self.slot(prop.index, PropKind::Env, "@Environment");
unsafe { environment_get::<K>(self.this.add(s.offset), s.meta as *const c_void) }
}
}
struct ComposedInstance {
body: Box<dyn Fn(&Props) -> ViewHandle + Send>,
slots: Vec<PropSlot>,
bindings: std::collections::HashMap<usize, Vec<u8>>,
focus: std::collections::HashMap<usize, [u64; 3]>,
runs: usize,
}
static COMPOSED: std::sync::Mutex<Option<std::collections::HashMap<i64, ComposedInstance>>> =
std::sync::Mutex::new(None);
fn with_composed<R>(
f: impl FnOnce(&mut std::collections::HashMap<i64, ComposedInstance>) -> R,
) -> R {
let mut g = COMPOSED.lock().expect("composed registry poisoned");
f(g.get_or_insert_with(std::collections::HashMap::new))
}
core::arch::global_asm!(
".p2align 2",
".globl _rswift_composed_body_thunk",
"_rswift_composed_body_thunk:",
"mov x1, x20",
"mov x0, x8",
"b _rswift_composed_body_getter",
);
unsafe extern "C" {
#[link_name = "rswift_composed_body_thunk"]
fn composed_body_thunk();
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_composed_body_getter(out: *mut u8, this: *const u8) {
unsafe {
let id = *(this as *const i64);
let view = with_composed(|m| {
let inst = m.get_mut(&id)?;
inst.runs += 1;
for (i, s) in inst.slots.iter().enumerate() {
if s.kind == PropKind::State && !inst.bindings.contains_key(&i) {
let meta = s.meta as *const c_void;
if *(this.add(s.offset + state_box_offset(meta)) as *const u64) != 0 {
inst.bindings
.insert(i, state_binding_bytes(this.add(s.offset), meta, s.binding_bytes));
}
}
}
for (i, s) in inst.slots.iter().enumerate() {
if s.kind == PropKind::Focus && !inst.focus.contains_key(&i) {
inst.focus
.insert(i, focus_projection(this.add(s.offset), s.meta as *const c_void));
}
}
let props = Props { this, slots: inst.slots.clone() };
Some((inst.body)(&props))
});
*(out as *mut u64) = view.unwrap_or_else(crate::views::empty_view).into_anyview_word();
}
}
pub struct ComposedBuilder {
specs: Vec<PropSpec>,
}
impl Default for ComposedBuilder {
fn default() -> Self {
Self::new()
}
}
impl ComposedBuilder {
pub fn new() -> Self {
Self { specs: Vec::new() }
}
pub fn state<T: StateValue>(&mut self, initial: T) -> StateProp<T> {
unsafe {
let ft = T::state_field_type();
let meta = abi::resolve_type_by_mangled_name(&ft[..ft.len() - 1]);
assert!(!meta.is_null(), "State<T> metadata");
let mut init = vec![0u8; abi::type_bytes(meta)];
let bytes = initial.encode();
init[..bytes.len()].copy_from_slice(&bytes);
self.specs.push(PropSpec {
field_type: ft,
meta,
binding_bytes: abi::type_bytes(binding_meta::<T>()),
init,
kind: PropKind::State,
});
StateProp { index: self.specs.len() - 1, marker: core::marker::PhantomData }
}
}
pub fn app_storage<T: StoredValue>(&mut self, key: &str, default: T) -> StoredProp<T> {
unsafe {
let ft = T::field_type();
let meta = abi::resolve_type_by_mangled_name(&ft[..ft.len() - 1]);
assert!(!meta.is_null(), "AppStorage<T> metadata");
self.specs.push(PropSpec {
field_type: ft,
meta,
binding_bytes: 0,
init: default.init_storage(key).to_le_bytes().to_vec(),
kind: PropKind::Stored,
});
StoredProp { index: self.specs.len() - 1, marker: core::marker::PhantomData }
}
}
pub fn focus_state(&mut self) -> FocusProp {
unsafe {
let meta = focus_state_meta();
self.specs.push(PropSpec {
field_type: FOCUS_STATE_BOOL,
meta,
binding_bytes: 0,
init: focus_state_init(meta),
kind: PropKind::Focus,
});
FocusProp { index: self.specs.len() - 1 }
}
}
pub fn environment<K: EnvKey>(&mut self) -> EnvProp<K> {
unsafe {
let ft = K::field_type();
let meta = abi::resolve_type_by_mangled_name(&ft[..ft.len() - 1]);
assert!(!meta.is_null(), "Environment<V> metadata");
self.specs.push(PropSpec {
field_type: ft,
meta,
binding_bytes: 0,
init: environment_init(K::key_path(), abi::type_bytes(meta)),
kind: PropKind::Env,
});
EnvProp { index: self.specs.len() - 1, marker: core::marker::PhantomData }
}
}
pub fn build(self, body: impl Fn(&Props) -> ViewHandle + Send + 'static) -> ComposedView {
assert!(!self.specs.is_empty(), "a composed view needs at least one property");
unsafe {
let id = NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let mut fields = vec![FieldSpec {
mangled_type: b"Si\0",
name: b"id\0",
meta: resolve::int_meta(),
}];
for (i, spec) in self.specs.iter().enumerate() {
fields.push(FieldSpec {
mangled_type: spec.field_type,
name: prop_name(i),
meta: spec.meta,
});
}
let (metadata, witness_table) = emit_type(
b"RustComposed\0",
b"6rswift12RustComposedV\0",
&fields,
composed_body_thunk as *const c_void,
);
debug_assert_eq!(field_offset(metadata, 0), 0, "the id must be field 0");
let mut value = vec![0u8; instance_bytes(metadata)];
value[..8].copy_from_slice(&id.to_le_bytes());
let mut slots = Vec::with_capacity(self.specs.len());
for (i, spec) in self.specs.iter().enumerate() {
let off = field_offset(metadata, i + 1);
value[off..off + spec.init.len()].copy_from_slice(&spec.init);
slots.push(PropSlot {
offset: off,
meta: spec.meta as usize,
binding_bytes: spec.binding_bytes,
kind: spec.kind,
});
}
with_composed(|m| {
m.insert(
id,
ComposedInstance {
body: Box::new(body),
slots: slots.clone(),
bindings: std::collections::HashMap::new(),
focus: std::collections::HashMap::new(),
runs: 0,
},
)
});
ComposedView { metadata, witness_table, value, id, slots }
}
}
}
pub struct ComposedView {
pub metadata: *const c_void,
pub witness_table: *const c_void,
value: Vec<u8>,
pub id: i64,
slots: Vec<PropSlot>,
}
impl ComposedView {
pub fn set_state<T: StateValue>(&self, prop: StateProp<T>, value: T) -> bool {
let i = prop.index;
let binding = with_composed(|m| m.get(&self.id).and_then(|c| c.bindings.get(&i).cloned()));
let Some(mut binding) = binding else { return false };
unsafe { binding_set_of::<T>(&mut binding, &value) };
true
}
fn stored_location(&self, i: usize) -> u64 {
let s = self.slots[i];
u64::from_le_bytes(self.value[s.offset..s.offset + 8].try_into().expect("location"))
}
pub fn stored<T: StoredValue>(&self, prop: StoredProp<T>) -> T {
unsafe { app_storage_get::<T>(self.stored_location(prop.index)) }
}
pub fn set_stored<T: StoredValue>(&self, prop: StoredProp<T>, value: T) {
unsafe { app_storage_set::<T>(self.stored_location(prop.index), &value) }
}
pub fn set_focus(&self, prop: FocusProp, value: bool) -> bool {
let i = prop.index;
let proj = with_composed(|m| m.get(&self.id).and_then(|c| c.focus.get(&i).copied()));
let Some(mut proj) = proj else { return false };
unsafe { focus_binding_set(&mut proj, value) };
true
}
pub fn focus(&self, prop: FocusProp) -> bool {
let i = prop.index;
let proj = with_composed(|m| m.get(&self.id).and_then(|c| c.focus.get(&i).copied()));
match proj {
Some(p) => unsafe { focus_binding_get(&p) },
None => false,
}
}
pub fn runs(&self) -> usize {
with_composed(|m| m.get(&self.id).map_or(0, |c| c.runs))
}
pub fn as_view(&self) -> ViewHandle {
unsafe { copy_as_view(&self.value, self.metadata, self.witness_table) }
}
}
impl Drop for StatefulView {
fn drop(&mut self) {
unsafe { destroy_instance(&mut self.value, self.metadata) };
}
}
impl<T: StoredValue> Drop for StoredView<T> {
fn drop(&mut self) {
unsafe { destroy_instance(&mut self.value, self.metadata) };
}
}
impl<K: EnvKey> Drop for EnvironmentView<K> {
fn drop(&mut self) {
unsafe { destroy_instance(&mut self.value, self.metadata) };
}
}
impl Drop for ComposedView {
fn drop(&mut self) {
unsafe { destroy_instance(&mut self.value, self.metadata) };
}
}
const FOCUS_STATE_BOOL: &[u8] = b"7SwiftUI10FocusStateVySbG\0";
unsafe fn focus_state_meta() -> *const c_void {
unsafe {
let m = abi::resolve_type_by_mangled_name(&FOCUS_STATE_BOOL[..FOCUS_STATE_BOOL.len() - 1]);
assert!(!m.is_null(), "FocusState<Bool> metadata");
m
}
}
unsafe fn focus_state_init(meta: *const c_void) -> Vec<u8> {
unsafe {
let mut out = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::focus_state_bool_init(),
in("x8") out.as_mut_ptr(),
in("x20") meta,
lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
out
}
}
unsafe fn focus_get(field: *const u8, meta: *const c_void) -> bool {
let mut out: u8 = 0;
unsafe {
core::arch::asm!("blr {f}", f = in(reg) resolve::focus_state_wrapped_get(),
in("x8") &mut out as *mut u8,
in("x0") meta,
in("x20") field,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
}
out != 0
}
unsafe fn focus_binding_meta() -> *const c_void {
unsafe {
let m = abi::resolve_type_by_mangled_name(b"7SwiftUI10FocusStateV7BindingVySb_G");
assert!(!m.is_null(), "FocusState<Bool>.Binding metadata");
m
}
}
unsafe fn focus_binding_set(projection: &mut [u64; 3], value: bool) {
unsafe {
let mut v = u8::from(value);
core::arch::asm!("blr {f}", f = in(reg) resolve::focus_binding_wrapped_set(),
in("x0") &mut v as *mut u8,
in("x1") focus_binding_meta(),
in("x20") projection.as_mut_ptr(),
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
}
}
unsafe fn focus_binding_get(projection: &[u64; 3]) -> bool {
let mut out: u8 = 0;
unsafe {
core::arch::asm!("blr {f}", f = in(reg) resolve::focus_binding_wrapped_get(),
in("x8") &mut out as *mut u8,
in("x0") focus_binding_meta(),
in("x20") projection.as_ptr(),
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
}
out != 0
}
unsafe fn focus_projection(field: *const u8, meta: *const c_void) -> [u64; 3] {
unsafe {
let mut out = [0u64; 3];
core::arch::asm!("blr {f}", f = in(reg) resolve::focus_state_projected(),
in("x8") out.as_mut_ptr(),
in("x0") meta,
in("x20") field,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
out
}
}
impl<T> Clone for StateProp<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for StateProp<T> {}
impl<T> Clone for StoredProp<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for StoredProp<T> {}
impl<K> Clone for EnvProp<K> {
fn clone(&self) -> Self {
*self
}
}
impl<K> Copy for EnvProp<K> {}