use core::ffi::c_void;
unsafe fn relative(at: *const i32) -> *const u8 {
unsafe {
if *at == 0 {
core::ptr::null()
} else {
(at as *const u8).offset(*at as isize)
}
}
}
pub struct Protocol {
descriptor: *const u32,
requirements: *const u32,
count: usize,
}
impl Protocol {
pub unsafe fn from_symbol(symbol: &'static core::ffi::CStr) -> Self {
unsafe {
let p = crate::resolve::sym(symbol) as *const u32;
assert!(!p.is_null(), "protocol descriptor not found: {symbol:?}");
let signature = *p.add(3) as usize;
let count = *p.add(4) as usize;
let requirements = (p.add(6) as *const u8).add(12 * signature) as *const u32;
Self { descriptor: p, requirements, count }
}
}
pub fn requirement_count(&self) -> usize {
self.count
}
pub fn describe(&self) {
unsafe {
for i in 0..self.count {
let flags = *self.requirements.add(2 * i);
let kind = flags & 0x0f;
let name = match kind {
0 => "base protocol", 1 => "method", 2 => "init", 3 => "getter",
4 => "setter", 5 => "read coroutine", 6 => "modify coroutine",
7 => "associated type", 8 => "associated conformance",
_ => "?",
};
let dflt = self.default_witness(i);
println!(
" [{i}] {name}{} {}",
if flags & 0x10 != 0 { " (instance)" } else { "" },
if dflt.is_null() { "— must supply" } else { "— has a default" },
);
}
}
}
pub fn has_default(&self, i: usize) -> bool {
!unsafe { self.default_witness(i) }.is_null()
}
unsafe fn default_witness(&self, i: usize) -> *const u8 {
unsafe { relative(self.requirements.add(2 * i + 1) as *const i32) }
}
}
#[derive(Clone, Copy)]
pub enum Witness {
Default,
Function(*const c_void),
Metadata(*const c_void),
}
#[repr(C)]
struct ConformanceDescriptor {
protocol_ref: i32,
type_ref: i32,
witness_table_pattern: i32,
flags: u32,
protocol_slot: *const c_void,
type_slot: *const c_void,
}
#[derive(Clone, Copy)]
pub enum Conformer {
TypeDescriptor(*const c_void),
ObjCClass(*mut c_void),
}
impl Conformer {
fn slot_value(self) -> *const c_void {
match self {
Self::TypeDescriptor(d) => d,
Self::ObjCClass(c) => c as *const c_void,
}
}
fn flags(self) -> u32 {
match self {
Self::TypeDescriptor(_) => 1 << 3,
Self::ObjCClass(_) => 3 << 3,
}
}
}
#[repr(C)]
struct ConformanceRecord {
descriptor: i32,
}
const RECORD_WORD: usize = 0;
const DESCRIPTOR_WORD: usize = 1;
const PROTOCOL_SLOT_WORD: usize = 3;
const TYPE_SLOT_WORD: usize = 4;
const TABLE_WORD: usize = 5;
unsafe fn offset(to: *const u8, at: *const u8) -> i32 {
let delta = to as isize - at as isize;
i32::try_from(delta).expect("relative pointer out of range")
}
pub struct Conformance {
table: *const c_void,
descriptor: *const c_void,
}
impl Conformance {
pub fn descriptor(&self) -> *const c_void {
self.descriptor
}
pub fn witness_table(&self) -> *const c_void {
self.table
}
pub unsafe fn patch(&self, slot: usize, witness: *const c_void) {
unsafe {
let words = self.table as *mut *const c_void;
*words.add(slot + 1) = witness;
}
}
}
unsafe fn register(record: *const ConformanceRecord) {
unsafe extern "C" {
fn swift_registerProtocolConformances(begin: *const c_void, end: *const c_void);
}
unsafe {
let begin = record as *const c_void;
let end = (record as *const u8).add(core::mem::size_of::<ConformanceRecord>());
swift_registerProtocolConformances(begin, end as *const c_void);
}
}
pub unsafe fn synthesize(
protocol: &Protocol,
type_descriptor: *const c_void,
witnesses: &[Witness],
) -> Conformance {
unsafe { synthesize_for(protocol, Conformer::TypeDescriptor(type_descriptor), witnesses) }
}
pub unsafe fn synthesize_for(
protocol: &Protocol,
conformer: Conformer,
witnesses: &[Witness],
) -> Conformance {
unsafe {
assert_eq!(
witnesses.len(),
protocol.count,
"a witness table needs one entry per requirement",
);
let words = TABLE_WORD + 1 + protocol.count;
let block = Box::leak(vec![0u64; words].into_boxed_slice());
let base = block.as_mut_ptr();
let table = base.add(TABLE_WORD);
for (i, w) in witnesses.iter().enumerate() {
let word = match *w {
Witness::Default => {
let d = protocol.default_witness(i);
assert!(!d.is_null(), "requirement {i} has no default implementation");
d as u64
}
Witness::Function(f) => f as u64,
Witness::Metadata(m) => m as u64,
};
*table.add(1 + i) = word;
}
let descriptor = base.add(DESCRIPTOR_WORD) as *mut ConformanceDescriptor;
*base.add(PROTOCOL_SLOT_WORD) = protocol.descriptor as u64;
*base.add(TYPE_SLOT_WORD) = conformer.slot_value() as u64;
*table = descriptor as u64;
let d = descriptor as *const u8;
let protocol_slot = base.add(PROTOCOL_SLOT_WORD) as *const u8;
let type_slot = base.add(TYPE_SLOT_WORD) as *const u8;
(*descriptor).protocol_ref = offset(protocol_slot, d) | 1;
(*descriptor).type_ref = offset(type_slot, d.add(4));
(*descriptor).witness_table_pattern = offset(table as *const u8, d.add(8));
(*descriptor).flags = conformer.flags();
let record = base.add(RECORD_WORD) as *mut ConformanceRecord;
(*record).descriptor = offset(d, record as *const u8);
let conformance = Conformance {
table: table as *const c_void,
descriptor: descriptor as *const c_void,
};
register(record);
conformance
}
}
core::arch::global_asm!(
".p2align 2",
".globl _rswift_pref_default_value",
"_rswift_pref_default_value:",
"mov x0, x8",
"b _rswift_pref_default_value_impl",
);
#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_pref_default_value_impl(out: *mut i64) {
unsafe { *out = 0 };
}
#[unsafe(no_mangle)]
unsafe extern "C" fn rswift_pref_reduce(
value: *mut i64,
next: *const c_void,
context: *mut c_void,
) {
unsafe {
let mut produced: i64 = 0;
core::arch::asm!("blr {f}", f = in(reg) next,
in("x8") &mut produced as *mut i64, in("x20") context,
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"),
);
*value = produced;
}
}
unsafe extern "C" {
#[link_name = "rswift_pref_default_value"]
fn pref_default_value();
}
pub fn preference_key() -> &'static Conformance {
use std::sync::OnceLock;
static CACHE: OnceLock<Conformance> = OnceLock::new();
CACHE.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$s7SwiftUI13PreferenceKeyMp");
assert_eq!(protocol.requirement_count(), 5, "PreferenceKey changed shape");
synthesize(
&protocol,
crate::resolve::spacer_descriptor(),
&[
Witness::Metadata(crate::resolve::int_meta()),
Witness::Function(pref_default_value as *const c_void),
Witness::Function(rswift_pref_reduce as *const c_void),
Witness::Default,
Witness::Default,
],
)
})
}
unsafe impl Send for Conformance {}
unsafe impl Sync for Conformance {}
pub fn layout_value_key() -> &'static Conformance {
use std::sync::OnceLock;
static CACHE: OnceLock<Conformance> = OnceLock::new();
CACHE.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$s7SwiftUI14LayoutValueKeyMp");
assert_eq!(protocol.requirement_count(), 2, "LayoutValueKey changed shape");
synthesize(
&protocol,
crate::resolve::divider_descriptor(),
&[
Witness::Metadata(crate::resolve::int_meta()),
Witness::Function(pref_default_value as *const c_void),
],
)
})
}
core::arch::global_asm!(
".p2align 2",
".globl _rswift_stub_out_witness",
"_rswift_stub_out_witness:",
"str xzr, [x8]",
"ret",
);
unsafe extern "C" {
#[link_name = "rswift_stub_out_witness"]
fn stub_out_witness();
}
pub fn presentation_sizing() -> &'static Conformance {
use std::sync::OnceLock;
static CACHE: OnceLock<Conformance> = OnceLock::new();
CACHE.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$s7SwiftUI18PresentationSizingMp");
let witnesses = vec![Witness::Default; protocol.requirement_count()];
synthesize(&protocol, crate::resolve::empty_view_descriptor(), &witnesses)
})
}
pub fn preview_context() -> &'static Conformance {
use std::sync::OnceLock;
static CACHE: OnceLock<Conformance> = OnceLock::new();
CACHE.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$s7SwiftUI14PreviewContextMp");
synthesize(
&protocol,
crate::resolve::empty_view_descriptor(),
&[Witness::Function(stub_out_witness as *const c_void)],
)
})
}
unsafe extern "C" fn text_renderer_draw(_layout: *const c_void, ctx: *mut c_void) {
unsafe {
let t = crate::views::text_value("Fx");
let storage = *(ctx as *const *const c_void);
core::arch::asm!("blr {f}", f = in(reg) crate::resolve::graphics_draw_text(),
in("x0") t[0], in("x1") t[1], in("x2") t[2], in("x3") t[3],
in("x4") storage,
in("d0") 0.0f64, in("d1") 0.0f64, in("d2") 0.0f64, in("d3") 0.0f64,
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"),
);
}
}
pub fn text_renderer() -> &'static Conformance {
use std::sync::OnceLock;
static CACHE: OnceLock<Conformance> = OnceLock::new();
CACHE.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$s7SwiftUI12TextRendererMp");
assert_eq!(protocol.requirement_count(), 4, "TextRenderer changed shape");
synthesize(
&protocol,
crate::resolve::edge_insets_descriptor(),
&[
Witness::Metadata(crate::resolve::edge_insets_animatable_wt()),
Witness::Function(text_renderer_draw as *const c_void),
Witness::Default,
Witness::Default,
],
)
})
}
core::arch::global_asm!(
".p2align 2",
".globl _rswift_identifiable_id",
"_rswift_identifiable_id:",
"mov x0, #0",
"str x0, [x8]",
"ret",
);
unsafe extern "C" {
#[link_name = "rswift_identifiable_id"]
fn identifiable_id();
}
pub fn identifiable() -> &'static Conformance {
use std::sync::OnceLock;
static CACHE: OnceLock<Conformance> = OnceLock::new();
CACHE.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$ss12IdentifiableMp");
assert_eq!(protocol.requirement_count(), 3, "Identifiable changed shape");
synthesize(
&protocol,
crate::resolve::edge_insets_descriptor(),
&[
Witness::Metadata(crate::resolve::int_hashable_wt()),
Witness::Metadata(crate::resolve::int_meta()),
Witness::Function(identifiable_id as *const c_void),
],
)
})
}
pub fn string_identifiable() -> &'static Conformance {
use std::sync::OnceLock;
static CACHE: OnceLock<Conformance> = OnceLock::new();
CACHE.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$ss12IdentifiableMp");
synthesize(
&protocol,
crate::resolve::string_descriptor(),
&[
Witness::Metadata(crate::resolve::int_hashable_wt()),
Witness::Metadata(crate::resolve::int_meta()),
Witness::Function(identifiable_id as *const c_void),
],
)
})
}
pub fn observable() -> &'static Conformance {
use std::sync::OnceLock;
static CACHE: OnceLock<Conformance> = OnceLock::new();
CACHE.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$s11Observation10ObservableMp");
assert_eq!(protocol.requirement_count(), 0, "Observable changed shape");
synthesize(&protocol, crate::resolve::json_decoder_descriptor(), &[])
})
}
crate::thunk::runtime_thunk! {
fn object_will_change = "rswift_object_will_change" via RSWIFT_OBJECT_WILL_CHANGE {
prologue: [
"stp x29, x30, [sp, #-32]!",
"mov x29, sp",
"str x8, [sp, #16]",
],
branch: "blr",
epilogue: [
"ldr x8, [sp, #16]",
"str x0, [x8]",
"ldp x29, x30, [sp], #32",
"ret",
],
}
}
pub fn observable_object() -> &'static Conformance {
use std::sync::OnceLock;
static CACHE: OnceLock<Conformance> = OnceLock::new();
CACHE.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$s7Combine16ObservableObjectMp");
assert_eq!(protocol.requirement_count(), 3, "ObservableObject changed shape");
RSWIFT_OBJECT_WILL_CHANGE = crate::resolve::observable_object_will_change();
synthesize(
&protocol,
crate::resolve::json_decoder_descriptor(),
&[
Witness::Default,
Witness::Default,
Witness::Function(object_will_change as *const c_void),
],
)
})
}
unsafe extern "C" fn describe_axis_value(_block: *mut c_void, value: f64) -> *mut c_void {
unsafe { crate::objc::nsstring(&format!("{value}")) }
}
unsafe extern "C" fn make_chart_descriptor() -> *mut c_void {
use crate::objc;
unsafe {
static DESCRIPTOR: objc::BlockDescriptor = objc::BlockDescriptor::empty();
let block = objc::GlobalBlock::new(describe_axis_value as *const c_void, &DESCRIPTOR);
let block = &block as *const objc::GlobalBlock as *mut c_void;
type AxisInit = unsafe extern "C" fn(
*mut c_void, *const c_void, *mut c_void, f64, f64, *mut c_void, *mut c_void,
) -> *mut c_void;
let axis_init: AxisInit = core::mem::transmute(objc::msg_send_addr());
let axis = axis_init(
objc::send(objc::class(c"OBJC_CLASS_$_AXNumericDataAxisDescriptor"), objc::sel(c"alloc")),
objc::sel(c"initWithTitle:lowerBound:upperBound:gridlinePositions:valueDescriptionProvider:"),
objc::nsstring("Value"),
0.0,
1.0,
core::ptr::null_mut(),
block,
);
type ChartInit = unsafe extern "C" fn(
*mut c_void, *const c_void, *mut c_void, *mut c_void, *mut c_void, *mut c_void,
*mut c_void,
) -> *mut c_void;
let chart_init: ChartInit = core::mem::transmute(objc::msg_send_addr());
let empty = objc::send(objc::class(c"OBJC_CLASS_$_NSArray"), objc::sel(c"array"));
chart_init(
objc::send(objc::class(c"OBJC_CLASS_$_AXChartDescriptor"), objc::sel(c"alloc")),
objc::sel(c"initWithTitle:summary:xAxisDescriptor:yAxisDescriptor:series:"),
objc::nsstring("Chart"),
core::ptr::null_mut(),
axis,
core::ptr::null_mut(),
empty,
)
}
}
pub fn chart_descriptor_representable() -> &'static Conformance {
use std::sync::OnceLock;
static CACHE: OnceLock<Conformance> = OnceLock::new();
CACHE.get_or_init(|| unsafe {
let protocol = Protocol::from_symbol(c"$s7SwiftUI30AXChartDescriptorRepresentableMp");
assert_eq!(protocol.requirement_count(), 2, "AXChartDescriptorRepresentable changed shape");
synthesize(
&protocol,
crate::resolve::edge_insets_descriptor(),
&[Witness::Function(make_chart_descriptor as *const c_void), Witness::Default],
)
})
}