Skip to content

Commit c61759b

Browse files
committed
Rework some of the _js module
1 parent cb9c9ec commit c61759b

File tree

5 files changed

+332
-179
lines changed

5 files changed

+332
-179
lines changed

wasm/lib/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ rustpython-compiler = { path = "../../compiler" }
1919
rustpython-parser = { path = "../../parser" }
2020
# no threading feature for rustpython-vm -- doesn't much matter anyway, but it might be more optimized
2121
rustpython-vm = { path = "../../vm", default-features = false, features = ["compile-parse"] }
22-
cfg-if = "0.1.2"
2322
wasm-bindgen = "0.2"
2423
wasm-bindgen-futures = "0.4"
2524
serde-wasm-bindgen = "0.1"

wasm/lib/src/browser.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from _browser import *
22

3-
from _js import JsValue
3+
from _js import JSValue, Promise
44
from _window import window
55

66

77
jsstr = window.new_from_str
8+
jsclosure = window.new_closure
89

910

1011
_alert = window.get_prop("alert")

wasm/lib/src/browser_module.rs

Lines changed: 4 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
use js_sys::Promise;
2-
use std::future::Future;
32
use wasm_bindgen::prelude::*;
43
use wasm_bindgen::JsCast;
5-
use wasm_bindgen_futures::{future_to_promise, JsFuture};
4+
use wasm_bindgen_futures::JsFuture;
65

76
use rustpython_vm::builtins::{PyDictRef, PyStrRef, PyTypeRef};
87
use rustpython_vm::common::rc::PyRc;
98
use rustpython_vm::function::OptionalArg;
109
use rustpython_vm::import::import_file;
1110
use rustpython_vm::pyobject::{
12-
BorrowValue, IntoPyObject, PyCallable, PyClassImpl, PyObject, PyObjectRef, PyRef, PyResult,
13-
PyValue,
11+
BorrowValue, IntoPyObject, PyCallable, PyClassImpl, PyObject, PyObjectRef, PyResult, PyValue,
1412
};
1513
use rustpython_vm::VirtualMachine;
1614

17-
use crate::{convert, vm_class::weak_vm, wasm_builtins::window};
15+
use crate::{convert, js_module::PyPromise, vm_class::weak_vm, wasm_builtins::window};
1816

1917
enum FetchResponseFormat {
2018
Json,
@@ -155,100 +153,6 @@ fn browser_cancel_animation_frame(id: i32, vm: &VirtualMachine) -> PyResult<()>
155153
Ok(())
156154
}
157155

