1use super::{wrap_owned, ViewHandle};
41use crate::abi;
42use crate::conformance::{synthesize, Conformance, Protocol, Witness};
43use crate::resolve;
44use core::ffi::c_void;
45use std::sync::{Mutex, OnceLock};
46
47pub trait Representing: Send + Sync + 'static {
49 fn make(&self) -> *mut c_void;
51 fn update(&self, view: *mut c_void);
53}
54
55pub struct NSViewRep {
57 index: i64,
58}
59
60struct Pair<M, U> {
62 make: M,
63 update: U,
64}
65
66impl<M, U> Representing for Pair<M, U>
67where
68 M: Fn() -> *mut c_void + Send + Sync + 'static,
69 U: Fn(*mut c_void) + Send + Sync + 'static,
70{
71 fn make(&self) -> *mut c_void {
72 (self.make)()
73 }
74 fn update(&self, view: *mut c_void) {
75 (self.update)(view)
76 }
77}
78
79fn registry() -> &'static Mutex<Vec<&'static dyn Representing>> {
80 static R: OnceLock<Mutex<Vec<&'static dyn Representing>>> = OnceLock::new();
81 R.get_or_init(|| Mutex::new(Vec::new()))
82}
83
84pub fn ns_view_fn(
88 make: impl Fn() -> *mut c_void + Send + Sync + 'static,
89 update: impl Fn(*mut c_void) + Send + Sync + 'static,
90) -> NSViewRep {
91 let leaked: &'static dyn Representing = Box::leak(Box::new(Pair { make, update }));
92 let mut r = registry().lock().expect("representable registry");
93 r.push(leaked);
94 NSViewRep { index: r.len() as i64 - 1 }
95}
96
97core::arch::global_asm!(
102 ".p2align 2",
103 ".globl _rswift_nsvr_make",
104 "_rswift_nsvr_make:",
105 "mov x0, x20",
106 "b _rswift_nsvr_make_body",
107 ".p2align 2",
110 ".globl _rswift_nsvr_update",
111 "_rswift_nsvr_update:",
112 "mov x1, x20",
113 "b _rswift_nsvr_update_body",
114 ".p2align 2",
117 ".globl _rswift_nsvr_coordinator",
118 "_rswift_nsvr_coordinator:",
119 "ret",
120);
121
122unsafe extern "C" {
123 #[link_name = "rswift_nsvr_make"]
124 fn nsvr_make_thunk();
125 #[link_name = "rswift_nsvr_update"]
126 fn nsvr_update_thunk();
127 #[link_name = "rswift_nsvr_coordinator"]
128 fn nsvr_coordinator_thunk();
129}
130
131fn representing(index: usize) -> &'static dyn Representing {
133 let r = registry().lock().expect("representable registry");
134 r.get(index).copied().expect("representable index out of range")
135}
136
137#[unsafe(no_mangle)]
142unsafe extern "C" fn rswift_nsvr_make_body(this: *const u8) -> *mut c_void {
143 let index = unsafe { *(this as *const i64) } as usize;
144 let view = representing(index).make();
145 assert!(!view.is_null(), "the representable's `make` returned nothing");
146 view
147}
148
149#[unsafe(no_mangle)]
154unsafe extern "C" fn rswift_nsvr_update_body(view: *mut c_void, this: *const u8) {
155 let index = unsafe { *(this as *const i64) } as usize;
156 representing(index).update(view);
157}
158
159#[unsafe(no_mangle)]
164static mut RSWIFT_NSVR_WT: *const c_void = core::ptr::null();
165
166crate::thunk::runtime_thunk! {
167 fn nsvr_make_view_thunk = "rswift_nsvr_make_view" via RSWIFT_NSVR_MAKE_VIEW {
169 prologue: [
170 "adrp x3, _RSWIFT_NSVR_WT@PAGE",
171 "add x3, x3, _RSWIFT_NSVR_WT@PAGEOFF",
172 "ldr x3, [x3]",
173 ],
174 branch: "br",
175 epilogue: [],
176 }
177 fn nsvr_make_view_list_thunk = "rswift_nsvr_make_list" via RSWIFT_NSVR_MAKE_LIST {
179 prologue: [
180 "adrp x3, _RSWIFT_NSVR_WT@PAGE",
181 "add x3, x3, _RSWIFT_NSVR_WT@PAGEOFF",
182 "ldr x3, [x3]",
183 ],
184 branch: "br",
185 epilogue: [],
186 }
187 fn nsvr_body_thunk = "rswift_nsvr_body" via RSWIFT_NSVR_BODY {
189 prologue: [
190 "adrp x1, _RSWIFT_NSVR_WT@PAGE",
191 "add x1, x1, _RSWIFT_NSVR_WT@PAGEOFF",
192 "ldr x1, [x1]",
193 ],
194 branch: "br",
195 epilogue: [],
196 }
197}
198
199fn carrier() -> (*const c_void, *const c_void) {
201 static C: OnceLock<(usize, usize)> = OnceLock::new();
202 let (d, m) = *C.get_or_init(|| unsafe {
203 let (d, m) =
204 crate::state::emit_int_carrier(b"RustNSViewRep\0", b"6rswift13RustNSViewRepV\0");
205 (d as usize, m as usize)
206 });
207 (d as *const c_void, m as *const c_void)
208}
209
210fn representable_conformance() -> &'static Conformance {
211 static C: OnceLock<Conformance> = OnceLock::new();
212 C.get_or_init(|| unsafe {
213 let protocol = Protocol::from_symbol(c"$s7SwiftUI19NSViewRepresentableMp");
214 assert_eq!(
215 protocol.requirement_count(),
216 15,
217 "NSViewRepresentable changed shape — it used to be View, two \
218 associated types, and twelve methods of which three must be supplied",
219 );
220
221 let class = crate::objc::class(c"OBJC_CLASS_$_NSView");
228 assert!(!class.is_null(), "NSView class");
229 let nsview =
230 swift_runtime_sys::MetadataIntrospection::swift_getObjCClassMetadata(class);
231 assert!(!nsview.is_null(), "NSView metadata");
232
233 let view_wt = abi::get_view_wt(carrier().1);
234 assert!(!view_wt.is_null(), "the carrier type has no View conformance");
235
236 let conformance = synthesize(
237 &protocol,
238 carrier().0,
239 &[
240 Witness::Metadata(view_wt),
241 Witness::Metadata(nsview),
242 Witness::Default,
251 Witness::Function(nsvr_make_thunk as *const c_void),
252 Witness::Function(nsvr_update_thunk as *const c_void),
253 Witness::Default,
254 Witness::Default,
255 Witness::Function(nsvr_coordinator_thunk as *const c_void),
256 Witness::Default,
257 Witness::Default,
258 Witness::Default,
259 Witness::Default,
260 Witness::Default,
261 Witness::Default,
262 Witness::Default,
263 ],
264 );
265 RSWIFT_NSVR_WT = conformance.witness_table();
266
267 RSWIFT_NSVR_MAKE_VIEW = resolve::nsvr_make_view();
271 RSWIFT_NSVR_MAKE_LIST = resolve::nsvr_make_view_list();
272 RSWIFT_NSVR_BODY = resolve::nsvr_body();
273 let words = view_wt as *mut *const c_void;
274 *words.add(1) = resolve::never_view_wt();
275 *words.add(2) = resolve::never_meta();
276 *words.add(3) = nsvr_make_view_thunk as *const c_void;
277 *words.add(4) = nsvr_make_view_list_thunk as *const c_void;
278 *words.add(6) = nsvr_body_thunk as *const c_void;
279
280 conformance
281 })
282}
283
284pub fn ns_view_representable(rep: &NSViewRep) -> ViewHandle {
286 unsafe {
287 let _ = representable_conformance();
288 let meta = carrier().1;
289 let wt = abi::get_view_wt(meta);
290 assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
291 let mut bytes = vec![0u8; abi::type_bytes(meta)];
292 bytes[..8].copy_from_slice(&rep.index.to_le_bytes());
293 wrap_owned(bytes, meta, wt)
294 }
295}