wry_bindgen/
convert.rs

1//! Conversion traits for wasm-bindgen API compatibility.
2//!
3//! These traits provide compatibility with code that uses wasm-bindgen's
4//! low-level ABI conversion types.
5
6use crate::JsValue;
7use crate::batch::with_runtime;
8use core::mem::ManuallyDrop;
9use core::ops::Deref;
10
11/// Trait for converting a Rust type to its WebAssembly ABI representation.
12///
13/// In wry-bindgen, this is simplified since we don't use the WebAssembly ABI directly.
14/// Instead, we use heap indices (u64) for JsValue references.
15pub trait IntoWasmAbi {
16    /// The ABI type that this converts into.
17    type Abi;
18
19    /// Convert this value into its ABI representation.
20    fn into_abi(self) -> Self::Abi;
21}
22
23/// Trait for converting from a WebAssembly ABI representation to a Rust type.
24///
25/// # Safety
26/// The caller must ensure the ABI value is valid for the target type.
27pub trait FromWasmAbi {
28    /// The ABI type that this converts from.
29    type Abi;
30
31    /// Convert from the ABI representation to this type.
32    ///
33    /// # Safety
34    /// The caller must ensure the ABI value is valid.
35    unsafe fn from_abi(js: Self::Abi) -> Self;
36}
37
38// JsValue uses u32 as its ABI type for wasm-bindgen compatibility
39// (internally we use u64, but the ABI layer uses u32 for compatibility)
40impl IntoWasmAbi for JsValue {
41    type Abi = u32;
42
43    fn into_abi(self) -> Self::Abi {
44        let id = self.id();
45        core::mem::forget(self); // Don't drop - ownership transferred
46        id as u32
47    }
48}
49
50impl FromWasmAbi for JsValue {
51    type Abi = u32;
52
53    unsafe fn from_abi(js: Self::Abi) -> Self {
54        JsValue::from_id(js as u64)
55    }
56}
57
58/// Trait for recovering a shared reference from the WebAssembly ABI boundary.
59///
60/// This is the shared reference variant of `FromWasmAbi`.
61pub trait RefFromWasmAbi {
62    /// The ABI type that references to `Self` are recovered from.
63    type Abi;
64
65    /// The type that holds the reference to `Self` for the duration of the
66    /// invocation. This ensures lifetimes don't persist beyond one function call.
67    type Anchor: Deref<Target = Self>;
68
69    /// Recover a `Self::Anchor` from `Self::Abi`.
70    ///
71    /// # Safety
72    /// The caller must ensure the ABI value is valid.
73    unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor;
74}
75
76impl RefFromWasmAbi for JsValue {
77    type Abi = u32;
78    type Anchor = ManuallyDrop<JsValue>;
79
80    #[inline]
81    unsafe fn ref_from_abi(js: u32) -> Self::Anchor {
82        ManuallyDrop::new(JsValue::from_id(js as u64))
83    }
84}
85
86// Implement for reference types
87impl IntoWasmAbi for &JsValue {
88    type Abi = u32;
89
90    fn into_abi(self) -> Self::Abi {
91        self.id() as u32
92    }
93}
94
95// Implement for Option<JsValue>
96impl IntoWasmAbi for Option<JsValue> {
97    type Abi = u32;
98
99    fn into_abi(self) -> Self::Abi {
100        match self {
101            Some(val) => val.into_abi(),
102            None => 0, // Use 0 as sentinel for None
103        }
104    }
105}
106
107use crate::JsCast;
108use crate::ipc::{DecodeError, DecodedData};
109use core::marker::PhantomData;
110
111/// Trait for types that can be decoded as references from binary data.
112///
113/// This is the wry-bindgen equivalent of wasm-bindgen's `RefFromWasmAbi`.
114/// The `Anchor` type holds the decoded value and keeps the reference valid
115/// during callback invocation.
116pub trait RefFromBinaryDecode {
117    /// The anchor type that keeps the decoded reference valid.
118    type Anchor: core::ops::Deref<Target = Self>;
119
120    /// Decode a reference anchor from binary data.
121    fn ref_decode(decoder: &mut DecodedData) -> Result<Self::Anchor, DecodeError>;
122}
123
124/// Anchor type for JsCast references.
125///
126/// This holds a `JsValue` and provides a reference to the target type `T`
127/// through the `JsCast` trait.
128pub struct JsCastAnchor<T: JsCast> {
129    value: JsValue,
130    _marker: PhantomData<T>,
131}
132
133impl<T: JsCast> core::ops::Deref for JsCastAnchor<T> {
134    type Target = T;
135
136    fn deref(&self) -> &Self::Target {
137        T::unchecked_from_js_ref(&self.value)
138    }
139}
140
141// Blanket implementation for all JsCast types (including JsValue)
142impl<T: JsCast + 'static> RefFromBinaryDecode for T {
143    type Anchor = JsCastAnchor<T>;
144
145    fn ref_decode(_decoder: &mut DecodedData) -> Result<Self::Anchor, DecodeError> {
146        // For borrowed refs, we use the borrow stack (indices 1-127) instead of heap IDs.
147        // JS puts the value on its borrow stack without sending an ID, so we sync by
148        // getting the next borrow ID from our batch state.
149        let id = with_runtime(|runtime| runtime.get_next_borrow_id());
150        let value = JsValue::from_id(id);
151        Ok(JsCastAnchor {
152            value,
153            _marker: PhantomData,
154        })
155    }
156}