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.
6#[cfg_attr(
7    feature = "__rkyv",
8    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
9)]
10#[cfg_attr(feature = "__rkyv", repr(C))]
11pub struct AllocatedBytesPtr(pub u32, pub u32);
12
13#[cfg(target_arch = "wasm32")]
14extern "C" {
15    fn __free(ptr: *mut u8, size: i32) -> i32;
16}
17#[cfg(target_arch = "wasm32")]
18impl Drop for AllocatedBytesPtr {
19    fn drop(&mut self) {
20        unsafe {
21            __free(self.0 as _, self.1 as _);
22        }
23    }
24}
25
26#[cfg(not(feature = "__rkyv"))]
27fn read_returned_result_from_host_inner<F>(f: F) -> Option<AllocatedBytesPtr> {
28    unimplemented!("Plugin proxy does not work without serialization support")
29}
30
31/// Performs an interop while calling host fn to get non-determined size return
32/// values from the host. This is based on the contract between host's imported
33/// fn, by imported fn allocated memory for the guest space then hand over its
34/// ptr and length via a struct. Refer plugin_runner/imported_fn/mod.rs for the
35/// detail.
36///
37/// Returns a struct AllocatedBytesPtr to the ptr for actual return value if
38/// host fn allocated return value, None otherwise.
39#[cfg(all(feature = "__rkyv", feature = "__plugin_mode", target_arch = "wasm32"))]
40#[tracing::instrument(level = "info", skip_all)]
41fn read_returned_result_from_host_inner<F>(f: F) -> Option<AllocatedBytesPtr>
42where
43    F: FnOnce(u32) -> u32,
44{
45    // Allocate AllocatedBytesPtr to get return value from the host
46    let allocated_bytes_ptr =
47        swc_common::plugin::serialized::VersionedSerializable::new(AllocatedBytesPtr(0, 0));
48    let serialized_allocated_bytes_ptr = PluginSerializedBytes::try_serialize(&allocated_bytes_ptr)
49        .expect("Should able to serialize AllocatedBytesPtr");
50    let (serialized_allocated_bytes_raw_ptr, serialized_allocated_bytes_raw_ptr_size) =
51        serialized_allocated_bytes_ptr.as_ptr();
52
53    std::mem::forget(allocated_bytes_ptr); // We should not drop AllocatedBytesPtr(0, 0)
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    // Return reconstructted AllocatedBytesPtr to reveal ptr to the allocated bytes
66    Some(
67        PluginSerializedBytes::from_raw_ptr(
68            serialized_allocated_bytes_raw_ptr,
69            serialized_allocated_bytes_raw_ptr_size
70                .try_into()
71                .expect("Should able to convert ptr length"),
72        )
73        .deserialize()
74        .expect("Should able to deserialize AllocatedBytesPtr")
75        .into_inner(),
76    )
77}
78
79#[cfg(not(feature = "__rkyv"))]
80pub fn read_returned_result_from_host<F, R>(f: F) -> Option<R> {
81    unimplemented!("Plugin proxy does not work without serialization support")
82}
83
84/// Performs deserialization to the actual return value type from returned ptr.
85///
86/// This fn is for the Infallible types works for most of the cases.
87#[cfg(all(feature = "__rkyv", feature = "__plugin_mode", target_arch = "wasm32"))]
88#[cfg_attr(not(target_arch = "wasm32"), allow(unused))]
89#[tracing::instrument(level = "info", skip_all)]
90pub fn read_returned_result_from_host<F, R>(f: F) -> Option<R>
91where
92    F: FnOnce(u32) -> u32,
93    R: rkyv::Archive,
94    R::Archived: rkyv::Deserialize<R, rancor::Strategy<rkyv::de::Pool, rancor::Error>>,
95    for<'a> R::Archived: bytecheck::CheckBytes<
96        rancor::Strategy<
97            rkyv::validation::Validator<
98                rkyv::validation::archive::ArchiveValidator<'a>,
99                rkyv::validation::shared::SharedValidator,
100            >,
101            rancor::Error,
102        >,
103    >,
104{
105    let allocated_returned_value_ptr = read_returned_result_from_host_inner(f);
106
107    // Using AllocatedBytesPtr's value, reconstruct actual return value
108    allocated_returned_value_ptr.map(|allocated_returned_value_ptr| {
109        PluginSerializedBytes::from_raw_ptr(
110            allocated_returned_value_ptr.0 as _,
111            allocated_returned_value_ptr
112                .1
113                .try_into()
114                .expect("Should able to convert ptr length"),
115        )
116        .deserialize()
117        .expect("Returned value should be serializable")
118        .into_inner()
119    })
120}