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}