diff --git a/examples/call_between_rust_and_python.rs b/examples/call_between_rust_and_python.rs index 5913042289..c139309e85 100644 --- a/examples/call_between_rust_and_python.rs +++ b/examples/call_between_rust_and_python.rs @@ -19,14 +19,12 @@ pub fn main() { let module = vm.import("call_between_rust_and_python", None, 0).unwrap(); let init_fn = module.get_attr("python_callback", vm).unwrap(); - vm.invoke(&init_fn, ()).unwrap(); + init_fn.call((), vm).unwrap(); let take_string_fn = module.get_attr("take_string", vm).unwrap(); - vm.invoke( - &take_string_fn, - (String::from("Rust string sent to python"),), - ) - .unwrap(); + take_string_fn + .call((String::from("Rust string sent to python"),), vm) + .unwrap(); }) } diff --git a/examples/package_embed.rs b/examples/package_embed.rs index fe6b07cec1..bd3d268856 100644 --- a/examples/package_embed.rs +++ b/examples/package_embed.rs @@ -8,7 +8,7 @@ fn py_main(interp: &Interpreter) -> vm::PyResult { .expect("add path"); let module = vm.import("package_embed", None, 0)?; let name_func = module.get_attr("context", vm)?; - let result = vm.invoke(&name_func, ())?; + let result = name_func.call((), vm)?; let result: PyStrRef = result.get_attr("name", vm)?.try_into_value(vm)?; vm::PyResult::Ok(result) }) diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs index d88f69a47a..b34206422b 100644 --- a/stdlib/src/array.rs +++ b/stdlib/src/array.rs @@ -17,13 +17,12 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyObjectRef { .get_attr("MutableSequence", vm) .expect("Expect collections.abc has MutableSequence type."); - vm.invoke( - &mutable_sequence - .get_attr("register", vm) - .expect("Expect collections.abc.MutableSequence has register method."), - (array,), - ) - .expect("Expect collections.abc.MutableSequence.register(array.array) not fail."); + let register = &mutable_sequence + .get_attr("register", vm) + .expect("Expect collections.abc.MutableSequence has register method."); + register + .call((array,), vm) + .expect("Expect collections.abc.MutableSequence.register(array.array) not fail."); module } diff --git a/stdlib/src/bisect.rs b/stdlib/src/bisect.rs index 1358ab22ef..25cc5b608b 100644 --- a/stdlib/src/bisect.rs +++ b/stdlib/src/bisect.rs @@ -70,7 +70,7 @@ mod _bisect { let mid = (lo + hi) / 2; let a_mid = a.get_item(&mid, vm)?; let comp = if let Some(ref key) = key { - vm.invoke(key, (a_mid,))? + key.call((a_mid,), vm)? } else { a_mid }; @@ -96,7 +96,7 @@ mod _bisect { let mid = (lo + hi) / 2; let a_mid = a.get_item(&mid, vm)?; let comp = if let Some(ref key) = key { - vm.invoke(key, (a_mid,))? + key.call((a_mid,), vm)? } else { a_mid }; @@ -112,7 +112,7 @@ mod _bisect { #[pyfunction] fn insort_left(BisectArgs { a, x, lo, hi, key }: BisectArgs, vm: &VirtualMachine) -> PyResult { let x = if let Some(ref key) = key { - vm.invoke(key, (x,))? + key.call((x,), vm)? } else { x }; @@ -132,7 +132,7 @@ mod _bisect { #[pyfunction] fn insort_right(BisectArgs { a, x, lo, hi, key }: BisectArgs, vm: &VirtualMachine) -> PyResult { let x = if let Some(ref key) = key { - vm.invoke(key, (x,))? + key.call((x,), vm)? } else { x }; diff --git a/stdlib/src/csv.rs b/stdlib/src/csv.rs index 5f01285deb..be1b687156 100644 --- a/stdlib/src/csv.rs +++ b/stdlib/src/csv.rs @@ -60,7 +60,7 @@ mod _csv { ) -> PyResult { let write = match vm.get_attribute_opt(file.clone(), "write")? { Some(write_meth) => write_meth, - None if vm.is_callable(&file) => file, + None if file.is_callable() => file, None => { return Err(vm.new_type_error("argument 1 must have a \"write\" method".to_owned())) } @@ -309,7 +309,7 @@ mod _csv { let s = std::str::from_utf8(&buffer[..buffer_offset]) .map_err(|_| vm.new_unicode_decode_error("csv not utf8".to_owned()))?; - vm.invoke(&self.write, (s.to_owned(),)) + self.write.call((s,), vm) } #[pymethod] diff --git a/stdlib/src/json.rs b/stdlib/src/json.rs index 7a2a1ce97c..31871130ca 100644 --- a/stdlib/src/json.rs +++ b/stdlib/src/json.rs @@ -5,9 +5,9 @@ mod machinery; mod _json { use super::machinery; use crate::vm::{ - builtins::{PyBaseExceptionRef, PyStrRef, PyTypeRef}, + builtins::{PyBaseExceptionRef, PyStrRef, PyType, PyTypeRef}, convert::{ToPyObject, ToPyResult}, - function::OptionalArg, + function::{IntoFuncArgs, OptionalArg}, protocol::PyIterReturn, types::{Callable, Constructor}, AsObject, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, @@ -91,25 +91,23 @@ mod _json { '{' => { // TODO: parse the object in rust let parse_obj = self.ctx.get_attr("parse_object", vm)?; - return PyIterReturn::from_pyresult( - vm.invoke( - &parse_obj, - ( - (pystr, next_idx), - self.strict, - scan_once, - self.object_hook.clone(), - self.object_pairs_hook.clone(), - ), + let result = parse_obj.call( + ( + (pystr, next_idx), + self.strict, + scan_once, + self.object_hook.clone(), + self.object_pairs_hook.clone(), ), vm, ); + return PyIterReturn::from_pyresult(result, vm); } '[' => { // TODO: parse the array in rust let parse_array = self.ctx.get_attr("parse_array", vm)?; return PyIterReturn::from_pyresult( - vm.invoke(&parse_array, ((pystr, next_idx), scan_once)), + parse_array.call(((pystr, next_idx), scan_once), vm), vm, ); } @@ -138,11 +136,8 @@ mod _json { ($s:literal) => { if s.starts_with($s) { return Ok(PyIterReturn::Return( - vm.new_tuple(( - vm.invoke(&self.parse_constant, ($s.to_owned(),))?, - idx + $s.len(), - )) - .into(), + vm.new_tuple((self.parse_constant.call(($s,), vm)?, idx + $s.len())) + .into(), )); } }; @@ -181,12 +176,12 @@ mod _json { let ret = if has_decimal || has_exponent { // float if let Some(ref parse_float) = self.parse_float { - vm.invoke(parse_float, (buf.to_owned(),)) + parse_float.call((buf,), vm) } else { Ok(vm.ctx.new_float(f64::from_str(buf).unwrap()).into()) } } else if let Some(ref parse_int) = self.parse_int { - vm.invoke(parse_int, (buf.to_owned(),)) + parse_int.call((buf,), vm) } else { Ok(vm.new_pyobj(BigInt::from_str(buf).unwrap())) }; @@ -243,7 +238,7 @@ mod _json { ) -> PyBaseExceptionRef { let get_error = || -> PyResult<_> { let cls = vm.try_class("json", "JSONDecodeError")?; - let exc = vm.invoke(&cls, (e.msg, s, e.pos))?; + let exc = PyType::call(&cls, (e.msg, s, e.pos).into_args(vm), vm)?; exc.try_into_value(vm) }; match get_error() { diff --git a/stdlib/src/math.rs b/stdlib/src/math.rs index 6ed6def79e..5f60a66830 100644 --- a/stdlib/src/math.rs +++ b/stdlib/src/math.rs @@ -512,7 +512,7 @@ mod math { func_name.as_str(), ) })?; - vm.invoke(&method, ()) + method.call((), vm) } #[pyfunction] diff --git a/stdlib/src/pyexpat.rs b/stdlib/src/pyexpat.rs index c865fc8163..d620419e1c 100644 --- a/stdlib/src/pyexpat.rs +++ b/stdlib/src/pyexpat.rs @@ -59,7 +59,7 @@ mod _pyexpat { where T: IntoFuncArgs, { - vm.invoke(&handler.read().clone(), args).ok(); + handler.read().call(args, vm).ok(); } #[pyclass] diff --git a/stdlib/src/select.rs b/stdlib/src/select.rs index a6ecd10d00..9d3afa0e44 100644 --- a/stdlib/src/select.rs +++ b/stdlib/src/select.rs @@ -80,7 +80,7 @@ impl TryFromObject for Selectable { vm.ctx.interned_str("fileno").unwrap(), || "select arg must be an int or object with a fileno() method".to_owned(), )?; - vm.invoke(&meth, ())?.try_into_value(vm) + meth.call((), vm)?.try_into_value(vm) })?; Ok(Selectable { obj, fno }) } diff --git a/stdlib/src/sqlite.rs b/stdlib/src/sqlite.rs index a828e72012..fca32f3cc6 100644 --- a/stdlib/src/sqlite.rs +++ b/stdlib/src/sqlite.rs @@ -390,7 +390,7 @@ mod _sqlite { .map(|val| value_to_object(val, db, vm)) .collect::>>()?; - let val = vm.invoke(func, args)?; + let val = func.call(args, vm)?; context.result_from_object(&val, vm) }; @@ -410,7 +410,7 @@ mod _sqlite { let args = std::slice::from_raw_parts(argv, argc as usize); let instance = context.aggregate_context::<*const PyObject>(); if (*instance).is_null() { - match vm.invoke(cls, ()) { + match cls.call((), vm) { Ok(obj) => *instance = obj.into_raw(), Err(exc) => { return context.result_exception( @@ -450,7 +450,7 @@ mod _sqlite { let text2 = ptr_to_string(b_ptr.cast(), b_len, null_mut(), vm)?; let text2 = vm.ctx.new_str(text2); - let val = vm.invoke(callable, (text1, text2))?; + let val = callable.call((text1, text2), vm)?; let Some(val) = val.to_number().index(vm) else { return Ok(0); }; @@ -505,7 +505,7 @@ mod _sqlite { let db_name = ptr_to_str(db_name, vm)?; let access = ptr_to_str(access, vm)?; - let val = vm.invoke(callable, (action, arg1, arg2, db_name, access))?; + let val = callable.call((action, arg1, arg2, db_name, access), vm)?; let Some(val) = val.payload::() else { return Ok(SQLITE_DENY); }; @@ -525,7 +525,8 @@ mod _sqlite { let expanded = sqlite3_expanded_sql(stmt.cast()); let f = || -> PyResult<()> { let stmt = ptr_to_str(expanded, vm).or_else(|_| ptr_to_str(sql.cast(), vm))?; - vm.invoke(callable, (stmt,)).map(drop) + callable.call((stmt,), vm)?; + Ok(()) }; let _ = f(); 0 @@ -533,7 +534,7 @@ mod _sqlite { unsafe extern "C" fn progress_callback(data: *mut c_void) -> c_int { let (callable, vm) = (*data.cast::()).retrive(); - if let Ok(val) = vm.invoke(callable, ()) { + if let Ok(val) = callable.call((), vm) { if let Ok(val) = val.is_true(vm) { return val as c_int; } @@ -661,10 +662,10 @@ mod _sqlite { .new_tuple(vec![obj.class().to_owned().into(), proto.clone()]); if let Some(adapter) = adapters().get_item_opt(key.as_object(), vm)? { - return vm.invoke(&adapter, (obj,)); + return adapter.call((obj,), vm); } if let Ok(adapter) = proto.get_attr("__adapt__", vm) { - match vm.invoke(&adapter, (obj,)) { + match adapter.call((obj,), vm) { Ok(val) => return Ok(val), Err(exc) => { if !exc.fast_isinstance(vm.ctx.exceptions.type_error) { @@ -674,7 +675,7 @@ mod _sqlite { } } if let Ok(adapter) = obj.get_attr("__conform__", vm) { - match vm.invoke(&adapter, (proto,)) { + match adapter.call((proto,), vm) { Ok(val) => return Ok(val), Err(exc) => { if !exc.fast_isinstance(vm.ctx.exceptions.type_error) { @@ -1228,7 +1229,7 @@ mod _sqlite { fn iterdump(zelf: PyRef, vm: &VirtualMachine) -> PyResult { let module = vm.import("sqlite3.dump", None, 0)?; let func = module.get_attr("_iterdump", vm)?; - vm.invoke(&func, (zelf,)) + func.call((zelf,), vm) } #[pymethod] @@ -1699,7 +1700,7 @@ mod _sqlite { std::slice::from_raw_parts(blob.cast::(), nbytes as usize) }; let blob = vm.ctx.new_bytes(blob.to_vec()); - vm.invoke(&converter, (blob,))? + converter.call((blob,), vm)? } } else { let col_type = st.column_type(i); @@ -1724,7 +1725,7 @@ mod _sqlite { PyByteArray::from(text).into_ref(vm).into() } else { let bytes = vm.ctx.new_bytes(text); - vm.invoke(&text_factory, (bytes,))? + text_factory.call((bytes,), vm)? } } SQLITE_BLOB => { @@ -1765,7 +1766,8 @@ mod _sqlite { let row = vm.ctx.new_tuple(row); if let Some(row_factory) = zelf.row_factory.to_owned() { - vm.invoke(&row_factory, (zelf.to_owned(), row)) + row_factory + .call((zelf.to_owned(), row), vm) .map(PyIterReturn::Return) } else { Ok(PyIterReturn::Return(row.into())) diff --git a/vm/src/builtins/bool.rs b/vm/src/builtins/bool.rs index 06cf95de50..ed21290f65 100644 --- a/vm/src/builtins/bool.rs +++ b/vm/src/builtins/bool.rs @@ -37,7 +37,7 @@ impl PyObjectRef { Some(method_or_err) => { // If descriptor returns Error, propagate it further let method = method_or_err?; - let bool_obj = vm.invoke(&method, ())?; + let bool_obj = method.call((), vm)?; if !bool_obj.fast_isinstance(vm.ctx.types.bool_type) { return Err(vm.new_type_error(format!( "__bool__ should return bool, returned type {}", @@ -50,7 +50,7 @@ impl PyObjectRef { None => match vm.get_method(self, identifier!(vm, __len__)) { Some(method_or_err) => { let method = method_or_err?; - let bool_obj = vm.invoke(&method, ())?; + let bool_obj = method.call((), vm)?; let int_obj = bool_obj.payload::().ok_or_else(|| { vm.new_type_error(format!( "'{}' object cannot be interpreted as an integer", diff --git a/vm/src/builtins/classmethod.rs b/vm/src/builtins/classmethod.rs index fb11113051..6398108adf 100644 --- a/vm/src/builtins/classmethod.rs +++ b/vm/src/builtins/classmethod.rs @@ -58,7 +58,7 @@ impl GetDescriptor for PyClassMethod { let call_descr_get: PyResult = zelf.callable.lock().get_attr("__get__", vm); match call_descr_get { Err(_) => Ok(PyBoundMethod::new_ref(cls, zelf.callable.lock().clone(), &vm.ctx).into()), - Ok(call_descr_get) => vm.invoke(&call_descr_get, (cls.clone(), cls)), + Ok(call_descr_get) => call_descr_get.call((cls.clone(), cls), vm), } } } diff --git a/vm/src/builtins/complex.rs b/vm/src/builtins/complex.rs index 3aaa4d6971..baf1d28754 100644 --- a/vm/src/builtins/complex.rs +++ b/vm/src/builtins/complex.rs @@ -60,7 +60,7 @@ impl PyObjectRef { return Ok(Some((complex.value, true))); } if let Some(method) = vm.get_method(self.clone(), identifier!(vm, __complex__)) { - let result = vm.invoke(&method?, ())?; + let result = method?.call((), vm)?; // TODO: returning strict subclasses of complex in __complex__ is deprecated return match result.payload::() { Some(complex_obj) => Ok(Some((complex_obj.value, true))), diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index 5c9c6fcded..8841bc6d8e 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -72,7 +72,7 @@ impl PyDict { }; let dict = &self.entries; if let Some(keys) = vm.get_method(other.clone(), vm.ctx.intern_str("keys")) { - let keys = vm.invoke(&keys?, ())?.get_iter(vm)?; + let keys = keys?.call((), vm)?.get_iter(vm)?; while let PyIterReturn::Return(key) = keys.next(vm)? { let val = other.get_item(&*key, vm)?; dict.insert(vm, &*key, val)?; @@ -511,7 +511,7 @@ impl Py { vm: &VirtualMachine, ) -> PyResult> { vm.get_method(self.to_owned().into(), identifier!(vm, __missing__)) - .map(|methods| vm.invoke(&methods?, (key.to_pyobject(vm),))) + .map(|methods| methods?.call((key.to_pyobject(vm),), vm)) .transpose() } diff --git a/vm/src/builtins/filter.rs b/vm/src/builtins/filter.rs index ccdbd0b110..0f823e6b65 100644 --- a/vm/src/builtins/filter.rs +++ b/vm/src/builtins/filter.rs @@ -57,7 +57,7 @@ impl IterNext for PyFilter { } else { // the predicate itself can raise StopIteration which does stop the filter // iteration - match PyIterReturn::from_pyresult(vm.invoke(predicate, (next_obj.clone(),)), vm)? { + match PyIterReturn::from_pyresult(predicate.call((next_obj.clone(),), vm), vm)? { PyIterReturn::Return(obj) => obj, PyIterReturn::StopIteration(v) => return Ok(PyIterReturn::StopIteration(v)), } diff --git a/vm/src/builtins/function.rs b/vm/src/builtins/function.rs index 2c41b5224a..6c476b55a7 100644 --- a/vm/src/builtins/function.rs +++ b/vm/src/builtins/function.rs @@ -469,7 +469,7 @@ impl Callable for PyBoundMethod { #[inline] fn call(zelf: &crate::Py, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult { args.prepend_arg(zelf.object.clone()); - vm.invoke(&zelf.function, args) + zelf.function.call(args, vm) } } diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index 2374e7746b..b53acce415 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -510,7 +510,7 @@ fn do_sort( if let Some(ref key_func) = key_func { let mut items = values .iter() - .map(|x| Ok((x.clone(), vm.invoke(key_func, (x.clone(),))?))) + .map(|x| Ok((x.clone(), key_func.call((x.clone(),), vm)?))) .collect::, _>>()?; timsort::try_sort_by_gt(&mut items, |a, b| cmp(&a.1, &b.1))?; *values = items.into_iter().map(|(val, _)| val).collect(); diff --git a/vm/src/builtins/map.rs b/vm/src/builtins/map.rs index 0851114e79..26fac14106 100644 --- a/vm/src/builtins/map.rs +++ b/vm/src/builtins/map.rs @@ -64,7 +64,7 @@ impl IterNext for PyMap { } // the mapper itself can raise StopIteration which does stop the map iteration - PyIterReturn::from_pyresult(vm.invoke(&zelf.mapper, next_objs), vm) + PyIterReturn::from_pyresult(zelf.mapper.call(next_objs, vm), vm) } } diff --git a/vm/src/builtins/module.rs b/vm/src/builtins/module.rs index f907a538b0..d73c02540f 100644 --- a/vm/src/builtins/module.rs +++ b/vm/src/builtins/module.rs @@ -50,7 +50,7 @@ impl PyModule { return Ok(attr); } if let Ok(getattr) = zelf.dict().get_item(identifier!(vm, __getattr__), vm) { - return vm.invoke(&getattr, (name,)); + return getattr.call((name,), vm); } let module_name = if let Some(name) = Self::name(zelf.to_owned(), vm) { format!(" '{name}'") @@ -71,7 +71,7 @@ impl PyModule { fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { let importlib = vm.import("_frozen_importlib", None, 0)?; let module_repr = importlib.get_attr("_module_repr", vm)?; - vm.invoke(&module_repr, (zelf,)) + module_repr.call((zelf,), vm) } #[pymethod(magic)] diff --git a/vm/src/builtins/object.rs b/vm/src/builtins/object.rs index 2b0e061cd6..6beef5eaad 100644 --- a/vm/src/builtins/object.rs +++ b/vm/src/builtins/object.rs @@ -313,7 +313,7 @@ impl PyBaseObject { let typ_obj: PyObjectRef = obj.class().to_owned().into(); let class_reduce = typ_obj.get_attr(__reduce__, vm)?; if !class_reduce.is(&object_reduce) { - return vm.invoke(&reduce, ()); + return reduce.call((), vm); } } common_reduce(obj, proto, vm) @@ -353,10 +353,10 @@ fn common_reduce(obj: PyObjectRef, proto: usize, vm: &VirtualMachine) -> PyResul if proto >= 2 { let reducelib = vm.import("__reducelib", None, 0)?; let reduce_2 = reducelib.get_attr("reduce_2", vm)?; - vm.invoke(&reduce_2, (obj,)) + reduce_2.call((obj,), vm) } else { let copyreg = vm.import("copyreg", None, 0)?; let reduce_ex = copyreg.get_attr("_reduce_ex", vm)?; - vm.invoke(&reduce_ex, (obj, proto)) + reduce_ex.call((obj, proto), vm) } } diff --git a/vm/src/builtins/property.rs b/vm/src/builtins/property.rs index fde9eb4036..075afc2cd1 100644 --- a/vm/src/builtins/property.rs +++ b/vm/src/builtins/property.rs @@ -49,7 +49,7 @@ impl GetDescriptor for PyProperty { if vm.is_none(&obj) { Ok(zelf.into()) } else if let Some(getter) = zelf.getter.read().as_ref() { - vm.invoke(getter, (obj,)) + getter.call((obj,), vm) } else { Err(vm.new_attribute_error("unreadable attribute".to_string())) } @@ -71,14 +71,14 @@ impl PyProperty { match value { PySetterValue::Assign(value) => { if let Some(setter) = zelf.setter.read().as_ref() { - vm.invoke(setter, (obj, value)).map(drop) + setter.call((obj, value), vm).map(drop) } else { Err(vm.new_attribute_error("can't set attribute".to_owned())) } } PySetterValue::Delete => { if let Some(deleter) = zelf.deleter.read().as_ref() { - vm.invoke(deleter, (obj,)).map(drop) + deleter.call((obj,), vm).map(drop) } else { Err(vm.new_attribute_error("can't delete attribute".to_owned())) } diff --git a/vm/src/builtins/staticmethod.rs b/vm/src/builtins/staticmethod.rs index 44add3e4e0..47630360cd 100644 --- a/vm/src/builtins/staticmethod.rs +++ b/vm/src/builtins/staticmethod.rs @@ -180,7 +180,7 @@ impl Callable for PyStaticMethod { #[inline] fn call(zelf: &crate::Py, args: FuncArgs, vm: &VirtualMachine) -> PyResult { let callable = zelf.callable.lock().clone(); - vm.invoke(&callable, args) + callable.call(args, vm) } } diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index ddc9aa92ba..514db2bfc5 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -377,7 +377,7 @@ impl PyStr { .to_pyobject(vm)) } else if let Some(radd) = vm.get_method(other.clone(), identifier!(vm, __radd__)) { // hack to get around not distinguishing number add from seq concat - vm.invoke(&radd?, (zelf,)) + radd?.call((zelf,), vm) } else { Err(vm.new_type_error(format!( "can only concatenate str (not \"{}\") to str", diff --git a/vm/src/builtins/super.rs b/vm/src/builtins/super.rs index 7f2494292e..dd68b5c3c3 100644 --- a/vm/src/builtins/super.rs +++ b/vm/src/builtins/super.rs @@ -6,8 +6,8 @@ See also [CPython source code.](https://github.com/python/cpython/blob/50b48572d use super::{PyStrRef, PyType, PyTypeRef}; use crate::{ class::PyClassImpl, - function::OptionalArg, - types::{Constructor, GetAttr, GetDescriptor}, + function::{IntoFuncArgs, OptionalArg}, + types::{Callable, Constructor, GetAttr, GetDescriptor}, AsObject, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; @@ -188,7 +188,7 @@ impl GetDescriptor for PySuper { Ok(PySuper::new(zelf.typ.clone(), obj, vm)?.into_pyobject(vm)) } else { let obj = vm.unwrap_or_none(zelf.obj.clone().map(|(o, _)| o)); - vm.invoke(zelf.class(), (zelf.typ.clone(), obj)) + PyType::call(zelf.class(), (zelf.typ.clone(), obj).into_args(vm), vm) } } } diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs index f277ae92b2..ffe184a7c7 100644 --- a/vm/src/builtins/type.rs +++ b/vm/src/builtins/type.rs @@ -858,24 +858,23 @@ impl PyType { }) .collect::>>()?; for (obj, name, set_name) in attributes { - vm.invoke(&set_name, (typ.clone(), name.to_owned())) - .map_err(|e| { - let err = vm.new_runtime_error(format!( - "Error calling __set_name__ on '{}' instance {} in '{}'", - obj.class().name(), - name, - typ.name() - )); - err.set_cause(Some(e)); - err - })?; + set_name.call((typ.clone(), name), vm).map_err(|e| { + let err = vm.new_runtime_error(format!( + "Error calling __set_name__ on '{}' instance {} in '{}'", + obj.class().name(), + name, + typ.name() + )); + err.set_cause(Some(e)); + err + })?; } if let Some(init_subclass) = typ.get_super_attr(identifier!(vm, __init_subclass__)) { let init_subclass = vm .call_get_descriptor_specific(init_subclass.clone(), None, Some(typ.clone().into())) .unwrap_or(Ok(init_subclass))?; - vm.invoke(&init_subclass, kwargs)?; + init_subclass.call(kwargs, vm)?; }; Ok(typ.into()) diff --git a/vm/src/bytesinner.rs b/vm/src/bytesinner.rs index 07ff56a476..fce0522a4b 100644 --- a/vm/src/bytesinner.rs +++ b/vm/src/bytesinner.rs @@ -90,7 +90,7 @@ impl ByteInnerNewOptions { if let Some(bytes_method) = vm.get_method(obj, identifier!(vm, __bytes__)) { // construct an exact bytes from __bytes__ slot. // if __bytes__ return a bytes, use the bytes object except we are the subclass of the bytes - let bytes = vm.invoke(&bytes_method?, ())?; + let bytes = bytes_method?.call((), vm)?; let bytes = if cls.is(PyBytes::class(vm)) { match bytes.downcast::() { Ok(b) => return Ok(b), diff --git a/vm/src/cformat.rs b/vm/src/cformat.rs index 998ac6744e..4deb874d87 100644 --- a/vm/src/cformat.rs +++ b/vm/src/cformat.rs @@ -59,7 +59,7 @@ fn spec_format_bytes( } obj => { if let Some(method) = vm.get_method(obj.clone(), identifier!(vm, __int__)) { - let result = vm.invoke(&method?, ())?; + let result = method?.call((), vm)?; if let Some(i) = result.payload::() { return Ok(spec.format_number(i.as_bigint()).into_bytes()); } @@ -152,7 +152,7 @@ fn spec_format_string( } obj => { if let Some(method) = vm.get_method(obj.clone(), identifier!(vm, __int__)) { - let result = vm.invoke(&method?, ())?; + let result = method?.call((), vm)?; if let Some(i) = result.payload::() { return Ok(spec.format_number(i.as_bigint())); } diff --git a/vm/src/codecs.rs b/vm/src/codecs.rs index af3563fbc1..4bf5709dd6 100644 --- a/vm/src/codecs.rs +++ b/vm/src/codecs.rs @@ -63,7 +63,7 @@ impl PyCodec { Some(errors) => vec![obj, errors.into()], None => vec![obj], }; - let res = vm.invoke(self.get_encode_func(), args)?; + let res = self.get_encode_func().call(args, vm)?; let res = res .downcast::() .ok() @@ -85,7 +85,7 @@ impl PyCodec { Some(errors) => vec![obj, errors.into()], None => vec![obj], }; - let res = vm.invoke(self.get_decode_func(), args)?; + let res = self.get_decode_func().call(args, vm)?; let res = res .downcast::() .ok() @@ -185,7 +185,7 @@ impl CodecsRegistry { } pub fn register(&self, search_function: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - if !vm.is_callable(&search_function) { + if !search_function.is_callable() { return Err(vm.new_type_error("argument must be callable".to_owned())); } self.inner.write().search_path.push(search_function); @@ -230,7 +230,7 @@ impl CodecsRegistry { }; let encoding = PyStr::from(encoding.into_owned()).into_ref(vm); for func in search_path { - let res = vm.invoke(&func, (encoding.clone(),))?; + let res = func.call((encoding.clone(),), vm)?; let res: Option = res.try_into_value(vm)?; if let Some(codec) = res { let mut inner = self.inner.write(); diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 5174301ac0..e6f3b40051 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -1,17 +1,17 @@ use self::types::{PyBaseException, PyBaseExceptionRef}; -use crate::builtins::tuple::IntoPyTuple; -use crate::common::lock::PyRwLock; -use crate::common::str::ReprOverflowError; +use crate::common::{lock::PyRwLock, str::ReprOverflowError}; use crate::{ builtins::{ - traceback::PyTracebackRef, PyNone, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, + traceback::PyTracebackRef, tuple::IntoPyTuple, PyNone, PyStr, PyStrRef, PyTuple, + PyTupleRef, PyType, PyTypeRef, }, class::{PyClassImpl, StaticType}, convert::{ToPyException, ToPyObject}, - function::{ArgIterable, FuncArgs}, + function::{ArgIterable, FuncArgs, IntoFuncArgs}, py_io::{self, Write}, stdlib::sys, suggestion::offer_suggestions, + types::Callable, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; @@ -54,7 +54,7 @@ impl VirtualMachine { }; if let Ok(excepthook) = vm.sys_module.get_attr("excepthook", vm) { let (exc_type, exc_val, exc_tb) = vm.split_exception(exc.clone()); - if let Err(eh_exc) = vm.invoke(&excepthook, (exc_type, exc_val, exc_tb)) { + if let Err(eh_exc) = excepthook.call((exc_type, exc_val, exc_tb), vm) { write_fallback(&eh_exc, "Error in sys.excepthook:"); write_fallback(&exc, "Original exception was:"); } @@ -208,7 +208,7 @@ impl VirtualMachine { args: Vec, ) -> PyResult { // TODO: fast-path built-in exceptions by directly instantiating them? Is that really worth it? - let res = self.invoke(&cls, args)?; + let res = PyType::call(&cls, args.into_args(self), self)?; PyBaseExceptionRef::try_from_object(self, res) } } diff --git a/vm/src/frame.rs b/vm/src/frame.rs index af3cd1aa1d..c3a9682b7a 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -436,7 +436,7 @@ impl ExecutingFrame<'_> { Either::A(coro) => coro .throw(gen, exc_type, exc_val, exc_tb, vm) .to_pyresult(vm), // FIXME: - Either::B(meth) => vm.invoke(&meth, (exc_type, exc_val, exc_tb)), + Either::B(meth) => meth.call((exc_type, exc_val, exc_tb), vm), }; return ret.map(ExecutionResult::Yield).or_else(|err| { self.pop_value(); @@ -889,7 +889,7 @@ impl ExecutingFrame<'_> { } else { (vm.ctx.none(), vm.ctx.none(), vm.ctx.none()) }; - let exit_res = vm.invoke(&exit, args)?; + let exit_res = exit.call(args, vm)?; self.push_value(exit_res); Ok(None) @@ -938,7 +938,7 @@ impl ExecutingFrame<'_> { ) }, )?; - vm.invoke(&await_method, ())? + await_method.call((), vm)? }; self.push_value(awaitable); Ok(None) @@ -1353,7 +1353,7 @@ impl ExecutingFrame<'_> { #[inline] fn execute_call(&mut self, args: FuncArgs, vm: &VirtualMachine) -> FrameResult { let func_ref = self.pop_value(); - let value = vm.invoke(&func_ref, args)?; + let value = func_ref.call(args, vm)?; self.push_value(value); Ok(None) } @@ -1429,7 +1429,7 @@ impl ExecutingFrame<'_> { None if vm.is_none(&val) => PyIter::new(gen).next(vm), None => { let meth = gen.to_owned().get_attr("send", vm)?; - PyIterReturn::from_pyresult(vm.invoke(&meth, (val,)), vm) + PyIterReturn::from_pyresult(meth.call((val,), vm), vm) } } } @@ -1716,7 +1716,7 @@ impl ExecutingFrame<'_> { .clone() .get_attr("displayhook", vm) .map_err(|_| vm.new_runtime_error("lost sys.displayhook".to_owned()))?; - vm.invoke(&displayhook, (expr,))?; + displayhook.call((expr,), vm)?; Ok(None) } diff --git a/vm/src/function/protocol.rs b/vm/src/function/protocol.rs index 7c0396da47..5d6c0df8af 100644 --- a/vm/src/function/protocol.rs +++ b/vm/src/function/protocol.rs @@ -4,20 +4,30 @@ use crate::{ convert::ToPyObject, identifier, protocol::{PyIter, PyIterIter, PyMapping, PyMappingMethods}, - types::AsMapping, + types::{AsMapping, GenericMethod}, AsObject, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, }; use std::{borrow::Borrow, marker::PhantomData, ops::Deref}; -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct ArgCallable { obj: PyObjectRef, + call: GenericMethod, } impl ArgCallable { #[inline(always)] pub fn invoke(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult { - vm.invoke(&self.obj, args) + (self.call)(&self.obj, args.into_args(vm), vm) + } +} + +impl std::fmt::Debug for ArgCallable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ArgCallable") + .field("obj", &self.obj) + .field("call", &format!("{:08x}", self.call as usize)) + .finish() } } @@ -44,11 +54,11 @@ impl From for PyObjectRef { impl TryFromObject for ArgCallable { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - if vm.is_callable(&obj) { - Ok(ArgCallable { obj }) - } else { - Err(vm.new_type_error(format!("'{}' object is not callable", obj.class().name()))) - } + let Some(callable) = obj.to_callable() else { + return Err(vm.new_type_error(format!("'{}' object is not callable", obj.class().name()))); + }; + let call = callable.call; + Ok(ArgCallable { obj, call }) } } diff --git a/vm/src/import.rs b/vm/src/import.rs index 9fc8e56ea4..1e036ee36b 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -24,7 +24,7 @@ pub(crate) fn init_importlib_base(vm: &mut VirtualMachine) -> PyResult Option> { + PyCallable::new(self) + } + + #[inline] + pub fn is_callable(&self) -> bool { + self.to_callable().is_some() + } + + /// PyObject_Call*Arg* series + pub fn call(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult { + self.call_with_args(args.into_args(vm), vm) + } + + /// PyObject_Call + pub fn call_with_args(&self, args: FuncArgs, vm: &VirtualMachine) -> PyResult { + vm_trace!("Invoke: {:?} {:?}", callable, args); + let Some(callable) = self.to_callable() else { + return Err(vm.new_type_error(format!( + "'{}' object is not callable", + self.class().name() + ))); + }; + callable.invoke(args, vm) + } +} + +pub struct PyCallable<'a> { + pub obj: &'a PyObject, + pub call: GenericMethod, +} + +impl<'a> PyCallable<'a> { + pub fn new(obj: &'a PyObject) -> Option { + let call = obj.class().mro_find_map(|cls| cls.slots.call.load())?; + Some(PyCallable { obj, call }) + } + + pub fn invoke(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult { + vm.trace_event(TraceEvent::Call)?; + let result = (self.call)(self.obj, args.into_args(vm), vm); + vm.trace_event(TraceEvent::Return)?; + result + } +} + +/// Trace events for sys.settrace and sys.setprofile. +enum TraceEvent { + Call, + Return, +} + +impl std::fmt::Display for TraceEvent { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use TraceEvent::*; + match self { + Call => write!(f, "call"), + Return => write!(f, "return"), + } + } +} + +impl VirtualMachine { + /// Call registered trace function. + #[inline] + fn trace_event(&self, event: TraceEvent) -> PyResult<()> { + if self.use_tracing.get() { + self._trace_event_inner(event) + } else { + Ok(()) + } + } + fn _trace_event_inner(&self, event: TraceEvent) -> PyResult<()> { + let trace_func = self.trace_func.borrow().to_owned(); + let profile_func = self.profile_func.borrow().to_owned(); + if self.is_none(&trace_func) && self.is_none(&profile_func) { + return Ok(()); + } + + let frame_ref = self.current_frame(); + if frame_ref.is_none() { + return Ok(()); + } + + let frame = frame_ref.unwrap().as_object().to_owned(); + let event = self.ctx.new_str(event.to_string()).into(); + let args = vec![frame, event, self.ctx.none()]; + + // temporarily disable tracing, during the call to the + // tracing function itself. + if !self.is_none(&trace_func) { + self.use_tracing.set(false); + let res = trace_func.call(args.clone(), self); + self.use_tracing.set(true); + res?; + } + + if !self.is_none(&profile_func) { + self.use_tracing.set(false); + let res = profile_func.call(args, self); + self.use_tracing.set(true); + res?; + } + Ok(()) + } +} diff --git a/vm/src/protocol/mod.rs b/vm/src/protocol/mod.rs index dae492628e..4170c20df6 100644 --- a/vm/src/protocol/mod.rs +++ b/vm/src/protocol/mod.rs @@ -1,4 +1,5 @@ mod buffer; +mod callable; mod iter; mod mapping; mod number; @@ -6,6 +7,7 @@ mod object; mod sequence; pub use buffer::{BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, VecBuffer}; +pub use callable::PyCallable; pub use iter::{PyIter, PyIterIter, PyIterReturn}; pub use mapping::{PyMapping, PyMappingMethods}; pub use number::{PyNumber, PyNumberMethods}; diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index 57e964d0fa..8aa90734cd 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -583,7 +583,7 @@ impl PyObject { if let Some(class_getitem) = vm.get_attribute_opt(self.to_owned(), identifier!(vm, __class_getitem__))? { - return vm.invoke(&class_getitem, (needle,)); + return class_getitem.call((needle,), vm); } } Err(vm.new_type_error(format!("'{}' object is not subscriptable", self.class()))) diff --git a/vm/src/signal.rs b/vm/src/signal.rs index bca7963426..7489282de2 100644 --- a/vm/src/signal.rs +++ b/vm/src/signal.rs @@ -37,8 +37,8 @@ fn trigger_signals(vm: &VirtualMachine) -> PyResult<()> { let triggered = trigger.swap(false, Ordering::Relaxed); if triggered { if let Some(handler) = &signal_handlers[signum] { - if vm.is_callable(handler) { - vm.invoke(handler, (signum, vm.ctx.none()))?; + if let Some(callable) = handler.to_callable() { + callable.invoke((signum, vm.ctx.none()), vm)?; } } } diff --git a/vm/src/stdlib/atexit.rs b/vm/src/stdlib/atexit.rs index 1359f461b2..dbeda76741 100644 --- a/vm/src/stdlib/atexit.rs +++ b/vm/src/stdlib/atexit.rs @@ -36,7 +36,7 @@ mod atexit { pub fn _run_exitfuncs(vm: &VirtualMachine) { let funcs: Vec<_> = std::mem::take(&mut *vm.state.atexit_funcs.lock()); for (func, args) in funcs.into_iter().rev() { - if let Err(e) = vm.invoke(&func, args) { + if let Err(e) = func.call(args, vm) { let exit = e.fast_isinstance(vm.ctx.exceptions.system_exit); vm.run_unraisable(e, Some("Error in atexit._run_exitfuncs".to_owned()), func); if exit { diff --git a/vm/src/stdlib/builtins.rs b/vm/src/stdlib/builtins.rs index a636d285ad..fa2d917682 100644 --- a/vm/src/stdlib/builtins.rs +++ b/vm/src/stdlib/builtins.rs @@ -77,8 +77,8 @@ mod builtins { } #[pyfunction] - fn callable(obj: PyObjectRef, vm: &VirtualMachine) -> bool { - vm.is_callable(&obj) + fn callable(obj: PyObjectRef) -> bool { + obj.is_callable() } #[pyfunction] @@ -354,7 +354,7 @@ mod builtins { .sys_module .get_attr(vm.ctx.intern_str("breakpointhook"), vm) { - Ok(hook) => vm.invoke(hook.as_ref(), args), + Ok(hook) => hook.as_ref().call(args, vm), Err(_) => Err(vm.new_runtime_error("lost sys.breakpointhook".to_owned())), } } @@ -498,9 +498,9 @@ mod builtins { let key_func = key_func.filter(|f| !vm.is_none(f)); if let Some(ref key_func) = key_func { - let mut x_key = vm.invoke(key_func, (x.clone(),))?; + let mut x_key = key_func.call((x.clone(),), vm)?; for y in candidates_iter { - let y_key = vm.invoke(key_func, (y.clone(),))?; + let y_key = key_func.call((y.clone(),), vm)?; if y_key.rich_compare_bool(&x_key, op, vm)? { x = y; x_key = y_key; @@ -617,7 +617,7 @@ mod builtins { args: (PyObjectRef, PyObjectRef, PyObjectRef)| -> Option { let method = obj.get_class_attr(identifier!(vm, __pow__))?; - let result = match vm.invoke(&method, args) { + let result = match method.call(args, vm) { Ok(x) => x, Err(e) => return Some(Err(e)), }; @@ -704,7 +704,7 @@ mod builtins { #[pyfunction] fn reversed(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { if let Some(reversed_method) = vm.get_method(obj.clone(), identifier!(vm, __reversed__)) { - vm.invoke(&reversed_method?, ()) + reversed_method?.call((), vm) } else { vm.get_method_or_type_error(obj.clone(), identifier!(vm, __getitem__), || { "argument to reversed() must be a sequence".to_owned() @@ -804,7 +804,7 @@ mod builtins { #[pyfunction] fn __import__(args: FuncArgs, vm: &VirtualMachine) -> PyResult { - vm.invoke(&vm.import_func, args) + vm.import_func.call(args, vm) } #[pyfunction] @@ -843,7 +843,7 @@ mod builtins { let mro_entries = vm.get_attribute_opt(base.clone(), identifier!(vm, __mro_entries__))?; let entries = match mro_entries { - Some(meth) => vm.invoke(&meth, (bases.clone(),))?, + Some(meth) => meth.call((bases.clone(),), vm)?, None => { if let Some(bases) = &mut new_bases { bases.push(base.clone()); @@ -900,10 +900,9 @@ mod builtins { let namespace = vm .get_attribute_opt(metaclass.clone(), identifier!(vm, __prepare__))? .map_or(Ok(vm.ctx.new_dict().into()), |prepare| { - vm.invoke( - &prepare, - FuncArgs::new(vec![name_obj.clone().into(), bases.clone()], kwargs.clone()), - ) + let args = + FuncArgs::new(vec![name_obj.clone().into(), bases.clone()], kwargs.clone()); + prepare.call(args, vm) })?; // Accept any PyMapping as namespace. @@ -926,10 +925,8 @@ mod builtins { )?; } - let class = vm.invoke( - &metaclass, - FuncArgs::new(vec![name_obj.into(), bases, namespace.into()], kwargs), - )?; + let args = FuncArgs::new(vec![name_obj.into(), bases, namespace.into()], kwargs); + let class = metaclass.call(args, vm)?; if let Some(ref classcell) = classcell { let classcell = classcell.get().ok_or_else(|| { diff --git a/vm/src/stdlib/codecs.rs b/vm/src/stdlib/codecs.rs index 6e7779fa8c..4eb639b64a 100644 --- a/vm/src/stdlib/codecs.rs +++ b/vm/src/stdlib/codecs.rs @@ -130,7 +130,7 @@ mod _codecs { vm.ctx.new_str(reason).into(), ], ); - let res = vm.invoke(self.handler_func()?, (encode_exc,))?; + let res = self.handler_func()?.call((encode_exc,), vm)?; let tuple_err = || { vm.new_type_error( "encoding error handler must return (str/bytes, int) tuple".to_owned(), @@ -174,7 +174,7 @@ mod _codecs { ], ); let handler = self.handler_func()?; - let res = vm.invoke(handler, (decode_exc.clone(),))?; + let res = handler.call((decode_exc.clone(),), vm)?; let new_data = decode_exc .get_arg(1) .ok_or_else(|| vm.new_type_error("object attribute not set".to_owned()))?; @@ -358,7 +358,7 @@ mod _codecs { let module = vm.import("_pycodecs", None, 0)?; module.get_attr(name, vm) })?; - vm.invoke(f, args) + f.call(args, vm) } macro_rules! delegate_pycodecs { ($name:ident, $args:ident, $vm:ident) => {{ diff --git a/vm/src/stdlib/functools.rs b/vm/src/stdlib/functools.rs index 9d653288a7..d13b9b84f6 100644 --- a/vm/src/stdlib/functools.rs +++ b/vm/src/stdlib/functools.rs @@ -26,7 +26,7 @@ mod _functools { let mut accumulator = start_value; for next_obj in iter { - accumulator = vm.invoke(&function, (accumulator, next_obj?))? + accumulator = function.call((accumulator, next_obj?), vm)? } Ok(accumulator) } diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 5d96457786..087d01008b 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -80,7 +80,8 @@ impl TryFromObject for Fildes { "argument must be an int, or have a fileno() method.".to_owned(), ) })?; - vm.invoke(&fileno_meth, ())? + fileno_meth + .call((), vm)? .downcast() .map_err(|_| vm.new_type_error("fileno() returned a non-integer".to_owned()))? } @@ -98,7 +99,6 @@ impl TryFromObject for Fildes { #[pymodule] mod _io { use super::*; - use crate::{ builtins::{ PyBaseExceptionRef, PyByteArray, PyBytes, PyBytesRef, PyIntRef, PyMemoryView, PyStr, @@ -111,14 +111,16 @@ mod _io { }, convert::ToPyObject, function::{ - ArgBytesLike, ArgIterable, ArgMemoryBuffer, Either, FuncArgs, OptionalArg, - OptionalOption, PySetterValue, + ArgBytesLike, ArgIterable, ArgMemoryBuffer, Either, FuncArgs, IntoFuncArgs, + OptionalArg, OptionalOption, PySetterValue, }, protocol::{ BufferDescriptor, BufferMethods, BufferResizeGuard, PyBuffer, PyIterReturn, VecBuffer, }, recursion::ReprGuard, - types::{Constructor, DefaultConstructor, Destructor, Initializer, IterNext, Iterable}, + types::{ + Callable, Constructor, DefaultConstructor, Destructor, Initializer, IterNext, Iterable, + }, vm::VirtualMachine, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, TryFromObject, @@ -463,7 +465,7 @@ mod _io { let read = instance.get_attr("read", vm)?; let mut res = Vec::new(); while size.map_or(true, |s| res.len() < s) { - let read_res = ArgBytesLike::try_from_object(vm, vm.invoke(&read, (1,))?)?; + let read_res = ArgBytesLike::try_from_object(vm, read.call((1,), vm)?)?; if read_res.with_ref(|b| b.is_empty()) { break; } @@ -865,7 +867,7 @@ mod _io { } } } - // vm.invoke(&raw.get_attr("seek", vm)?, args) + // raw.get_attr("seek", vm)?.call(args, vm) if self.writable() { self.flush(vm)?; } @@ -1198,7 +1200,7 @@ mod _io { .get_str_method(self.raw.clone().unwrap(), "readall") .transpose()?; if let Some(readall) = readall { - let res = vm.invoke(&readall, ())?; + let res = readall.call((), vm)?; let res = >::try_from_object(vm, res)?; let ret = if let Some(mut data) = data { if let Some(bytes) = res { @@ -3596,9 +3598,10 @@ mod _io { "Couldn't get FileIO, io.open likely isn't supported on your platform".to_owned(), ) })?; - let raw = vm.invoke( + let raw = PyType::call( file_io_class, - (file, mode.rawmode(), opts.closefd, opts.opener), + (file, mode.rawmode(), opts.closefd, opts.opener).into_args(vm), + vm, )?; let isatty = opts.buffering < 0 && { @@ -3631,12 +3634,12 @@ mod _io { } else { BufferedWriter::static_type() }; - let buffered = vm.invoke(cls, (raw, buffering))?; + let buffered = PyType::call(cls, (raw, buffering).into_args(vm), vm)?; match mode.encode { EncodeMode::Text => { let tio = TextIOWrapper::static_type(); - let wrapper = vm.invoke( + let wrapper = PyType::call( tio, ( buffered, @@ -3644,7 +3647,9 @@ mod _io { opts.errors, opts.newline, line_buffering, - ), + ) + .into_args(vm), + vm, )?; wrapper.set_attr("mode", vm.new_pyobj(mode_string), vm)?; Ok(wrapper) @@ -3886,7 +3891,7 @@ mod fileio { compute_mode(mode_str).map_err(|e| vm.new_value_error(e.error_msg(mode_str)))?; zelf.mode.store(mode); let fd = if let Some(opener) = args.opener { - let fd = vm.invoke(&opener, (name.clone(), flags))?; + let fd = opener.call((name.clone(), flags), vm)?; if !fd.fast_isinstance(vm.ctx.types.int_type) { return Err(vm.new_type_error("expected integer from opener".to_owned())); } diff --git a/vm/src/stdlib/itertools.rs b/vm/src/stdlib/itertools.rs index b5f5686c42..ee22a25748 100644 --- a/vm/src/stdlib/itertools.rs +++ b/vm/src/stdlib/itertools.rs @@ -470,7 +470,7 @@ mod decl { match obj { PyIterReturn::Return(obj) => { let args: Vec<_> = obj.try_to_value(vm)?; - PyIterReturn::from_pyresult(vm.invoke(function, args), vm) + PyIterReturn::from_pyresult(function.call(args, vm), vm) } PyIterReturn::StopIteration(v) => Ok(PyIterReturn::StopIteration(v)), } @@ -547,7 +547,7 @@ mod decl { }; let predicate = &zelf.predicate; - let verdict = vm.invoke(predicate, (obj.clone(),))?; + let verdict = predicate.call((obj.clone(),), vm)?; let verdict = verdict.try_to_bool(vm)?; if verdict { Ok(PyIterReturn::Return(obj)) @@ -629,7 +629,7 @@ mod decl { } }; let pred = predicate.clone(); - let pred_value = vm.invoke(&pred, (obj.clone(),))?; + let pred_value = pred.invoke((obj.clone(),), vm)?; if !pred_value.try_to_bool(vm)? { zelf.start_flag.store(true); return Ok(PyIterReturn::Return(obj)); @@ -726,7 +726,7 @@ mod decl { PyIterReturn::StopIteration(v) => return Ok(PyIterReturn::StopIteration(v)), }; let new_key = if let Some(ref kf) = self.key_func { - vm.invoke(kf, (new_value.clone(),))? + kf.call((new_value.clone(),), vm)? } else { new_value.clone() }; @@ -1043,7 +1043,7 @@ mod decl { let pred_value = if vm.is_none(predicate) { obj.clone() } else { - vm.invoke(predicate, (obj.clone(),))? + predicate.call((obj.clone(),), vm)? }; if !pred_value.try_to_bool(vm)? { @@ -1116,7 +1116,7 @@ mod decl { }; match &zelf.binop { None => vm._add(&value, &obj)?, - Some(op) => vm.invoke(op, (value, obj))?, + Some(op) => op.call((value, obj), vm)?, } } }; diff --git a/vm/src/stdlib/operator.rs b/vm/src/stdlib/operator.rs index f3cbfc4981..85124ab97c 100644 --- a/vm/src/stdlib/operator.rs +++ b/vm/src/stdlib/operator.rs @@ -563,13 +563,11 @@ mod _operator { // If we have kwargs, create a partial function that contains them and pass back that // along with the args. let partial = vm.import("functools", None, 0)?.get_attr("partial", vm)?; - let callable = vm.invoke( - &partial, - FuncArgs::new( - vec![zelf.class().to_owned().into(), zelf.name.clone().into()], - KwArgs::new(zelf.args.kwargs.clone()), - ), - )?; + let args = FuncArgs::new( + vec![zelf.class().to_owned().into(), zelf.name.clone().into()], + KwArgs::new(zelf.args.kwargs.clone()), + ); + let callable = partial.call(args, vm)?; Ok(vm.new_tuple((callable, vm.ctx.new_tuple(zelf.args.args.clone())))) } } diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 7804a4a2af..6069fd606a 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -150,7 +150,7 @@ impl FsPath { obj.class().name() ) })?; - let result = vm.invoke(&method, ())?; + let result = method.call((), vm)?; match1(result)?.map_err(|result| { vm.new_type_error(format!( "expected {}.__fspath__() to return str or bytes, not {}", diff --git a/vm/src/stdlib/signal.rs b/vm/src/stdlib/signal.rs index cb9b205edf..975296ba11 100644 --- a/vm/src/stdlib/signal.rs +++ b/vm/src/stdlib/signal.rs @@ -124,7 +124,7 @@ pub(crate) mod _signal { match usize::try_from_borrowed_object(vm, &handler).ok() { Some(SIG_DFL) => SIG_DFL, Some(SIG_IGN) => SIG_IGN, - None if vm.is_callable(&handler) => run_signal as libc::sighandler_t, + None if handler.is_callable() => run_signal as libc::sighandler_t, _ => return Err(vm.new_type_error( "signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object" .to_owned(), diff --git a/vm/src/stdlib/sre.rs b/vm/src/stdlib/sre.rs index 23e6725e5d..a07a048bf8 100644 --- a/vm/src/stdlib/sre.rs +++ b/vm/src/stdlib/sre.rs @@ -410,7 +410,7 @@ mod _sre { count, } = sub_args; - let (is_callable, filter) = if vm.is_callable(&repl) { + let (is_callable, filter) = if repl.is_callable() { (true, repl) } else { let is_template = if zelf.isbytes { @@ -421,8 +421,8 @@ mod _sre { if is_template { let re = vm.import("re", None, 0)?; let func = re.get_attr("_subx", vm)?; - let filter = vm.invoke(&func, (zelf.clone(), repl))?; - (vm.is_callable(&filter), filter) + let filter = func.call((zelf.clone(), repl), vm)?; + (filter.is_callable(), filter) } else { (false, repl) } @@ -444,7 +444,7 @@ mod _sre { if is_callable { let m = Match::new(&iter.state, zelf.clone(), string.clone()); - let ret = vm.invoke(&filter, (m.into_ref(vm),))?; + let ret = filter.call((m.into_ref(vm),), vm)?; sublist.push(ret); } else { sublist.push(filter.clone()); @@ -617,7 +617,7 @@ mod _sre { fn expand(zelf: PyRef, template: PyStrRef, vm: &VirtualMachine) -> PyResult { let re = vm.import("re", None, 0)?; let func = re.get_attr("_expand", vm)?; - vm.invoke(&func, (zelf.pattern.clone(), zelf, template)) + func.call((zelf.pattern.clone(), zelf, template), vm) } #[pymethod] diff --git a/vm/src/stdlib/sys.rs b/vm/src/stdlib/sys.rs index e202958b50..ad98ebc510 100644 --- a/vm/src/stdlib/sys.rs +++ b/vm/src/stdlib/sys.rs @@ -381,7 +381,7 @@ mod sys { }; match vm.get_attribute_opt(module, attrname) { - Ok(Some(hook)) => vm.invoke(hook.as_ref(), args), + Ok(Some(hook)) => hook.as_ref().call(args, vm), _ => print_unimportable_module_warn(), } } @@ -560,7 +560,7 @@ mod sys { // TODO: print received unraisable.exc_traceback let tb_module = vm.import("traceback", None, 0)?; let print_stack = tb_module.get_attr("print_stack", vm)?; - vm.invoke(&print_stack, ())?; + print_stack.call((), vm)?; if vm.is_none(unraisable.exc_type.as_object()) { // TODO: early return, but with what error? diff --git a/vm/src/stdlib/warnings.rs b/vm/src/stdlib/warnings.rs index c5da9be56d..ef72490f14 100644 --- a/vm/src/stdlib/warnings.rs +++ b/vm/src/stdlib/warnings.rs @@ -11,7 +11,7 @@ pub fn warn( // TODO: use rust warnings module if let Ok(module) = vm.import("warnings", None, 0) { if let Ok(func) = module.get_attr("warn", vm) { - let _ = vm.invoke(&func, (message, category.to_owned(), stack_level)); + let _ = func.call((message, category.to_owned(), stack_level), vm); } } Ok(()) diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 1be87b4f14..84100a3656 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -337,7 +337,7 @@ fn init_wrapper(obj: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResu fn new_wrapper(cls: PyTypeRef, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult { let new = cls.get_attr(identifier!(vm, __new__)).unwrap(); args.prepend_arg(cls.into()); - vm.invoke(&new, args) + new.call(args, vm) } fn del_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<()> { diff --git a/vm/src/vm/compile.rs b/vm/src/vm/compile.rs index 3b1be7ec5b..d2bfb18abf 100644 --- a/vm/src/vm/compile.rs +++ b/vm/src/vm/compile.rs @@ -31,10 +31,7 @@ impl VirtualMachine { self.insert_sys_path(self.new_pyobj(path))?; let runpy = self.import("runpy", None, 0)?; let run_module_as_main = runpy.get_attr("_run_module_as_main", self)?; - self.invoke( - &run_module_as_main, - (identifier!(self, __main__).to_owned(), false), - )?; + run_module_as_main.call((identifier!(self, __main__).to_owned(), false), self)?; return Ok(()); } @@ -91,7 +88,7 @@ fn get_importer(path: &str, vm: &VirtualMachine) -> PyResult let mut importer = None; let path_hooks: Vec = path_hooks.try_into_value(vm)?; for path_hook in path_hooks { - match vm.invoke(&path_hook, (path.clone(),)) { + match path_hook.call((path.clone(),), vm) { Ok(imp) => { importer = Some(imp); break; diff --git a/vm/src/vm/method.rs b/vm/src/vm/method.rs index a1e3bb2506..0436f5028c 100644 --- a/vm/src/vm/method.rs +++ b/vm/src/vm/method.rs @@ -72,7 +72,7 @@ impl PyMethod { None => Ok(Self::Attribute(attr)), } } else if let Some(getter) = cls.get_attr(identifier!(vm, __getattr__)) { - vm.invoke(&getter, (obj, name)).map(Self::Attribute) + getter.call((obj, name), vm).map(Self::Attribute) } else { let exc = vm.new_attribute_error(format!( "'{}' object has no attribute '{}'", @@ -118,7 +118,7 @@ impl PyMethod { PyMethod::Function { target, func } => (func, args.into_method_args(target, vm)), PyMethod::Attribute(func) => (func, args.into_args(vm)), }; - vm.invoke(&func, args) + func.call(args, vm) } #[allow(dead_code)] @@ -129,6 +129,6 @@ impl PyMethod { } PyMethod::Attribute(func) => (func, args.into_args(vm)), }; - vm.invoke(func, args) + func.call(args, vm) } } diff --git a/vm/src/vm/mod.rs b/vm/src/vm/mod.rs index 2b32b6db04..240df3e721 100644 --- a/vm/src/vm/mod.rs +++ b/vm/src/vm/mod.rs @@ -226,7 +226,7 @@ impl VirtualMachine { }; let encoding_module = import::import_frozen(self, encoding_module_name)?; let getregentry = encoding_module.get_attr("getregentry", self)?; - let codec_info = self.invoke(&getregentry, ())?; + let codec_info = getregentry.call((), self)?; self.state .codec_registry .register_manual("utf-8", codec_info.try_into_value(self)?)?; @@ -360,7 +360,7 @@ impl VirtualMachine { err_msg: self.new_pyobj(msg), object, }; - if let Err(e) = self.invoke(&unraisablehook, (args,)) { + if let Err(e) = unraisablehook.call((args,), self) { println!("{}", e.as_object().repr(self).unwrap().as_str()); } } @@ -521,7 +521,8 @@ impl VirtualMachine { Some(tup) => tup.to_pyobject(self), None => self.new_tuple(()).into(), }; - self.invoke(&import_func, (module, globals, locals, from_list, level)) + import_func + .call((module, globals, locals, from_list, level), self) .map_err(|exc| import::remove_importlib_frames(self, &exc)) } } @@ -666,12 +667,6 @@ impl VirtualMachine { self.get_method(obj, method_name) } - pub fn is_callable(&self, obj: &PyObject) -> bool { - obj.class() - .mro_find_map(|cls| cls.slots.call.load()) - .is_some() - } - #[inline] /// Checks for triggered signals and calls the appropriate handlers. A no-op on /// platforms where signals are not supported. @@ -820,7 +815,7 @@ impl VirtualMachine { pub fn run_module(&self, module: &str) -> PyResult<()> { let runpy = self.import("runpy", None, 0)?; let run_module_as_main = runpy.get_attr("_run_module_as_main", self)?; - self.invoke(&run_module_as_main, (module,))?; + run_module_as_main.call((module,), self)?; Ok(()) } } diff --git a/vm/src/vm/vm_object.rs b/vm/src/vm/vm_object.rs index b20c446b96..ee0fdbe2f3 100644 --- a/vm/src/vm/vm_object.rs +++ b/vm/src/vm/vm_object.rs @@ -1,28 +1,12 @@ use super::PyMethod; use crate::{ builtins::{PyBaseExceptionRef, PyList, PyStr, PyStrInterned}, - function::{FuncArgs, IntoFuncArgs}, + function::IntoFuncArgs, identifier, object::{AsObject, PyObject, PyObjectRef, PyPayload, PyResult}, vm::VirtualMachine, }; -/// Trace events for sys.settrace and sys.setprofile. -enum TraceEvent { - Call, - Return, -} - -impl std::fmt::Display for TraceEvent { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - use TraceEvent::*; - match self { - Call => write!(f, "call"), - Return => write!(f, "return"), - } - } -} - /// PyObject support impl VirtualMachine { #[track_caller] @@ -167,68 +151,8 @@ impl VirtualMachine { .invoke(args, self) } - fn _invoke(&self, callable: &PyObject, args: FuncArgs) -> PyResult { - vm_trace!("Invoke: {:?} {:?}", callable, args); - let slot_call = callable.class().mro_find_map(|cls| cls.slots.call.load()); - match slot_call { - Some(slot_call) => { - self.trace_event(TraceEvent::Call)?; - let result = slot_call(callable, args, self); - self.trace_event(TraceEvent::Return)?; - result - } - None => Err(self.new_type_error(format!( - "'{}' object is not callable", - callable.class().name() - ))), - } - } - - #[inline(always)] - pub fn invoke(&self, func: &impl AsObject, args: impl IntoFuncArgs) -> PyResult { - self._invoke(func.as_object(), args.into_args(self)) - } - - /// Call registered trace function. - #[inline] - fn trace_event(&self, event: TraceEvent) -> PyResult<()> { - if self.use_tracing.get() { - self._trace_event_inner(event) - } else { - Ok(()) - } - } - fn _trace_event_inner(&self, event: TraceEvent) -> PyResult<()> { - let trace_func = self.trace_func.borrow().to_owned(); - let profile_func = self.profile_func.borrow().to_owned(); - if self.is_none(&trace_func) && self.is_none(&profile_func) { - return Ok(()); - } - - let frame_ref = self.current_frame(); - if frame_ref.is_none() { - return Ok(()); - } - - let frame = frame_ref.unwrap().as_object().to_owned(); - let event = self.ctx.new_str(event.to_string()).into(); - let args = vec![frame, event, self.ctx.none()]; - - // temporarily disable tracing, during the call to the - // tracing function itself. - if !self.is_none(&trace_func) { - self.use_tracing.set(false); - let res = self.invoke(&trace_func, args.clone()); - self.use_tracing.set(true); - res?; - } - - if !self.is_none(&profile_func) { - self.use_tracing.set(false); - let res = self.invoke(&profile_func, args); - self.use_tracing.set(true); - res?; - } - Ok(()) + #[deprecated(note = "in favor of `obj.call(args, vm)`")] + pub fn invoke(&self, obj: &impl AsObject, args: impl IntoFuncArgs) -> PyResult { + obj.as_object().call(args, self) } } diff --git a/vm/src/vm/vm_ops.rs b/vm/src/vm/vm_ops.rs index d38d0d007c..c0530a6916 100644 --- a/vm/src/vm/vm_ops.rs +++ b/vm/src/vm/vm_ops.rs @@ -57,7 +57,7 @@ impl VirtualMachine { Some(hint) => hint?, None => return Ok(None), }; - let result = match self.invoke(&hint, ()) { + let result = match hint.call((), self) { Ok(res) => { if res.is(&self.ctx.not_implemented) { return Ok(None); @@ -119,7 +119,7 @@ impl VirtualMachine { { if let Some(method_or_err) = self.get_method(obj.to_owned(), method) { let method = method_or_err?; - let result = self.invoke(&method, (arg.to_owned(),))?; + let result = method.call((arg.to_owned(),), self)?; if let PyArithmeticValue::Implemented(x) = PyArithmeticValue::from_object(self, result) { return Ok(x); diff --git a/vm/src/warn.rs b/vm/src/warn.rs index d6dfd7a8e1..28460be630 100644 --- a/vm/src/warn.rs +++ b/vm/src/warn.rs @@ -44,7 +44,7 @@ fn check_matched(obj: &PyObjectRef, arg: &PyObjectRef, vm: &VirtualMachine) -> P return Ok(false); } - let result = vm.invoke(obj, (arg.to_owned(),)); + let result = obj.call((arg.to_owned(),), vm); Ok(result.is_ok()) } @@ -57,7 +57,7 @@ pub fn py_warn( // TODO: use rust warnings module if let Ok(module) = vm.import("warnings", None, 0) { if let Ok(func) = module.get_attr("warn", vm) { - let _ = vm.invoke(&func, (message, category.to_owned(), stack_level)); + let _ = func.call((message, category.to_owned(), stack_level), vm); } } Ok(()) diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 9f302440cc..32e7877a53 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -134,7 +134,7 @@ mod _browser { stored_vm.interp.enter(|vm| { let func = func.clone(); let args = vec![vm.ctx.new_float(time).into()]; - let _ = vm.invoke(&func, args); + let _ = func.invoke(args, vm); let closure = f.borrow_mut().take(); drop(closure); diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index ed7aa8f29e..5c78ed8d63 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -113,7 +113,7 @@ pub fn py_to_js(vm: &VirtualMachine, py_obj: PyObjectRef) -> JsValue { .insert(js_sys::JsString::from(key).into(), js_to_py(vm, val)); } } - let result = vm.invoke(&py_obj, py_func_args); + let result = py_obj.call(py_func_args, vm); pyresult_to_jsresult(vm, result) }) }; diff --git a/wasm/lib/src/js_module.rs b/wasm/lib/src/js_module.rs index a707b6f493..9c6cf64cd7 100644 --- a/wasm/lib/src/js_module.rs +++ b/wasm/lib/src/js_module.rs @@ -324,7 +324,7 @@ mod _js { .into_iter() .map(|arg| PyJsValue::new(arg).into_pyobject(vm)), ); - let res = vm.invoke(&py_obj, pyargs); + let res = py_obj.call(pyargs, vm); convert::pyresult_to_jsresult(vm, res) }) }; @@ -420,12 +420,12 @@ mod _js { let _ = js_reject .call1(&JsValue::UNDEFINED, &convert::py_err_to_js_err(vm, &err)); }; - let _ = vm.invoke( - then, + let _ = then.call( ( vm.ctx.new_function("resolve", resolve), vm.ctx.new_function("reject", reject), ), + vm, ); }), PromiseKind::PyResolved(obj) => { @@ -439,7 +439,7 @@ mod _js { fn cast(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { let then = vm.get_attribute_opt(obj.clone(), "then")?; - let value = if let Some(then) = then.filter(|obj| vm.is_callable(obj)) { + let value = if let Some(then) = then.filter(|obj| obj.is_callable()) { PromiseKind::PyProm { then } } else { PromiseKind::PyResolved(obj) @@ -517,12 +517,12 @@ mod _js { Ok(PyPromise::from_future(ret_future)) } PromiseKind::PyProm { then } => Self::cast_result( - vm.invoke( - then, + then.call( ( on_fulfill.map(IntoObject::into_object), on_reject.map(IntoObject::into_object), ), + vm, ), vm, ),