Merge pull request #2141 from RustPython/coolreader18/pynativefunc-rework

Just use normal Box for PyNativeFunc
This commit is contained in:
Noah authored and GitHub committed 2020-08-22 22:17:37 -05:00
commit 4e80bfd158
14 files changed
+75 -105

No files matched your search

+6 -1
View File
@@ -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
Generated
-18
View File
@@ -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"
+1 -1
View File
@@ -272,7 +272,7 @@ fn extract_impl_items(mut items: Vec<ItemIdent>) -> Result<TokenStream2, Diagnos
let transform = if vec!["new", "call"].contains(&slot_ident.to_string().as_str()) {
quote! { ::rustpython_vm::function::IntoPyNativeFunc::into_func }
} else {
quote! { ::rustpython_vm::__exports::smallbox! }
quote! { ::std::boxed::Box::new }
};
let into_func = quote_spanned! {item_ident.span()=>
#transform(Self::#item_ident)
-2
View File
@@ -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"
+36 -33
View File
@@ -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<T> = SmallBox<T, S1>;
/// A built-in Python function.
pub type PyNativeFunc =
FunctionBox<dyn Fn(&VirtualMachine, PyFuncArgs) -> PyResult + 'static + Send + Sync>;
pub type PyNativeFunc = Box<py_dyn_fn!(dyn Fn(&VirtualMachine, PyFuncArgs) -> 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<T, R, VM> {
fn into_func(self) -> PyNativeFunc;
pub trait IntoPyNativeFunc<T, R, VM>: 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<F> IntoPyNativeFunc<PyFuncArgs, PyResult, VirtualMachine> 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<F, $($T,)* R> 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<F, S, $($T,)* R> IntoPyNativeFunc<(RefParam<S>, $(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<S>, $($T,)*)>(vm)?;
fn call(&self, vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
let (zelf, $($n,)*) = args.bind::<(PyRef<S>, $($T,)*)>(vm)?;
(self)(&zelf, $($n,)* vm).into_pyresult(vm)
})
(self)(&zelf, $($n,)* vm).into_pyresult(vm)
}
}
impl<F, $($T,)* R> 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<F, S, $($T,)* R> IntoPyNativeFunc<(RefParam<S>, $(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<S>, $($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());
}
}
-5
View File
@@ -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;
}
+18
View File
@@ -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
};
}
}
}
+7 -7
View File
@@ -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<dyn Fn(&VirtualMachine, PyObjectRef) -> PyResult + Send + Sync>;
pub type PyGetterFunc = Box<py_dyn_fn!(dyn Fn(&VirtualMachine, PyObjectRef) -> PyResult)>;
pub type PySetterFunc =
FunctionBox<dyn Fn(&VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult<()> + Send + Sync>;
Box<py_dyn_fn!(dyn Fn(&VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult<()>)>;
pub trait IntoPyGetterFunc<T> {
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::<S>::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::<S>::try_from_object(vm, obj)?;
let value = V::try_from_object(vm, value)?;
(self)(&zelf, value, vm).into_noresult()
+2 -2
View File
@@ -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<PyObjectRef>, OptionalArg<PyObjectRef>) -> PyResult
+ Send
+ Sync,
+1 -1
View File
@@ -68,7 +68,7 @@ mod winreg;
#[cfg(not(any(target_arch = "wasm32", target_os = "redox")))]
mod zlib;
pub type StdlibInitFunc = Box<dyn Fn(&VirtualMachine) -> PyObjectRef + Send + Sync>;
pub type StdlibInitFunc = Box<py_dyn_fn!(dyn Fn(&VirtualMachine) -> PyObjectRef)>;
pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
#[allow(unused_mut)]
-1
View File
@@ -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"] }
+1 -30
View File
@@ -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<Arena<JsValue>> = 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))
+2 -3
View File
@@ -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(())
+1 -1
View File
@@ -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(