158-
#[pyclass(module = "browser", name = "Promise")]
159-
#[derive(Debug)]
160-
pub struct PyPromise {
161-
value: Promise,
162-
}
163-
pub type PyPromiseRef = PyRef<PyPromise>;
164-
165-
impl PyValue for PyPromise {
166-
fn class(vm: &VirtualMachine) -> PyTypeRef {
167-
vm.class("browser", "Promise")
168-
}
169-
}
170-
171-
#[pyimpl]
172-
impl PyPromise {
173-
pub fn new(value: Promise) -> PyPromise {
174-
PyPromise { value }
175-
}
176-
pub fn from_future<F>(future: F) -> PyPromise
177-
where
178-
F: Future<Output = Result<JsValue, JsValue>> + 'static,
179-
{
180-
PyPromise::new(future_to_promise(future))
181-
}
182-
pub fn value(&self) -> Promise {
183-
self.value.clone()
184-
}
185-
186-
#[pymethod]
187-
fn then(
188-
&self,
189-
on_fulfill: PyCallable,
190-
on_reject: OptionalArg<PyCallable>,
191-
vm: &VirtualMachine,
192-
) -> PyPromiseRef {
193-
let weak_vm = weak_vm(vm);
194-
let prom = JsFuture::from(self.value.clone());
195-
196-
let ret_future = async move {
197-
let stored_vm = &weak_vm
198-
.upgrade()
199-
.expect("that the vm is valid when the promise resolves");
200-
let res = prom.await;
201-
match res {
202-
Ok(val) => stored_vm.interp.enter(move |vm| {
203-
let args = if val.is_null() {
204-
vec![]
205-
} else {
206-
vec![convert::js_to_py(vm, val)]
207-
};
208-
let res = vm.invoke(&on_fulfill.into_object(), args);
209-
convert::pyresult_to_jsresult(vm, res)
210-
}),
211-
Err(err) => {
212-
if let OptionalArg::Present(on_reject) = on_reject {
213-
stored_vm.interp.enter(move |vm| {
214-
let err = convert::js_to_py(vm, err);
215-
let res = vm.invoke(&on_reject.into_object(), (err,));
216-
convert::pyresult_to_jsresult(vm, res)
217-
})
218-
} else {
219-
Err(err)
220-
}
221-
}
222-
}
223-
};
224-
225-
PyPromise::from_future(ret_future).into_ref(vm)
226-
}
227-
228-
#[pymethod]
229-
fn catch(&self, on_reject: PyCallable, vm: &VirtualMachine) -> PyPromiseRef {
230-
let weak_vm = weak_vm(vm);
231-
let prom = JsFuture::from(self.value.clone());
232-
233-
let ret_future = async move {
234-
let err = match prom.await {
235-
Ok(x) => return Ok(x),
236-
Err(e) => e,
237-
};
238-
let stored_vm = weak_vm
239-
.upgrade()
240-
.expect("that the vm is valid when the promise resolves");
241-
stored_vm.interp.enter(move |vm| {
242-
let err = convert::js_to_py(vm, err);
243-
let res = vm.invoke(&on_reject.into_object(), (err,));
244-
convert::pyresult_to_jsresult(vm, res)
245-
})
246-
};
247-
248-
PyPromise::from_future(ret_future).into_ref(vm)
249-
}
250-
}
251-
252156
#[pyclass(module = "browser", name)]
253157
#[derive(Debug)]
254158
struct Document {
@@ -347,8 +251,6 @@ fn browser_load_module(module: PyStrRef, path: PyStrRef, vm: &VirtualMachine) ->
347251
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
348252
let ctx = &vm.ctx;
349253

350-
let promise = PyPromise::make_class(ctx);
351-
352254
let document_class = Document::make_class(ctx);
353255

354256
let document = PyObject::new(
@@ -361,11 +263,10 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
361263

362264
let element = Element::make_class(ctx);
363265

364-
py_module!(vm, "browser", {
266+
py_module!(vm, "_browser", {
365267
"fetch" => ctx.new_function(browser_fetch),
366268
"request_animation_frame" => ctx.new_function(browser_request_animation_frame),
367269
"cancel_animation_frame" => ctx.new_function(browser_cancel_animation_frame),
368-
"Promise" => promise,
369270
"Document" => document_class,
370271
"document" => document,
371272
"Element" => element,

wasm/lib/src/convert.rs

Lines changed: 61 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustpython_vm::pyobject::{
1212
use rustpython_vm::VirtualMachine;
1313
use rustpython_vm::{exceptions, py_serde};
1414

15-
use crate::browser_module;
15+
use crate::js_module;
1616
use crate::vm_class::{stored_vm_from_wasm, WASMVirtualMachine};
1717

1818
#[wasm_bindgen(inline_js = r"
@@ -35,10 +35,25 @@ extern "C" {
3535
}
3636

3737
pub fn py_err_to_js_err(vm: &VirtualMachine, py_err: &PyBaseExceptionRef) -> JsValue {
38-
let res = serde_wasm_bindgen::to_value(&exceptions::SerializeException::new(vm, py_err));
39-
match res {
40-
Ok(err_info) => PyError::new(err_info).into(),
41-
Err(e) => e.into(),
38+
let jserr = vm.try_class("_js", "JSError").ok();
39+
let js_arg = if jserr.map_or(false, |jserr| py_err.isinstance(&jserr)) {
40+
py_err.get_arg(0)
41+
} else {
42+
None
43+
};
44+
let js_arg = js_arg
45+
.as_ref()
46+
.and_then(|x| x.payload::<js_module::PyJsValue>());
47+
match js_arg {
48+
Some(val) => val.value.clone(),
49+
None => {
50+
let res =
51+
serde_wasm_bindgen::to_value(&exceptions::SerializeException::new(vm, py_err));
52+
match res {
53+
Ok(err_info) => PyError::new(err_info).into(),
54+
Err(e) => e.into(),
55+
}
56+
}
4257
}
4358
}
4459

@@ -74,37 +89,42 @@ pub fn py_to_js(vm: &VirtualMachine, py_obj: PyObjectRef) -> JsValue {
7489
};
7590
let weak_py_obj = wasm_vm.push_held_rc(py_obj).unwrap();
7691

77-
let closure =
78-
move |args: Option<Array>, kwargs: Option<Object>| -> Result<JsValue, JsValue> {
79-
let py_obj = match wasm_vm.assert_valid() {
80-
Ok(_) => weak_py_obj
81-
.upgrade()
82-
.expect("weak_py_obj to be valid if VM is valid"),
83-
Err(err) => {
84-
return Err(err);
85-
}
92+
let closure = move |args: Option<Box<[JsValue]>>,
93+
kwargs: Option<Object>|
94+
-> Result<JsValue, JsValue> {
95+
let py_obj = match wasm_vm.assert_valid() {
96+
Ok(_) => weak_py_obj
97+
.upgrade()
98+
.expect("weak_py_obj to be valid if VM is valid"),
99+
Err(err) => {
100+
return Err(err);
101+
}
102+
};
103+
stored_vm_from_wasm(&wasm_vm).interp.enter(move |vm| {
104+
let args = match args {
105+
Some(args) => Vec::from(args)
106+
.into_iter()
107+
.map(|arg| js_to_py(vm, arg))
108+
.collect::<Vec<_>>(),
109+
None => Vec::new(),
86110
};
87-
stored_vm_from_wasm(&wasm_vm).interp.enter(move |vm| {
88-
let mut py_func_args = FuncArgs::default();
89-
if let Some(ref args) = args {
90-
for arg in args.values() {
91-
py_func_args.args.push(js_to_py(vm, arg?));
92-
}
111+
let mut py_func_args = FuncArgs::from(args);
112+
if let Some(ref kwargs) = kwargs {
113+
for pair in object_entries(kwargs) {
114+
let (key, val) = pair?;
115+
py_func_args
116+
.kwargs
117+
.insert(js_sys::JsString::from(key).into(), js_to_py(vm, val));
93118
}
94-
if let Some(ref kwargs) = kwargs {
95-
for pair in object_entries(kwargs) {
96-
let (key, val) = pair?;
97-
py_func_args
98-
.kwargs
99-
.insert(js_sys::JsString::from(key).into(), js_to_py(vm, val));
100-
}
101-
}
102-
let result = vm.invoke(&py_obj, py_func_args);
103-
pyresult_to_jsresult(vm, result)
104-
})
105-
};
119+
}
120+
let result = vm.invoke(&py_obj, py_func_args);
121+
pyresult_to_jsresult(vm, result)
122+
})
123+
};
106124
let closure = Closure::wrap(Box::new(closure)
107-
as Box<dyn FnMut(Option<Array>, Option<Object>) -> Result<JsValue, JsValue>>);
125+
as Box<
126+
dyn FnMut(Option<Box<[JsValue]>>, Option<Object>) -> Result<JsValue, JsValue>,
127+
>);
108128
let func = closure.as_ref().clone();
109129

110130
// stores pretty much nothing, it's fine to leak this because if it gets dropped
@@ -115,8 +135,8 @@ pub fn py_to_js(vm: &VirtualMachine, py_obj: PyObjectRef) -> JsValue {
115135
}
116136
}
117137
// the browser module might not be injected
118-
if vm.try_class("browser", "Promise").is_ok() {
119-
if let Some(py_prom) = py_obj.payload::<browser_module::PyPromise>() {
138+
if vm.try_class("_js", "Promise").is_ok() {
139+
if let Some(py_prom) = py_obj.payload::<js_module::PyPromise>() {
120140
return py_prom.value().into();
121141
}
122142
}
@@ -157,7 +177,7 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef {
157177
if let Some(promise) = js_val.dyn_ref::<Promise>() {
158178
// the browser module might not be injected
159179
if vm.try_class("browser", "Promise").is_ok() {
160-
return browser_module::PyPromise::new(promise.clone())
180+
return js_module::PyPromise::new(promise.clone())
161181
.into_ref(vm)
162182
.into_object();
163183
}
@@ -205,10 +225,11 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef {
205225
Reflect::set(&this, &k.into(), &py_to_js(vm, v))
206226
.expect("property to be settable");
207227
}
208-
let js_args = Array::new();
209-
for v in args.args {
210-
js_args.push(&py_to_js(vm, v));
211-
}
228+
let js_args = args
229+
.args
230+
.into_iter()
231+
.map(|v| py_to_js(vm, v))
232+
.collect::<Array>();
212233
func.apply(&this, &js_args)
213234
.map(|val| js_to_py(vm, val))
214235
.map_err(|err| js_err_to_py_err(vm, &err))

0 commit comments

Comments
 (0)