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 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
use super::{MetacallNull, MetacallValue};
use crate::{
bindings::{metacall_await_future, metacall_value_destroy, metacall_value_to_future},
helpers, parsers,
};
use std::{
ffi::c_void,
fmt::{self, Debug, Formatter},
ptr,
};
/// Function pointer type used for resolving/rejecting Metacall futures. The first argument is the result
/// and the second argument is the data that you may want to access when the function gets called.
/// Checkout [MetacallFuture resolve](MetacallFuture#method.then) or
/// [MetacallFuture reject](MetacallFuture#method.catch) for usage.
pub type MetacallFutureHandler = fn(Box<dyn MetacallValue>, Box<dyn MetacallValue>);
/// Represents MetacallFuture. Keep in mind that it's not supported to pass a future as an argument.
/// Usage example: ...
/// ```
/// use metacall::{MetacallValue, MetacallFuture, metacall};
///
/// fn resolve(result: impl MetacallValue, data: impl MetacallValue) {
/// println!("Resolve:: result: {:#?}, data: {:#?}", result, data);
/// }
///
/// fn reject(result: impl MetacallValue, data: impl MetacallValue) {
/// println!("Reject:: result: {:#?}, data: {:#?}", result, data);
/// }
///
/// let future = metacall::<MetacallFuture>("async_function", [1]).unwrap();
/// future.then(resolve).catch(reject).await_fut();
/// ```
#[repr(C)]
pub struct MetacallFuture {
data: *mut dyn MetacallValue,
leak: bool,
reject: Option<MetacallFutureHandler>,
resolve: Option<MetacallFutureHandler>,
value: *mut c_void,
}
unsafe impl Send for MetacallFuture {}
unsafe impl Sync for MetacallFuture {}
impl Clone for MetacallFuture {
fn clone(&self) -> Self {
Self {
data: self.data,
leak: true,
reject: self.reject,
resolve: self.resolve,
value: self.value,
}
}
}
impl Debug for MetacallFuture {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let boxed_data = unsafe { Box::from_raw(self.data) };
let data = if boxed_data.is::<MetacallNull>() {
None
} else {
Some(format!("{:#?}", boxed_data))
};
Box::leak(boxed_data);
let resolve = if self.resolve.is_none() {
"None"
} else {
"Some"
};
let reject = if self.reject.is_none() {
"None"
} else {
"Some"
};
f.debug_struct("MetacallFuture")
.field("data", &data)
.field("resolve", &resolve)
.field("reject", &reject)
.finish()
}
}
type MetacallFutureFFIData = (
// Resolve
Option<MetacallFutureHandler>,
// Reject
Option<MetacallFutureHandler>,
// User data
*mut dyn MetacallValue,
);
unsafe extern "C" fn resolver(resolve_data: *mut c_void, upper_data: *mut c_void) -> *mut c_void {
let (resolve, _, data) = *Box::from_raw(upper_data as *mut MetacallFutureFFIData);
let user_data = Box::from_raw(data);
(resolve.unwrap())(
parsers::raw_to_metacallobj_untyped_leak(resolve_data),
user_data,
);
ptr::null_mut()
}
unsafe extern "C" fn rejecter(reject_data: *mut c_void, upper_data: *mut c_void) -> *mut c_void {
let (_, reject, data) = *Box::from_raw(upper_data as *mut MetacallFutureFFIData);
let user_data = Box::from_raw(data);
(reject.unwrap())(
parsers::raw_to_metacallobj_untyped_leak(reject_data),
user_data,
);
ptr::null_mut()
}
impl MetacallFuture {
fn create_null_data() -> *mut dyn MetacallValue {
Box::into_raw(helpers::metacall_implementer_to_traitobj(MetacallNull()))
}
#[doc(hidden)]
pub fn new_raw(value: *mut c_void) -> Self {
Self {
data: Self::create_null_data(),
leak: false,
reject: None,
resolve: None,
value,
}
}
#[doc(hidden)]
pub fn new_raw_leak(value: *mut c_void) -> Self {
Self {
data: Self::create_null_data(),
leak: true,
reject: None,
resolve: None,
value,
}
}
/// Adds a resolve callback.
pub fn then(mut self, resolve: MetacallFutureHandler) -> Self {
self.resolve = Some(resolve);
self
}
/// Adds a reject callback.
pub fn catch(mut self, reject: MetacallFutureHandler) -> Self {
self.reject = Some(reject);
self
}
/// Adds data.
pub fn data(mut self, data: impl MetacallValue) -> Self {
unsafe { drop(Box::from_raw(self.data)) };
self.data = Box::into_raw(Box::new(data) as Box<dyn MetacallValue>);
self
}
/// Awaits the future.
pub fn await_fut(self) {
let resolve_is_some = self.resolve.is_some();
let reject_is_some = self.reject.is_some();
unsafe {
metacall_value_destroy(metacall_await_future(
metacall_value_to_future(self.value),
if resolve_is_some {
Some(resolver)
} else {
None
},
if reject_is_some { Some(rejecter) } else { None },
// TODO: Solve the memory leak that happens here
// For reproducing the error, use the following commands:
// cargo test --no-run
// valgrind --trace-children=yes --leak-check=full --tool=memcheck --suppressions=../../../source/tests/memcheck/valgrind-node.supp ./target/debug/deps/metacall_test-248af33824f71bd1 &> output
// ==20664== 60 (32 direct, 28 indirect) bytes in 1 blocks are definitely lost in loss record 11 of 35
// ==20664== at 0x4842839: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
// ==20664== by 0x17B549: alloc (alloc.rs:93)
// ==20664== by 0x17B549: alloc::alloc::Global::alloc_impl (alloc.rs:175)
// ==20664== by 0x17B342: allocate (alloc.rs:235)
// ==20664== by 0x17B342: alloc::alloc::exchange_malloc (alloc.rs:324)
// ==20664== by 0x1873D0: new<(core::option::Option<fn(alloc::boxed::Box<dyn metacall::types::metacall_value::MetacallValue, alloc::alloc::Global>, alloc::boxed::Box<dyn metacall::types::metacall_value::MetacallValue, alloc::alloc::Global>)>, core::option::Option<fn(alloc::boxed::Box<dyn metacall::types::metacall_value::MetacallValue, alloc::alloc::Global>, alloc::boxed::Box<dyn metacall::types::metacall_value::MetacallValue, alloc::alloc::Global>)>, *mut dyn metacall::types::metacall_value::MetacallValue)> (boxed.rs:217)
// ==20664== by 0x1873D0: metacall::types::metacall_future::MetacallFuture::await_fut (metacall_future.rs:182)
// ==20664== by 0x1296E6: metacall_test::test_future::{{closure}} (metacall_test.rs:202)
// ==20664== by 0x1286A2: metacall_test::generate_test_custom_validation (metacall_test.rs:42)
// ==20664== by 0x12625A: metacall_test::test_future (metacall_test.rs:193)
// ==20664== by 0x126954: metacall_test::metacall (metacall_test.rs:368)
// ==20664== by 0x129736: metacall_test::metacall::{{closure}} (metacall_test.rs:337)
// ==20664== by 0x1256B4: core::ops::function::FnOnce::call_once (function.rs:250)
// ==20664== by 0x166EBE: call_once<fn() -> core::result::Result<(), alloc::string::String>, ()> (function.rs:250)
// ==20664== by 0x166EBE: test::__rust_begin_short_backtrace (lib.rs:655)
// ==20664== by 0x13456B: {closure#1} (lib.rs:646)
// ==20664== by 0x13456B: core::ops::function::FnOnce::call_once{{vtable-shim}} (function.rs:250)
Box::into_raw(Box::new((self.resolve, self.reject, self.data))) as *mut c_void,
))
};
}
#[doc(hidden)]
pub fn into_raw(self) -> *mut c_void {
// TODO:
// It's not implemented in any loader as the time of writing this block of code.
// Feel free to implement as any loader adopted accepting Future as an argument.
panic!("Passing MetacallFuture as an argument is not supported!");
}
}
impl Drop for MetacallFuture {
fn drop(&mut self) {
if !self.leak {
unsafe { metacall_value_destroy(self.value) };
}
}
}