pyodide_webassembly_runtime_layer/
externref.rs1use std::{any::Any, sync::Arc};
2
3use pyo3::prelude::*;
4use wasm_runtime_layer::backend::{AsContextMut, WasmExternRef};
5
6use crate::{
7 conversion::{py_to_js_proxy, ToPy},
8 store::StoreContext,
9 Engine,
10};
11
12#[derive(Debug)]
14pub struct ExternRef {
15 host: Option<Arc<AnyExternRef>>,
17 guest: Py<PyAny>,
19}
20
21impl Clone for ExternRef {
22 fn clone(&self) -> Self {
23 Python::with_gil(|py| Self {
24 host: self.host.clone(),
25 guest: self.guest.clone_ref(py),
26 })
27 }
28}
29
30impl WasmExternRef<Engine> for ExternRef {
31 fn new<T: 'static + Send + Sync>(_ctx: impl AsContextMut<Engine>, object: T) -> Self {
32 Python::with_gil(|py| -> Result<Self, PyErr> {
33 let object: Arc<AnyExternRef> = Arc::new(object);
34
35 let guest = Bound::new(
36 py,
37 PyExternRef {
38 object: Arc::clone(&object),
39 },
40 )?;
41 let guest = py_to_js_proxy(guest)?;
42
43 Ok(Self {
44 host: Some(object),
45 guest: guest.unbind(),
46 })
47 })
48 .expect("ExternRef::new should not fail")
49 }
50
51 fn downcast<'a, 's: 'a, T: 'static, S: 's>(
52 &'a self,
53 _ctx: StoreContext<'s, S>,
54 ) -> anyhow::Result<&'a T> {
55 let Some(object) = self.host.as_ref() else {
57 anyhow::bail!("extern ref is from a different source");
58 };
59
60 let Some(object) = object.downcast_ref() else {
61 anyhow::bail!("incorrect extern ref type");
62 };
63
64 Ok(object)
65 }
66}
67
68impl ToPy for ExternRef {
69 fn to_py(&self, py: Python) -> Py<PyAny> {
70 self.guest.clone_ref(py)
71 }
72}
73
74impl ExternRef {
75 pub(crate) fn from_exported_externref(object: Bound<PyAny>) -> Self {
77 let Ok(host): Result<Bound<PyExternRef>, _> = object.extract() else {
80 return Self {
81 host: None,
82 guest: object.unbind(),
83 };
84 };
85
86 let host = Arc::clone(&host.get().object);
87
88 Self {
89 host: Some(host),
90 guest: object.unbind(),
91 }
92 }
93}
94
95type AnyExternRef = dyn 'static + Any + Send + Sync;
96
97#[pyclass(frozen)]
98struct PyExternRef {
99 object: Arc<AnyExternRef>,
101}