1#![no_std]
16
17pub extern crate alloc;
18#[macro_use]
19extern crate std;
20
21pub mod batch;
22mod cast;
23pub mod convert;
24pub mod encode;
25pub mod function;
26mod function_registry;
27mod intern;
28pub(crate) mod ipc;
29mod js_helpers;
30mod lazy;
31#[doc(hidden)]
32pub mod object_store;
33pub mod runtime;
34mod value;
35pub mod wry;
36
37pub use intern::*;
38
39pub mod closure {
42 pub use crate::Closure;
43 pub use crate::WasmClosure;
44}
45
46pub mod __rt {
49 use crate::{
50 __wry_submit_js_function, JsValue, LazyJsFunction,
51 encode::{BatchableResult, BinaryEncode, EncodeTypeDef},
52 };
53
54 #[inline]
60 pub fn wbg_cast<From, To>(value: From) -> To
61 where
62 From: BinaryEncode + EncodeTypeDef,
63 To: BatchableResult + EncodeTypeDef,
64 {
65 let func: LazyJsFunction<fn(From) -> To> = __wry_submit_js_function!("(a0) => a0");
66 func.call(value)
67 }
68
69 #[cfg(feature = "std")]
73 pub fn panic_to_panic_error(val: std::boxed::Box<dyn std::any::Any + Send>) -> JsValue {
74 let maybe_panic_msg: Option<&str> = if let Some(s) = val.downcast_ref::<&str>() {
75 Some(s)
76 } else if let Some(s) = val.downcast_ref::<std::string::String>() {
77 Some(s)
78 } else {
79 None
80 };
81 JsValue::from_str(maybe_panic_msg.unwrap_or("Rust panic"))
83 }
84}
85
86macro_rules! cast {
87 (($from:ty => $to:ty) $val:expr) => {{ $crate::__rt::wbg_cast::<$from, $to>($val) }};
88}
89
90macro_rules! to_js_value {
91 ($ty:ty) => {
92 impl From<$ty> for $crate::JsValue {
93 fn from(val: $ty) -> Self {
94 cast! {($ty => $crate::JsValue) val}
95 }
96 }
97 };
98}
99
100macro_rules! from_js_value {
101 ($ty:ty) => {
102 impl From<$crate::JsValue> for $ty {
103 fn from(val: $crate::JsValue) -> Self {
104 cast! {($crate::JsValue => $ty) val}
105 }
106 }
107 };
108}
109
110impl TryFrom<JsValue> for u64 {
111 type Error = JsValue;
112
113 fn try_from(value: JsValue) -> Result<Self, Self::Error> {
114 eprintln!("TryFrom<JsValue> for u64 is likely wrong");
115 #[wasm_bindgen(crate = crate, inline_js = "export function BigIntAsU64(val) {
116 if (typeof val !== 'bigint') {
117 throw new Error('Value is not a BigInt');
118 }
119 return Number(val);
120 }")]
121 extern "C" {
122 #[wasm_bindgen(js_name = "BigIntAsU64")]
123 fn big_int_as_u64(val: &JsValue) -> Result<u64, JsValue>;
124 }
125
126 big_int_as_u64(&value)
127 }
128}
129
130impl TryFrom<JsValue> for i64 {
131 type Error = JsValue;
132
133 fn try_from(value: JsValue) -> Result<Self, Self::Error> {
134 eprintln!("TryFrom<JsValue> for u64 is likely wrong");
135 #[wasm_bindgen(crate = crate, inline_js = "export function BigIntAsU64(val) {
136 if (typeof val !== 'bigint') {
137 throw new Error('Value is not a BigInt');
138 }
139 return Number(val);
140 }")]
141 extern "C" {
142 #[wasm_bindgen(js_name = "BigIntAsU64")]
143 fn big_int_as_i64(val: &JsValue) -> Result<i64, JsValue>;
144 }
145
146 big_int_as_i64(&value)
147 }
148}
149
150impl TryFrom<JsValue> for f64 {
151 type Error = JsValue;
152
153 fn try_from(value: JsValue) -> Result<Self, Self::Error> {
154 value.as_f64().ok_or(value)
155 }
156}
157
158impl TryFrom<&JsValue> for f64 {
159 type Error = JsValue;
160
161 fn try_from(value: &JsValue) -> Result<Self, Self::Error> {
162 value.as_f64().ok_or_else(|| value.clone())
163 }
164}
165
166impl TryFrom<JsValue> for i128 {
167 type Error = JsValue;
168
169 fn try_from(value: JsValue) -> Result<Self, Self::Error> {
170 #[wasm_bindgen(crate = crate, inline_js = "export function BigIntAsI128(val) {
171 if (typeof val !== 'bigint') {
172 throw new Error('Value is not a BigInt');
173 }
174 return Number(val);
175 }")]
176 extern "C" {
177 #[wasm_bindgen(js_name = "BigIntAsI128")]
178 fn big_int_as_i128(val: &JsValue) -> Result<i128, JsValue>;
179 }
180
181 big_int_as_i128(&value)
182 }
183}
184
185impl TryFrom<JsValue> for u128 {
186 type Error = JsValue;
187
188 fn try_from(value: JsValue) -> Result<Self, Self::Error> {
189 #[wasm_bindgen(crate = crate, inline_js = "export function BigIntAsU128(val) {
190 if (typeof val !== 'bigint') {
191 throw new Error('Value is not a BigInt');
192 }
193 if (val < 0n) {
194 throw new Error('Value is negative');
195 }
196 return Number(val);
197 }")]
198 extern "C" {
199 #[wasm_bindgen(js_name = "BigIntAsU128")]
200 fn big_int_as_u128(val: &JsValue) -> Result<u128, JsValue>;
201 }
202
203 big_int_as_u128(&value)
204 }
205}
206
207impl TryFrom<JsValue> for String {
208 type Error = JsValue;
209
210 fn try_from(value: JsValue) -> Result<Self, Self::Error> {
211 value.as_string().ok_or(value)
212 }
213}
214
215to_js_value!(i8);
216from_js_value!(i8);
217to_js_value!(i16);
218from_js_value!(i16);
219to_js_value!(i32);
220from_js_value!(i32);
221to_js_value!(i64);
222to_js_value!(i128);
223to_js_value!(u8);
224from_js_value!(u8);
225to_js_value!(u16);
226from_js_value!(u16);
227to_js_value!(u32);
228from_js_value!(u32);
229to_js_value!(u64);
230to_js_value!(u128);
231to_js_value!(f32);
232from_js_value!(f32);
233to_js_value!(f64);
234to_js_value!(usize);
235from_js_value!(usize);
236to_js_value!(isize);
237from_js_value!(isize);
238impl From<&str> for JsValue {
239 fn from(val: &str) -> Self {
240 cast! {(String => JsValue) val.to_string()}
241 }
242}
243impl From<&String> for JsValue {
244 fn from(val: &String) -> Self {
245 cast! {(String => JsValue) val.clone()}
246 }
247}
248to_js_value!(String);
249to_js_value!(());
250from_js_value!(());
251
252pub struct Closure<T: ?Sized> {
254 _phantom: core::marker::PhantomData<Box<T>>,
257 pub(crate) value: JsValue,
258}
259
260impl<T: ?Sized> Closure<T> {
261 pub fn new<M, F: IntoClosure<M, Self>>(f: F) -> Self {
262 f.into_closure()
263 }
264
265 pub fn once<F, M>(fn_once: F) -> Closure<T>
271 where
272 F: WasmClosureFnOnce<T, M>,
273 {
274 fn_once.into_closure()
275 }
276
277 pub fn forget(self) {
279 core::mem::forget(self);
280 }
281
282 pub(crate) fn wrap_encode_decode<FnPtr>(
284 encode_decode: impl Fn(&mut DecodedData, &mut EncodedData) + 'static,
285 ) -> Self
286 where
287 CallbackKey<FnPtr>: BinaryEncode + EncodeTypeDef,
288 {
289 let key = insert_object(RustCallback::new_fn(encode_decode));
290 crate::__rt::wbg_cast::<CallbackKey<FnPtr>, crate::Closure<T>>(CallbackKey::new(key))
293 }
294
295 pub(crate) fn wrap_encode_decode_mut<FnPtr>(
297 encode_decode: impl FnMut(&mut DecodedData, &mut EncodedData) + 'static,
298 ) -> Self
299 where
300 CallbackKey<FnPtr>: BinaryEncode + EncodeTypeDef,
301 {
302 let key = insert_object(RustCallback::new_fn_mut(encode_decode));
303 crate::__rt::wbg_cast::<CallbackKey<FnPtr>, crate::Closure<T>>(CallbackKey::new(key))
306 }
307}
308
309#[doc(hidden)]
311pub trait WasmClosureFnOnce<T: ?Sized, M>: Sized + 'static {
312 fn into_closure(self) -> Closure<T>;
313}
314
315impl<T: ?Sized> AsRef<JsValue> for Closure<T> {
316 fn as_ref(&self) -> &JsValue {
317 &self.value
318 }
319}
320
321impl<T: ?Sized> core::fmt::Debug for Closure<T> {
322 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
323 f.debug_struct("Closure")
324 .field("value", &self.value)
325 .finish()
326 }
327}
328
329pub trait WasmClosure<M> {
332 fn into_js_closure(boxed: Box<Self>) -> Closure<Self>;
334}
335
336impl<T: ?Sized> Closure<T> {
337 pub fn wrap<M>(data: Box<T>) -> Closure<T>
341 where
342 T: WasmClosure<M>,
343 {
344 T::into_js_closure(data)
345 }
346
347 pub fn into_js_value(self) -> JsValue {
349 let value = core::mem::ManuallyDrop::new(self);
350 value.value.clone()
352 }
353
354 pub fn once_into_js<F, M>(fn_once: F) -> JsValue
359 where
360 F: WasmClosureFnOnce<T, M>,
361 T: Sized,
362 {
363 Closure::once(fn_once).into_js_value()
364 }
365}
366
367use alloc::boxed::Box;
368use alloc::string::{String, ToString};
369use core::ops::{Deref, DerefMut};
370pub use cast::JsCast;
372pub use lazy::JsThreadLocal;
373pub use value::JsValue;
374
375#[derive(Copy, Clone, PartialEq, Debug, Eq)]
382pub struct Clamped<T>(pub T);
383
384impl<T> Deref for Clamped<T> {
385 type Target = T;
386 fn deref(&self) -> &T {
387 &self.0
388 }
389}
390
391impl<T> DerefMut for Clamped<T> {
392 fn deref_mut(&mut self) -> &mut T {
393 &mut self.0
394 }
395}
396
397#[derive(Debug)]
401#[repr(transparent)]
402pub struct JsError {
403 value: JsValue,
404}
405
406impl JsError {
407 pub fn new(message: &str) -> Self {
409 JsError {
410 value: __wry_call_js_function!(
411 "(msg) => new Error(msg)",
412 fn(&str) -> JsValue,
413 (message)
414 ),
415 }
416 }
417}
418
419impl From<JsError> for JsValue {
420 fn from(e: JsError) -> Self {
421 e.value
422 }
423}
424
425impl<T> From<Option<T>> for JsValue
426where
427 T: Into<JsValue>,
428{
429 fn from(s: Option<T>) -> JsValue {
430 match s {
431 Some(s) => s.into(),
432 None => JsValue::undefined(),
433 }
434 }
435}
436
437impl AsRef<JsValue> for JsError {
438 fn as_ref(&self) -> &JsValue {
439 &self.value
440 }
441}
442
443impl core::fmt::Display for JsError {
444 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
445 write!(f, "JsError")
446 }
447}
448
449impl core::error::Error for JsError {}
450
451impl JsCast for JsError {
452 fn instanceof(val: &JsValue) -> bool {
453 crate::js_helpers::js_is_error(val)
454 }
455
456 fn unchecked_from_js(val: JsValue) -> Self {
457 JsError { value: val }
458 }
459
460 fn unchecked_from_js_ref(val: &JsValue) -> &Self {
461 unsafe { &*(val as *const JsValue as *const JsError) }
463 }
464}
465
466pub use batch::batch;
468pub use encode::{BatchableResult, BinaryDecode, BinaryEncode, EncodeTypeDef};
469pub use function::JSFunction;
470pub use ipc::{DecodeError, DecodedData, EncodedData};
471
472pub use wry_bindgen_macro::link_to;
474pub use wry_bindgen_macro::wasm_bindgen;
475
476pub use inventory;
478
479use crate::encode::{CallbackKey, IntoClosure};
480use crate::function::RustCallback;
481use crate::object_store::insert_object;
482
483pub use function_registry::{
485 InlineJsModule, JsClassMemberKind, JsClassMemberSpec, JsExportSpec, JsFunctionSpec,
486 LazyJsFunction,
487};
488
489#[macro_export]
502#[doc(hidden)]
503macro_rules! __wry_call_js_function {
504 ($js_code:expr, $fn_type:ty, ($($args:expr),*)) => {{
505 static __FUNC: $crate::LazyJsFunction<$fn_type> = $crate::__wry_submit_js_function!($js_code);
506
507 __FUNC.call($($args),*)
508 }};
509}
510
511#[macro_export]
523#[doc(hidden)]
524macro_rules! __wry_submit_js_function {
525 ($js_code:expr) => {{
526 static __SPEC: $crate::JsFunctionSpec =
527 $crate::JsFunctionSpec::new(|| $crate::alloc::format!($js_code));
528
529 $crate::inventory::submit! {
530 __SPEC
531 }
532
533 __SPEC.resolve_as()
534 }};
535}
536
537pub trait UnwrapThrowExt<T>: Sized {
540 fn unwrap_throw(self) -> T;
542
543 fn expect_throw(self, message: &str) -> T;
545}
546
547impl<T> UnwrapThrowExt<T> for Option<T> {
548 fn unwrap_throw(self) -> T {
549 self.expect("called `Option::unwrap_throw()` on a `None` value")
550 }
551
552 fn expect_throw(self, message: &str) -> T {
553 self.expect(message)
554 }
555}
556
557impl<T, E> UnwrapThrowExt<T> for Result<T, E>
558where
559 E: core::fmt::Debug,
560{
561 fn unwrap_throw(self) -> T {
562 self.expect("called `Result::unwrap_throw()` on an `Err` value")
563 }
564
565 fn expect_throw(self, message: &str) -> T {
566 self.expect(message)
567 }
568}
569
570#[cold]
571#[inline(never)]
572pub fn throw_val(s: JsValue) -> ! {
573 panic!("{s:?}");
574}
575
576#[cold]
581#[inline(never)]
582pub fn throw_str(s: &str) -> ! {
583 panic!("cannot throw JS exception when running outside of wasm: {s}");
584}
585
586pub fn externref_heap_live_count() -> u32 {
591 panic!("cannot introspect wasm memory when running outside of wasm")
592}
593
594pub fn module() -> JsValue {
599 panic!("cannot introspect wasm memory when running outside of wasm")
600}
601
602pub fn exports() -> JsValue {
607 panic!("cannot introspect wasm memory when running outside of wasm")
608}
609
610pub fn memory() -> JsValue {
615 panic!("cannot introspect wasm memory when running outside of wasm")
616}
617
618pub fn function_table() -> JsValue {
623 panic!("cannot introspect wasm memory when running outside of wasm")
624}
625
626pub use js_helpers::js_extract_rust_handle as extract_rust_handle;
628
629pub mod prelude {
631 pub use crate::Clamped;
632 pub use crate::Closure;
633 pub use crate::JsError;
634 pub use crate::UnwrapThrowExt;
635 pub use crate::WasmClosure;
636 pub use crate::batch::batch;
637 pub use crate::cast::JsCast;
638 pub use crate::encode::{BatchableResult, BinaryDecode, BinaryEncode, EncodeTypeDef};
639 pub use crate::function::JSFunction;
640 pub use crate::lazy::JsThreadLocal;
641 pub use crate::value::JsValue;
642 pub use crate::wasm_bindgen;
643}