Skip to main content

swc_plugin_proxy/memory_interop/
read_returned_result_from_host.rs

1#[cfg_attr(not(target_arch = "wasm32"), allow(unused))]
2#[cfg(any(feature = "__plugin_rt", feature = "__plugin_mode"))]
3use swc_common::plugin::serialized::PluginSerializedBytes;
4
5/// A struct to exchange allocated data between memory spaces.
6pub struct AllocatedBytesPtr(pub u32, pub u32);
7
8#[cfg(target_arch = "wasm32")]
9extern "C" {
10    fn __free(ptr: *mut u8, size: i32) -> i32;
11}
12#[cfg(target_arch = "wasm32")]
13impl Drop for AllocatedBytesPtr {
14    fn drop(&mut self) {
15        unsafe {
16            __free(self.0 as _, self.1 as _);
17        }
18    }
19}
20
21#[cfg(not(feature = "encoding-impl"))]
22fn read_returned_result_from_host_inner<F>(f: F) -> Option<AllocatedBytesPtr> {
23    unimplemented!("Plugin proxy does not work without serialization support")
24}
25
26/// Performs an interop while calling host fn to get non-determined size return
27/// values from the host. This is based on the contract between host's imported
28/// fn, by imported fn allocated memory for the guest space then hand over its
29/// ptr and length via a struct. Refer plugin_runner/imported_fn/mod.rs for the
30/// detail.
31///
32/// Returns a struct AllocatedBytesPtr to the ptr for actual return value if
33/// host fn allocated return value, None otherwise.
34#[cfg(all(
35    feature = "encoding-impl",
36    feature = "__plugin_mode",
37    target_arch = "wasm32"
38))]
39#[tracing::instrument(level = "info", skip_all)]
40fn read_returned_result_from_host_inner<F>(f: F) -> Option<AllocatedBytesPtr>
41where
42    F: FnOnce(u32) -> u32,
43{
44    use std::cell::UnsafeCell;
45
46    // Allocate AllocatedBytesPtr to get return value from the host
47    //
48    // @quininer: I'm not sure if `.as_mut_ptr()` creates an alias for array,
49    // so using `UnsafeCell` here makes the semantics clearer.
50    let allocated_bytes_ptr: UnsafeCell<[u32; 2]> = UnsafeCell::new([0; 2]);
51    let serialized_allocated_bytes_raw_ptr = allocated_bytes_ptr.get();
52
53    assert_eq!(std::mem::size_of::<*mut u32>(), std::mem::size_of::<u32>());
54
55    let ret = f(serialized_allocated_bytes_raw_ptr as _);
56
57    // Host fn call completes: by contract in host proxy, if return value is 0
58    // we know there's no value to read. Otherwise, we know host filled in
59    // AllocatedBytesPtr to the pointer for the actual value for the
60    // results.
61    if ret == 0 {
62        return None;
63    }
64
65    let allocated_bytes_ptr = allocated_bytes_ptr.into_inner();
66
67    // Return reconstructted AllocatedBytesPtr to reveal ptr to the allocated bytes
68    Some(AllocatedBytesPtr(
69        allocated_bytes_ptr[0],
70        allocated_bytes_ptr[1],
71    ))
72}
73
74#[cfg(not(feature = "encoding-impl"))]
75pub fn read_returned_result_from_host<F, R>(f: F) -> Option<R> {
76    unimplemented!("Plugin proxy does not work without serialization support")
77}
78
79/// Performs deserialization to the actual return value type from returned ptr.
80///
81/// This fn is for the Infallible types works for most of the cases.
82#[cfg(all(
83    feature = "encoding-impl",
84    feature = "__plugin_mode",
85    target_arch = "wasm32"
86))]
87#[cfg_attr(not(target_arch = "wasm32"), allow(unused))]
88#[tracing::instrument(level = "info", skip_all)]
89pub fn read_returned_result_from_host<F, R>(f: F) -> Option<R>
90where
91    F: FnOnce(u32) -> u32,
92    R: for<'de> cbor4ii::core::dec::Decode<'de>,
93{
94    let allocated_returned_value_ptr = read_returned_result_from_host_inner(f);
95
96    // Using AllocatedBytesPtr's value, reconstruct actual return value
97    allocated_returned_value_ptr.map(|allocated_returned_value_ptr| {
98        PluginSerializedBytes::from_raw_ptr(
99            allocated_returned_value_ptr.0 as _,
100            allocated_returned_value_ptr
101                .1
102                .try_into()
103                .expect("Should able to convert ptr length"),
104        )
105        .deserialize()
106        .expect("Returned value should be serializable")
107        .into_inner()
108    })
109}