use crate::abi;
#[cfg(target_os = "macos")]
use crate::mangle::{self, swift_type};
use crate::resolve;
use crate::view::IntoView;
use crate::views::{self, ViewHandle};
use core::ffi::c_void;
struct Value {
bytes: Vec<u8>,
meta: *const c_void,
wt: *const c_void,
}
impl Value {
unsafe fn wrap(bytes: Vec<u8>, meta: *const c_void, wt: *const c_void) -> Self {
debug_assert!(!meta.is_null() && !wt.is_null(), "a value needs a type and a witness");
Self { bytes, meta, wt }
}
fn ptr(&self) -> *const c_void {
self.bytes.as_ptr() as *const c_void
}
unsafe fn move_into(self, out: *mut u8) {
let me = core::mem::ManuallyDrop::new(self);
unsafe { core::ptr::copy_nonoverlapping(me.bytes.as_ptr(), out, me.bytes.len()) };
}
}
impl Drop for Value {
fn drop(&mut self) {
unsafe {
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(self.meta);
if vwt.size > 0 {
(vwt.destroy)(self.bytes.as_mut_ptr() as *mut c_void, self.meta);
}
}
}
}
pub struct SceneHandle(Value);
pub struct CommandsHandle(Value);
impl SceneHandle {
pub fn type_name(&self) -> String {
crate::diag::type_name(self.0.meta)
}
pub(crate) unsafe fn move_into(self, out: *mut u8) {
unsafe { self.0.move_into(out) }
}
pub(crate) fn meta(&self) -> *const c_void {
self.0.meta
}
pub(crate) fn wt(&self) -> *const c_void {
self.0.wt
}
}
impl CommandsHandle {
pub fn type_name(&self) -> String {
crate::diag::type_name(self.0.meta)
}
}
struct Content<'a> {
builder: &'a dyn Fn() -> ViewHandle,
}
unsafe extern "C" fn emit_content(out: *mut u8, payload: *mut c_void) {
let p = unsafe { &*(payload as *const Content<'_>) };
let view = (p.builder)();
unsafe { *(out as *mut u64) = view.into_anyview_word() };
}
unsafe fn conformance(
meta: *const c_void,
proto: *const c_void,
name: &str,
) -> *const c_void {
let wt = unsafe { abi::get_view_wt_for_proto(meta, proto) };
assert!(!wt.is_null(), "{} !: {name}", crate::diag::type_name(meta));
wt
}
unsafe fn over_anyview(ma: *const c_void) -> *const c_void {
let meta = unsafe {
abi::generic_metadata2(ma, resolve::anyview_meta(), resolve::anyview_wt())
};
assert!(!meta.is_null(), "metadata instantiation failed");
meta
}
unsafe fn build_from_content(
init: *const c_void,
meta: *const c_void,
builder: &dyn Fn() -> ViewHandle,
) -> Vec<u8> {
unsafe {
let payload = Content { builder };
let mut ctx = abi::ClosureCtx::new(
emit_content,
&payload as *const Content<'_> as *mut c_void,
);
let mut bytes = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) init,
in("x0") abi::swift_closure_thunk as *const c_void,
in("x1") &mut ctx as *mut abi::ClosureCtx,
in("x2") resolve::anyview_meta(),
in("x3") resolve::anyview_wt(),
in("x8") bytes.as_mut_ptr(),
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"),
);
bytes
}
}
#[cfg(target_os = "macos")]
unsafe fn build_from_key_string_content(
init: *const c_void,
meta: *const c_void,
key: &str,
string: &str,
builder: &dyn Fn() -> ViewHandle,
) -> Vec<u8> {
unsafe {
let (k0, k1, k2, k3) = views::lsk_words(key);
let s = abi::swift_string(string);
let s0 = u64::from_le_bytes(s[..8].try_into().expect("string lo"));
let s1 = u64::from_le_bytes(s[8..].try_into().expect("string hi"));
let payload = Content { builder };
let mut ctx = abi::ClosureCtx::new(
emit_content,
&payload as *const Content<'_> as *mut c_void,
);
let mut bytes = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!(
"sub sp, sp, #16",
"stp {cm}, {cw}, [sp]",
"blr {f}",
"add sp, sp, #16",
f = in(reg) init,
cm = in(reg) resolve::anyview_meta(),
cw = in(reg) resolve::anyview_wt(),
in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
in("x4") s0, in("x5") s1,
in("x6") abi::swift_closure_thunk as *const c_void,
in("x7") &mut ctx as *mut abi::ClosureCtx,
in("x8") bytes.as_mut_ptr(),
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"),
);
bytes
}
}
pub fn window_group<V: IntoView>(content: impl Fn() -> V) -> SceneHandle {
let content = move || content().into_view().into_handle();
unsafe {
let meta = over_anyview(resolve::windowgroup_ma());
let bytes = build_from_content(resolve::windowgroup_content_init(), meta, &content);
SceneHandle(Value::wrap(bytes, meta, conformance(meta, resolve::scene_proto(), "Scene")))
}
}
#[cfg(target_os = "macos")]
pub fn settings<V: IntoView>(content: impl Fn() -> V) -> SceneHandle {
let content = move || content().into_view().into_handle();
unsafe {
let meta = over_anyview(resolve::settings_ma());
let bytes = build_from_content(resolve::settings_content_init(), meta, &content);
SceneHandle(Value::wrap(bytes, meta, conformance(meta, resolve::scene_proto(), "Scene")))
}
}
#[cfg(target_os = "macos")]
pub fn window<V: IntoView>(title: &str, id: &str, content: impl Fn() -> V) -> SceneHandle {
let content = move || content().into_view().into_handle();
unsafe {
let meta = over_anyview(resolve::window_ma());
let bytes =
build_from_key_string_content(resolve::window_init(), meta, title, id, &content);
SceneHandle(Value::wrap(bytes, meta, conformance(meta, resolve::scene_proto(), "Scene")))
}
}
#[cfg(target_os = "macos")]
pub fn menu_bar_extra<V: IntoView>(
title: &str,
system_image: &str,
content: impl Fn() -> V,
) -> SceneHandle {
let content = move || content().into_view().into_handle();
unsafe {
let label = mangle::bound(
&swift_type!(SwiftUI.Label),
&[&swift_type!(SwiftUI.Text), &swift_type!(SwiftUI.Image)],
);
let named = mangle::bound(
&swift_type!(SwiftUI.MenuBarExtra),
&[&label, &swift_type!(SwiftUI.AnyView)],
);
let meta = abi::resolve_type_by_mangled_name(named.as_bytes());
assert!(!meta.is_null(), "{}", mangle::demangle(&named));
let bytes = build_from_key_string_content(
resolve::menu_bar_extra_init(),
meta,
title,
system_image,
&content,
);
SceneHandle(Value::wrap(bytes, meta, conformance(meta, resolve::scene_proto(), "Scene")))
}
}
pub fn group(scenes: Vec<SceneHandle>) -> SceneHandle {
assert!(!scenes.is_empty(), "a scene group needs at least one scene");
if scenes.len() == 1 {
return scenes.into_iter().next().expect("one scene");
}
unsafe {
SceneHandle(tuple_of(
scenes.into_iter().map(|s| s.0).collect(),
resolve::tuple_scene_ma(),
resolve::tuple_scene_init(),
resolve::scene_proto(),
"Scene",
))
}
}
unsafe fn tuple_of(
parts: Vec<Value>,
ma: *const c_void,
init: *const c_void,
proto: *const c_void,
name: &str,
) -> Value {
unsafe {
let elements: Vec<*const c_void> = parts.iter().map(|p| p.meta).collect();
let tuple_meta = abi::tuple_metadata(&elements);
assert!(!tuple_meta.is_null(), "tuple metadata instantiation failed");
let offsets = abi::tuple_element_offsets(tuple_meta, parts.len());
let mut tuple = vec![0u8; abi::type_bytes(tuple_meta)];
for (part, &offset) in parts.iter().zip(&offsets) {
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(part.meta);
if vwt.size > 0 {
(vwt.initialize_with_copy)(
tuple.as_mut_ptr().add(offset) as *mut c_void,
part.ptr() as *mut c_void,
part.meta,
);
}
}
let meta = abi::generic_metadata1(ma, tuple_meta);
assert!(!meta.is_null(), "tuple wrapper metadata instantiation failed");
let mut bytes = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) init,
in("x0") tuple.as_mut_ptr(),
in("x1") tuple_meta,
in("x8") bytes.as_mut_ptr(),
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"),
);
drop(parts);
Value::wrap(bytes, meta, conformance(meta, proto, name))
}
}
pub fn command_menu<V: IntoView>(title: &str, content: impl Fn() -> V) -> CommandsHandle {
let content = move || content().into_view().into_handle();
unsafe {
let meta = over_anyview(resolve::command_menu_ma());
let builder: &dyn Fn() -> ViewHandle = &content;
let (k0, k1, k2, k3) = views::lsk_words(title);
let payload = Content { builder };
let mut ctx = abi::ClosureCtx::new(
emit_content,
&payload as *const Content<'_> as *mut c_void,
);
let mut bytes = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::command_menu_init(),
in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
in("x4") abi::swift_closure_thunk as *const c_void,
in("x5") &mut ctx as *mut abi::ClosureCtx,
in("x6") resolve::anyview_meta(),
in("x7") resolve::anyview_wt(),
in("x8") bytes.as_mut_ptr(),
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"),
);
CommandsHandle(Value::wrap(
bytes,
meta,
conformance(meta, resolve::commands_proto(), "Commands"),
))
}
}
swift_statics! {
pub enum CommandPlacement {
AppInfo = c"$s7SwiftUI21CommandGroupPlacementV7appInfoACvgZ",
AppSettings = c"$s7SwiftUI21CommandGroupPlacementV11appSettingsACvgZ",
AppShortcuts = c"$s7SwiftUI21CommandGroupPlacementV12appShortcutsACvgZ",
AppSpecificSettings = c"$s7SwiftUI21CommandGroupPlacementV11appSettingsACvgZ",
AppTermination = c"$s7SwiftUI21CommandGroupPlacementV14appTerminationACvgZ",
AppVisibility = c"$s7SwiftUI21CommandGroupPlacementV13appVisibilityACvgZ",
Help = c"$s7SwiftUI21CommandGroupPlacementV4helpACvgZ",
ImportExport = c"$s7SwiftUI21CommandGroupPlacementV12importExportACvgZ",
NewItem = c"$s7SwiftUI21CommandGroupPlacementV7newItemACvgZ",
Pasteboard = c"$s7SwiftUI21CommandGroupPlacementV10pasteboardACvgZ",
PrintItem = c"$s7SwiftUI21CommandGroupPlacementV9printItemACvgZ",
SaveItem = c"$s7SwiftUI21CommandGroupPlacementV8saveItemACvgZ",
Sidebar = c"$s7SwiftUI21CommandGroupPlacementV7sidebarACvgZ",
SingleWindowList = c"$s7SwiftUI21CommandGroupPlacementV16singleWindowListACvgZ",
SystemServices = c"$s7SwiftUI21CommandGroupPlacementV14systemServicesACvgZ",
TextEditing = c"$s7SwiftUI21CommandGroupPlacementV11textEditingACvgZ",
TextFormatting = c"$s7SwiftUI21CommandGroupPlacementV14textFormattingACvgZ",
Toolbar = c"$s7SwiftUI21CommandGroupPlacementV7toolbarACvgZ",
UndoRedo = c"$s7SwiftUI21CommandGroupPlacementV8undoRedoACvgZ",
WindowArrangement = c"$s7SwiftUI21CommandGroupPlacementV17windowArrangementACvgZ",
WindowList = c"$s7SwiftUI21CommandGroupPlacementV10windowListACvgZ",
WindowSize = c"$s7SwiftUI21CommandGroupPlacementV10windowSizeACvgZ",
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Relative {
Replacing,
After,
Before,
}
pub fn command_group<V: IntoView>(
placement: CommandPlacement,
where_: Relative,
content: impl Fn() -> V,
) -> CommandsHandle {
let content = move || content().into_view().into_handle();
unsafe {
let meta = over_anyview(resolve::command_group_ma());
let placement_meta = abi::metadata0(resolve::command_group_placement_ma());
assert!(!placement_meta.is_null(), "CommandGroupPlacement metadata");
let mut place =
views::static_getter_indirect(placement.resolved(), placement_meta, 0);
let init = match where_ {
Relative::Replacing => resolve::command_group_replacing_init(),
Relative::After => resolve::command_group_after_init(),
Relative::Before => resolve::command_group_before_init(),
};
let builder: &dyn Fn() -> ViewHandle = &content;
let payload = Content { builder };
let mut ctx = abi::ClosureCtx::new(
emit_content,
&payload as *const Content<'_> as *mut c_void,
);
let mut bytes = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) init,
in("x0") place.as_mut_ptr(),
in("x1") abi::swift_closure_thunk as *const c_void,
in("x2") &mut ctx as *mut abi::ClosureCtx,
in("x3") resolve::anyview_meta(),
in("x4") resolve::anyview_wt(),
in("x8") bytes.as_mut_ptr(),
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"),
);
CommandsHandle(Value::wrap(
bytes,
meta,
conformance(meta, resolve::commands_proto(), "Commands"),
))
}
}
pub fn commands(scene: SceneHandle, menus: Vec<CommandsHandle>) -> SceneHandle {
assert!(!menus.is_empty(), "`commands` needs at least one menu");
unsafe {
let content = if menus.len() == 1 {
menus.into_iter().next().expect("one menu").0
} else {
tuple_of(
menus.into_iter().map(|m| m.0).collect(),
resolve::tuple_commands_ma(),
resolve::tuple_commands_init(),
resolve::commands_proto(),
"Commands",
)
};
let payload = EmitValue { value: content.ptr(), meta: content.meta };
let mut ctx = abi::ClosureCtx::new(
emit_value,
&payload as *const EmitValue as *mut c_void,
);
let subst = [scene.meta(), content.meta, scene.wt(), content.wt];
let (meta, wt, size) =
views::opaque_result_type(resolve::scene_commands_oret(), &subst);
let mut bytes = vec![0u8; size];
let this = scene.0.ptr();
core::arch::asm!("blr {f}", f = in(reg) resolve::scene_commands_fn(),
in("x0") abi::swift_closure_thunk as *const c_void,
in("x1") &mut ctx as *mut abi::ClosureCtx,
in("x2") scene.meta(),
in("x3") content.meta,
in("x4") scene.wt(),
in("x5") content.wt,
in("x8") bytes.as_mut_ptr(),
in("x20") this,
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"),
);
SceneHandle(Value::wrap(bytes, meta, wt))
}
}
#[repr(C)]
struct EmitValue {
value: *const c_void,
meta: *const c_void,
}
unsafe extern "C" fn emit_value(out: *mut u8, payload: *mut c_void) {
let p = unsafe { &*(payload as *const EmitValue) };
unsafe {
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(p.meta);
if vwt.size > 0 {
(vwt.initialize_with_copy)(out as *mut c_void, p.value as *mut c_void, p.meta);
}
}
}