Merge pull request #2290 from RustPython/coolreader18/update-browser

Update the browser module
This commit is contained in:
Noah authored and GitHub committed 2020-10-17 23:07:27 -05:00
commit efa809bd17
11 files changed
+396 -229

No files matched your search

Generated
+46 -41
View File
@@ -26,9 +26,9 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "aho-corasick"
version = "0.7.13"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86"
checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d"
dependencies = [
"memchr",
]
@@ -140,7 +140,7 @@ checksum = "f1c85344eb535a31b62f0af37be84441ba9e7f0f4111eb0530f43d15e513fe57"
dependencies = [
"bitflags",
"cexpr",
"cfg-if",
"cfg-if 0.1.10",
"clang-sys",
"clap",
"env_logger",
@@ -288,6 +288,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.19"
@@ -482,7 +488,7 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
]
[[package]]
@@ -492,7 +498,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
"cfg-if",
"cfg-if 0.1.10",
"lazy_static 1.4.0",
]
@@ -578,19 +584,19 @@ dependencies = [
[[package]]
name = "dirs-next"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cbcf9241d9e8d106295bd496bbe2e9cffd5fa098f2a8c9e2bbcbf09773c11a8"
checksum = "cf36e65a80337bea855cd4ef9b8401ffce06a7baedf2e85ec467b1ac3f6e82b6"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.0"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c60f7b8a8953926148223260454befb50c751d3c50e1c178c4fd1ace4083c9a"
checksum = "99de365f605554ae33f115102a02057d4fc18b01f3284d6870be0938743cfe7d"
dependencies = [
"libc",
"redox_users",
@@ -603,7 +609,7 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f69635ffdfbaea44241d7cca30a5e3a2e1c892613a6a8ad8ef03deeb6803480"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"socket2",
"winapi",
@@ -730,7 +736,7 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da80be589a72651dcda34d8b35bcdc9b7254ad06325611074d9cc0fbb19f60ee"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"crc32fast",
"libc",
"libz-sys",
@@ -789,7 +795,7 @@ version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
"wasm-bindgen",
@@ -853,7 +859,7 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63312a18f7ea8760cdd0a7c5aac1a619752a246b833545e3e36d1f81f7cd9e66"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"js-sys",
"wasm-bindgen",
"web-sys",
@@ -959,16 +965,16 @@ checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
dependencies = [
"arrayvec",
"bitflags",
"cfg-if",
"cfg-if 0.1.10",
"ryu",
"static_assertions",
]
[[package]]
name = "libc"
version = "0.2.76"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
[[package]]
name = "libffi"
@@ -1030,7 +1036,7 @@ version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
]
[[package]]
@@ -1117,7 +1123,7 @@ checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
dependencies = [
"bitflags",
"cc",
"cfg-if",
"cfg-if 0.1.10",
"libc",
]
@@ -1246,7 +1252,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4"
dependencies = [
"bitflags",
"cfg-if",
"cfg-if 0.1.10",
"foreign-types",
"lazy_static 1.4.0",
"libc",
@@ -1299,7 +1305,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"cloudabi",
"instant",
"libc",
@@ -1354,9 +1360,9 @@ dependencies = [
[[package]]
name = "pkg-config"
version = "0.3.18"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "pmutil"
@@ -1512,9 +1518,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36f45b719a674bf4b828ff318906d6c133264c793eff7a41e30074a45b5099e2"
checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b"
dependencies = [
"aho-corasick",
"memchr",
@@ -1533,9 +1539,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.6.19"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17be88d9eaa858870aa5e48cc406c206e4600e983fc4f06bbe5750d93d09761"
checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c"
[[package]]
name = "region"
@@ -1599,7 +1605,7 @@ dependencies = [
name = "rustpython"
version = "0.1.2"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"clap",
"cpython",
"dirs-next",
@@ -1633,7 +1639,7 @@ dependencies = [
name = "rustpython-common"
version = "0.0.0"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"derive_more",
"hexf-parse",
"lexical-core",
@@ -1725,7 +1731,7 @@ dependencies = [
"bstr",
"byteorder",
"caseless",
"cfg-if",
"cfg-if 0.1.10",
"chrono",
"crc",
"crc32fast",
@@ -1810,7 +1816,6 @@ dependencies = [
name = "rustpython_wasm"
version = "0.1.2"
dependencies = [
"cfg-if",
"js-sys",
"parking_lot",
"rustpython-compiler",
@@ -1829,7 +1834,7 @@ version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0d5e7b0219a3eadd5439498525d4765c59b7c993ef0c12244865cd2d988413"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"dirs-next",
"libc",
"log",
@@ -1881,9 +1886,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
version = "1.0.116"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5"
checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
dependencies = [
"serde_derive",
]
@@ -1902,9 +1907,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.116"
version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8"
checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
dependencies = [
"proc-macro2",
"quote",
@@ -1913,9 +1918,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.58"
version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a230ea9107ca2220eea9d46de97eddcb04cd00e92d13dda78e478dd33fa82bd4"
checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
dependencies = [
"itoa",
"ryu",
@@ -1983,7 +1988,7 @@ version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"redox_syscall",
"winapi",
@@ -2399,7 +2404,7 @@ version = "0.2.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"wasm-bindgen-macro",
]
@@ -2424,7 +2429,7 @@ version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7866cab0aa01de1edf8b5d7936938a7e397ee50ce24119aef3e1eaa3b6171da"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"js-sys",
"wasm-bindgen",
"web-sys",
+1 -1
View File
@@ -502,7 +502,7 @@ impl ToTokens for GetSetNursery {
class.set_str_attr(
#name,
::rustpython_vm::pyobject::PyObject::new(
::rustpython_vm::builtins::getset::PyGetSet::#constructor(#name.into(), &Self::#getter #setter),
::rustpython_vm::builtins::PyGetSet::#constructor(#name.into(), &Self::#getter #setter),
ctx.types.getset_type.clone(), None)
);
}
+1
View File
@@ -29,6 +29,7 @@ pub use function::PyFunction;
pub(crate) mod generator;
pub use generator::PyGenerator;
pub(crate) mod getset;
pub use getset::PyGetSet;
pub(crate) mod int;
pub use int::{PyInt, PyIntRef};
pub(crate) mod iter;
+5 -1
View File
@@ -72,6 +72,10 @@ impl PyBaseException {
Ok(())
}
pub fn get_arg(&self, idx: usize) -> Option<PyObjectRef> {
self.args.read().borrow_value().get(idx).cloned()
}
#[pyproperty]
pub fn args(&self) -> PyTupleRef {
self.args.read().clone()
@@ -641,7 +645,7 @@ fn none_getter(_obj: PyObjectRef, vm: &VirtualMachine) -> PyNoneRef {
}
fn make_arg_getter(idx: usize) -> impl Fn(PyBaseExceptionRef) -> Option<PyObjectRef> {
move |exc| exc.args.read().borrow_value().get(idx).cloned()
move |exc| exc.get_arg(idx)
}
fn key_error_str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyStrRef {
+4
View File
@@ -352,6 +352,10 @@ impl<T> Args<T> {
pub fn into_vec(self) -> Vec<T> {
self.0
}
pub fn iter(&self) -> std::slice::Iter<T> {
self.0.iter()
}
}
impl<T> From<Vec<T>> for Args<T> {
+6 -6
View File
@@ -2079,27 +2079,27 @@ mod posix {
#[pyfunction(name = "WIFSIGNALED")]
fn wifsignaled(status: i32) -> bool {
unsafe { libc::WIFSIGNALED(status) }
libc::WIFSIGNALED(status)
}
#[pyfunction(name = "WIFSTOPPED")]
fn wifstopped(status: i32) -> bool {
unsafe { libc::WIFSTOPPED(status) }
libc::WIFSTOPPED(status)
}
#[pyfunction(name = "WIFEXITED")]
fn wifexited(status: i32) -> bool {
unsafe { libc::WIFEXITED(status) }
libc::WIFEXITED(status)
}
#[pyfunction(name = "WTERMSIG")]
fn wtermsig(status: i32) -> i32 {
unsafe { libc::WTERMSIG(status) }
libc::WTERMSIG(status)
}
#[pyfunction(name = "WSTOPSIG")]
fn wstopsig(status: i32) -> i32 {
unsafe { libc::WSTOPSIG(status) }
libc::WSTOPSIG(status)
}
#[pyfunction(name = "WEXITSTATUS")]
fn wexitstatus(status: i32) -> i32 {
unsafe { libc::WEXITSTATUS(status) }
libc::WEXITSTATUS(status)
}
#[pyfunction]
-1
View File
@@ -19,7 +19,6 @@ rustpython-compiler = { path = "../../compiler" }
rustpython-parser = { path = "../../parser" }
# no threading feature for rustpython-vm -- doesn't much matter anyway, but it might be more optimized
rustpython-vm = { path = "../../vm", default-features = false, features = ["compile-parse"] }
cfg-if = "0.1.2"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
serde-wasm-bindgen = "0.1"
+2 -1
View File
@@ -1,10 +1,11 @@
from _browser import *
from _js import JsValue
from _js import JSValue, Promise
from _window import window
jsstr = window.new_from_str
jsclosure = window.new_closure
_alert = window.get_prop("alert")
+4 -103
View File
@@ -1,20 +1,18 @@
use js_sys::Promise;
use std::future::Future;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::{future_to_promise, JsFuture};
use wasm_bindgen_futures::JsFuture;
use rustpython_vm::builtins::{PyDictRef, PyStrRef, PyTypeRef};
use rustpython_vm::common::rc::PyRc;
use rustpython_vm::function::OptionalArg;
use rustpython_vm::import::import_file;
use rustpython_vm::pyobject::{
BorrowValue, IntoPyObject, PyCallable, PyClassImpl, PyObject, PyObjectRef, PyRef, PyResult,
PyValue,
BorrowValue, IntoPyObject, PyCallable, PyClassImpl, PyObject, PyObjectRef, PyResult, PyValue,
};
use rustpython_vm::VirtualMachine;
use crate::{convert, vm_class::weak_vm, wasm_builtins::window};
use crate::{convert, js_module::PyPromise, vm_class::weak_vm, wasm_builtins::window};
enum FetchResponseFormat {
Json,
@@ -155,100 +153,6 @@ fn browser_cancel_animation_frame(id: i32, vm: &VirtualMachine) -> PyResult<()>
Ok(())
}
#[pyclass(module = "browser", name = "Promise")]
#[derive(Debug)]
pub struct PyPromise {
value: Promise,
}
pub type PyPromiseRef = PyRef<PyPromise>;
impl PyValue for PyPromise {
fn class(vm: &VirtualMachine) -> PyTypeRef {
vm.class("browser", "Promise")
}
}
#[pyimpl]
impl PyPromise {
pub fn new(value: Promise) -> PyPromise {
PyPromise { value }
}
pub fn from_future<F>(future: F) -> PyPromise
where
F: Future<Output = Result<JsValue, JsValue>> + 'static,
{
PyPromise::new(future_to_promise(future))
}
pub fn value(&self) -> Promise {
self.value.clone()
}
#[pymethod]
fn then(
&self,
on_fulfill: PyCallable,
on_reject: OptionalArg<PyCallable>,
vm: &VirtualMachine,
) -> PyPromiseRef {
let weak_vm = weak_vm(vm);
let prom = JsFuture::from(self.value.clone());
let ret_future = async move {
let stored_vm = &weak_vm
.upgrade()
.expect("that the vm is valid when the promise resolves");
let res = prom.await;
match res {
Ok(val) => stored_vm.interp.enter(move |vm| {
let args = if val.is_null() {
vec![]
} else {
vec![convert::js_to_py(vm, val)]
};
let res = vm.invoke(&on_fulfill.into_object(), args);
convert::pyresult_to_jsresult(vm, res)
}),
Err(err) => {
if let OptionalArg::Present(on_reject) = on_reject {
stored_vm.interp.enter(move |vm| {
let err = convert::js_to_py(vm, err);
let res = vm.invoke(&on_reject.into_object(), (err,));
convert::pyresult_to_jsresult(vm, res)
})
} else {
Err(err)
}
}
}
};
PyPromise::from_future(ret_future).into_ref(vm)
}
#[pymethod]
fn catch(&self, on_reject: PyCallable, vm: &VirtualMachine) -> PyPromiseRef {
let weak_vm = weak_vm(vm);
let prom = JsFuture::from(self.value.clone());
let ret_future = async move {
let err = match prom.await {
Ok(x) => return Ok(x),
Err(e) => e,
};
let stored_vm = weak_vm
.upgrade()
.expect("that the vm is valid when the promise resolves");
stored_vm.interp.enter(move |vm| {
let err = convert::js_to_py(vm, err);
let res = vm.invoke(&on_reject.into_object(), (err,));
convert::pyresult_to_jsresult(vm, res)
})
};
PyPromise::from_future(ret_future).into_ref(vm)
}
}
#[pyclass(module = "browser", name)]
#[derive(Debug)]
struct Document {
@@ -347,8 +251,6 @@ fn browser_load_module(module: PyStrRef, path: PyStrRef, vm: &VirtualMachine) ->
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
let promise = PyPromise::make_class(ctx);
let document_class = Document::make_class(ctx);
let document = PyObject::new(
@@ -361,11 +263,10 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let element = Element::make_class(ctx);
py_module!(vm, "browser", {
py_module!(vm, "_browser", {
"fetch" => ctx.new_function(browser_fetch),
"request_animation_frame" => ctx.new_function(browser_request_animation_frame),
"cancel_animation_frame" => ctx.new_function(browser_cancel_animation_frame),
"Promise" => promise,
"Document" => document_class,
"document" => document,
"Element" => element,
+62 -41
View File
@@ -12,7 +12,7 @@ use rustpython_vm::pyobject::{
use rustpython_vm::VirtualMachine;
use rustpython_vm::{exceptions, py_serde};
use crate::browser_module;
use crate::js_module;
use crate::vm_class::{stored_vm_from_wasm, WASMVirtualMachine};
#[wasm_bindgen(inline_js = r"
@@ -35,10 +35,25 @@ extern "C" {
}
pub fn py_err_to_js_err(vm: &VirtualMachine, py_err: &PyBaseExceptionRef) -> JsValue {
let res = serde_wasm_bindgen::to_value(&exceptions::SerializeException::new(vm, py_err));
match res {
Ok(err_info) => PyError::new(err_info).into(),
Err(e) => e.into(),
let jserr = vm.try_class("_js", "JSError").ok();
let js_arg = if jserr.map_or(false, |jserr| py_err.isinstance(&jserr)) {
py_err.get_arg(0)
} else {
None
};
let js_arg = js_arg
.as_ref()
.and_then(|x| x.payload::<js_module::PyJsValue>());
match js_arg {
Some(val) => val.value.clone(),
None => {
let res =
serde_wasm_bindgen::to_value(&exceptions::SerializeException::new(vm, py_err));
match res {
Ok(err_info) => PyError::new(err_info).into(),
Err(e) => e.into(),
}
}
}
}
@@ -74,37 +89,42 @@ pub fn py_to_js(vm: &VirtualMachine, py_obj: PyObjectRef) -> JsValue {
};
let weak_py_obj = wasm_vm.push_held_rc(py_obj).unwrap();
let closure =
move |args: Option<Array>, kwargs: Option<Object>| -> Result<JsValue, JsValue> {
let py_obj = match wasm_vm.assert_valid() {
Ok(_) => weak_py_obj
.upgrade()
.expect("weak_py_obj to be valid if VM is valid"),
Err(err) => {
return Err(err);
}
};
stored_vm_from_wasm(&wasm_vm).interp.enter(move |vm| {
let mut py_func_args = FuncArgs::default();
if let Some(ref args) = args {
for arg in args.values() {
py_func_args.args.push(js_to_py(vm, arg?));
}
}
if let Some(ref kwargs) = kwargs {
for pair in object_entries(kwargs) {
let (key, val) = pair?;
py_func_args
.kwargs
.insert(js_sys::JsString::from(key).into(), js_to_py(vm, val));
}
}
let result = vm.invoke(&py_obj, py_func_args);
pyresult_to_jsresult(vm, result)
})
let closure = move |args: Option<Box<[JsValue]>>,
kwargs: Option<Object>|
-> Result<JsValue, JsValue> {
let py_obj = match wasm_vm.assert_valid() {
Ok(_) => weak_py_obj
.upgrade()
.expect("weak_py_obj to be valid if VM is valid"),
Err(err) => {
return Err(err);
}
};
stored_vm_from_wasm(&wasm_vm).interp.enter(move |vm| {
let args = match args {
Some(args) => Vec::from(args)
.into_iter()
.map(|arg| js_to_py(vm, arg))
.collect::<Vec<_>>(),
None => Vec::new(),
};
let mut py_func_args = FuncArgs::from(args);
if let Some(ref kwargs) = kwargs {
for pair in object_entries(kwargs) {
let (key, val) = pair?;
py_func_args
.kwargs
.insert(js_sys::JsString::from(key).into(), js_to_py(vm, val));
}
}
let result = vm.invoke(&py_obj, py_func_args);
pyresult_to_jsresult(vm, result)
})
};
let closure = Closure::wrap(Box::new(closure)
as Box<dyn FnMut(Option<Array>, Option<Object>) -> Result<JsValue, JsValue>>);
as Box<
dyn FnMut(Option<Box<[JsValue]>>, Option<Object>) -> Result<JsValue, JsValue>,
>);
let func = closure.as_ref().clone();
// 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 {
}
}
// the browser module might not be injected
if vm.try_class("browser", "Promise").is_ok() {
if let Some(py_prom) = py_obj.payload::<browser_module::PyPromise>() {
if vm.try_class("_js", "Promise").is_ok() {
if let Some(py_prom) = py_obj.payload::<js_module::PyPromise>() {
return py_prom.value().into();
}
}
@@ -157,7 +177,7 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef {
if let Some(promise) = js_val.dyn_ref::<Promise>() {
// the browser module might not be injected
if vm.try_class("browser", "Promise").is_ok() {
return browser_module::PyPromise::new(promise.clone())
return js_module::PyPromise::new(promise.clone())
.into_ref(vm)
.into_object();
}
@@ -205,10 +225,11 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef {
Reflect::set(&this, &k.into(), &py_to_js(vm, v))
.expect("property to be settable");
}
let js_args = Array::new();
for v in args.args {
js_args.push(&py_to_js(vm, v));
}
let js_args = args
.args
.into_iter()
.map(|v| py_to_js(vm, v))
.collect::<Array>();
func.apply(&this, &js_args)
.map(|val| js_to_py(vm, val))
.map_err(|err| js_err_to_py_err(vm, &err))
+265 -34
View File
@@ -1,14 +1,21 @@
use js_sys::{Array, Object, Reflect};
use crate::convert;
use crate::vm_class::{stored_vm_from_wasm, WASMVirtualMachine};
use crate::weak_vm;
use js_sys::{Array, Object, Promise, Reflect};
use std::{cell, fmt, future};
use wasm_bindgen::{closure::Closure, prelude::*, JsCast};
use wasm_bindgen_futures::{future_to_promise, JsFuture};
use rustpython_vm::builtins::{PyFloatRef, PyStrRef, PyTypeRef};
use rustpython_vm::common::rc::PyRc;
use rustpython_vm::exceptions::PyBaseExceptionRef;
use rustpython_vm::function::Args;
use rustpython_vm::function::{Args, OptionalArg};
use rustpython_vm::pyobject::{
BorrowValue, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
BorrowValue, IntoPyObject, PyCallable, PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue,
TryFromObject,
};
use rustpython_vm::types::create_type;
use rustpython_vm::VirtualMachine;
use wasm_bindgen::{prelude::*, JsCast};
#[wasm_bindgen(inline_js = "
export function has_prop(target, prop) { return prop in Object(target); }
@@ -16,6 +23,13 @@ export function get_prop(target, prop) { return target[prop]; }
export function set_prop(target, prop, value) { target[prop] = value; }
export function type_of(a) { return typeof a; }
export function instance_of(lhs, rhs) { return lhs instanceof rhs; }
export function call_func(func, args) { return func(...args); }
export function call_method(obj, method, args) { return obj[method](...args) }
export function wrap_closure(closure) {
return function pyfunction(...args) {
closure(this, args)
}
}
")]
extern "C" {
#[wasm_bindgen(catch)]
@@ -28,22 +42,30 @@ extern "C" {
fn type_of(a: &JsValue) -> String;
#[wasm_bindgen(catch)]
fn instance_of(lhs: &JsValue, rhs: &JsValue) -> Result<bool, JsValue>;
#[wasm_bindgen(catch)]
fn call_func(func: &JsValue, args: &Array) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
fn call_method(obj: &JsValue, method: &JsValue, args: &Array) -> Result<JsValue, JsValue>;
#[wasm_bindgen]
fn wrap_closure(closure: &JsValue) -> JsValue;
}
#[pyclass(module = "_js", name = "JsValue")]
#[pyclass(module = "_js", name = "JSValue")]
#[derive(Debug)]
pub struct PyJsValue {
value: JsValue,
pub(crate) value: JsValue,
}
type PyJsValueRef = PyRef<PyJsValue>;
// TODO: Fix this when threading is supported in WASM.
unsafe impl Send for PyJsValue {}
unsafe impl Sync for PyJsValue {}
impl PyValue for PyJsValue {
fn class(vm: &VirtualMachine) -> PyTypeRef {
vm.class("_js", "JsValue")
vm.class("_js", "JSValue")
}
}
impl AsRef<JsValue> for PyJsValue {
fn as_ref(&self) -> &JsValue {
&self.value
}
}
@@ -98,6 +120,11 @@ impl PyJsValue {
PyJsValue::new(n.to_f64())
}
#[pymethod]
fn new_closure(&self, obj: PyObjectRef, vm: &VirtualMachine) -> JsClosure {
JsClosure::new(obj, vm)
}
#[pymethod]
fn new_object(&self, opts: NewObjectOptions, vm: &VirtualMachine) -> PyResult<PyJsValue> {
let value = if let Some(proto) = opts.prototype {
@@ -148,15 +175,23 @@ impl PyJsValue {
.value
.dyn_ref::<js_sys::Function>()
.ok_or_else(|| vm.new_type_error("JS value is not callable".to_owned()))?;
let this = opts
.this
.map(|this| this.value.clone())
.unwrap_or(JsValue::UNDEFINED);
let js_args = Array::new();
for arg in args {
js_args.push(&arg.value);
}
Reflect::apply(func, &this, &js_args)
let js_args = args.iter().map(|x| x.as_ref()).collect::<Array>();
let res = match opts.this {
Some(this) => Reflect::apply(func, &this.value, &js_args),
None => call_func(func, &js_args),
};
res.map(PyJsValue::new).map_err(|err| new_js_error(vm, err))
}
#[pymethod]
fn call_method(
&self,
name: JsProperty,
args: Args<PyJsValueRef>,
vm: &VirtualMachine,
) -> PyResult<PyJsValue> {
let js_args = args.iter().map(|x| x.as_ref()).collect::<Array>();
call_method(&self.value, &name.into_jsvalue(), &js_args)
.map(PyJsValue::new)
.map_err(|err| new_js_error(vm, err))
}
@@ -176,10 +211,7 @@ impl PyJsValue {
.prototype
.as_ref()
.and_then(|proto| proto.value.dyn_ref::<js_sys::Function>());
let js_args = Array::new();
for arg in args {
js_args.push(&arg.value);
}
let js_args = args.iter().map(|x| x.as_ref()).collect::<Array>();
let constructed_result = if let Some(proto) = proto {
Reflect::construct_with_new_target(ctor, &js_args, &proto)
} else {
@@ -211,9 +243,9 @@ impl PyJsValue {
type_of(&self.value)
}
#[pymethod]
/// Checks that `typeof self == "object" && self !== null`. Use instead
/// of `value.typeof() == "object"`
#[pymethod]
fn is_object(&self) -> bool {
self.value.is_object()
}
@@ -241,22 +273,221 @@ struct NewObjectOptions {
prototype: Option<PyJsValueRef>,
}
type ClosureType = Closure<dyn Fn(JsValue, Box<[JsValue]>) -> Result<JsValue, JsValue>>;
#[pyclass(module = "_js", name = "JSClosure")]
struct JsClosure {
closure: cell::RefCell<Option<(ClosureType, PyJsValueRef)>>,
destroyed: cell::Cell<bool>,
detached: cell::Cell<bool>,
}
impl fmt::Debug for JsClosure {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("JsClosure")
}
}
impl PyValue for JsClosure {
fn class(vm: &VirtualMachine) -> PyTypeRef {
vm.class("_js", "JSClosure")
}
}
#[pyimpl]
impl JsClosure {
fn new(obj: PyObjectRef, vm: &VirtualMachine) -> Self {
let wasm_vm = WASMVirtualMachine {
id: vm.wasm_id.clone().unwrap(),
};
let weak_py_obj = wasm_vm.push_held_rc(obj).unwrap();
let f = move |this: JsValue, args: Box<[JsValue]>| {
let py_obj = match wasm_vm.assert_valid() {
Ok(_) => weak_py_obj
.upgrade()
.expect("weak_py_obj to be valid if VM is valid"),
Err(err) => {
return Err(err);
}
};
stored_vm_from_wasm(&wasm_vm).interp.enter(move |vm| {
let mut pyargs = vec![PyJsValue::new(this).into_object(vm)];
pyargs.extend(
Vec::from(args)
.into_iter()
.map(|arg| PyJsValue::new(arg).into_object(vm)),
);
let res = vm.invoke(&py_obj, pyargs);
convert::pyresult_to_jsresult(vm, res)
})
};
let closure = Closure::wrap(Box::new(f) as _);
let wrapped = PyJsValue::new(wrap_closure(closure.as_ref())).into_ref(vm);
JsClosure {
closure: Some((closure, wrapped)).into(),
destroyed: false.into(),
detached: false.into(),
}
}
#[pyproperty]
fn value(&self) -> Option<PyJsValueRef> {
self.closure
.borrow()
.as_ref()
.map(|(_, jsval)| jsval.clone())
}
#[pyproperty]
fn destroyed(&self) -> bool {
self.destroyed.get()
}
#[pyproperty]
fn detached(&self) -> bool {
self.detached.get()
}
#[pymethod]
fn destroy(&self, vm: &VirtualMachine) -> PyResult<()> {
let (closure, _) = self.closure.replace(None).ok_or_else(|| {
vm.new_value_error(
"can't destroy closure has already been destroyed or detached".to_owned(),
)
})?;
drop(closure);
self.destroyed.set(true);
Ok(())
}
#[pymethod]
fn detach(&self, vm: &VirtualMachine) -> PyResult<PyJsValueRef> {
let (closure, jsval) = self.closure.replace(None).ok_or_else(|| {
vm.new_value_error(
"can't detach closure has already been detached or destroyed".to_owned(),
)
})?;
closure.forget();
self.detached.set(true);
Ok(jsval)
}
}
#[pyclass(module = "browser", name = "Promise")]
#[derive(Debug)]
pub struct PyPromise {
value: Promise,
}
pub type PyPromiseRef = PyRef<PyPromise>;
impl PyValue for PyPromise {
fn class(vm: &VirtualMachine) -> PyTypeRef {
vm.class("browser", "Promise")
}
}
#[pyimpl]
impl PyPromise {
pub fn new(value: Promise) -> PyPromise {
PyPromise { value }
}
pub fn from_future<F>(future: F) -> PyPromise
where
F: future::Future<Output = Result<JsValue, JsValue>> + 'static,
{
PyPromise::new(future_to_promise(future))
}
pub fn value(&self) -> Promise {
self.value.clone()
}
#[pymethod]
fn then(
&self,
on_fulfill: PyCallable,
on_reject: OptionalArg<PyCallable>,
vm: &VirtualMachine,
) -> PyPromiseRef {
let weak_vm = weak_vm(vm);
let prom = JsFuture::from(self.value.clone());
let ret_future = async move {
let stored_vm = &weak_vm
.upgrade()
.expect("that the vm is valid when the promise resolves");
let res = prom.await;
match res {
Ok(val) => stored_vm.interp.enter(move |vm| {
let args = if val.is_null() {
vec![]
} else {
vec![convert::js_to_py(vm, val)]
};
let res = vm.invoke(&on_fulfill.into_object(), args);
convert::pyresult_to_jsresult(vm, res)
}),
Err(err) => {
if let OptionalArg::Present(on_reject) = on_reject {
stored_vm.interp.enter(move |vm| {
let err = convert::js_to_py(vm, err);
let res = vm.invoke(&on_reject.into_object(), (err,));
convert::pyresult_to_jsresult(vm, res)
})
} else {
Err(err)
}
}
}
};
PyPromise::from_future(ret_future).into_ref(vm)
}
#[pymethod]
fn catch(&self, on_reject: PyCallable, vm: &VirtualMachine) -> PyPromiseRef {
let weak_vm = weak_vm(vm);
let prom = JsFuture::from(self.value.clone());
let ret_future = async move {
let err = match prom.await {
Ok(x) => return Ok(x),
Err(e) => e,
};
let stored_vm = weak_vm
.upgrade()
.expect("that the vm is valid when the promise resolves");
stored_vm.interp.enter(move |vm| {
let err = convert::js_to_py(vm, err);
let res = vm.invoke(&on_reject.into_object(), (err,));
convert::pyresult_to_jsresult(vm, res)
})
};
PyPromise::from_future(ret_future).into_ref(vm)
}
}
fn new_js_error(vm: &VirtualMachine, err: JsValue) -> PyBaseExceptionRef {
let exc = vm.new_exception_msg(vm.class("_js", "JsError"), format!("{:?}", err));
vm.set_attr(
exc.as_object(),
"js_value",
PyJsValue::new(err).into_ref(vm),
vm.new_exception(
vm.class("_js", "JSError"),
vec![PyJsValue::new(err).into_pyobject(vm)],
)
.unwrap();
exc
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
let js_error = create_type(
"JSError",
&ctx.types.type_type,
ctx.exceptions.exception_type.clone(),
);
extend_class!(ctx, &js_error, {
"value" => ctx.new_readonly_getset("value", |exc: PyBaseExceptionRef| exc.get_arg(0)),
});
py_module!(vm, "_js", {
"JsError" => create_type("JsError", &ctx.types.type_type, ctx.exceptions.exception_type.clone()),
"JsValue" => PyJsValue::make_class(ctx),
"JSError" => js_error,
"JSValue" => PyJsValue::make_class(ctx),
"JSClosure" => JsClosure::make_class(ctx),
"Promise" => PyPromise::make_class(ctx),
})
}