1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
macro_rules! failwith {
(
$expr:expr $(, $($extra:tt)* )?
) => ({
eprintln!(
"c_callback error: {}",
format_args!($expr $(, $($extra)* )?),
);
::std::process::exit(1);
})
}
macro_rules! ffi_unwrap {
($expr:expr, $msg:expr $(,)?) => {
if let ::std::option::Option::Some(inner) = $expr {
inner
} else {
failwith!($msg)
}
};
}
macro_rules! c_callback {
{
$(#[$attr:meta])*
$vis:vis unsafe extern "C" fn $name:ident $([$myself:ident])? ($($c_arg:ident: $c_arg_ty:ty),*) $(-> $c_ret_ty:ty)? $(: ($($arg:ident: $arg_ty:ty),*))? $(=> $ret:ident: $ret_ty:ty)?
{
$($body:tt)*
}
$($ret_body:block)?
} => {
// Source: https://users.rust-lang.org/t/callback-based-c-ffi/26583/5
//
// This could be seen as a "function constructor":
// for each concrete Env type parameter,
// a new static function is defined by monomorphisation
$(#[$attr])*
$vis unsafe extern "C" fn $name<Env>(callback_data: *mut ::std::ffi::c_void, $($c_arg: $c_arg_ty),*)
$(-> $c_ret_ty)?
where
Env: ::std::ops::FnMut($($($arg_ty),*)?) $(-> $ret_ty)? + 'static,
{
// Prevent unwinding accross the FFI
::scopeguard::defer_on_unwind!({
::std::process::abort();
});
let at_env_raw_ptr: *mut ::std::boxed::Box<Holder<Env>> = callback_data as *mut ::std::boxed::Box<Holder<Env>>;
let holder: &mut ::std::boxed::Box<Holder<Env>> = ffi_unwrap!(at_env_raw_ptr.as_mut(), "null ptr",);
let callback = &mut holder.callback;
$(let $myself = &holder.myself;)?
$($body)*
// For each given Env type parameter,
// Rust knows how to call this since it is using the static address
// <Env as FnMut<_>>::call_mut(at_env, result, data)
// (this is the only part of the code that depends on the Env type)
let _ret = callback($($($arg),*)?);
$(let $ret = _ret;)?
$(let _ret = $ret_body;)?
_ret
}
};
}
macro_rules! set_callback {
{
$(#[$attr:meta])*
$vis:vis fn $name:ident(&self, callback: FnMut($($($arg:ident: $arg_ty:ty),+)?) $(-> $ret:ident: $ret_ty:ty)?):
[$selfty:tt :: $($lib:tt)+]$([$myself:ident])? $ul_callback_setter:ident($($ul_arg:ident: $ul_arg_ty:ty),*) $(-> $ul_ret_ty:ty)?
{
$($body:tt)*
}
$($ret_body:block)?
} => {
$(#[$attr])*
$vis fn $name<F>(&self, callback: F)
where
F: ::std::ops::FnMut($($($arg_ty),*)?) $(-> $ret_ty)? + 'static,
{
// FIXME: this is a bit effy, not sure if its safe to include a local lifetime here
// its not producing error because we are casting it to raw ptr and back
// so it loses track of the lifetime
// check this later and make sure its safe. or change it.
struct Holder<'a, T> {
#[allow(dead_code)]
myself: &'a $selfty,
callback: T,
}
c_callback! {
unsafe extern "C" fn trampoline$([$myself])?($($ul_arg: $ul_arg_ty),*) $(-> $ul_ret_ty)? $(: ($($arg: $arg_ty),+))? $(=> $ret: $ret_ty)?
{
$($body)*
}
$($ret_body)?
}
let holder_data = Holder {
myself: self,
callback,
};
// Note that we need to double-box the callback, because a `*mut FnMut()` is a fat pointer
// that can't be cast to a `*const c_void`.
//
// Note that we leak the box here, which will result in memory leak
// as we can't get hold of the data later and free it. But since
// setting the handler is only done once, this may not be a big problem.
// FIXME: should we store it in instead?
let holder: *mut ::std::boxed::Box<Holder<F>> =
::std::boxed::Box::into_raw(::std::boxed::Box::new(::std::boxed::Box::new(holder_data)));
let data = holder as *mut ::std::ffi::c_void;
// SAFETY: We're passing a pointer to a function that has a static lifetime.
unsafe {
self.$($lib)+.$ul_callback_setter(
self.internal,
Some(trampoline::<F>),
data
);
}
}
};
}
// note that `arg_ty` and `ret_ty` are not used in the macro, but are there
// just for clarification and to make implementations of the macro easy
macro_rules! platform_set_interface_macro {
{
$(#[$attr:meta])*
$vis:vis $setter_name:ident<$rust_ty:ident>($lib:ident, $setter_arg_name:ident -> $static_name:ident) -> $ul_setter:ident($ul_struct_arg_ty:ident)
{
$(
$fn_name:ident($( ( $($ul_arg:ident: $ul_arg_ty:ty),* ) $(-> $ul_ret_ty:ty)? )?) -> ($(($($arg:ident: $arg_ty:ty),*) $(-> $ret:ident: $ret_ty:ty)? )? )
{
$($from_ul_to_rs_body:tt)*
}
$($from_rs_to_ul_body:block)?
)+
}
} => {
$(#[$attr])*
$vis fn $setter_name<T: $rust_ty + Send + 'static>(lib: ::std::sync::Arc<$crate::Library>, $setter_arg_name: T) {
let $setter_arg_name = Box::new($setter_arg_name);
$static_name.lib.lock().unwrap().replace(lib.clone());
$static_name.obj.lock().unwrap().replace($setter_arg_name);
$(
unsafe extern "C" fn $fn_name($($($ul_arg: $ul_arg_ty),*)?) $($(-> $ul_ret_ty)?)? {
#[allow(unused_variables)]
let $lib = $static_name.lib.lock().unwrap().as_ref().unwrap().clone();
$($from_ul_to_rs_body)*
let mut $setter_arg_name = $static_name.obj.lock().unwrap();
// the $setter_arg_name must always be `Some` at this point.
let _r $($(: $ret_ty)?)? = $setter_arg_name.as_mut().unwrap().$fn_name($($($arg),*)?);
$($(let $ret = _r;)?)?
$(
let _r = $from_rs_to_ul_body;
)?
_r
}
)+
let ul_struct = ul_sys::$ul_struct_arg_ty {
$(
$fn_name: Some($fn_name)
),+
};
unsafe {
lib.ultralight().$ul_setter(ul_struct);
}
}
};
}