pyodide_webassembly_runtime_layer/
global.rs1use pyo3::{intern, prelude::*, sync::GILOnceCell};
2use wasm_runtime_layer::{
3 backend::{AsContext, AsContextMut, Value, WasmGlobal},
4 GlobalType,
5};
6
7use crate::{
8 conversion::{create_js_object, instanceof, ToPy, ValueExt, ValueTypeExt},
9 Engine,
10};
11
12#[derive(Debug)]
18pub struct Global {
19 global: Py<PyAny>,
21 ty: GlobalType,
23}
24
25impl Clone for Global {
26 fn clone(&self) -> Self {
27 Python::with_gil(|py| Self {
28 global: self.global.clone_ref(py),
29 ty: self.ty,
30 })
31 }
32}
33
34impl WasmGlobal<Engine> for Global {
35 fn new(_ctx: impl AsContextMut<Engine>, value: Value<Engine>, mutable: bool) -> Self {
36 Python::with_gil(|py| -> Result<Self, PyErr> {
37 #[cfg(feature = "tracing")]
38 tracing::debug!(?value, mutable, "Global::new");
39
40 let ty = GlobalType::new(ValueExt::ty(&value), mutable);
41
42 let desc = create_js_object(py)?;
43 desc.setattr(
44 intern!(py, "value"),
45 ValueExt::ty(&value).as_js_descriptor(),
46 )?;
47 desc.setattr(intern!(py, "mutable"), mutable)?;
48
49 let value = value.to_py(py);
50
51 let global = web_assembly_global_new(py)?.call1((desc, value))?;
52
53 Ok(Self {
54 global: global.unbind(),
55 ty,
56 })
57 })
58 .expect("Global::new should not fail")
59 }
60
61 fn ty(&self, _ctx: impl AsContext<Engine>) -> GlobalType {
62 self.ty
63 }
64
65 fn set(&self, _ctx: impl AsContextMut<Engine>, new_value: Value<Engine>) -> anyhow::Result<()> {
66 if !self.ty.mutable() {
67 return Err(anyhow::anyhow!("Global is not mutable"));
68 }
69
70 Python::with_gil(|py| {
71 let global = self.global.bind(py);
72
73 #[cfg(feature = "tracing")]
74 tracing::debug!(global = %global, ?self.ty, ?new_value, "Global::set");
75
76 let new_value = new_value.to_py(py);
77
78 global.setattr(intern!(py, "value"), new_value)?;
79
80 Ok(())
81 })
82 }
83
84 fn get(&self, _ctx: impl AsContextMut<Engine>) -> Value<Engine> {
85 Python::with_gil(|py| {
86 let global = self.global.bind(py);
87
88 #[cfg(feature = "tracing")]
89 tracing::debug!(global = %global, ?self.ty, "Global::get");
90
91 let value = global.getattr(intern!(py, "value"))?;
92
93 Value::from_py_typed(value, self.ty.content())
94 })
95 .expect("Global::get should not fail")
96 }
97}
98
99impl ToPy for Global {
100 fn to_py(&self, py: Python) -> Py<PyAny> {
101 #[cfg(feature = "tracing")]
102 tracing::trace!(value = %self.global, ?self.ty, "Global::to_py");
103
104 self.global.clone_ref(py)
105 }
106}
107
108impl Global {
109 pub(crate) fn from_exported_global(
111 global: Bound<PyAny>,
112 ty: GlobalType,
113 ) -> anyhow::Result<Self> {
114 if !instanceof(&global, web_assembly_global(global.py())?)? {
115 anyhow::bail!("expected WebAssembly.Global but found {global}");
116 }
117
118 #[cfg(feature = "tracing")]
119 tracing::debug!(global = %global, ?ty, "Global::from_exported_global");
120
121 Ok(Self {
122 global: global.unbind(),
123 ty,
124 })
125 }
126}
127
128fn web_assembly_global(py: Python) -> Result<&Bound<PyAny>, PyErr> {
129 static WEB_ASSEMBLY_GLOBAL: GILOnceCell<Py<PyAny>> = GILOnceCell::new();
130 WEB_ASSEMBLY_GLOBAL.import(py, "js.WebAssembly", "Global")
131}
132
133fn web_assembly_global_new(py: Python) -> Result<&Bound<PyAny>, PyErr> {
134 static WEB_ASSEMBLY_GLOBAL_NEW: GILOnceCell<Py<PyAny>> = GILOnceCell::new();
135 WEB_ASSEMBLY_GLOBAL_NEW.import(py, "js.WebAssembly.Global", "new")
136}