diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d30eb909f..5efca9a45 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -6,7 +6,7 @@ on: name: CI env: - CARGO_ARGS: --all --features ssl + CARGO_ARGS: --features ssl jobs: rust_tests: @@ -112,6 +112,11 @@ jobs: with: command: clippy args: ${{ env.CARGO_ARGS }} -- -Dwarnings + - name: run clippy on wasm + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --manifest-path=wasm/lib/Cargo.toml -- -Dwarnings - uses: actions/setup-python@v1 with: python-version: 3.8 diff --git a/Cargo.lock b/Cargo.lock index 4b62146d3..78f26b6d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -552,15 +552,6 @@ version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" -[[package]] -name = "generational-arena" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601" -dependencies = [ - "cfg-if", -] - [[package]] name = "generic-array" version = "0.12.3" @@ -1398,7 +1389,6 @@ dependencies = [ "flamer", "flate2", "foreign-types-shared", - "generational-arena", "gethostname", "getrandom", "hex", @@ -1444,7 +1434,6 @@ dependencies = [ "sha-1", "sha2", "sha3", - "smallbox", "socket2", "statrs", "thread_local", @@ -1470,7 +1459,6 @@ version = "0.1.2" dependencies = [ "cfg-if", "futures", - "generational-arena", "js-sys", "parking_lot", "rustpython-compiler", @@ -1625,12 +1613,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" -[[package]] -name = "smallbox" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa0eec6425219a55511a35afac416a2eecc62f5fa1bec14f9b97de0caa7daebe" - [[package]] name = "smallvec" version = "1.4.1" diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index 3ffab6930..52ac439e6 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -272,7 +272,7 @@ fn extract_impl_items(mut items: Vec) -> Result #transform(Self::#item_ident) diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 5d0481834..521e7626b 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -69,10 +69,8 @@ is-macro = "0.1" result-like = "^0.2.1" foreign-types-shared = "0.1" num_enum = "0.4" -smallbox = "0.8" bstr = "0.2.12" crossbeam-utils = "0.7" -generational-arena = "0.2" parking_lot = "0.11" thread_local = "1.0" cfg-if = "0.1.10" diff --git a/vm/src/function.rs b/vm/src/function.rs index 4a88e76ee..89883cb92 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -4,13 +4,13 @@ use std::ops::RangeInclusive; use indexmap::IndexMap; use result_like::impl_option_like; -use smallbox::{smallbox, space::S1, SmallBox}; use crate::exceptions::PyBaseExceptionRef; use crate::obj::objtuple::PyTupleRef; use crate::obj::objtype::{isinstance, PyClassRef}; use crate::pyobject::{ - BorrowValue, IntoPyResult, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, + BorrowValue, IntoPyResult, PyObjectRef, PyRef, PyResult, PyThreadingConstraint, PyValue, + TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -470,12 +470,8 @@ tuple_from_py_func_args!(A, B, C, D); tuple_from_py_func_args!(A, B, C, D, E); tuple_from_py_func_args!(A, B, C, D, E, F); -/// A container that can hold a `dyn Fn*` trait object, but doesn't allocate if it's only a fn() pointer -pub type FunctionBox = SmallBox; - /// A built-in Python function. -pub type PyNativeFunc = - FunctionBox PyResult + 'static + Send + Sync>; +pub type PyNativeFunc = Box PyResult)>; /// Implemented by types that are or can generate built-in functions. /// @@ -491,16 +487,19 @@ pub type PyNativeFunc = /// /// A bare `PyNativeFunc` also implements this trait, allowing the above to be /// done manually, for rare situations that don't fit into this model. -pub trait IntoPyNativeFunc { - fn into_func(self) -> PyNativeFunc; +pub trait IntoPyNativeFunc: Sized + PyThreadingConstraint + 'static { + fn call(&self, vm: &VirtualMachine, args: PyFuncArgs) -> PyResult; + fn into_func(self) -> PyNativeFunc { + Box::new(move |vm: &VirtualMachine, args| self.call(vm, args)) + } } impl IntoPyNativeFunc for F where - F: Fn(&VirtualMachine, PyFuncArgs) -> PyResult + 'static + Send + Sync, + F: Fn(&VirtualMachine, PyFuncArgs) -> PyResult + PyThreadingConstraint + 'static, { - fn into_func(self) -> PyNativeFunc { - smallbox!(self) + fn call(&self, vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { + (self)(vm, args) } } @@ -515,55 +514,55 @@ macro_rules! into_py_native_func_tuple { ($(($n:tt, $T:ident)),*) => { impl IntoPyNativeFunc<($(OwnedParam<$T>,)*), R, VirtualMachine> for F where - F: Fn($($T,)* &VirtualMachine) -> R + 'static + Send + Sync, + F: Fn($($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static, $($T: FromArgs,)* R: IntoPyResult, { - fn into_func(self) -> PyNativeFunc { - smallbox!(move |vm: &VirtualMachine, args: PyFuncArgs| { - let ($($n,)*) = args.bind::<($($T,)*)>(vm)?; + fn call(&self, vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { + let ($($n,)*) = args.bind::<($($T,)*)>(vm)?; - (self)($($n,)* vm).into_pyresult(vm) - }) + (self)($($n,)* vm).into_pyresult(vm) } } impl IntoPyNativeFunc<(RefParam, $(OwnedParam<$T>,)*), R, VirtualMachine> for F where - F: Fn(&S, $($T,)* &VirtualMachine) -> R + 'static + Send + Sync, + F: Fn(&S, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static, S: PyValue, $($T: FromArgs,)* R: IntoPyResult, { - fn into_func(self) -> PyNativeFunc { - smallbox!(move |vm: &VirtualMachine, args: PyFuncArgs| { - let (zelf, $($n,)*) = args.bind::<(PyRef, $($T,)*)>(vm)?; + fn call(&self, vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { + let (zelf, $($n,)*) = args.bind::<(PyRef, $($T,)*)>(vm)?; - (self)(&zelf, $($n,)* vm).into_pyresult(vm) - }) + (self)(&zelf, $($n,)* vm).into_pyresult(vm) } } impl IntoPyNativeFunc<($(OwnedParam<$T>,)*), R, ()> for F where - F: Fn($($T,)*) -> R + 'static + Send + Sync, + F: Fn($($T,)*) -> R + PyThreadingConstraint + 'static, $($T: FromArgs,)* R: IntoPyResult, { - fn into_func(self) -> PyNativeFunc { - IntoPyNativeFunc::into_func(move |$($n,)* _vm: &VirtualMachine| (self)($($n,)*)) + fn call(&self, vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { + let ($($n,)*) = args.bind::<($($T,)*)>(vm)?; + + (self)($($n,)*).into_pyresult(vm) } } impl IntoPyNativeFunc<(RefParam, $(OwnedParam<$T>,)*), R, ()> for F where - F: Fn(&S, $($T,)*) -> R + 'static + Send + Sync, + F: Fn(&S, $($T,)*) -> R + PyThreadingConstraint + 'static, S: PyValue, $($T: FromArgs,)* R: IntoPyResult, { - fn into_func(self) -> PyNativeFunc { - IntoPyNativeFunc::into_func(move |zelf: &S, $($n,)* _vm: &VirtualMachine| (self)(zelf, $($n,)*)) + fn call(&self, vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { + let (zelf, $($n,)*) = args.bind::<(PyRef, $($T,)*)>(vm)?; + + (self)(&zelf, $($n,)*).into_pyresult(vm) } } }; @@ -633,12 +632,16 @@ where #[cfg(test)] mod tests { + use super::*; + #[test] - fn test_functionbox_noalloc() { + fn test_intonativefunc_noalloc() { + let check_zst = |f: PyNativeFunc| assert_eq!(std::mem::size_of_val(f.as_ref()), 0); fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 { 1 } - let f = super::IntoPyNativeFunc::into_func(py_func); - assert!(!f.is_heap()); + check_zst(py_func.into_func()); + let empty_closure = || "foo".to_owned(); + check_zst(empty_closure.into_func()); } } diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 0ab03beac..3fec9fc5f 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -73,8 +73,3 @@ mod vm; pub use self::vm::{InitParameter, PySettings, VirtualMachine}; pub use rustpython_bytecode::*; pub use rustpython_common as common; - -#[doc(hidden)] -pub mod __exports { - pub use smallbox::smallbox; -} diff --git a/vm/src/macros.rs b/vm/src/macros.rs index ee56bed03..9349c7c09 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -240,3 +240,21 @@ macro_rules! named_function { } }}; } + +// can't use PyThreadingConstraint for stuff like this since it's not an auto trait, and +// therefore we can't add it ad-hoc to a trait object +cfg_if::cfg_if! { + if #[cfg(feature = "threading")] { + macro_rules! py_dyn_fn { + (dyn Fn($($arg:ty),*$(,)*) -> $ret:ty) => { + dyn Fn($($arg),*) -> $ret + Send + Sync + 'static + }; + } + } else { + macro_rules! py_dyn_fn { + (dyn Fn($($arg:ty),*$(,)*) -> $ret:ty) => { + dyn Fn($($arg),*) -> $ret + 'static + }; + } + } +} diff --git a/vm/src/obj/objgetset.rs b/vm/src/obj/objgetset.rs index 2bdd41cfb..b7c43358e 100644 --- a/vm/src/obj/objgetset.rs +++ b/vm/src/obj/objgetset.rs @@ -2,7 +2,7 @@ */ use super::objtype::PyClassRef; -use crate::function::{FunctionBox, OptionalArg, OwnedParam, RefParam}; +use crate::function::{OptionalArg, OwnedParam, RefParam}; use crate::pyobject::{ IntoPyResult, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, @@ -10,9 +10,9 @@ use crate::pyobject::{ use crate::slots::SlotDescriptor; use crate::vm::VirtualMachine; -pub type PyGetterFunc = FunctionBox PyResult + Send + Sync>; +pub type PyGetterFunc = Box PyResult)>; pub type PySetterFunc = - FunctionBox PyResult<()> + Send + Sync>; + Box PyResult<()>)>; pub trait IntoPyGetterFunc { fn into_getter(self) -> PyGetterFunc; @@ -25,7 +25,7 @@ where R: IntoPyResult, { fn into_getter(self) -> PyGetterFunc { - smallbox::smallbox!(move |vm: &VirtualMachine, obj| { + Box::new(move |vm: &VirtualMachine, obj| { let obj = T::try_from_object(vm, obj)?; (self)(obj, vm).into_pyresult(vm) }) @@ -39,7 +39,7 @@ where R: IntoPyResult, { fn into_getter(self) -> PyGetterFunc { - smallbox::smallbox!(move |vm: &VirtualMachine, obj| { + Box::new(move |vm: &VirtualMachine, obj| { let zelf = PyRef::::try_from_object(vm, obj)?; (self)(&zelf, vm).into_pyresult(vm) }) @@ -96,7 +96,7 @@ where R: IntoPyNoResult, { fn into_setter(self) -> PySetterFunc { - smallbox::smallbox!(move |vm: &VirtualMachine, obj, value| { + Box::new(move |vm: &VirtualMachine, obj, value| { let obj = T::try_from_object(vm, obj)?; let value = V::try_from_object(vm, value)?; (self)(obj, value, vm).into_noresult() @@ -112,7 +112,7 @@ where R: IntoPyNoResult, { fn into_setter(self) -> PySetterFunc { - smallbox::smallbox!(move |vm: &VirtualMachine, obj, value| { + Box::new(move |vm: &VirtualMachine, obj, value| { let zelf = PyRef::::try_from_object(vm, obj)?; let value = V::try_from_object(vm, value)?; (self)(&zelf, value, vm).into_noresult() diff --git a/vm/src/slots.rs b/vm/src/slots.rs index 7ddf217ab..a4653c7b9 100644 --- a/vm/src/slots.rs +++ b/vm/src/slots.rs @@ -1,4 +1,4 @@ -use crate::function::{FunctionBox, OptionalArg, PyFuncArgs, PyNativeFunc}; +use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc}; use crate::pyobject::{IdProtocol, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject}; use crate::VirtualMachine; @@ -54,7 +54,7 @@ pub trait SlotCall: PyValue { fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult; } -pub type PyDescrGetFunc = FunctionBox< +pub type PyDescrGetFunc = Box< dyn Fn(&VirtualMachine, PyObjectRef, Option, OptionalArg) -> PyResult + Send + Sync, diff --git a/vm/src/stdlib/mod.rs b/vm/src/stdlib/mod.rs index 880c1f9f2..d62bec6c4 100644 --- a/vm/src/stdlib/mod.rs +++ b/vm/src/stdlib/mod.rs @@ -68,7 +68,7 @@ mod winreg; #[cfg(not(any(target_arch = "wasm32", target_os = "redox")))] mod zlib; -pub type StdlibInitFunc = Box PyObjectRef + Send + Sync>; +pub type StdlibInitFunc = Box PyObjectRef)>; pub fn get_module_inits() -> HashMap { #[allow(unused_mut)] diff --git a/wasm/lib/Cargo.toml b/wasm/lib/Cargo.toml index 9f74c5a98..9004816cf 100644 --- a/wasm/lib/Cargo.toml +++ b/wasm/lib/Cargo.toml @@ -26,7 +26,6 @@ serde-wasm-bindgen = "0.1" serde = "1.0" js-sys = "0.3" futures = "0.1" -generational-arena = "0.2" # make parking_lot use wasm-bingden for instant parking_lot = { version = "0.11", features = ["wasm-bindgen"] } diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 7626434fa..793fa3f15 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -1,6 +1,3 @@ -use generational_arena::Arena; -use std::cell::RefCell; - use js_sys::{Array, ArrayBuffer, Object, Promise, Reflect, SyntaxError, Uint8Array}; use wasm_bindgen::{closure::Closure, prelude::*, JsCast}; @@ -17,31 +14,6 @@ use rustpython_vm::{exceptions, py_serde}; use crate::browser_module; use crate::vm_class::{stored_vm_from_wasm, WASMVirtualMachine}; -// Currently WASM do not support multithreading. We should change this once it is enabled. -thread_local!(static JS_HANDLES: RefCell> = RefCell::new(Arena::new())); - -pub struct JsHandle(generational_arena::Index); -impl JsHandle { - pub fn new(js: JsValue) -> Self { - let idx = JS_HANDLES.with(|arena| arena.borrow_mut().insert(js)); - JsHandle(idx) - } - pub fn get(&self) -> JsValue { - JS_HANDLES.with(|arena| { - arena - .borrow() - .get(self.0) - .expect("index was removed") - .clone() - }) - } -} -impl Drop for JsHandle { - fn drop(&mut self) { - JS_HANDLES.with(|arena| arena.borrow_mut().remove(self.0)); - } -} - #[wasm_bindgen(inline_js = r" export class PyError extends Error { constructor(info) { @@ -223,7 +195,7 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef { dict.into_object() } } else if js_val.is_function() { - let js_handle = JsHandle::new(js_val); + let func = js_sys::Function::from(js_val); vm.ctx .new_method(move |vm: &VirtualMachine, args: PyFuncArgs| -> PyResult { let this = Object::new(); @@ -235,7 +207,6 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef { for v in args.args { js_args.push(&py_to_js(vm, v)); } - let func = js_sys::Function::from(js_handle.get()); func.apply(&this, &js_args) .map(|val| js_to_py(vm, val)) .map_err(|err| js_err_to_py_err(vm, &err)) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 76b1cb082..8483b99d4 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -12,7 +12,7 @@ use rustpython_vm::scope::{NameProtocol, Scope}; use rustpython_vm::{InitParameter, PySettings, VirtualMachine}; use crate::browser_module::setup_browser_module; -use crate::convert::{self, JsHandle, PyResultExt}; +use crate::convert::{self, PyResultExt}; use crate::js_module; use crate::wasm_builtins; use rustpython_compiler::mode::Mode; @@ -227,9 +227,8 @@ impl WASMVirtualMachine { _ => return Err(error()), } } else if stdout.is_function() { - let func_handle = JsHandle::new(stdout); + let func = js_sys::Function::from(stdout); make_stdout_object(vm, move |data, vm| { - let func = js_sys::Function::from(func_handle.get()); func.call1(&JsValue::UNDEFINED, &data.into()) .map_err(|err| convert::js_py_typeerror(vm, err))?; Ok(()) diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index fd565d0df..2366ff903 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -21,7 +21,7 @@ pub fn sys_stdout_write_console(data: &str, _vm: &VirtualMachine) -> PyResult<() pub fn make_stdout_object( vm: &VirtualMachine, - write_f: impl Fn(&str, &VirtualMachine) -> PyResult<()> + Send + Sync + 'static, + write_f: impl Fn(&str, &VirtualMachine) -> PyResult<()> + 'static, ) -> PyObjectRef { let ctx = &vm.ctx; let write_method = ctx.new_method(