runmat_runtime/native/
host.rs1use std::ffi::c_void;
2
3use super::{
4 NativeCall, NativeExecuteSiteFn, NativeExit, NativeSafepoint, NativeSourceMapEntry,
5 NativeValueRef, NATIVE_ABI_VERSION,
6};
7
8pub type NativeEntryPoint =
16 unsafe extern "C" fn(call: *mut NativeCall, exit: *mut NativeExit) -> NativeHostStatus;
17
18#[repr(transparent)]
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20pub struct NativeHostStatus(pub u32);
21
22impl NativeHostStatus {
23 pub const OK: Self = Self(0);
24 pub const INVALID_ARGUMENT: Self = Self(1);
25 pub const STALE_VALUE: Self = Self(2);
26 pub const HOST_FAILURE: Self = Self(3);
27
28 pub const fn is_known(self) -> bool {
29 self.0 <= Self::HOST_FAILURE.0
30 }
31}
32
33pub type NativeRetainValueFn =
34 unsafe extern "C" fn(context: *mut c_void, value: NativeValueRef) -> NativeHostStatus;
35pub type NativeReleaseValueFn =
36 unsafe extern "C" fn(context: *mut c_void, value: NativeValueRef) -> NativeHostStatus;
37pub type NativeSafepointFn = unsafe extern "C" fn(
38 context: *mut c_void,
39 safepoint: *const NativeSafepoint,
40 exit: *mut NativeExit,
41) -> NativeHostStatus;
42pub type NativeSourceLookupFn = unsafe extern "C" fn(
43 context: *mut c_void,
44 source: u32,
45 output: *mut NativeSourceMapEntry,
46) -> NativeHostStatus;
47
48#[repr(C)]
54#[derive(Clone, Copy, Debug)]
55pub struct NativeHostVTable {
56 pub abi_version: u32,
57 pub struct_size: u32,
58 pub context: *mut c_void,
59 pub retain_value: Option<NativeRetainValueFn>,
60 pub release_value: Option<NativeReleaseValueFn>,
61 pub poll_safepoint: Option<NativeSafepointFn>,
62 pub source_lookup: Option<NativeSourceLookupFn>,
63 pub execute_site: Option<NativeExecuteSiteFn>,
64}
65
66impl NativeHostVTable {
67 pub fn validate(&self) -> Result<(), super::NativeAbiError> {
68 if self.abi_version != NATIVE_ABI_VERSION.encoded() {
69 return Err(super::NativeAbiError::new(
70 "native.host.abi_version",
71 "unsupported native ABI version",
72 ));
73 }
74 if self.struct_size as usize != std::mem::size_of::<Self>() {
75 return Err(super::NativeAbiError::new(
76 "native.host.struct_size",
77 "host table size does not match this ABI revision",
78 ));
79 }
80 if self.retain_value.is_none()
81 || self.release_value.is_none()
82 || self.poll_safepoint.is_none()
83 || self.source_lookup.is_none()
84 || self.execute_site.is_none()
85 {
86 return Err(super::NativeAbiError::new(
87 "native.host.callbacks",
88 "all native host callbacks are required",
89 ));
90 }
91 Ok(())
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 unsafe extern "C" fn retain(_: *mut c_void, _: NativeValueRef) -> NativeHostStatus {
100 NativeHostStatus::OK
101 }
102
103 unsafe extern "C" fn release(_: *mut c_void, _: NativeValueRef) -> NativeHostStatus {
104 NativeHostStatus::OK
105 }
106
107 unsafe extern "C" fn safepoint(
108 _: *mut c_void,
109 _: *const NativeSafepoint,
110 _: *mut NativeExit,
111 ) -> NativeHostStatus {
112 NativeHostStatus::OK
113 }
114
115 unsafe extern "C" fn source(
116 _: *mut c_void,
117 _: u32,
118 _: *mut NativeSourceMapEntry,
119 ) -> NativeHostStatus {
120 NativeHostStatus::OK
121 }
122
123 unsafe extern "C" fn site(
124 _: *mut c_void,
125 _: *mut NativeCall,
126 _: *const crate::native::NativeSiteRequest,
127 _: *mut crate::native::NativeSiteOutcome,
128 _: *mut NativeExit,
129 ) -> NativeHostStatus {
130 NativeHostStatus::OK
131 }
132
133 fn table() -> NativeHostVTable {
134 NativeHostVTable {
135 abi_version: NATIVE_ABI_VERSION.encoded(),
136 struct_size: std::mem::size_of::<NativeHostVTable>() as u32,
137 context: std::ptr::null_mut(),
138 retain_value: Some(retain),
139 release_value: Some(release),
140 poll_safepoint: Some(safepoint),
141 source_lookup: Some(source),
142 execute_site: Some(site),
143 }
144 }
145
146 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
147 #[cfg_attr(not(target_arch = "wasm32"), test)]
148 fn host_table_requires_version_size_and_every_service() {
149 assert_eq!(table().validate(), Ok(()));
150 assert!(NativeHostStatus::OK.is_known());
151 assert!(!NativeHostStatus(u32::MAX).is_known());
152
153 let mut missing = table();
154 missing.execute_site = None;
155 assert_eq!(
156 missing.validate().unwrap_err().field,
157 "native.host.callbacks"
158 );
159
160 let mut future = table();
161 future.abi_version += 1;
162 assert_eq!(
163 future.validate().unwrap_err().field,
164 "native.host.abi_version"
165 );
166 }
167}