From 2abf21b134c699303ea41b29b84d16d7edde4f40 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 29 Apr 2023 17:37:28 +0900 Subject: [PATCH 1/6] PyObject_SelfIter --- vm/src/types/slot.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 0b8ddb6fce..f095b6385b 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -305,6 +305,10 @@ fn iter_wrapper(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { vm.call_special_method(&zelf, identifier!(vm, __iter__), ()) } +fn self_iter(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult { + Ok(zelf) +} + fn iternext_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult { PyIterReturn::from_pyresult( vm.call_special_method(zelf, identifier!(vm, __next__), ()), @@ -1238,6 +1242,8 @@ pub trait Iterable: PyPayload { } fn iter(zelf: PyRef, vm: &VirtualMachine) -> PyResult; + + fn extend_slots(_slots: &mut PyTypeSlots) {} } // `Iterator` fits better, but to avoid confusion with rust std::iter::Iterator @@ -1266,13 +1272,18 @@ impl Iterable for T where T: IterNextIterable, { - #[inline] - fn slot_iter(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult { - Ok(zelf) + #[cold] + fn slot_iter(_zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult { + unreachable!("slot is overriden"); } #[cold] fn iter(_zelf: PyRef, _vm: &VirtualMachine) -> PyResult { unreachable!("slot_iter is implemented"); } + + fn extend_slots(slots: &mut PyTypeSlots) { + let prev = slots.iter.swap(Some(self_iter)); + debug_assert!(prev.is_some()); // slot_iter would be set + } } From 9b9dd1100a4331ee77ad00ab00684a95a6462286 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 29 Apr 2023 15:50:38 +0900 Subject: [PATCH 2/6] Add missing with(Iterable) --- stdlib/src/array.rs | 2 +- stdlib/src/csv.rs | 4 +-- stdlib/src/pystruct.rs | 4 +-- stdlib/src/sqlite.rs | 2 +- vm/src/builtins/asyncgenerator.rs | 6 ++-- vm/src/builtins/bytearray.rs | 2 +- vm/src/builtins/bytes.rs | 2 +- vm/src/builtins/coroutine.rs | 4 +-- vm/src/builtins/dict.rs | 4 +-- vm/src/builtins/enumerate.rs | 6 ++-- vm/src/builtins/filter.rs | 4 +-- vm/src/builtins/generator.rs | 4 +-- vm/src/builtins/iter.rs | 6 ++-- vm/src/builtins/list.rs | 4 +-- vm/src/builtins/map.rs | 4 +-- vm/src/builtins/memory.rs | 2 +- vm/src/builtins/range.rs | 4 +-- vm/src/builtins/set.rs | 2 +- vm/src/builtins/str.rs | 2 +- vm/src/builtins/tuple.rs | 2 +- vm/src/builtins/zip.rs | 4 +-- vm/src/stdlib/collections.rs | 4 +-- vm/src/stdlib/io.rs | 2 +- vm/src/stdlib/itertools.rs | 48 +++++++++++++++---------------- vm/src/stdlib/os.rs | 4 +-- vm/src/types/slot.rs | 18 +++++++++--- 26 files changed, 80 insertions(+), 70 deletions(-) diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs index aee4ab65ae..9b52ce3793 100644 --- a/stdlib/src/array.rs +++ b/stdlib/src/array.rs @@ -1405,7 +1405,7 @@ mod array { internal: PyMutex>, } - #[pyclass(with(IterNext), flags(HAS_DICT))] + #[pyclass(with(IterNext, Iterable), flags(HAS_DICT))] impl PyArrayIter { #[pymethod(magic)] fn setstate(&self, state: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { diff --git a/stdlib/src/csv.rs b/stdlib/src/csv.rs index c49dec0798..8ed1592370 100644 --- a/stdlib/src/csv.rs +++ b/stdlib/src/csv.rs @@ -8,7 +8,7 @@ mod _csv { function::{ArgIterable, ArgumentError, FromArgs, FuncArgs}, match_class, protocol::{PyIter, PyIterReturn}, - types::{IterNext, IterNextIterable}, + types::{IterNext, IterNextIterable, Iterable}, AsObject, Py, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, }; use itertools::{self, Itertools}; @@ -166,7 +166,7 @@ mod _csv { } } - #[pyclass(with(IterNext))] + #[pyclass(with(IterNext, Iterable))] impl Reader {} impl IterNextIterable for Reader {} impl IterNext for Reader { diff --git a/stdlib/src/pystruct.rs b/stdlib/src/pystruct.rs index 96c6d9dd2c..6ab6bba2bb 100644 --- a/stdlib/src/pystruct.rs +++ b/stdlib/src/pystruct.rs @@ -15,7 +15,7 @@ pub(crate) mod _struct { function::{ArgBytesLike, ArgMemoryBuffer, PosArgs}, match_class, protocol::PyIterReturn, - types::{Constructor, IterNext, IterNextIterable}, + types::{Constructor, IterNext, IterNextIterable, Iterable}, AsObject, Py, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; @@ -194,7 +194,7 @@ pub(crate) mod _struct { } } - #[pyclass(with(IterNext))] + #[pyclass(with(IterNext, Iterable))] impl UnpackIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { diff --git a/stdlib/src/sqlite.rs b/stdlib/src/sqlite.rs index addc781ac2..00e9a142fc 100644 --- a/stdlib/src/sqlite.rs +++ b/stdlib/src/sqlite.rs @@ -1377,7 +1377,7 @@ mod _sqlite { statement: Option>, } - #[pyclass(with(Constructor, IterNext), flags(BASETYPE))] + #[pyclass(with(Constructor, IterNext, Iterable), flags(BASETYPE))] impl Cursor { fn new( connection: PyRef, diff --git a/vm/src/builtins/asyncgenerator.rs b/vm/src/builtins/asyncgenerator.rs index 287ab920c7..ee5c01491f 100644 --- a/vm/src/builtins/asyncgenerator.rs +++ b/vm/src/builtins/asyncgenerator.rs @@ -6,7 +6,7 @@ use crate::{ frame::FrameRef, function::OptionalArg, protocol::PyIterReturn, - types::{Constructor, IterNext, IterNextIterable, Representable, Unconstructible}, + types::{Constructor, IterNext, IterNextIterable, Iterable, Representable, Unconstructible}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -195,7 +195,7 @@ impl PyPayload for PyAsyncGenASend { } } -#[pyclass(with(IterNext))] +#[pyclass(with(IterNext, Iterable))] impl PyAsyncGenASend { #[pymethod(name = "__await__")] fn r#await(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { @@ -290,7 +290,7 @@ impl PyPayload for PyAsyncGenAThrow { } } -#[pyclass(with(IterNext))] +#[pyclass(with(IterNext, Iterable))] impl PyAsyncGenAThrow { #[pymethod(name = "__await__")] fn r#await(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 87757fae36..6d94602851 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -891,7 +891,7 @@ impl PyPayload for PyByteArrayIterator { } } -#[pyclass(with(Constructor, IterNext))] +#[pyclass(with(Constructor, IterNext, Iterable))] impl PyByteArrayIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index 18ea0d01ca..d915e152b2 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -688,7 +688,7 @@ impl PyPayload for PyBytesIterator { } } -#[pyclass(with(Constructor, IterNext))] +#[pyclass(with(Constructor, IterNext, Iterable))] impl PyBytesIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { diff --git a/vm/src/builtins/coroutine.rs b/vm/src/builtins/coroutine.rs index 440ff46b50..58ea21d2c3 100644 --- a/vm/src/builtins/coroutine.rs +++ b/vm/src/builtins/coroutine.rs @@ -5,7 +5,7 @@ use crate::{ frame::FrameRef, function::OptionalArg, protocol::PyIterReturn, - types::{Constructor, IterNext, IterNextIterable, Representable, Unconstructible}, + types::{Constructor, IterNext, IterNextIterable, Iterable, Representable, Unconstructible}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -128,7 +128,7 @@ impl PyPayload for PyCoroutineWrapper { } } -#[pyclass(with(IterNext))] +#[pyclass(with(IterNext, Iterable))] impl PyCoroutineWrapper { #[pymethod] fn send(&self, val: PyObjectRef, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index b2a191fd93..1b8e886eba 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -839,7 +839,7 @@ macro_rules! dict_view { } } - #[pyclass(with(Constructor, IterNext))] + #[pyclass(with(Constructor, IterNext, Iterable))] impl $iter_name { fn new(dict: PyDictRef) -> Self { $iter_name { @@ -912,7 +912,7 @@ macro_rules! dict_view { } } - #[pyclass(with(Constructor, IterNext))] + #[pyclass(with(Constructor, IterNext, Iterable))] impl $reverse_iter_name { fn new(dict: PyDictRef) -> Self { let size = dict.size(); diff --git a/vm/src/builtins/enumerate.rs b/vm/src/builtins/enumerate.rs index c1582f08d5..772187276a 100644 --- a/vm/src/builtins/enumerate.rs +++ b/vm/src/builtins/enumerate.rs @@ -7,7 +7,7 @@ use crate::{ convert::ToPyObject, function::OptionalArg, protocol::{PyIter, PyIterReturn}, - types::{Constructor, IterNext, IterNextIterable}, + types::{Constructor, IterNext, IterNextIterable, Iterable}, AsObject, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; use num_bigint::BigInt; @@ -52,7 +52,7 @@ impl Constructor for PyEnumerate { } } -#[pyclass(with(Py, IterNext, Constructor), flags(BASETYPE))] +#[pyclass(with(Py, IterNext, Iterable, Constructor), flags(BASETYPE))] impl PyEnumerate { #[pyclassmethod(magic)] fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { @@ -97,7 +97,7 @@ impl PyPayload for PyReverseSequenceIterator { } } -#[pyclass(with(IterNext))] +#[pyclass(with(IterNext, Iterable))] impl PyReverseSequenceIterator { pub fn new(obj: PyObjectRef, len: usize) -> Self { let position = len.saturating_sub(1); diff --git a/vm/src/builtins/filter.rs b/vm/src/builtins/filter.rs index 660be88ea8..7b69b0e109 100644 --- a/vm/src/builtins/filter.rs +++ b/vm/src/builtins/filter.rs @@ -2,7 +2,7 @@ use super::{PyType, PyTypeRef}; use crate::{ class::PyClassImpl, protocol::{PyIter, PyIterReturn}, - types::{Constructor, IterNext, IterNextIterable}, + types::{Constructor, IterNext, IterNextIterable, Iterable}, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; @@ -32,7 +32,7 @@ impl Constructor for PyFilter { } } -#[pyclass(with(IterNext, Constructor), flags(BASETYPE))] +#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))] impl PyFilter { #[pymethod(magic)] fn reduce(&self, vm: &VirtualMachine) -> (PyTypeRef, (PyObjectRef, PyIter)) { diff --git a/vm/src/builtins/generator.rs b/vm/src/builtins/generator.rs index 745c33aad9..2edb0d6dfe 100644 --- a/vm/src/builtins/generator.rs +++ b/vm/src/builtins/generator.rs @@ -9,7 +9,7 @@ use crate::{ frame::FrameRef, function::OptionalArg, protocol::PyIterReturn, - types::{Constructor, IterNext, IterNextIterable, Representable, Unconstructible}, + types::{Constructor, IterNext, IterNextIterable, Iterable, Representable, Unconstructible}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -25,7 +25,7 @@ impl PyPayload for PyGenerator { } } -#[pyclass(with(Py, Constructor, IterNext))] +#[pyclass(with(Py, Constructor, IterNext, Iterable))] impl PyGenerator { pub fn as_coro(&self) -> &Coro { &self.inner diff --git a/vm/src/builtins/iter.rs b/vm/src/builtins/iter.rs index 6f8a01e057..1bc85e83c0 100644 --- a/vm/src/builtins/iter.rs +++ b/vm/src/builtins/iter.rs @@ -8,7 +8,7 @@ use crate::{ function::ArgCallable, object::{Traverse, TraverseFn}, protocol::{PyIterReturn, PySequence, PySequenceMethods}, - types::{IterNext, IterNextIterable}, + types::{IterNext, IterNextIterable, Iterable}, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; use rustpython_common::{ @@ -189,7 +189,7 @@ impl PyPayload for PySequenceIterator { } } -#[pyclass(with(IterNext))] +#[pyclass(with(IterNext, Iterable))] impl PySequenceIterator { pub fn new(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { let seq = PySequence::try_protocol(&obj, vm)?; @@ -252,7 +252,7 @@ impl PyPayload for PyCallableIterator { } } -#[pyclass(with(IterNext))] +#[pyclass(with(IterNext, Iterable))] impl PyCallableIterator { pub fn new(callable: ArgCallable, sentinel: PyObjectRef) -> Self { Self { diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index 581c0fc9e5..2cc0381526 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -543,7 +543,7 @@ impl PyPayload for PyListIterator { } } -#[pyclass(with(Constructor, IterNext))] +#[pyclass(with(Constructor, IterNext, Iterable))] impl PyListIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { @@ -588,7 +588,7 @@ impl PyPayload for PyListReverseIterator { } } -#[pyclass(with(Constructor, IterNext))] +#[pyclass(with(Constructor, IterNext, Iterable))] impl PyListReverseIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { diff --git a/vm/src/builtins/map.rs b/vm/src/builtins/map.rs index 6fb842f413..90878c3f4d 100644 --- a/vm/src/builtins/map.rs +++ b/vm/src/builtins/map.rs @@ -4,7 +4,7 @@ use crate::{ class::PyClassImpl, function::PosArgs, protocol::{PyIter, PyIterReturn}, - types::{Constructor, IterNext, IterNextIterable}, + types::{Constructor, IterNext, IterNextIterable, Iterable}, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; @@ -32,7 +32,7 @@ impl Constructor for PyMap { } } -#[pyclass(with(IterNext, Constructor), flags(BASETYPE))] +#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))] impl PyMap { #[pymethod(magic)] fn length_hint(&self, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index 312364a744..4485d5d12e 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -1137,7 +1137,7 @@ impl PyPayload for PyMemoryViewIterator { } } -#[pyclass(with(Constructor, IterNext))] +#[pyclass(with(Constructor, IterNext, Iterable))] impl PyMemoryViewIterator { #[pymethod(magic)] fn reduce(&self, vm: &VirtualMachine) -> PyTupleRef { diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs index bc15a09093..9cef0b7a55 100644 --- a/vm/src/builtins/range.rs +++ b/vm/src/builtins/range.rs @@ -537,7 +537,7 @@ impl PyPayload for PyLongRangeIterator { } } -#[pyclass(with(Constructor, IterNext))] +#[pyclass(with(Constructor, IterNext, Iterable))] impl PyLongRangeIterator { #[pymethod(magic)] fn length_hint(&self) -> BigInt { @@ -602,7 +602,7 @@ impl PyPayload for PyRangeIterator { } } -#[pyclass(with(Constructor, IterNext))] +#[pyclass(with(Constructor, IterNext, Iterable))] impl PyRangeIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index 003b0496ed..b495215d67 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -1232,7 +1232,7 @@ impl PyPayload for PySetIterator { } } -#[pyclass(with(Constructor, IterNext))] +#[pyclass(with(Constructor, IterNext, Iterable))] impl PySetIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index 1e29c8d22a..146e48d3b8 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -206,7 +206,7 @@ impl PyPayload for PyStrIterator { } } -#[pyclass(with(Constructor, IterNext))] +#[pyclass(with(Constructor, IterNext, Iterable))] impl PyStrIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs index 59133b0876..283a5d3160 100644 --- a/vm/src/builtins/tuple.rs +++ b/vm/src/builtins/tuple.rs @@ -434,7 +434,7 @@ impl PyPayload for PyTupleIterator { } } -#[pyclass(with(Constructor, IterNext))] +#[pyclass(with(Constructor, IterNext, Iterable))] impl PyTupleIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { diff --git a/vm/src/builtins/zip.rs b/vm/src/builtins/zip.rs index d8b28e8eb7..5aad48d064 100644 --- a/vm/src/builtins/zip.rs +++ b/vm/src/builtins/zip.rs @@ -4,7 +4,7 @@ use crate::{ class::PyClassImpl, function::{ArgIntoBool, OptionalArg, PosArgs}, protocol::{PyIter, PyIterReturn}, - types::{Constructor, IterNext, IterNextIterable}, + types::{Constructor, IterNext, IterNextIterable, Iterable}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, }; use rustpython_common::atomic::{self, PyAtomic, Radium}; @@ -41,7 +41,7 @@ impl Constructor for PyZip { } } -#[pyclass(with(IterNext, Constructor), flags(BASETYPE))] +#[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))] impl PyZip { #[pymethod(magic)] fn reduce(zelf: PyRef, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/stdlib/collections.rs b/vm/src/stdlib/collections.rs index 5935b124f9..34c5e4157c 100644 --- a/vm/src/stdlib/collections.rs +++ b/vm/src/stdlib/collections.rs @@ -623,7 +623,7 @@ mod _collections { } } - #[pyclass(with(IterNext, Constructor))] + #[pyclass(with(IterNext, Iterable, Constructor))] impl PyDequeIterator { pub(crate) fn new(deque: PyDequeRef) -> Self { PyDequeIterator { @@ -696,7 +696,7 @@ mod _collections { } } - #[pyclass(with(IterNext, Constructor))] + #[pyclass(with(IterNext, Iterable, Constructor))] impl PyReverseDequeIterator { #[pymethod(magic)] fn length_hint(&self) -> usize { diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 3c98c146f3..2a9d224460 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -382,7 +382,7 @@ mod _io { #[derive(Debug, PyPayload)] pub struct _IOBase; - #[pyclass(with(IterNext, Destructor), flags(BASETYPE, HAS_DICT))] + #[pyclass(with(IterNext, Iterable, Destructor), flags(BASETYPE, HAS_DICT))] impl _IOBase { #[pymethod] fn seek( diff --git a/vm/src/stdlib/itertools.rs b/vm/src/stdlib/itertools.rs index 66b4eb50d2..f8321f22b9 100644 --- a/vm/src/stdlib/itertools.rs +++ b/vm/src/stdlib/itertools.rs @@ -16,7 +16,7 @@ mod decl { identifier, protocol::{PyIter, PyIterReturn, PyNumber}, stdlib::sys, - types::{Constructor, IterNext, IterNextIterable, Representable}, + types::{Constructor, IterNext, IterNextIterable, Iterable, Representable}, AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, PyWeakRef, TryFromObject, VirtualMachine, }; @@ -32,7 +32,7 @@ mod decl { active: PyRwLock>, } - #[pyclass(with(IterNext), flags(BASETYPE, HAS_DICT))] + #[pyclass(with(IterNext, Iterable), flags(BASETYPE, HAS_DICT))] impl PyItertoolsChain { #[pyslot] fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult { @@ -189,7 +189,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor), flags(BASETYPE))] + #[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))] impl PyItertoolsCompress { #[pymethod(magic)] fn reduce(zelf: PyRef) -> (PyTypeRef, (PyIter, PyIter)) { @@ -249,7 +249,7 @@ mod decl { return Err(vm.new_type_error("a number is required".to_owned())); } - PyItertoolsCount { + Self { cur: PyRwLock::new(start), step, } @@ -258,7 +258,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor, Representable))] + #[pyclass(with(IterNext, Iterable, Constructor, Representable))] impl PyItertoolsCount { // TODO: Implement this // if (lz->cnt == PY_SSIZE_T_MAX) @@ -314,7 +314,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor), flags(BASETYPE))] + #[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))] impl PyItertoolsCycle {} impl IterNextIterable for PyItertoolsCycle {} impl IterNext for PyItertoolsCycle { @@ -378,7 +378,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor, Representable), flags(BASETYPE))] + #[pyclass(with(IterNext, Iterable, Constructor, Representable), flags(BASETYPE))] impl PyItertoolsRepeat { #[pymethod(magic)] fn length_hint(&self, vm: &VirtualMachine) -> PyResult { @@ -456,7 +456,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor), flags(BASETYPE))] + #[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))] impl PyItertoolsStarmap { #[pymethod(magic)] fn reduce(zelf: PyRef) -> (PyTypeRef, (PyObjectRef, PyIter)) { @@ -519,7 +519,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor), flags(BASETYPE))] + #[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))] impl PyItertoolsTakewhile { #[pymethod(magic)] fn reduce(zelf: PyRef) -> (PyTypeRef, (PyObjectRef, PyIter), u32) { @@ -600,7 +600,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor), flags(BASETYPE))] + #[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))] impl PyItertoolsDropwhile { #[pymethod(magic)] fn reduce(zelf: PyRef) -> (PyTypeRef, (PyObjectRef, PyIter), u32) { @@ -719,7 +719,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor))] + #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsGroupBy { pub(super) fn advance( &self, @@ -795,7 +795,7 @@ mod decl { groupby: PyRef, } - #[pyclass(with(IterNext))] + #[pyclass(with(IterNext, Iterable))] impl PyItertoolsGrouper {} impl IterNextIterable for PyItertoolsGrouper {} impl IterNext for PyItertoolsGrouper { @@ -865,7 +865,7 @@ mod decl { ))) } - #[pyclass(with(IterNext), flags(BASETYPE))] + #[pyclass(with(IterNext, Iterable), flags(BASETYPE))] impl PyItertoolsIslice { #[pyslot] fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult { @@ -1023,7 +1023,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor), flags(BASETYPE))] + #[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))] impl PyItertoolsFilterFalse { #[pymethod(magic)] fn reduce(zelf: PyRef) -> (PyTypeRef, (PyObjectRef, PyIter)) { @@ -1091,7 +1091,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor))] + #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsAccumulate {} impl IterNextIterable for PyItertoolsAccumulate {} @@ -1199,7 +1199,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor))] + #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsTee { fn from_iter(iterator: PyIter, vm: &VirtualMachine) -> PyResult { let class = PyItertoolsTee::class(&vm.ctx); @@ -1277,7 +1277,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor))] + #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsProduct { fn update_idxs(&self, mut idxs: PyRwLockWriteGuard<'_, Vec>) { if idxs.len() == 0 { @@ -1370,7 +1370,7 @@ mod decl { let n = pool.len(); - PyItertoolsCombinations { + Self { pool, indices: PyRwLock::new((0..r).collect()), result: PyRwLock::new(None), @@ -1382,7 +1382,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor))] + #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsCombinations { #[pymethod(magic)] fn reduce(zelf: PyRef, vm: &VirtualMachine) -> PyTupleRef { @@ -1512,7 +1512,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor))] + #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsCombinationsWithReplacement {} impl IterNextIterable for PyItertoolsCombinationsWithReplacement {} @@ -1609,7 +1609,7 @@ mod decl { None => n, }; - PyItertoolsPermutations { + Self { pool, indices: PyRwLock::new((0..n).collect()), cycles: PyRwLock::new((0..r.min(n)).map(|i| n - i).collect()), @@ -1622,7 +1622,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor))] + #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsPermutations {} impl IterNextIterable for PyItertoolsPermutations {} impl IterNext for PyItertoolsPermutations { @@ -1725,7 +1725,7 @@ mod decl { fillvalue: PyRwLock, } - #[pyclass(with(IterNext, Constructor))] + #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsZipLongest { #[pymethod(magic)] fn reduce(zelf: PyRef, vm: &VirtualMachine) -> PyResult { @@ -1794,7 +1794,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor))] + #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsPairwise {} impl IterNextIterable for PyItertoolsPairwise {} impl IterNext for PyItertoolsPairwise { diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index e016c21a12..6bac2e7e78 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -316,7 +316,7 @@ pub(super) mod _os { function::{ArgBytesLike, Either, FsPath, FuncArgs, OptionalArg}, protocol::PyIterReturn, recursion::ReprGuard, - types::{IterNext, IterNextIterable, PyStructSequence, Representable}, + types::{IterNext, IterNextIterable, Iterable, PyStructSequence, Representable}, vm::VirtualMachine, AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, }; @@ -791,7 +791,7 @@ pub(super) mod _os { mode: OutputMode, } - #[pyclass(with(IterNext))] + #[pyclass(with(IterNext, Iterable))] impl ScandirIterator { #[pymethod] fn close(&self) { diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index f095b6385b..5b3bbd8ba8 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -305,6 +305,7 @@ fn iter_wrapper(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { vm.call_special_method(&zelf, identifier!(vm, __iter__), ()) } +// PyObject_IterNextIterable in CPython fn self_iter(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult { Ok(zelf) } @@ -1233,7 +1234,6 @@ pub trait AsNumber: PyPayload { #[pyclass] pub trait Iterable: PyPayload { #[pyslot] - #[pymethod(name = "__iter__")] fn slot_iter(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { let zelf = zelf .downcast() @@ -1241,6 +1241,11 @@ pub trait Iterable: PyPayload { Self::iter(zelf, vm) } + #[pymethod] + fn __iter__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { + Self::slot_iter(zelf, vm) + } + fn iter(zelf: PyRef, vm: &VirtualMachine) -> PyResult; fn extend_slots(_slots: &mut PyTypeSlots) {} @@ -1273,8 +1278,13 @@ where T: IterNextIterable, { #[cold] - fn slot_iter(_zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult { - unreachable!("slot is overriden"); + fn slot_iter(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let repr = zelf.repr(vm)?; + unreachable!("slot must be overriden for {}", repr.as_str()); + } + + fn __iter__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { + self_iter(zelf, vm) } #[cold] @@ -1284,6 +1294,6 @@ where fn extend_slots(slots: &mut PyTypeSlots) { let prev = slots.iter.swap(Some(self_iter)); - debug_assert!(prev.is_some()); // slot_iter would be set + debug_assert!(prev.is_some()); // slot_iter would be set } } From 9f58921a6f4cf423365649c69ab2c1e04f4566ac Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 30 Apr 2023 00:18:02 +0900 Subject: [PATCH 3/6] Rename IterNextIterable -> SelfIter by following CPython --- stdlib/src/array.rs | 6 ++--- stdlib/src/csv.rs | 4 +-- stdlib/src/pystruct.rs | 4 +-- stdlib/src/sqlite.rs | 6 ++--- vm/src/builtins/asyncgenerator.rs | 6 ++--- vm/src/builtins/bytearray.rs | 4 +-- vm/src/builtins/bytes.rs | 4 +-- vm/src/builtins/coroutine.rs | 6 ++--- vm/src/builtins/dict.rs | 6 ++--- vm/src/builtins/enumerate.rs | 6 ++--- vm/src/builtins/filter.rs | 4 +-- vm/src/builtins/generator.rs | 4 +-- vm/src/builtins/iter.rs | 6 ++--- vm/src/builtins/list.rs | 8 +++--- vm/src/builtins/map.rs | 4 +-- vm/src/builtins/memory.rs | 6 ++--- vm/src/builtins/range.rs | 8 +++--- vm/src/builtins/set.rs | 6 ++--- vm/src/builtins/str.rs | 6 ++--- vm/src/builtins/tuple.rs | 6 ++--- vm/src/builtins/zip.rs | 4 +-- vm/src/stdlib/collections.rs | 8 +++--- vm/src/stdlib/itertools.rs | 42 +++++++++++++++---------------- vm/src/stdlib/os.rs | 4 +-- vm/src/types/slot.rs | 6 ++--- wasm/lib/src/js_module.rs | 4 +-- 26 files changed, 89 insertions(+), 89 deletions(-) diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs index 9b52ce3793..762573ba67 100644 --- a/stdlib/src/array.rs +++ b/stdlib/src/array.rs @@ -61,8 +61,8 @@ mod array { SliceableSequenceOp, }, types::{ - AsBuffer, AsMapping, AsSequence, Comparable, Constructor, IterNext, - IterNextIterable, Iterable, PyComparisonOp, Representable, + AsBuffer, AsMapping, AsSequence, Comparable, Constructor, IterNext, Iterable, + PyComparisonOp, Representable, SelfIter, }, AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }, @@ -1422,7 +1422,7 @@ mod array { } } - impl IterNextIterable for PyArrayIter {} + impl SelfIter for PyArrayIter {} impl IterNext for PyArrayIter { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { zelf.internal.lock().next(|array, pos| { diff --git a/stdlib/src/csv.rs b/stdlib/src/csv.rs index 8ed1592370..bee3fd5faa 100644 --- a/stdlib/src/csv.rs +++ b/stdlib/src/csv.rs @@ -8,7 +8,7 @@ mod _csv { function::{ArgIterable, ArgumentError, FromArgs, FuncArgs}, match_class, protocol::{PyIter, PyIterReturn}, - types::{IterNext, IterNextIterable, Iterable}, + types::{IterNext, Iterable, SelfIter}, AsObject, Py, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, }; use itertools::{self, Itertools}; @@ -168,7 +168,7 @@ mod _csv { #[pyclass(with(IterNext, Iterable))] impl Reader {} - impl IterNextIterable for Reader {} + impl SelfIter for Reader {} impl IterNext for Reader { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let string = match zelf.iter.next(vm)? { diff --git a/stdlib/src/pystruct.rs b/stdlib/src/pystruct.rs index 6ab6bba2bb..2d83e9570d 100644 --- a/stdlib/src/pystruct.rs +++ b/stdlib/src/pystruct.rs @@ -15,7 +15,7 @@ pub(crate) mod _struct { function::{ArgBytesLike, ArgMemoryBuffer, PosArgs}, match_class, protocol::PyIterReturn, - types::{Constructor, IterNext, IterNextIterable, Iterable}, + types::{Constructor, IterNext, Iterable, SelfIter}, AsObject, Py, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; @@ -201,7 +201,7 @@ pub(crate) mod _struct { self.buffer.len().saturating_sub(self.offset.load()) / self.format_spec.size } } - impl IterNextIterable for UnpackIterator {} + impl SelfIter for UnpackIterator {} impl IterNext for UnpackIterator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let size = zelf.format_spec.size; diff --git a/stdlib/src/sqlite.rs b/stdlib/src/sqlite.rs index 00e9a142fc..32ea0d0cd5 100644 --- a/stdlib/src/sqlite.rs +++ b/stdlib/src/sqlite.rs @@ -62,8 +62,8 @@ mod _sqlite { protocol::{PyBuffer, PyIterReturn, PyMappingMethods, PySequence, PySequenceMethods}, sliceable::{SaturatedSliceIter, SliceableSequenceOp}, types::{ - AsMapping, AsSequence, Callable, Comparable, Constructor, Hashable, IterNext, - IterNextIterable, Iterable, PyComparisonOp, + AsMapping, AsSequence, Callable, Comparable, Constructor, Hashable, IterNext, Iterable, + PyComparisonOp, SelfIter, }, utils::ToCString, AsObject, Py, PyAtomicRef, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, @@ -1708,7 +1708,7 @@ mod _sqlite { } } - impl IterNextIterable for Cursor {} + impl SelfIter for Cursor {} impl IterNext for Cursor { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let mut inner = zelf.inner(vm)?; diff --git a/vm/src/builtins/asyncgenerator.rs b/vm/src/builtins/asyncgenerator.rs index ee5c01491f..d58744da0a 100644 --- a/vm/src/builtins/asyncgenerator.rs +++ b/vm/src/builtins/asyncgenerator.rs @@ -6,7 +6,7 @@ use crate::{ frame::FrameRef, function::OptionalArg, protocol::PyIterReturn, - types::{Constructor, IterNext, IterNextIterable, Iterable, Representable, Unconstructible}, + types::{Constructor, IterNext, Iterable, Representable, SelfIter, Unconstructible}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -268,7 +268,7 @@ impl PyAsyncGenASend { } } -impl IterNextIterable for PyAsyncGenASend {} +impl SelfIter for PyAsyncGenASend {} impl IterNext for PyAsyncGenASend { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { PyIterReturn::from_pyresult(zelf.send(vm.ctx.none(), vm), vm) @@ -414,7 +414,7 @@ impl PyAsyncGenAThrow { } } -impl IterNextIterable for PyAsyncGenAThrow {} +impl SelfIter for PyAsyncGenAThrow {} impl IterNext for PyAsyncGenAThrow { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { PyIterReturn::from_pyresult(zelf.send(vm.ctx.none(), vm), vm) diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 6d94602851..f1279389e4 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -31,7 +31,7 @@ use crate::{ sliceable::{SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp}, types::{ AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Initializer, - IterNext, IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible, + IterNext, Iterable, PyComparisonOp, Representable, SelfIter, Unconstructible, }, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, @@ -914,7 +914,7 @@ impl PyByteArrayIterator { impl Unconstructible for PyByteArrayIterator {} -impl IterNextIterable for PyByteArrayIterator {} +impl SelfIter for PyByteArrayIterator {} impl IterNext for PyByteArrayIterator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { zelf.internal.lock().next(|bytearray, pos| { diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index d915e152b2..b27a8a2df3 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -21,7 +21,7 @@ use crate::{ sliceable::{SequenceIndex, SliceableSequenceOp}, types::{ AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Hashable, - IterNext, IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible, + IterNext, Iterable, PyComparisonOp, Representable, SelfIter, Unconstructible, }, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, TryFromObject, VirtualMachine, @@ -711,7 +711,7 @@ impl PyBytesIterator { } impl Unconstructible for PyBytesIterator {} -impl IterNextIterable for PyBytesIterator {} +impl SelfIter for PyBytesIterator {} impl IterNext for PyBytesIterator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { zelf.internal.lock().next(|bytes, pos| { diff --git a/vm/src/builtins/coroutine.rs b/vm/src/builtins/coroutine.rs index 58ea21d2c3..2454e27e2c 100644 --- a/vm/src/builtins/coroutine.rs +++ b/vm/src/builtins/coroutine.rs @@ -5,7 +5,7 @@ use crate::{ frame::FrameRef, function::OptionalArg, protocol::PyIterReturn, - types::{Constructor, IterNext, IterNextIterable, Iterable, Representable, Unconstructible}, + types::{Constructor, IterNext, Iterable, Representable, SelfIter, Unconstructible}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -108,7 +108,7 @@ impl Representable for PyCoroutine { } } -impl IterNextIterable for PyCoroutine {} +impl SelfIter for PyCoroutine {} impl IterNext for PyCoroutine { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { Self::send(zelf, vm.ctx.none(), vm) @@ -147,7 +147,7 @@ impl PyCoroutineWrapper { } } -impl IterNextIterable for PyCoroutineWrapper {} +impl SelfIter for PyCoroutineWrapper {} impl IterNext for PyCoroutineWrapper { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { Self::send(zelf, vm.ctx.none(), vm) diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index 1b8e886eba..a164fc11b6 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -20,7 +20,7 @@ use crate::{ recursion::ReprGuard, types::{ AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Initializer, IterNext, - IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible, + Iterable, PyComparisonOp, Representable, SelfIter, Unconstructible, }, vm::VirtualMachine, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult, @@ -870,7 +870,7 @@ macro_rules! dict_view { } impl Unconstructible for $iter_name {} - impl IterNextIterable for $iter_name {} + impl SelfIter for $iter_name {} impl IterNext for $iter_name { #[allow(clippy::redundant_closure_call)] fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { @@ -948,7 +948,7 @@ macro_rules! dict_view { } impl Unconstructible for $reverse_iter_name {} - impl IterNextIterable for $reverse_iter_name {} + impl SelfIter for $reverse_iter_name {} impl IterNext for $reverse_iter_name { #[allow(clippy::redundant_closure_call)] fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/builtins/enumerate.rs b/vm/src/builtins/enumerate.rs index 772187276a..00a3215ad6 100644 --- a/vm/src/builtins/enumerate.rs +++ b/vm/src/builtins/enumerate.rs @@ -7,7 +7,7 @@ use crate::{ convert::ToPyObject, function::OptionalArg, protocol::{PyIter, PyIterReturn}, - types::{Constructor, IterNext, IterNextIterable, Iterable}, + types::{Constructor, IterNext, Iterable, SelfIter}, AsObject, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; use num_bigint::BigInt; @@ -71,7 +71,7 @@ impl Py { } } -impl IterNextIterable for PyEnumerate {} +impl SelfIter for PyEnumerate {} impl IterNext for PyEnumerate { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let next_obj = match zelf.iterator.next(vm)? { @@ -130,7 +130,7 @@ impl PyReverseSequenceIterator { } } -impl IterNextIterable for PyReverseSequenceIterator {} +impl SelfIter for PyReverseSequenceIterator {} impl IterNext for PyReverseSequenceIterator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { zelf.internal diff --git a/vm/src/builtins/filter.rs b/vm/src/builtins/filter.rs index 7b69b0e109..3b33ff766f 100644 --- a/vm/src/builtins/filter.rs +++ b/vm/src/builtins/filter.rs @@ -2,7 +2,7 @@ use super::{PyType, PyTypeRef}; use crate::{ class::PyClassImpl, protocol::{PyIter, PyIterReturn}, - types::{Constructor, IterNext, IterNextIterable, Iterable}, + types::{Constructor, IterNext, Iterable, SelfIter}, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; @@ -43,7 +43,7 @@ impl PyFilter { } } -impl IterNextIterable for PyFilter {} +impl SelfIter for PyFilter {} impl IterNext for PyFilter { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let predicate = &zelf.predicate; diff --git a/vm/src/builtins/generator.rs b/vm/src/builtins/generator.rs index 2edb0d6dfe..eceac5ba93 100644 --- a/vm/src/builtins/generator.rs +++ b/vm/src/builtins/generator.rs @@ -9,7 +9,7 @@ use crate::{ frame::FrameRef, function::OptionalArg, protocol::PyIterReturn, - types::{Constructor, IterNext, IterNextIterable, Iterable, Representable, Unconstructible}, + types::{Constructor, IterNext, Iterable, Representable, SelfIter, Unconstructible}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -104,7 +104,7 @@ impl Representable for PyGenerator { } } -impl IterNextIterable for PyGenerator {} +impl SelfIter for PyGenerator {} impl IterNext for PyGenerator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { zelf.send(vm.ctx.none(), vm) diff --git a/vm/src/builtins/iter.rs b/vm/src/builtins/iter.rs index 1bc85e83c0..2fed23a5c4 100644 --- a/vm/src/builtins/iter.rs +++ b/vm/src/builtins/iter.rs @@ -8,7 +8,7 @@ use crate::{ function::ArgCallable, object::{Traverse, TraverseFn}, protocol::{PyIterReturn, PySequence, PySequenceMethods}, - types::{IterNext, IterNextIterable, Iterable}, + types::{IterNext, Iterable, SelfIter}, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; use rustpython_common::{ @@ -226,7 +226,7 @@ impl PySequenceIterator { } } -impl IterNextIterable for PySequenceIterator {} +impl SelfIter for PySequenceIterator {} impl IterNext for PySequenceIterator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { zelf.internal.lock().next(|obj, pos| { @@ -262,7 +262,7 @@ impl PyCallableIterator { } } -impl IterNextIterable for PyCallableIterator {} +impl SelfIter for PyCallableIterator {} impl IterNext for PyCallableIterator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let status = zelf.status.upgradable_read(); diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index 2cc0381526..03503a0cea 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -13,8 +13,8 @@ use crate::{ sequence::{MutObjectSequenceOp, OptionalRangeArgs, SequenceExt, SequenceMutExt}, sliceable::{SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp}, types::{ - AsMapping, AsSequence, Comparable, Constructor, Initializer, IterNext, IterNextIterable, - Iterable, PyComparisonOp, Representable, Unconstructible, + AsMapping, AsSequence, Comparable, Constructor, Initializer, IterNext, Iterable, + PyComparisonOp, Representable, SelfIter, Unconstructible, }, utils::collection_repr, vm::VirtualMachine, @@ -566,7 +566,7 @@ impl PyListIterator { } impl Unconstructible for PyListIterator {} -impl IterNextIterable for PyListIterator {} +impl SelfIter for PyListIterator {} impl IterNext for PyListIterator { fn next(zelf: &Py, _vm: &VirtualMachine) -> PyResult { zelf.internal.lock().next(|list, pos| { @@ -611,7 +611,7 @@ impl PyListReverseIterator { } impl Unconstructible for PyListReverseIterator {} -impl IterNextIterable for PyListReverseIterator {} +impl SelfIter for PyListReverseIterator {} impl IterNext for PyListReverseIterator { fn next(zelf: &Py, _vm: &VirtualMachine) -> PyResult { zelf.internal.lock().rev_next(|list, pos| { diff --git a/vm/src/builtins/map.rs b/vm/src/builtins/map.rs index 90878c3f4d..44bacf587e 100644 --- a/vm/src/builtins/map.rs +++ b/vm/src/builtins/map.rs @@ -4,7 +4,7 @@ use crate::{ class::PyClassImpl, function::PosArgs, protocol::{PyIter, PyIterReturn}, - types::{Constructor, IterNext, IterNextIterable, Iterable}, + types::{Constructor, IterNext, Iterable, SelfIter}, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; @@ -51,7 +51,7 @@ impl PyMap { } } -impl IterNextIterable for PyMap {} +impl SelfIter for PyMap {} impl IterNext for PyMap { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let mut next_objs = Vec::new(); diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index 4485d5d12e..2c436ca316 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -21,8 +21,8 @@ use crate::{ }, sliceable::SequenceIndexOp, types::{ - AsBuffer, AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, - IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible, + AsBuffer, AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, Iterable, + PyComparisonOp, Representable, SelfIter, Unconstructible, }, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, TryFromObject, VirtualMachine, @@ -1148,7 +1148,7 @@ impl PyMemoryViewIterator { } impl Unconstructible for PyMemoryViewIterator {} -impl IterNextIterable for PyMemoryViewIterator {} +impl SelfIter for PyMemoryViewIterator {} impl IterNext for PyMemoryViewIterator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { zelf.internal.lock().next(|mv, pos| { diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs index 9cef0b7a55..276c94c1d0 100644 --- a/vm/src/builtins/range.rs +++ b/vm/src/builtins/range.rs @@ -8,8 +8,8 @@ use crate::{ function::{ArgIndex, FuncArgs, OptionalArg, PyComparisonValue}, protocol::{PyIterReturn, PyMappingMethods, PySequenceMethods}, types::{ - AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, IterNextIterable, - Iterable, PyComparisonOp, Representable, Unconstructible, + AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, Iterable, + PyComparisonOp, Representable, SelfIter, Unconstructible, }, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, @@ -568,7 +568,7 @@ impl PyLongRangeIterator { } impl Unconstructible for PyLongRangeIterator {} -impl IterNextIterable for PyLongRangeIterator {} +impl SelfIter for PyLongRangeIterator {} impl IterNext for PyLongRangeIterator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { // TODO: In pathological case (index == usize::MAX) this can wrap around @@ -634,7 +634,7 @@ impl PyRangeIterator { } impl Unconstructible for PyRangeIterator {} -impl IterNextIterable for PyRangeIterator {} +impl SelfIter for PyRangeIterator {} impl IterNext for PyRangeIterator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { // TODO: In pathological case (index == usize::MAX) this can wrap around diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index b495215d67..29ead02915 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -16,8 +16,8 @@ use crate::{ recursion::ReprGuard, types::AsNumber, types::{ - AsSequence, Comparable, Constructor, Hashable, Initializer, IterNext, IterNextIterable, - Iterable, PyComparisonOp, Representable, Unconstructible, + AsSequence, Comparable, Constructor, Hashable, Initializer, IterNext, Iterable, + PyComparisonOp, Representable, SelfIter, Unconstructible, }, utils::collection_repr, vm::VirtualMachine, @@ -1257,7 +1257,7 @@ impl PySetIterator { } impl Unconstructible for PySetIterator {} -impl IterNextIterable for PySetIterator {} +impl SelfIter for PySetIterator {} impl IterNext for PySetIterator { fn next(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { let mut internal = zelf.internal.lock(); diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index 146e48d3b8..bdb32981de 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -20,8 +20,8 @@ use crate::{ sequence::SequenceExt, sliceable::{SequenceIndex, SliceableSequenceOp}, types::{ - AsMapping, AsNumber, AsSequence, Comparable, Constructor, Hashable, IterNext, - IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible, + AsMapping, AsNumber, AsSequence, Comparable, Constructor, Hashable, IterNext, Iterable, + PyComparisonOp, Representable, SelfIter, Unconstructible, }, AsObject, Context, Py, PyExact, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult, TryFromBorrowedObject, VirtualMachine, @@ -232,7 +232,7 @@ impl PyStrIterator { } impl Unconstructible for PyStrIterator {} -impl IterNextIterable for PyStrIterator {} +impl SelfIter for PyStrIterator {} impl IterNext for PyStrIterator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let mut internal = zelf.internal.lock(); diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs index 283a5d3160..63cd3e4335 100644 --- a/vm/src/builtins/tuple.rs +++ b/vm/src/builtins/tuple.rs @@ -12,8 +12,8 @@ use crate::{ sequence::{OptionalRangeArgs, SequenceExt}, sliceable::{SequenceIndex, SliceableSequenceOp}, types::{ - AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, IterNextIterable, - Iterable, PyComparisonOp, Representable, Unconstructible, + AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, Iterable, + PyComparisonOp, Representable, SelfIter, Unconstructible, }, utils::collection_repr, vm::VirtualMachine, @@ -457,7 +457,7 @@ impl PyTupleIterator { } impl Unconstructible for PyTupleIterator {} -impl IterNextIterable for PyTupleIterator {} +impl SelfIter for PyTupleIterator {} impl IterNext for PyTupleIterator { fn next(zelf: &Py, _vm: &VirtualMachine) -> PyResult { zelf.internal.lock().next(|tuple, pos| { diff --git a/vm/src/builtins/zip.rs b/vm/src/builtins/zip.rs index 5aad48d064..56c88f14c6 100644 --- a/vm/src/builtins/zip.rs +++ b/vm/src/builtins/zip.rs @@ -4,7 +4,7 @@ use crate::{ class::PyClassImpl, function::{ArgIntoBool, OptionalArg, PosArgs}, protocol::{PyIter, PyIterReturn}, - types::{Constructor, IterNext, IterNextIterable, Iterable}, + types::{Constructor, IterNext, Iterable, SelfIter}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, }; use rustpython_common::atomic::{self, PyAtomic, Radium}; @@ -68,7 +68,7 @@ impl PyZip { } } -impl IterNextIterable for PyZip {} +impl SelfIter for PyZip {} impl IterNext for PyZip { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { if zelf.iterators.is_empty() { diff --git a/vm/src/stdlib/collections.rs b/vm/src/stdlib/collections.rs index 34c5e4157c..e416792d05 100644 --- a/vm/src/stdlib/collections.rs +++ b/vm/src/stdlib/collections.rs @@ -16,8 +16,8 @@ mod _collections { sequence::{MutObjectSequenceOp, OptionalRangeArgs}, sliceable::SequenceIndexOp, types::{ - AsSequence, Comparable, Constructor, Initializer, IterNext, IterNextIterable, Iterable, - PyComparisonOp, Representable, + AsSequence, Comparable, Constructor, Initializer, IterNext, Iterable, PyComparisonOp, + Representable, SelfIter, }, utils::collection_repr, AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, @@ -654,7 +654,7 @@ mod _collections { } } - impl IterNextIterable for PyDequeIterator {} + impl SelfIter for PyDequeIterator {} impl IterNext for PyDequeIterator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { zelf.internal.lock().next(|deque, pos| { @@ -720,7 +720,7 @@ mod _collections { } } - impl IterNextIterable for PyReverseDequeIterator {} + impl SelfIter for PyReverseDequeIterator {} impl IterNext for PyReverseDequeIterator { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { zelf.internal.lock().next(|deque, pos| { diff --git a/vm/src/stdlib/itertools.rs b/vm/src/stdlib/itertools.rs index f8321f22b9..cc5cd35df7 100644 --- a/vm/src/stdlib/itertools.rs +++ b/vm/src/stdlib/itertools.rs @@ -16,7 +16,7 @@ mod decl { identifier, protocol::{PyIter, PyIterReturn, PyNumber}, stdlib::sys, - types::{Constructor, IterNext, IterNextIterable, Iterable, Representable}, + types::{Constructor, IterNext, Iterable, Representable, SelfIter}, AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, PyWeakRef, TryFromObject, VirtualMachine, }; @@ -110,7 +110,7 @@ mod decl { Ok(()) } } - impl IterNextIterable for PyItertoolsChain {} + impl SelfIter for PyItertoolsChain {} impl IterNext for PyItertoolsChain { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let Some(source) = zelf.source.read().clone() else { @@ -200,7 +200,7 @@ mod decl { } } - impl IterNextIterable for PyItertoolsCompress {} + impl SelfIter for PyItertoolsCompress {} impl IterNext for PyItertoolsCompress { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { loop { @@ -268,7 +268,7 @@ mod decl { (zelf.class().to_owned(), (zelf.cur.read().clone(),)) } } - impl IterNextIterable for PyItertoolsCount {} + impl SelfIter for PyItertoolsCount {} impl IterNext for PyItertoolsCount { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let mut cur = zelf.cur.write(); @@ -316,7 +316,7 @@ mod decl { #[pyclass(with(IterNext, Iterable, Constructor), flags(BASETYPE))] impl PyItertoolsCycle {} - impl IterNextIterable for PyItertoolsCycle {} + impl SelfIter for PyItertoolsCycle {} impl IterNext for PyItertoolsCycle { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let item = if let PyIterReturn::Return(item) = zelf.iter.next(vm)? { @@ -400,7 +400,7 @@ mod decl { } } - impl IterNextIterable for PyItertoolsRepeat {} + impl SelfIter for PyItertoolsRepeat {} impl IterNext for PyItertoolsRepeat { fn next(zelf: &Py, _vm: &VirtualMachine) -> PyResult { if let Some(ref times) = zelf.times { @@ -466,7 +466,7 @@ mod decl { ) } } - impl IterNextIterable for PyItertoolsStarmap {} + impl SelfIter for PyItertoolsStarmap {} impl IterNext for PyItertoolsStarmap { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let obj = zelf.iterable.next(vm)?; @@ -537,7 +537,7 @@ mod decl { Ok(()) } } - impl IterNextIterable for PyItertoolsTakewhile {} + impl SelfIter for PyItertoolsTakewhile {} impl IterNext for PyItertoolsTakewhile { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { if zelf.stop_flag.load() { @@ -618,7 +618,7 @@ mod decl { Ok(()) } } - impl IterNextIterable for PyItertoolsDropwhile {} + impl SelfIter for PyItertoolsDropwhile {} impl IterNext for PyItertoolsDropwhile { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let predicate = &zelf.predicate; @@ -737,7 +737,7 @@ mod decl { Ok(PyIterReturn::Return((new_value, new_key))) } } - impl IterNextIterable for PyItertoolsGroupBy {} + impl SelfIter for PyItertoolsGroupBy {} impl IterNext for PyItertoolsGroupBy { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let mut state = zelf.state.lock(); @@ -797,7 +797,7 @@ mod decl { #[pyclass(with(IterNext, Iterable))] impl PyItertoolsGrouper {} - impl IterNextIterable for PyItertoolsGrouper {} + impl SelfIter for PyItertoolsGrouper {} impl IterNext for PyItertoolsGrouper { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let old_key = { @@ -959,7 +959,7 @@ mod decl { } } - impl IterNextIterable for PyItertoolsIslice {} + impl SelfIter for PyItertoolsIslice {} impl IterNext for PyItertoolsIslice { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { while zelf.cur.load() < zelf.next.load() { @@ -1033,7 +1033,7 @@ mod decl { ) } } - impl IterNextIterable for PyItertoolsFilterFalse {} + impl SelfIter for PyItertoolsFilterFalse {} impl IterNext for PyItertoolsFilterFalse { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let predicate = &zelf.predicate; @@ -1094,7 +1094,7 @@ mod decl { #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsAccumulate {} - impl IterNextIterable for PyItertoolsAccumulate {} + impl SelfIter for PyItertoolsAccumulate {} impl IterNext for PyItertoolsAccumulate { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let iterable = &zelf.iterable; @@ -1222,7 +1222,7 @@ mod decl { } } } - impl IterNextIterable for PyItertoolsTee {} + impl SelfIter for PyItertoolsTee {} impl IterNext for PyItertoolsTee { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let value = match zelf.tee_data.get_item(vm, zelf.index.load())? { @@ -1302,7 +1302,7 @@ mod decl { } } } - impl IterNextIterable for PyItertoolsProduct {} + impl SelfIter for PyItertoolsProduct {} impl IterNext for PyItertoolsProduct { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { // stop signal @@ -1413,7 +1413,7 @@ mod decl { } } - impl IterNextIterable for PyItertoolsCombinations {} + impl SelfIter for PyItertoolsCombinations {} impl IterNext for PyItertoolsCombinations { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { // stop signal @@ -1515,7 +1515,7 @@ mod decl { #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsCombinationsWithReplacement {} - impl IterNextIterable for PyItertoolsCombinationsWithReplacement {} + impl SelfIter for PyItertoolsCombinationsWithReplacement {} impl IterNext for PyItertoolsCombinationsWithReplacement { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { // stop signal @@ -1624,7 +1624,7 @@ mod decl { #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsPermutations {} - impl IterNextIterable for PyItertoolsPermutations {} + impl SelfIter for PyItertoolsPermutations {} impl IterNext for PyItertoolsPermutations { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { // stop signal @@ -1747,7 +1747,7 @@ mod decl { Ok(()) } } - impl IterNextIterable for PyItertoolsZipLongest {} + impl SelfIter for PyItertoolsZipLongest {} impl IterNext for PyItertoolsZipLongest { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { if zelf.iterators.is_empty() { @@ -1796,7 +1796,7 @@ mod decl { #[pyclass(with(IterNext, Iterable, Constructor))] impl PyItertoolsPairwise {} - impl IterNextIterable for PyItertoolsPairwise {} + impl SelfIter for PyItertoolsPairwise {} impl IterNext for PyItertoolsPairwise { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { let old = match zelf.old.read().clone() { diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 6bac2e7e78..9854bf59ed 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -316,7 +316,7 @@ pub(super) mod _os { function::{ArgBytesLike, Either, FsPath, FuncArgs, OptionalArg}, protocol::PyIterReturn, recursion::ReprGuard, - types::{IterNext, IterNextIterable, Iterable, PyStructSequence, Representable}, + types::{IterNext, Iterable, PyStructSequence, Representable, SelfIter}, vm::VirtualMachine, AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, }; @@ -809,7 +809,7 @@ pub(super) mod _os { zelf.close() } } - impl IterNextIterable for ScandirIterator {} + impl SelfIter for ScandirIterator {} impl IterNext for ScandirIterator { fn next(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { let entryref: &mut Option = &mut zelf.entries.write(); diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 5b3bbd8ba8..4d2c273c5a 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -305,7 +305,7 @@ fn iter_wrapper(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { vm.call_special_method(&zelf, identifier!(vm, __iter__), ()) } -// PyObject_IterNextIterable in CPython +// PyObject_SelfIter in CPython fn self_iter(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult { Ok(zelf) } @@ -1271,11 +1271,11 @@ pub trait IterNext: PyPayload + Iterable { } } -pub trait IterNextIterable: PyPayload {} +pub trait SelfIter: PyPayload {} impl Iterable for T where - T: IterNextIterable, + T: SelfIter, { #[cold] fn slot_iter(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { diff --git a/wasm/lib/src/js_module.rs b/wasm/lib/src/js_module.rs index e71130c5cc..7cdfd9d398 100644 --- a/wasm/lib/src/js_module.rs +++ b/wasm/lib/src/js_module.rs @@ -14,7 +14,7 @@ mod _js { convert::{IntoObject, ToPyObject}, function::{ArgCallable, OptionalArg, OptionalOption, PosArgs}, protocol::PyIterReturn, - types::{IterNext, IterNextIterable, Representable}, + types::{IterNext, Representable, SelfIter}, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, }; use std::{cell, fmt, future}; @@ -602,7 +602,7 @@ mod _js { } } - impl IterNextIterable for AwaitPromise {} + impl SelfIter for AwaitPromise {} impl IterNext for AwaitPromise { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { zelf.send(None, vm) From 4a099ab6bc3e2029879e92feca5d18e425656a63 Mon Sep 17 00:00:00 2001 From: Kim Seungha Date: Tue, 14 Mar 2023 19:13:11 +0900 Subject: [PATCH 4/6] Make builtin method type same as that of builtin function --- derive-impl/src/pyclass.rs | 2 +- vm/src/builtins/builtin_func.rs | 94 ++++++++++++++++++++++++--------- vm/src/function/builtin.rs | 10 ++-- 3 files changed, 77 insertions(+), 29 deletions(-) diff --git a/derive-impl/src/pyclass.rs b/derive-impl/src/pyclass.rs index 2b8689669d..8b1759dee9 100644 --- a/derive-impl/src/pyclass.rs +++ b/derive-impl/src/pyclass.rs @@ -738,7 +738,7 @@ where quote!(.with_doc(#doc.to_owned(), ctx)) }); let build_func = match self.inner.attr_name { - AttrName::Method => quote!(.build_method(ctx, class)), + AttrName::Method => quote!(.build_method(ctx, class, false)), AttrName::ClassMethod => quote!(.build_classmethod(ctx, class)), AttrName::StaticMethod => quote!(.build_staticmethod(ctx, class)), other => unreachable!( diff --git a/vm/src/builtins/builtin_func.rs b/vm/src/builtins/builtin_func.rs index 39a5353aa7..80a4d404b1 100644 --- a/vm/src/builtins/builtin_func.rs +++ b/vm/src/builtins/builtin_func.rs @@ -1,6 +1,5 @@ use super::{type_, PyClassMethod, PyStaticMethod, PyStr, PyStrInterned, PyStrRef, PyType}; use crate::{ - builtins::PyBoundMethod, class::PyClassImpl, function::{FuncArgs, IntoPyNativeFunc, PyNativeFunc}, types::{Callable, Constructor, GetDescriptor, Representable, Unconstructible}, @@ -8,14 +7,15 @@ use crate::{ }; use std::fmt; +#[derive(Clone)] pub struct PyNativeFuncDef { - pub func: PyNativeFunc, + pub func: &'static PyNativeFunc, pub name: &'static PyStrInterned, pub doc: Option, } impl PyNativeFuncDef { - pub fn new(func: PyNativeFunc, name: &'static PyStrInterned) -> Self { + pub fn new(func: &'static PyNativeFunc, name: &'static PyStrInterned) -> Self { Self { func, name, @@ -29,14 +29,36 @@ impl PyNativeFuncDef { } pub fn into_function(self) -> PyBuiltinFunction { - self.into() + PyBuiltinFunction { + zelf: None, + value: self, + module: None, + is_classmethod: false, + } + } + pub fn into_method(self, obj: PyObjectRef, is_classmethod: bool) -> PyBuiltinFunction { + PyBuiltinFunction { + zelf: Some(obj), + value: self, + module: None, + is_classmethod, + } } pub fn build_function(self, ctx: &Context) -> PyRef { self.into_function().into_ref(ctx) } - pub fn build_method(self, ctx: &Context, class: &'static Py) -> PyRef { + pub fn build_method( + self, + ctx: &Context, + class: &'static Py, + is_classmethod: bool, + ) -> PyRef { PyRef::new_ref( - PyBuiltinMethod { value: self, class }, + PyBuiltinMethod { + value: self, + class, + is_classmethod, + }, ctx.types.method_descriptor_type.to_owned(), None, ) @@ -47,7 +69,7 @@ impl PyNativeFuncDef { class: &'static Py, ) -> PyRef { // TODO: classmethod_descriptor - let callable = self.build_method(ctx, class).into(); + let callable = self.build_method(ctx, class, true).into(); PyClassMethod::new_ref(callable, ctx) } pub fn build_staticmethod( @@ -55,15 +77,18 @@ impl PyNativeFuncDef { ctx: &Context, class: &'static Py, ) -> PyRef { - let callable = self.build_method(ctx, class).into(); + // TODO + let callable = self.build_method(ctx, class, true).into(); PyStaticMethod::new_ref(callable, ctx) } } #[pyclass(name = "builtin_function_or_method", module = false)] pub struct PyBuiltinFunction { + zelf: Option, value: PyNativeFuncDef, module: Option, + is_classmethod: bool, } impl PyPayload for PyBuiltinFunction { @@ -78,15 +103,6 @@ impl fmt::Debug for PyBuiltinFunction { } } -impl From for PyBuiltinFunction { - fn from(value: PyNativeFuncDef) -> Self { - Self { - value, - module: None, - } - } -} - impl PyBuiltinFunction { pub fn with_module(mut self, module: PyObjectRef) -> Self { self.module = Some(module); @@ -101,15 +117,18 @@ impl PyBuiltinFunction { ) } - pub fn as_func(&self) -> &PyNativeFunc { - &self.value.func + pub fn as_func(&self) -> &'static PyNativeFunc { + self.value.func } } impl Callable for PyBuiltinFunction { type Args = FuncArgs; #[inline] - fn call(zelf: &Py, args: FuncArgs, vm: &VirtualMachine) -> PyResult { + fn call(zelf: &Py, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult { + if let Some(z) = &zelf.zelf { + args.prepend_arg(z.clone()); + } (zelf.value.func)(vm, args) } } @@ -125,8 +144,22 @@ impl PyBuiltinFunction { self.value.name.to_owned() } #[pygetset(magic)] - fn qualname(&self) -> PyStrRef { - self.name() + fn qualname(&self, vm: &VirtualMachine) -> PyStrRef { + if let Some(zelf) = &self.zelf { + // TODO: is_classmethod 이면 zelf 의 이름을 알 방법이 없나? + let prefix = if self.is_classmethod { + zelf.get_attr("__qualname__", vm) + .unwrap() + .str(vm) + .unwrap() + .to_string() + } else { + zelf.class().name().to_string() + }; + PyStr::from(format!("{}.{}", prefix, &self.value.name)).into_ref(&vm.ctx) + } else { + self.name() + } } #[pygetset(magic)] fn doc(&self) -> Option { @@ -173,6 +206,7 @@ impl Unconstructible for PyBuiltinFunction {} pub struct PyBuiltinMethod { value: PyNativeFuncDef, class: &'static Py, + is_classmethod: bool, } impl PyPayload for PyBuiltinMethod { @@ -200,8 +234,20 @@ impl GetDescriptor for PyBuiltinMethod { }; let r = if vm.is_none(&obj) && !Self::_cls_is(&cls, obj.class()) { zelf + } else if _zelf.is_classmethod { + _zelf + .value + .clone() + .into_method(cls.unwrap(), _zelf.is_classmethod) + .into_ref(&vm.ctx) + .into() } else { - PyBoundMethod::new_ref(obj, zelf, &vm.ctx).into() + _zelf + .value + .clone() + .into_method(obj, _zelf.is_classmethod) + .into_ref(&vm.ctx) + .into() }; Ok(r) } @@ -225,7 +271,7 @@ impl PyBuiltinMethod { where F: IntoPyNativeFunc, { - ctx.make_func_def(name, f).build_method(ctx, class) + ctx.make_func_def(name, f).build_method(ctx, class, false) } } diff --git a/vm/src/function/builtin.rs b/vm/src/function/builtin.rs index 3a1591cb45..2bc058824d 100644 --- a/vm/src/function/builtin.rs +++ b/vm/src/function/builtin.rs @@ -6,7 +6,7 @@ use crate::{ use std::marker::PhantomData; /// A built-in Python function. -pub type PyNativeFunc = Box PyResult)>; +pub type PyNativeFunc = py_dyn_fn!(dyn Fn(&VirtualMachine, FuncArgs) -> PyResult); /// Implemented by types that are or can generate built-in functions. /// @@ -32,8 +32,10 @@ pub trait IntoPyNativeFunc: Sized + PyThreadingConstraint + 'static { /// `IntoPyNativeFunc::into_func()` generates a PyNativeFunc that performs the /// appropriate type and arity checking, any requested conversions, and then if /// successful calls the function with the extracted parameters. - fn into_func(self) -> PyNativeFunc { - Box::new(move |vm: &VirtualMachine, args| self.call(vm, args)) + fn into_func(self) -> &'static PyNativeFunc { + Box::leak(Box::new(move |vm: &VirtualMachine, args| { + self.call(vm, args) + })) } } @@ -177,7 +179,7 @@ mod tests { #[test] fn test_intonativefunc_noalloc() { - let check_zst = |f: PyNativeFunc| assert_eq!(std::mem::size_of_val(f.as_ref()), 0); + let check_zst = |f: &'static PyNativeFunc| assert_eq!(std::mem::size_of_val(f), 0); fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 { 1 } From 98eef0804e3e34def6e2610e9c1c57b3cb40075d Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 29 Apr 2023 06:31:08 +0900 Subject: [PATCH 5/6] Method overhaul with PyMethodDef --- derive-impl/src/pyclass.rs | 184 ++++++++++---- derive-impl/src/pymodule.rs | 121 +++++---- derive-impl/src/util.rs | 1 + examples/mini_repl.rs | 2 +- extra_tests/snippets/builtin_type.py | 7 +- extra_tests/snippets/syntax_class.py | 18 +- vm/src/builtins/builtin_func.rs | 361 +++++++++++---------------- vm/src/builtins/descriptor.rs | 160 +++++++++++- vm/src/builtins/module.rs | 22 +- vm/src/builtins/staticmethod.rs | 26 +- vm/src/builtins/super.rs | 5 +- vm/src/builtins/type.rs | 29 ++- vm/src/class.rs | 28 ++- vm/src/codecs.rs | 53 ++-- vm/src/exceptions.rs | 181 +++++++------- vm/src/frame.rs | 15 +- vm/src/function/builtin.rs | 64 ++--- vm/src/function/method.rs | 259 +++++++++++++++++++ vm/src/function/mod.rs | 4 +- vm/src/macros.rs | 11 +- vm/src/object/core.rs | 7 +- vm/src/object/payload.rs | 4 + vm/src/protocol/callable.rs | 1 + vm/src/stdlib/builtins.rs | 1 + vm/src/stdlib/sys.rs | 2 +- vm/src/types/slot.rs | 21 +- vm/src/types/zoo.rs | 11 +- vm/src/vm/context.rs | 79 +++--- vm/src/vm/interpreter.rs | 2 + vm/src/vm/mod.rs | 36 +-- vm/src/vm/vm_new.rs | 41 ++- wasm/lib/src/convert.rs | 39 ++- wasm/lib/src/js_module.rs | 4 +- wasm/lib/src/vm_class.rs | 2 +- wasm/lib/src/wasm_builtins.rs | 6 +- 35 files changed, 1128 insertions(+), 679 deletions(-) create mode 100644 vm/src/function/method.rs diff --git a/derive-impl/src/pyclass.rs b/derive-impl/src/pyclass.rs index 8b1759dee9..3f78811073 100644 --- a/derive-impl/src/pyclass.rs +++ b/derive-impl/src/pyclass.rs @@ -6,7 +6,7 @@ use crate::util::{ }; use proc_macro2::{Span, TokenStream}; use quote::{quote, quote_spanned, ToTokens}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::str::FromStr; use syn::{ parse_quote, spanned::Spanned, Attribute, AttributeArgs, Ident, Item, Meta, NestedMeta, Result, @@ -62,7 +62,8 @@ impl FromStr for AttrName { #[derive(Default)] struct ImplContext { - impl_extend_items: ItemNursery, + attribute_items: ItemNursery, + method_items: MethodNursery, getset_items: GetSetNursery, member_items: MemberNursery, extend_slots_items: ItemNursery, @@ -92,6 +93,7 @@ fn extract_items_into_context<'a, Item>( }); context.errors.ok_or_push(r); } + context.errors.ok_or_push(context.method_items.validate()); context.errors.ok_or_push(context.getset_items.validate()); context.errors.ok_or_push(context.member_items.validate()); } @@ -157,18 +159,28 @@ pub(crate) fn impl_pyclass_impl(attr: AttributeArgs, item: Item) -> Result, + ) { + #method_def + } + }, parse_quote! { fn __extend_py_class( ctx: &::rustpython_vm::Context, @@ -202,6 +214,14 @@ pub(crate) fn impl_pyclass_impl(attr: AttributeArgs, item: Item) -> Result, + ) { + #impl_ty::__extend_method_def(method_defs); + #with_method_defs + } + fn extend_slots(slots: &mut ::rustpython_vm::types::PyTypeSlots) { #impl_ty::__extend_slots(slots); #with_slots @@ -235,9 +255,10 @@ pub(crate) fn impl_pyclass_impl(attr: AttributeArgs, item: Item) -> Result Result, + ) { + #method_def + } + }, parse_quote! { fn __extend_py_class( ctx: &::rustpython_vm::Context, @@ -732,51 +761,14 @@ where let py_name = item_meta.method_name()?; let sig_doc = text_signature(func.sig(), &py_name); - let tokens = { - let doc = args.attrs.doc().map_or_else(TokenStream::new, |mut doc| { - doc = format_doc(&sig_doc, &doc); - quote!(.with_doc(#doc.to_owned(), ctx)) - }); - let build_func = match self.inner.attr_name { - AttrName::Method => quote!(.build_method(ctx, class, false)), - AttrName::ClassMethod => quote!(.build_classmethod(ctx, class)), - AttrName::StaticMethod => quote!(.build_staticmethod(ctx, class)), - other => unreachable!( - "Only 'method', 'classmethod' and 'staticmethod' are supported, got {:?}", - other - ), - }; - if py_name.starts_with("__") && py_name.ends_with("__") { - let name_ident = Ident::new(&py_name, ident.span()); - quote_spanned! { ident.span() => - class.set_attr( - ctx.names.#name_ident, - ctx.make_func_def(ctx.intern_str(#py_name), Self::#ident) - #doc - #build_func - .into(), - ); - } - } else { - quote_spanned! { ident.span() => - class.set_str_attr( - #py_name, - ctx.make_func_def(ctx.intern_str(#py_name), Self::#ident) - #doc - #build_func, - ctx, - ); - } - } - }; - - args.context.impl_extend_items.add_item( - ident.clone(), - vec![py_name], - args.cfgs.to_vec(), - tokens, - 5, - )?; + let doc = args.attrs.doc().map(|doc| format_doc(&sig_doc, &doc)); + args.context.method_items.add_item(MethodNurseryItem { + py_name, + cfgs: args.cfgs.to_vec(), + ident: ident.to_owned(), + doc, + attr_name: self.inner.attr_name, + }); Ok(()) } } @@ -898,7 +890,7 @@ where }; args.context - .impl_extend_items + .attribute_items .add_item(ident.clone(), vec![py_name], cfgs, tokens, 1)?; Ok(()) @@ -960,6 +952,78 @@ where } } +#[derive(Default)] +struct MethodNursery { + items: Vec, +} + +struct MethodNurseryItem { + py_name: String, + cfgs: Vec, + ident: Ident, + doc: Option, + attr_name: AttrName, +} + +impl MethodNursery { + fn add_item(&mut self, item: MethodNurseryItem) { + self.items.push(item); + } + + fn validate(&mut self) -> Result<()> { + let mut name_set = HashSet::new(); + for item in &self.items { + if !name_set.insert((&item.py_name, &item.cfgs)) { + bail_span!(item.ident, "duplicate method name `{}`", item.py_name); + } + } + Ok(()) + } +} + +impl ToTokens for MethodNursery { + fn to_tokens(&self, tokens: &mut TokenStream) { + for item in &self.items { + let py_name = &item.py_name; + let ident = &item.ident; + let cfgs = &item.cfgs; + let doc = if let Some(doc) = item.doc.as_ref() { + quote! { Some(#doc) } + } else { + quote! { None } + }; + let flags = match &item.attr_name { + AttrName::Method => { + quote! { rustpython_vm::function::PyMethodFlags::METHOD } + } + AttrName::ClassMethod => { + quote! { rustpython_vm::function::PyMethodFlags::CLASS } + } + AttrName::StaticMethod => { + quote! { rustpython_vm::function::PyMethodFlags::STATIC } + } + _ => unreachable!(), + }; + // TODO: intern + // let py_name = if py_name.starts_with("__") && py_name.ends_with("__") { + // let name_ident = Ident::new(&py_name, ident.span()); + // quote_spanned! { ident.span() => ctx.names.#name_ident } + // } else { + // quote_spanned! { ident.span() => #py_name } + // }; + tokens.extend(quote! { + #(#cfgs)* + method_defs.push(rustpython_vm::function::PyMethodDef { + name: #py_name, + func: rustpython_vm::function::IntoPyNativeFn::into_func(Self::#ident), + flags: #flags, + doc: #doc, + }); + }); + } + } +} + #[derive(Default)] #[allow(clippy::type_complexity)] struct GetSetNursery { @@ -1367,13 +1431,15 @@ impl MemberItemMeta { struct ExtractedImplAttrs { payload: Option, + flags: TokenStream, with_impl: TokenStream, + with_method_defs: TokenStream, with_slots: TokenStream, - flags: TokenStream, } fn extract_impl_attrs(attr: AttributeArgs, item: &Ident) -> Result { let mut withs = Vec::new(); + let mut with_method_defs = Vec::new(); let mut with_slots = Vec::new(); let mut flags = vec![quote! { { @@ -1396,16 +1462,18 @@ fn extract_impl_attrs(attr: AttributeArgs, item: &Ident) -> Result::__extend_py_class), + quote!(#path::::__extend_method_def), quote!(#path::::__extend_slots), ) } else { ( quote!(::__extend_py_class), + quote!(::__extend_method_def), quote!(::__extend_slots), ) }; @@ -1413,6 +1481,9 @@ fn extract_impl_attrs(attr: AttributeArgs, item: &Ident) -> Result #extend_class(ctx, class); }); + with_method_defs.push(quote_spanned! { path.span() => + #extend_method_def(method_defs); + }); with_slots.push(quote_spanned! { item_span => #extend_slots(slots); }); @@ -1450,11 +1521,14 @@ fn extract_impl_attrs(attr: AttributeArgs, item: &Ident) -> Result Result &'static ::rustpython_vm::builtins::PyModuleDef { DEF.get_or_init(|| { + #[allow(clippy::ptr_arg)] + let method_defs = { + let mut method_defs = Vec::new(); + extend_method_def(ctx, &mut method_defs); + method_defs + }; let mut def = ::rustpython_vm::builtins::PyModuleDef { name: ctx.intern_str(MODULE_NAME), doc: DOC.map(|doc| ctx.intern_str(doc)), + methods: Box::leak(method_defs.into_boxed_slice()), slots: Default::default(), }; def.slots.exec = Some(extend_module); @@ -160,6 +167,18 @@ pub fn impl_pymodule(attr: AttributeArgs, module_item: Item) -> Result, + ) { + #( + super::#withs::extend_method_def(ctx, method_defs); + )* + #function_items + } + }, parse_quote! { pub(crate) fn __init_attributes( vm: &::rustpython_vm::VirtualMachine, @@ -177,23 +196,10 @@ pub fn impl_pymodule(attr: AttributeArgs, module_item: Item) -> Result, ) { - __init_methods(vm, module); + module.__init_methods(vm).unwrap(); __init_attributes(vm, module); } }, - parse_quote! { - // TODO: remove once PyMethodDef done - pub(crate) fn __init_methods( - vm: &::rustpython_vm::VirtualMachine, - module: &::rustpython_vm::Py<::rustpython_vm::builtins::PyModule>, - ) { - #( - super::#withs::__init_methods(vm, module); - )* - let ctx = &vm.ctx; - #function_items - } - }, parse_quote! { pub(crate) fn __init_dict( vm: &::rustpython_vm::VirtualMachine, @@ -330,9 +336,7 @@ struct FunctionNurseryItem { py_names: Vec, cfgs: Vec, ident: Ident, - #[allow(dead_code)] doc: String, - tokens: TokenStream, } impl FunctionNursery { @@ -358,11 +362,23 @@ struct ValidatedFunctionNursery(FunctionNursery); impl ToTokens for ValidatedFunctionNursery { fn to_tokens(&self, tokens: &mut TokenStream) { for item in &self.0.items { + let ident = &item.ident; let cfgs = &item.cfgs; - let item_tokens = &item.tokens; + let py_names = &item.py_names; + let doc = &item.doc; + let flags = quote! { rustpython_vm::function::PyMethodFlags::empty() }; + tokens.extend(quote! { #(#cfgs)* - #item_tokens + { + let doc = Some(#doc); + #(method_defs.push(rustpython_vm::function::PyMethodDef::new( + (#py_names), + #ident, + #flags, + doc, + ));)* + } }); } } @@ -438,38 +454,23 @@ impl ModuleItem for FunctionItem { let py_name = item_meta.simple_name()?; let sig_doc = text_signature(func.sig(), &py_name); - let (tokens, py_names, doc) = { - let module = args.module_name(); - let doc = args.attrs.doc().or_else(|| { - crate::doc::Database::shared() - .try_module_item(module, &py_name) - .ok() // TODO: doc must exist at least one of code or CPython - .flatten() - .map(str::to_owned) - }); - let doc = if let Some(doc) = doc { - format_doc(&sig_doc, &doc) - } else { - sig_doc - }; - let with_doc = quote!(.with_doc(#doc.to_owned(), &vm.ctx)); - let new_func = quote_spanned!(ident.span()=> - vm.ctx.make_func_def(vm.ctx.intern_str(#py_name), #ident) - #with_doc - .into_function() - .with_module(vm.new_pyobj(#module.to_owned())) - .into_ref(&vm.ctx) - ); + let module = args.module_name(); + let doc = args.attrs.doc().or_else(|| { + crate::doc::Database::shared() + .try_module_item(module, &py_name) + .ok() // TODO: doc must exist at least one of code or CPython + .flatten() + .map(str::to_owned) + }); + let doc = if let Some(doc) = doc { + format_doc(&sig_doc, &doc) + } else { + sig_doc + }; + let py_names = { if self.py_attrs.is_empty() { - ( - quote_spanned! { ident.span() => { - let func = #new_func; - vm.__module_set_attr(module, #py_name, func).unwrap(); - }}, - vec![py_name], - doc, - ) + vec![py_name] } else { let mut py_names = HashSet::new(); py_names.insert(py_name); @@ -494,16 +495,7 @@ impl ModuleItem for FunctionItem { args.context.errors.ok_or_push(r); } let py_names: Vec<_> = py_names.into_iter().collect(); - ( - quote_spanned! { ident.span().resolved_at(Span::call_site()) => { - let func = #new_func; - for name in [#(#py_names,)*] { - vm.__module_set_attr(module, name, func.clone()).unwrap(); - } - }}, - py_names, - doc, - ) + py_names } }; @@ -512,7 +504,6 @@ impl ModuleItem for FunctionItem { py_names, cfgs: args.cfgs.to_vec(), doc, - tokens, }); Ok(()) } @@ -585,12 +576,12 @@ impl ModuleItem for ClassItem { 1 => { let py_name = &py_names[0]; quote! { - vm.__module_set_attr(&module, #py_name, new_class).unwrap(); + vm.__module_set_attr(&module, vm.ctx.intern_str(#py_name), new_class).unwrap(); } } _ => quote! { for name in [#(#py_names,)*] { - vm.__module_set_attr(&module, name, new_class.clone()).unwrap(); + vm.__module_set_attr(&module, vm.ctx.intern_str(name), new_class.clone()).unwrap(); } }, }; @@ -681,7 +672,7 @@ impl ModuleItem for AttributeItem { ident.to_string() }; let tokens = quote_spanned! { ident.span() => - vm.__module_set_attr(module, #py_name, vm.new_pyobj(#ident)).unwrap(); + vm.__module_set_attr(module, vm.ctx.intern_str(#py_name), vm.new_pyobj(#ident)).unwrap(); }; args.context.attribute_items.add_item( ident.clone(), @@ -705,7 +696,7 @@ impl ModuleItem for AttributeItem { ( quote_spanned! { ident.span() => { #let_obj - vm.__module_set_attr(module, #py_name, obj).unwrap(); + vm.__module_set_attr(module, vm.ctx.intern_str(#py_name), obj).unwrap(); }}, vec![py_name], ) @@ -738,7 +729,7 @@ impl ModuleItem for AttributeItem { quote_spanned! { ident.span() => { #let_obj for name in [(#(#names,)*)] { - vm.__module_set_attr(module, name, obj.clone()).unwrap(); + vm.__module_set_attr(module, vm.ctx.intern_str(name), obj.clone()).unwrap(); } }}, names, diff --git a/derive-impl/src/util.rs b/derive-impl/src/util.rs index c0979f0807..c7b98ab2ce 100644 --- a/derive-impl/src/util.rs +++ b/derive-impl/src/util.rs @@ -320,6 +320,7 @@ impl ItemMeta for ClassItemMeta { "base", "metaclass", "unhashable", + "ctx", "impl", "traverse", ]; diff --git a/examples/mini_repl.rs b/examples/mini_repl.rs index 4e64419512..c5c2114acf 100644 --- a/examples/mini_repl.rs +++ b/examples/mini_repl.rs @@ -41,7 +41,7 @@ fn run(vm: &vm::VirtualMachine) -> vm::PyResult<()> { // typing `quit()` is too long, let's make `on(False)` work instead. scope .globals - .set_item("on", vm.ctx.new_function("on", on).into(), vm)?; + .set_item("on", vm.new_function("on", on).into(), vm)?; // let's include a fibonacci function, but let's be lazy and write it in Python add_python_function!( diff --git a/extra_tests/snippets/builtin_type.py b/extra_tests/snippets/builtin_type.py index 68ef65efd1..9f30b9b0ed 100644 --- a/extra_tests/snippets/builtin_type.py +++ b/extra_tests/snippets/builtin_type.py @@ -1,3 +1,4 @@ +import types from testutils import assert_raises @@ -256,7 +257,7 @@ class B: assert object.__init_subclass__.__qualname__ == 'object.__init_subclass__' # Dynamic with `#[extend_class]`: -assert bytearray.maketrans.__qualname__ == 'bytearray.maketrans' +assert bytearray.maketrans.__qualname__ == 'bytearray.maketrans', bytearray.maketrans.__qualname__ # Third-party: @@ -560,3 +561,7 @@ def my_repr_func(): pass assert repr(my_repr_func).startswith(', -} - -impl PyNativeFuncDef { - pub fn new(func: &'static PyNativeFunc, name: &'static PyStrInterned) -> Self { - Self { - func, - name, - doc: None, - } - } - - pub fn with_doc(mut self, doc: String, ctx: &Context) -> Self { - self.doc = Some(PyStr::new_ref(doc, ctx)); - self - } - - pub fn into_function(self) -> PyBuiltinFunction { - PyBuiltinFunction { - zelf: None, - value: self, - module: None, - is_classmethod: false, - } - } - pub fn into_method(self, obj: PyObjectRef, is_classmethod: bool) -> PyBuiltinFunction { - PyBuiltinFunction { - zelf: Some(obj), - value: self, - module: None, - is_classmethod, - } - } - pub fn build_function(self, ctx: &Context) -> PyRef { - self.into_function().into_ref(ctx) - } - pub fn build_method( - self, - ctx: &Context, - class: &'static Py, - is_classmethod: bool, - ) -> PyRef { - PyRef::new_ref( - PyBuiltinMethod { - value: self, - class, - is_classmethod, - }, - ctx.types.method_descriptor_type.to_owned(), - None, - ) - } - pub fn build_classmethod( - self, - ctx: &Context, - class: &'static Py, - ) -> PyRef { - // TODO: classmethod_descriptor - let callable = self.build_method(ctx, class, true).into(); - PyClassMethod::new_ref(callable, ctx) - } - pub fn build_staticmethod( - self, - ctx: &Context, - class: &'static Py, - ) -> PyRef { - // TODO - let callable = self.build_method(ctx, class, true).into(); - PyStaticMethod::new_ref(callable, ctx) - } -} - +// PyCFunctionObject in CPython #[pyclass(name = "builtin_function_or_method", module = false)] -pub struct PyBuiltinFunction { - zelf: Option, - value: PyNativeFuncDef, - module: Option, - is_classmethod: bool, +pub struct PyNativeFunction { + pub(crate) value: &'static PyMethodDef, + pub(crate) zelf: Option, + pub(crate) module: Option<&'static PyStrInterned>, // None for bound method } -impl PyPayload for PyBuiltinFunction { +impl PyPayload for PyNativeFunction { fn class(ctx: &Context) -> &'static Py { ctx.types.builtin_function_or_method_type } } -impl fmt::Debug for PyBuiltinFunction { +impl fmt::Debug for PyNativeFunction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "builtin function {}", self.value.name.as_str()) + write!( + f, + "builtin function {}.{} ({:?}) self as instance of {:?}", + self.module.map_or("", |m| m.as_str()), + self.value.name, + self.value.flags, + self.zelf.as_ref().map(|z| z.class().name().to_owned()) + ) } } -impl PyBuiltinFunction { - pub fn with_module(mut self, module: PyObjectRef) -> Self { +impl PyNativeFunction { + pub fn with_module(mut self, module: &'static PyStrInterned) -> Self { self.module = Some(module); self } @@ -117,12 +49,12 @@ impl PyBuiltinFunction { ) } - pub fn as_func(&self) -> &'static PyNativeFunc { + pub fn as_func(&self) -> &'static PyNativeFn { self.value.func } } -impl Callable for PyBuiltinFunction { +impl Callable for PyNativeFunction { type Args = FuncArgs; #[inline] fn call(zelf: &Py, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult { @@ -134,196 +66,185 @@ impl Callable for PyBuiltinFunction { } #[pyclass(with(Callable, Constructor), flags(HAS_DICT))] -impl PyBuiltinFunction { +impl PyNativeFunction { #[pygetset(magic)] - fn module(&self, vm: &VirtualMachine) -> PyObjectRef { - vm.unwrap_or_none(self.module.clone()) + fn module(zelf: NativeFunctionOrMethod) -> Option<&'static PyStrInterned> { + zelf.0.module } #[pygetset(magic)] - fn name(&self) -> PyStrRef { - self.value.name.to_owned() + fn name(zelf: NativeFunctionOrMethod) -> &'static str { + zelf.0.value.name } #[pygetset(magic)] - fn qualname(&self, vm: &VirtualMachine) -> PyStrRef { - if let Some(zelf) = &self.zelf { - // TODO: is_classmethod 이면 zelf 의 이름을 알 방법이 없나? - let prefix = if self.is_classmethod { - zelf.get_attr("__qualname__", vm) + fn qualname(zelf: NativeFunctionOrMethod, vm: &VirtualMachine) -> PyResult { + let zelf = zelf.0; + let flags = zelf.value.flags; + // if flags.contains(PyMethodFlags::CLASS) || flags.contains(PyMethodFlags::STATIC) { + let qualname = if let Some(bound) = &zelf.zelf { + let prefix = if flags.contains(PyMethodFlags::CLASS) { + bound + .get_attr("__qualname__", vm) .unwrap() .str(vm) .unwrap() .to_string() } else { - zelf.class().name().to_string() + bound.class().name().to_string() }; - PyStr::from(format!("{}.{}", prefix, &self.value.name)).into_ref(&vm.ctx) + vm.ctx.new_str(format!("{}.{}", prefix, &zelf.value.name)) } else { - self.name() - } + vm.ctx.intern_str(zelf.value.name).to_owned() + }; + Ok(qualname) } #[pygetset(magic)] - fn doc(&self) -> Option { - self.value.doc.clone() + fn doc(zelf: NativeFunctionOrMethod) -> Option<&'static str> { + zelf.0.value.doc } #[pygetset(name = "__self__")] - fn get_self(&self, vm: &VirtualMachine) -> PyObjectRef { + fn __self__(_zelf: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { vm.ctx.none() } #[pymethod(magic)] - fn reduce(&self) -> PyStrRef { + fn reduce(&self) -> &'static str { // TODO: return (getattr, (self.object, self.name)) if this is a method - self.name() + self.value.name } #[pymethod(magic)] - fn reduce_ex(&self, _ver: PyObjectRef) -> PyStrRef { - self.name() + fn reduce_ex(zelf: PyObjectRef, _ver: PyObjectRef, vm: &VirtualMachine) -> PyResult { + vm.call_special_method(&zelf, identifier!(vm, __reduce__), ()) } #[pygetset(magic)] - fn text_signature(&self) -> Option { - let doc = self.value.doc.as_ref()?; - let signature = - type_::get_text_signature_from_internal_doc(self.value.name.as_str(), doc.as_str())?; - Some(signature.to_owned()) + fn text_signature(zelf: NativeFunctionOrMethod) -> Option<&'static str> { + let doc = zelf.0.value.doc?; + let signature = type_::get_text_signature_from_internal_doc(zelf.0.value.name, doc)?; + Some(signature) } } -impl Representable for PyBuiltinFunction { +impl Representable for PyNativeFunction { #[inline] fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { Ok(format!("", zelf.value.name)) } } -impl Unconstructible for PyBuiltinFunction {} +impl Unconstructible for PyNativeFunction {} -// `PyBuiltinMethod` is similar to both `PyMethodDescrObject` in -// https://github.com/python/cpython/blob/main/Include/descrobject.h -// https://github.com/python/cpython/blob/main/Objects/descrobject.c -// and `PyCMethodObject` in -// https://github.com/python/cpython/blob/main/Include/cpython/methodobject.h -// https://github.com/python/cpython/blob/main/Objects/methodobject.c -#[pyclass(module = false, name = "method_descriptor")] -pub struct PyBuiltinMethod { - value: PyNativeFuncDef, - class: &'static Py, - is_classmethod: bool, +// `PyCMethodObject` in CPython +#[pyclass(name = "builtin_method", module = false, base = "PyNativeFunction")] +pub struct PyNativeMethod { + pub(crate) func: PyNativeFunction, + pub(crate) class: &'static Py, // TODO: the actual life is &'self } -impl PyPayload for PyBuiltinMethod { - fn class(ctx: &Context) -> &'static Py { - ctx.types.method_descriptor_type +#[pyclass(with(Callable, Comparable, Representable), flags(HAS_DICT))] +impl PyNativeMethod { + #[pygetset(magic)] + fn qualname(zelf: PyRef, vm: &VirtualMachine) -> PyResult { + let prefix = zelf.class.name().to_string(); + Ok(vm + .ctx + .new_str(format!("{}.{}", prefix, &zelf.func.value.name))) } -} -impl fmt::Debug for PyBuiltinMethod { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "method descriptor for '{}'", self.value.name) + #[pymethod(magic)] + fn reduce(&self, vm: &VirtualMachine) -> PyResult<(PyObjectRef, (PyObjectRef, &'static str))> { + // TODO: return (getattr, (self.object, self.name)) if this is a method + let getattr = vm.builtins.get_attr("getattr", vm)?; + let target = self + .func + .zelf + .clone() + .unwrap_or_else(|| self.class.to_owned().into()); + let name = self.func.value.name; + Ok((getattr, (target, name))) } -} -impl GetDescriptor for PyBuiltinMethod { - fn descr_get( - zelf: PyObjectRef, - obj: Option, - cls: Option, - vm: &VirtualMachine, - ) -> PyResult { - let (_zelf, obj) = match Self::_check(&zelf, obj, vm) { - Some(obj) => obj, - None => return Ok(zelf), - }; - let r = if vm.is_none(&obj) && !Self::_cls_is(&cls, obj.class()) { - zelf - } else if _zelf.is_classmethod { - _zelf - .value - .clone() - .into_method(cls.unwrap(), _zelf.is_classmethod) - .into_ref(&vm.ctx) - .into() - } else { - _zelf - .value - .clone() - .into_method(obj, _zelf.is_classmethod) - .into_ref(&vm.ctx) - .into() - }; - Ok(r) + #[pygetset(name = "__self__")] + fn __self__(zelf: PyRef, _vm: &VirtualMachine) -> Option { + zelf.func.zelf.clone() } } -impl Callable for PyBuiltinMethod { - type Args = FuncArgs; - #[inline] - fn call(zelf: &Py, args: FuncArgs, vm: &VirtualMachine) -> PyResult { - (zelf.value.func)(vm, args) +impl PyPayload for PyNativeMethod { + fn class(ctx: &Context) -> &'static Py { + ctx.types.builtin_method_type } } -impl PyBuiltinMethod { - pub fn new_ref( - name: &'static PyStrInterned, - class: &'static Py, - f: F, - ctx: &Context, - ) -> PyRef - where - F: IntoPyNativeFunc, - { - ctx.make_func_def(name, f).build_method(ctx, class, false) +impl fmt::Debug for PyNativeMethod { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "builtin method of {:?} with {:?}", + &*self.class.name(), + &self.func + ) } } -#[pyclass( - with(GetDescriptor, Callable, Constructor, Representable), - flags(METHOD_DESCR) -)] -impl PyBuiltinMethod { - #[pygetset(magic)] - fn name(&self) -> PyStrRef { - self.value.name.to_owned() - } - #[pygetset(magic)] - fn qualname(&self) -> String { - format!("{}.{}", self.class.name(), &self.value.name) - } - #[pygetset(magic)] - fn doc(&self) -> Option { - self.value.doc.clone() - } - #[pygetset(magic)] - fn text_signature(&self) -> Option { - self.value.doc.as_ref().and_then(|doc| { - type_::get_text_signature_from_internal_doc(self.value.name.as_str(), doc.as_str()) - .map(|signature| signature.to_string()) +impl Comparable for PyNativeMethod { + fn cmp( + zelf: &Py, + other: &PyObject, + op: PyComparisonOp, + _vm: &VirtualMachine, + ) -> PyResult { + op.eq_only(|| { + if let Some(other) = other.payload::() { + let eq = match (zelf.func.zelf.as_ref(), other.func.zelf.as_ref()) { + (Some(z), Some(o)) => z.is(o), + (None, None) => true, + _ => false, + }; + let eq = eq && std::ptr::eq(zelf.func.value, other.func.value); + Ok(eq.into()) + } else { + Ok(PyComparisonValue::NotImplemented) + } }) } - #[pymethod(magic)] - fn reduce( - &self, - vm: &VirtualMachine, - ) -> (Option, (Option, PyStrRef)) { - let builtins_getattr = vm.builtins.get_attr("getattr", vm).ok(); - let classname = vm.builtins.get_attr(&self.class.__name__(vm), vm).ok(); - (builtins_getattr, (classname, self.value.name.to_owned())) +} + +impl Callable for PyNativeMethod { + type Args = FuncArgs; + #[inline] + fn call(zelf: &Py, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult { + if let Some(zelf) = &zelf.func.zelf { + args.prepend_arg(zelf.clone()); + } + (zelf.func.value.func)(vm, args) } } -impl Representable for PyBuiltinMethod { +impl Representable for PyNativeMethod { #[inline] fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { Ok(format!( - "", - &zelf.value.name, + "", + &zelf.func.value.name, zelf.class.name() )) } } -impl Unconstructible for PyBuiltinMethod {} +impl Unconstructible for PyNativeMethod {} pub fn init(context: &Context) { - PyBuiltinFunction::extend_class(context, context.types.builtin_function_or_method_type); - PyBuiltinMethod::extend_class(context, context.types.method_descriptor_type); + PyNativeFunction::extend_class(context, context.types.builtin_function_or_method_type); + PyNativeMethod::extend_class(context, context.types.builtin_method_type); +} + +struct NativeFunctionOrMethod(PyRef); + +impl TryFromObject for NativeFunctionOrMethod { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + let class = vm.ctx.types.builtin_function_or_method_type; + if obj.fast_isinstance(class) { + Ok(NativeFunctionOrMethod(unsafe { obj.downcast_unchecked() })) + } else { + Err(vm.new_downcast_type_error(class, &obj)) + } + } } diff --git a/vm/src/builtins/descriptor.rs b/vm/src/builtins/descriptor.rs index 2425f3ffe4..eb4afbd2f7 100644 --- a/vm/src/builtins/descriptor.rs +++ b/vm/src/builtins/descriptor.rs @@ -1,19 +1,155 @@ -use super::{PyStr, PyStrInterned, PyType, PyTypeRef}; +use super::{PyStr, PyStrInterned, PyType}; use crate::{ + builtins::{builtin_func::PyNativeMethod, type_}, class::PyClassImpl, - function::PySetterValue, - types::{Constructor, GetDescriptor, Representable, Unconstructible}, - AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine, + function::{FuncArgs, PyMethodDef, PyMethodFlags, PySetterValue}, + types::{Callable, Constructor, GetDescriptor, Representable, Unconstructible}, + AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; use rustpython_common::lock::PyRwLock; #[derive(Debug)] -pub struct DescrObject { - pub typ: PyTypeRef, +pub struct PyDescriptor { + pub typ: &'static Py, pub name: &'static PyStrInterned, pub qualname: PyRwLock>, } +#[derive(Debug)] +pub struct PyDescriptorOwned { + pub typ: PyRef, + pub name: &'static PyStrInterned, + pub qualname: PyRwLock>, +} + +#[pyclass(name = "method_descriptor", module = false)] +pub struct PyMethodDescriptor { + pub common: PyDescriptor, + pub method: &'static PyMethodDef, + // vectorcall: vectorcallfunc, +} + +impl PyMethodDescriptor { + pub fn new(method: &'static PyMethodDef, typ: &'static Py, ctx: &Context) -> Self { + Self { + common: PyDescriptor { + typ, + name: ctx.intern_str(method.name), + qualname: PyRwLock::new(None), + }, + method, + } + } +} + +impl PyPayload for PyMethodDescriptor { + fn class(ctx: &Context) -> &'static Py { + ctx.types.method_descriptor_type + } +} + +impl std::fmt::Debug for PyMethodDescriptor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "method descriptor for '{}'", self.common.name) + } +} + +impl GetDescriptor for PyMethodDescriptor { + fn descr_get( + zelf: PyObjectRef, + obj: Option, + cls: Option, + vm: &VirtualMachine, + ) -> PyResult { + let descr = Self::_as_pyref(&zelf, vm).unwrap(); + let bound = match obj { + Some(obj) => { + if descr.method.flags.contains(PyMethodFlags::METHOD) { + if cls.map_or(false, |c| c.fast_isinstance(vm.ctx.types.type_type)) { + obj + } else { + return Err(vm.new_type_error(format!( + "descriptor '{}' needs a type, not '{}', as arg 2", + descr.common.name.as_str(), + obj.class().name() + ))); + } + } else if descr.method.flags.contains(PyMethodFlags::CLASS) { + obj.class().to_owned().into() + } else { + unimplemented!() + } + } + None if descr.method.flags.contains(PyMethodFlags::CLASS) => cls.unwrap(), + None => return Ok(zelf), + }; + // Ok(descr.method.build_bound_method(&vm.ctx, bound, class).into()) + Ok(descr.bind(bound, &vm.ctx).into()) + } +} + +impl Callable for PyMethodDescriptor { + type Args = FuncArgs; + #[inline] + fn call(zelf: &Py, args: FuncArgs, vm: &VirtualMachine) -> PyResult { + (zelf.method.func)(vm, args) + } +} + +impl PyMethodDescriptor { + pub fn bind(&self, obj: PyObjectRef, ctx: &Context) -> PyRef { + self.method.build_bound_method(ctx, obj, self.common.typ) + } +} + +#[pyclass( + with(GetDescriptor, Callable, Constructor, Representable), + flags(METHOD_DESCR) +)] +impl PyMethodDescriptor { + #[pygetset(magic)] + fn name(&self) -> &'static PyStrInterned { + self.common.name + } + #[pygetset(magic)] + fn qualname(&self) -> String { + format!("{}.{}", self.common.typ.name(), &self.common.name) + } + #[pygetset(magic)] + fn doc(&self) -> Option<&'static str> { + self.method.doc + } + #[pygetset(magic)] + fn text_signature(&self) -> Option { + self.method.doc.and_then(|doc| { + type_::get_text_signature_from_internal_doc(self.method.name, doc) + .map(|signature| signature.to_string()) + }) + } + #[pymethod(magic)] + fn reduce( + &self, + vm: &VirtualMachine, + ) -> (Option, (Option, &'static str)) { + let builtins_getattr = vm.builtins.get_attr("getattr", vm).ok(); + let classname = vm.builtins.get_attr(&self.common.typ.__name__(vm), vm).ok(); + (builtins_getattr, (classname, self.method.name)) + } +} + +impl Representable for PyMethodDescriptor { + #[inline] + fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { + Ok(format!( + "", + &zelf.method.name, + zelf.common.typ.name() + )) + } +} + +impl Unconstructible for PyMethodDescriptor {} + #[derive(Debug)] pub enum MemberKind { Bool = 14, @@ -78,7 +214,7 @@ impl std::fmt::Debug for PyMemberDef { #[pyclass(name = "member_descriptor", module = false)] #[derive(Debug)] pub struct PyMemberDescriptor { - pub common: DescrObject, + pub common: PyDescriptorOwned, pub member: PyMemberDef, } @@ -88,8 +224,8 @@ impl PyPayload for PyMemberDescriptor { } } -fn calculate_qualname(descr: &DescrObject, vm: &VirtualMachine) -> PyResult> { - if let Some(qualname) = vm.get_attribute_opt(descr.typ.to_owned().into(), "__qualname__")? { +fn calculate_qualname(descr: &PyDescriptorOwned, vm: &VirtualMachine) -> PyResult> { + if let Some(qualname) = vm.get_attribute_opt(descr.typ.clone().into(), "__qualname__")? { let str = qualname.downcast::().map_err(|_| { vm.new_type_error( ".__objclass__.__qualname__ is not a unicode object".to_owned(), @@ -217,7 +353,7 @@ impl GetDescriptor for PyMemberDescriptor { } } -pub fn init(context: &Context) { - let member_descriptor_type = &context.types.member_descriptor_type; - PyMemberDescriptor::extend_class(context, member_descriptor_type); +pub fn init(ctx: &Context) { + PyMemberDescriptor::extend_class(ctx, ctx.types.member_descriptor_type); + PyMethodDescriptor::extend_class(ctx, ctx.types.method_descriptor_type); } diff --git a/vm/src/builtins/module.rs b/vm/src/builtins/module.rs index 4c77bc2677..58174082d5 100644 --- a/vm/src/builtins/module.rs +++ b/vm/src/builtins/module.rs @@ -3,7 +3,7 @@ use crate::{ builtins::{pystr::AsPyStr, PyStrInterned}, class::PyClassImpl, convert::ToPyObject, - function::FuncArgs, + function::{FuncArgs, PyMethodDef}, types::{GetAttr, Initializer, Representable}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -15,7 +15,7 @@ pub struct PyModuleDef { pub name: &'static PyStrInterned, pub doc: Option<&'static PyStrInterned>, // pub size: isize, - // pub methods: &'static [PyMethodDef], + pub methods: &'static [PyMethodDef], pub slots: PyModuleSlots, // traverse: traverseproc // clear: inquiry @@ -82,11 +82,23 @@ impl PyModule { } pub fn __init_dict_from_def(vm: &VirtualMachine, module: &Py) { let doc = module.def.unwrap().doc.map(|doc| doc.to_owned()); - module.init_module_dict(module.name.unwrap(), doc, vm); + module.init_dict(module.name.unwrap(), doc, vm); } } impl Py { + pub fn __init_methods(&self, vm: &VirtualMachine) -> PyResult<()> { + debug_assert!(self.def.is_some()); + for method in self.def.unwrap().methods { + let func = method + .to_function() + .with_module(self.name.unwrap()) + .into_ref(&vm.ctx); + vm.__module_set_attr(self, vm.ctx.intern_str(method.name), func)?; + } + Ok(()) + } + fn getattr_inner(&self, name: &Py, vm: &VirtualMachine) -> PyResult { if let Some(attr) = self.as_object().generic_getattr_opt(name, None, vm)? { return Ok(attr); @@ -115,7 +127,7 @@ impl Py { self.as_object().dict().unwrap() } // TODO: should be on PyModule, not Py - pub(crate) fn init_module_dict( + pub(crate) fn init_dict( &self, name: &'static PyStrInterned, doc: Option, @@ -176,7 +188,7 @@ impl Initializer for PyModule { .slots .flags .has_feature(crate::types::PyTypeFlags::HAS_DICT)); - zelf.init_module_dict(vm.ctx.intern_str(args.name.as_str()), args.doc, vm); + zelf.init_dict(vm.ctx.intern_str(args.name.as_str()), args.doc, vm); Ok(()) } } diff --git a/vm/src/builtins/staticmethod.rs b/vm/src/builtins/staticmethod.rs index bb99960ce3..59a5b18b5d 100644 --- a/vm/src/builtins/staticmethod.rs +++ b/vm/src/builtins/staticmethod.rs @@ -1,9 +1,8 @@ -use super::{PyStr, PyStrInterned, PyType, PyTypeRef}; +use super::{PyStr, PyType, PyTypeRef}; use crate::{ - builtins::builtin_func::PyBuiltinMethod, class::PyClassImpl, common::lock::PyMutex, - function::{FuncArgs, IntoPyNativeFunc}, + function::FuncArgs, types::{Callable, Constructor, GetDescriptor, Initializer, Representable}, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -73,27 +72,6 @@ impl PyStaticMethod { } } -impl PyStaticMethod { - pub fn new_builtin_ref( - name: &'static PyStrInterned, - class: &'static Py, - f: F, - ctx: &Context, - ) -> PyRef - where - F: IntoPyNativeFunc, - { - let callable = PyBuiltinMethod::new_ref(name, class, f, ctx).into(); - PyRef::new_ref( - Self { - callable: PyMutex::new(callable), - }, - ctx.types.staticmethod_type.to_owned(), - None, - ) - } -} - impl Initializer for PyStaticMethod { type Args = PyObjectRef; diff --git a/vm/src/builtins/super.rs b/vm/src/builtins/super.rs index 19377305ec..a7eefea321 100644 --- a/vm/src/builtins/super.rs +++ b/vm/src/builtins/super.rs @@ -153,9 +153,8 @@ impl PySuper { impl GetAttr for PySuper { fn getattro(zelf: &Py, name: &Py, vm: &VirtualMachine) -> PyResult { let skip = |zelf: &Py, name| zelf.as_object().generic_getattr(name, vm); - let obj = zelf.inner.read().obj.clone(); - let (obj, start_type): (PyObjectRef, PyTypeRef) = match obj { - Some(o) => o, + let (obj, start_type): (PyObjectRef, PyTypeRef) = match &zelf.inner.read().obj { + Some(o) => o.clone(), None => return skip(zelf, name), }; // We want __class__ to return the class of the super object diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs index 5aca7ed07a..e0da4f7dc4 100644 --- a/vm/src/builtins/type.rs +++ b/vm/src/builtins/type.rs @@ -1,11 +1,12 @@ use super::{ - mappingproxy::PyMappingProxy, object, union_, PyClassMethod, PyDictRef, PyList, PyStaticMethod, - PyStr, PyStrInterned, PyStrRef, PyTuple, PyTupleRef, PyWeak, + mappingproxy::PyMappingProxy, object, union_, PyClassMethod, PyDictRef, PyList, PyStr, + PyStrInterned, PyStrRef, PyTuple, PyTupleRef, PyWeak, }; use crate::{ builtins::{ descriptor::{ - DescrObject, MemberGetter, MemberKind, MemberSetter, PyMemberDef, PyMemberDescriptor, + MemberGetter, MemberKind, MemberSetter, PyDescriptorOwned, PyMemberDef, + PyMemberDescriptor, }, function::PyCellRef, tuple::{IntoPyTuple, PyTupleTyped}, @@ -18,7 +19,7 @@ use crate::{ lock::{PyRwLock, PyRwLockReadGuard}, }, convert::ToPyResult, - function::{FuncArgs, KwArgs, OptionalArg, PySetterValue}, + function::{FuncArgs, KwArgs, OptionalArg, PyMethodDef, PySetterValue}, identifier, object::{Traverse, TraverseFn}, protocol::{PyIterReturn, PyMappingMethods, PyNumberMethods, PySequenceMethods}, @@ -426,6 +427,13 @@ impl Py { pub fn iter_base_chain(&self) -> impl Iterator> { std::iter::successors(Some(self), |cls| cls.base.as_deref()) } + + pub fn extend_methods(&'static self, method_defs: &'static [PyMethodDef], ctx: &Context) { + for method_def in method_defs { + let method = method_def.to_proper_method(self, ctx); + self.set_attr(ctx.intern_str(method_def.name), method); + } + } } #[pyclass( @@ -696,11 +704,6 @@ impl PyType { }; let mut attributes = dict.to_attributes(vm); - if let Some(f) = attributes.get_mut(identifier!(vm, __new__)) { - if f.class().is(vm.ctx.types.function_type) { - *f = PyStaticMethod::from(f.clone()).into_pyobject(vm); - } - } if let Some(f) = attributes.get_mut(identifier!(vm, __init_subclass__)) { if f.class().is(vm.ctx.types.function_type) { @@ -813,7 +816,7 @@ impl PyType { }; let member_descriptor: PyRef = vm.ctx.new_pyref(PyMemberDescriptor { - common: DescrObject { + common: PyDescriptorOwned { typ: typ.clone(), name: vm.ctx.intern_str(member.as_str()), qualname: PyRwLock::new(None), @@ -977,11 +980,7 @@ fn get_signature(doc: &str) -> Option<&str> { fn find_signature<'a>(name: &str, doc: &'a str) -> Option<&'a str> { let name = name.rsplit('.').next().unwrap(); let doc = doc.strip_prefix(name)?; - if !doc.starts_with('(') { - None - } else { - Some(doc) - } + doc.starts_with('(').then_some(doc) } pub(crate) fn get_text_signature_from_internal_doc<'a>( diff --git a/vm/src/class.rs b/vm/src/class.rs index a9a00c4a8d..8b10c5e626 100644 --- a/vm/src/class.rs +++ b/vm/src/class.rs @@ -1,7 +1,8 @@ //! Utilities to define a new Python class use crate::{ - builtins::{PyBaseObject, PyBoundMethod, PyType, PyTypeRef}, + builtins::{PyBaseObject, PyType, PyTypeRef}, + function::PyMethodDef, identifier, object::Py, types::{hash_not_implemented, PyTypeFlags, PyTypeSlots}, @@ -69,8 +70,6 @@ pub trait PyClassDef { pub trait PyClassImpl: PyClassDef { const TP_FLAGS: PyTypeFlags = PyTypeFlags::DEFAULT; - fn impl_extend_class(ctx: &Context, class: &'static Py); - fn extend_class(ctx: &Context, class: &'static Py) { #[cfg(debug_assertions)] { @@ -102,16 +101,21 @@ pub trait PyClassImpl: PyClassDef { ctx.new_str(module_name).into(), ); } - let bound_new = PyBoundMethod::new_ref( - class.to_owned().into(), - ctx.slot_new_wrapper.clone().into(), - ctx, - ); - class.set_attr(identifier!(ctx, __new__), bound_new.into()); + + if class.slots.new.load().is_some() { + let bound_new = Context::genesis().slot_new_wrapper.build_bound_method( + ctx, + class.to_owned().into(), + class, + ); + class.set_attr(identifier!(ctx, __new__), bound_new.into()); + } if class.slots.hash.load().map_or(0, |h| h as usize) == hash_not_implemented as usize { class.set_attr(ctx.names.__hash__, ctx.none.clone().into()); } + + class.extend_methods(class.slots.methods, ctx); } fn make_class(ctx: &Context) -> PyTypeRef @@ -130,14 +134,20 @@ pub trait PyClassImpl: PyClassDef { .to_owned() } + fn impl_extend_class(ctx: &Context, class: &'static Py); + fn impl_extend_method_def(method_defs: &mut Vec); fn extend_slots(slots: &mut PyTypeSlots); fn make_slots() -> PyTypeSlots { + let mut method_defs = Vec::new(); + Self::impl_extend_method_def(&mut method_defs); + let mut slots = PyTypeSlots { flags: Self::TP_FLAGS, name: Self::TP_NAME, basicsize: Self::BASICSIZE, doc: Self::DOC, + methods: Box::leak(method_defs.into_boxed_slice()), ..Default::default() }; diff --git a/vm/src/codecs.rs b/vm/src/codecs.rs index 29b58749f0..ff7bc48915 100644 --- a/vm/src/codecs.rs +++ b/vm/src/codecs.rs @@ -2,6 +2,7 @@ use crate::{ builtins::{PyBaseExceptionRef, PyBytesRef, PyStr, PyStrRef, PyTuple, PyTupleRef}, common::{ascii, lock::PyRwLock}, convert::ToPyObject, + function::PyMethodDef, AsObject, Context, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, }; use std::{borrow::Cow, collections::HashMap, fmt::Write, ops::Range}; @@ -142,33 +143,33 @@ impl ToPyObject for PyCodec { impl CodecsRegistry { pub(crate) fn new(ctx: &Context) -> Self { + ::rustpython_vm::common::static_cell! { + static METHODS: Box<[PyMethodDef]>; + } + + let methods = METHODS.get_or_init(|| { + crate::define_methods![ + "strict_errors" => strict_errors as EMPTY, + "ignore_errors" => ignore_errors as EMPTY, + "replace_errors" => replace_errors as EMPTY, + "xmlcharrefreplace_errors" => xmlcharrefreplace_errors as EMPTY, + "backslashreplace_errors" => backslashreplace_errors as EMPTY, + "namereplace_errors" => namereplace_errors as EMPTY, + "surrogatepass_errors" => surrogatepass_errors as EMPTY, + "surrogateescape_errors" => surrogateescape_errors as EMPTY + ] + .into_boxed_slice() + }); + let errors = [ - ("strict", ctx.new_function("strict_errors", strict_errors)), - ("ignore", ctx.new_function("ignore_errors", ignore_errors)), - ( - "replace", - ctx.new_function("replace_errors", replace_errors), - ), - ( - "xmlcharrefreplace", - ctx.new_function("xmlcharrefreplace_errors", xmlcharrefreplace_errors), - ), - ( - "backslashreplace", - ctx.new_function("backslashreplace_errors", backslashreplace_errors), - ), - ( - "namereplace", - ctx.new_function("namereplace_errors", namereplace_errors), - ), - ( - "surrogatepass", - ctx.new_function("surrogatepass_errors", surrogatepass_errors), - ), - ( - "surrogateescape", - ctx.new_function("surrogateescape_errors", surrogateescape_errors), - ), + ("strict", methods[0].build_function(ctx)), + ("ignore", methods[1].build_function(ctx)), + ("replace", methods[2].build_function(ctx)), + ("xmlcharrefreplace", methods[3].build_function(ctx)), + ("backslashreplace", methods[4].build_function(ctx)), + ("namereplace", methods[5].build_function(ctx)), + ("surrogatepass", methods[6].build_function(ctx)), + ("surrogateescape", methods[7].build_function(ctx)), ]; let errors = errors .into_iter() diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 3762ee5d06..9de24bb67b 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -3,8 +3,7 @@ use crate::common::{lock::PyRwLock, str::ReprOverflowError}; use crate::object::{Traverse, TraverseFn}; use crate::{ builtins::{ - traceback::PyTracebackRef, tuple::IntoPyTuple, PyNone, PyStr, PyStrRef, PyTuple, - PyTupleRef, PyType, PyTypeRef, + traceback::PyTracebackRef, PyNone, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, }, class::{PyClassImpl, StaticType}, convert::{ToPyException, ToPyObject}, @@ -767,9 +766,8 @@ impl ExceptionZoo { extend_exception!(PyLookupError, ctx, excs.lookup_error); extend_exception!(PyIndexError, ctx, excs.index_error); - extend_exception!(PyKeyError, ctx, excs.key_error, { - "__str__" => ctx.new_method(identifier!(ctx, __str__), excs.key_error, key_error_str), - }); + + extend_exception!(PyKeyError, ctx, excs.key_error); extend_exception!(PyMemoryError, ctx, excs.memory_error); extend_exception!(PyNameError, ctx, excs.name_error, { @@ -801,8 +799,6 @@ impl ExceptionZoo { "filename" => ctx.none(), // second exception filename "filename2" => ctx.none(), - "__str__" => ctx.new_method(identifier!(ctx, __str__), excs.os_error, os_error_str), - "__reduce__" => ctx.new_method(identifier!(ctx, __reduce__), excs.os_error, os_error_reduce), }); // TODO: this isn't really accurate #[cfg(windows)] @@ -895,84 +891,6 @@ fn make_arg_getter(idx: usize) -> impl Fn(PyBaseExceptionRef) -> Option PyStrRef { - let args = exc.args(); - if args.len() == 1 { - vm.exception_args_as_string(args, false) - .into_iter() - .exactly_one() - .unwrap() - } else { - exc.str(vm) - } -} - -fn os_error_str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult { - let args = exc.args(); - let obj = exc.as_object().to_owned(); - - if args.len() == 2 { - // SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic - let errno = exc.get_arg(0).unwrap().str(vm)?; - let msg = exc.get_arg(1).unwrap().str(vm)?; - - let s = match obj.get_attr("filename", vm) { - Ok(filename) => match obj.get_attr("filename2", vm) { - Ok(filename2) => format!( - "[Errno {}] {}: '{}' -> '{}'", - errno, - msg, - filename.str(vm)?, - filename2.str(vm)? - ), - Err(_) => format!("[Errno {}] {}: '{}'", errno, msg, filename.str(vm)?), - }, - Err(_) => { - format!("[Errno {errno}] {msg}") - } - }; - Ok(vm.ctx.new_str(s)) - } else { - Ok(exc.str(vm)) - } -} - -fn os_error_reduce(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyTupleRef { - let args = exc.args(); - let obj = exc.as_object().to_owned(); - let mut result: Vec = vec![obj.class().to_owned().into()]; - - if args.len() >= 2 && args.len() <= 5 { - // SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic - let errno = exc.get_arg(0).unwrap(); - let msg = exc.get_arg(1).unwrap(); - - if let Ok(filename) = obj.get_attr("filename", vm) { - if !vm.is_none(&filename) { - let mut args_reduced: Vec = vec![errno, msg, filename]; - - if let Ok(filename2) = obj.get_attr("filename2", vm) { - if !vm.is_none(&filename2) { - args_reduced.push(filename2); - } - } - result.push(args_reduced.into_pytuple(vm).into()); - } else { - result.push(vm.new_tuple((errno, msg)).into()); - } - } else { - result.push(vm.new_tuple((errno, msg)).into()); - } - } else { - result.push(args.into()); - } - - if let Some(dict) = obj.dict().filter(|x| !x.is_empty()) { - result.push(dict.into()); - } - result.into_pytuple(vm) -} - fn system_exit_code(exc: PyBaseExceptionRef) -> Option { exc.args.read().first().map(|code| { match_class!(match code { @@ -1137,13 +1055,16 @@ pub(super) mod types { use crate::common::lock::PyRwLock; #[cfg_attr(target_arch = "wasm32", allow(unused_imports))] use crate::{ - builtins::{traceback::PyTracebackRef, PyInt, PyTupleRef, PyTypeRef}, + builtins::{ + traceback::PyTracebackRef, tuple::IntoPyTuple, PyInt, PyStrRef, PyTupleRef, PyTypeRef, + }, convert::ToPyResult, function::FuncArgs, types::{Constructor, Initializer}, - PyObjectRef, PyRef, PyResult, VirtualMachine, + AsObject, PyObjectRef, PyRef, PyResult, VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; + use itertools::Itertools; // This module is designed to be used as `use builtins::*;`. // Do not add any pub symbols not included in builtins module. @@ -1275,10 +1196,26 @@ pub(super) mod types { #[derive(Debug)] pub struct PyIndexError {} - #[pyexception(name, base = "PyLookupError", ctx = "key_error", impl)] + #[pyexception(name, base = "PyLookupError", ctx = "key_error")] #[derive(Debug)] pub struct PyKeyError {} + #[pyexception] + impl PyKeyError { + #[pymethod(magic)] + fn str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyStrRef { + let args = exc.args(); + if args.len() == 1 { + vm.exception_args_as_string(args, false) + .into_iter() + .exactly_one() + .unwrap() + } else { + exc.str(vm) + } + } + } + #[pyexception(name, base = "PyException", ctx = "memory_error", impl)] #[derive(Debug)] pub struct PyMemoryError {} @@ -1347,6 +1284,74 @@ pub(super) mod types { } PyBaseException::slot_init(zelf, new_args, vm) } + + #[pymethod(magic)] + fn str(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult { + let args = exc.args(); + let obj = exc.as_object().to_owned(); + + if args.len() == 2 { + // SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic + let errno = exc.get_arg(0).unwrap().str(vm)?; + let msg = exc.get_arg(1).unwrap().str(vm)?; + + let s = match obj.get_attr("filename", vm) { + Ok(filename) => match obj.get_attr("filename2", vm) { + Ok(filename2) => format!( + "[Errno {}] {}: '{}' -> '{}'", + errno, + msg, + filename.str(vm)?, + filename2.str(vm)? + ), + Err(_) => format!("[Errno {}] {}: '{}'", errno, msg, filename.str(vm)?), + }, + Err(_) => { + format!("[Errno {errno}] {msg}") + } + }; + Ok(vm.ctx.new_str(s)) + } else { + Ok(exc.str(vm)) + } + } + + #[pymethod(magic)] + fn reduce(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyTupleRef { + let args = exc.args(); + let obj = exc.as_object().to_owned(); + let mut result: Vec = vec![obj.class().to_owned().into()]; + + if args.len() >= 2 && args.len() <= 5 { + // SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic + let errno = exc.get_arg(0).unwrap(); + let msg = exc.get_arg(1).unwrap(); + + if let Ok(filename) = obj.get_attr("filename", vm) { + if !vm.is_none(&filename) { + let mut args_reduced: Vec = vec![errno, msg, filename]; + + if let Ok(filename2) = obj.get_attr("filename2", vm) { + if !vm.is_none(&filename2) { + args_reduced.push(filename2); + } + } + result.push(args_reduced.into_pytuple(vm).into()); + } else { + result.push(vm.new_tuple((errno, msg)).into()); + } + } else { + result.push(vm.new_tuple((errno, msg)).into()); + } + } else { + result.push(args.into()); + } + + if let Some(dict) = obj.dict().filter(|x| !x.is_empty()) { + result.push(dict.into()); + } + result.into_pytuple(vm) + } } #[pyexception(name, base = "PyOSError", ctx = "blocking_io_error", impl)] diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 3ec6ab7887..f2fd1756b3 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -1390,13 +1390,20 @@ impl ExecutingFrame<'_> { let func = self.pop_value(); let is_method = self.pop_value().is(&vm.ctx.true_value); let target = self.pop_value(); - let method = if is_method { - PyMethod::Function { target, func } + + // TODO: It was PyMethod before #4873. Check if it's correct. + let func = if is_method { + if let Some(descr_get) = func.class().mro_find_map(|cls| cls.slots.descr_get.load()) { + let cls = target.class().to_owned().into(); + descr_get(func, Some(target), Some(cls), vm)? + } else { + func + } } else { drop(target); // should be None - PyMethod::Attribute(func) + func }; - let value = method.invoke(args, vm)?; + let value = func.call(args, vm)?; self.push_value(value); Ok(None) } diff --git a/vm/src/function/builtin.rs b/vm/src/function/builtin.rs index 2bc058824d..e37270bcc9 100644 --- a/vm/src/function/builtin.rs +++ b/vm/src/function/builtin.rs @@ -6,7 +6,8 @@ use crate::{ use std::marker::PhantomData; /// A built-in Python function. -pub type PyNativeFunc = py_dyn_fn!(dyn Fn(&VirtualMachine, FuncArgs) -> PyResult); +// PyCFunction in CPython +pub type PyNativeFn = py_dyn_fn!(dyn Fn(&VirtualMachine, FuncArgs) -> PyResult); /// Implemented by types that are or can generate built-in functions. /// @@ -19,31 +20,30 @@ pub type PyNativeFunc = py_dyn_fn!(dyn Fn(&VirtualMachine, FuncArgs) -> PyResult /// For example, anything from `Fn()` to `Fn(vm: &VirtualMachine) -> u32` to /// `Fn(PyIntRef, PyIntRef) -> String` to /// `Fn(&self, PyStrRef, FooOptions, vm: &VirtualMachine) -> PyResult` -/// is `IntoPyNativeFunc`. If you do want a really general function signature, e.g. +/// is `IntoPyNativeFn`. If you do want a really general function signature, e.g. /// to forward the args to another function, you can define a function like /// `Fn(FuncArgs [, &VirtualMachine]) -> ...` /// /// Note that the `Kind` type parameter is meaningless and should be considered -/// an implementation detail; if you need to use `IntoPyNativeFunc` as a trait bound +/// an implementation detail; if you need to use `IntoPyNativeFn` as a trait bound /// just pass an unconstrained generic type, e.g. -/// `fn foo(f: F) where F: IntoPyNativeFunc` -pub trait IntoPyNativeFunc: Sized + PyThreadingConstraint + 'static { +/// `fn foo(f: F) where F: IntoPyNativeFn` +pub trait IntoPyNativeFn: Sized + PyThreadingConstraint + 'static { fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult; - /// `IntoPyNativeFunc::into_func()` generates a PyNativeFunc that performs the + /// `IntoPyNativeFn::into_func()` generates a PyNativeFn that performs the /// appropriate type and arity checking, any requested conversions, and then if /// successful calls the function with the extracted parameters. - fn into_func(self) -> &'static PyNativeFunc { - Box::leak(Box::new(move |vm: &VirtualMachine, args| { - self.call(vm, args) - })) + fn into_func(self) -> &'static PyNativeFn { + let boxed = Box::new(move |vm: &VirtualMachine, args| self.call(vm, args)); + Box::leak(boxed) } } // TODO: once higher-rank trait bounds are stabilized, remove the `Kind` type -// parameter and impl for F where F: for PyNativeFuncInternal -impl IntoPyNativeFunc<(T, R, VM)> for F +// parameter and impl for F where F: for PyNativeFnInternal +impl IntoPyNativeFn<(T, R, VM)> for F where - F: PyNativeFuncInternal, + F: PyNativeFnInternal, { #[inline(always)] fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult { @@ -53,11 +53,11 @@ where mod sealed { use super::*; - pub trait PyNativeFuncInternal: Sized + PyThreadingConstraint + 'static { + pub trait PyNativeFnInternal: Sized + PyThreadingConstraint + 'static { fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult; } } -use sealed::PyNativeFuncInternal; +use sealed::PyNativeFnInternal; #[doc(hidden)] pub struct OwnedParam(PhantomData); @@ -70,9 +70,9 @@ pub struct RefParam(PhantomData); // generate native python functions. // // Note that this could be done without a macro - it is simply to avoid repetition. -macro_rules! into_py_native_func_tuple { +macro_rules! into_py_native_fn_tuple { ($(($n:tt, $T:ident)),*) => { - impl PyNativeFuncInternal<($(OwnedParam<$T>,)*), R, VirtualMachine> for F + impl PyNativeFnInternal<($(OwnedParam<$T>,)*), R, VirtualMachine> for F where F: Fn($($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static, $($T: FromArgs,)* @@ -85,7 +85,7 @@ macro_rules! into_py_native_func_tuple { } } - impl PyNativeFuncInternal<(BorrowedParam, $(OwnedParam<$T>,)*), R, VirtualMachine> for F + impl PyNativeFnInternal<(BorrowedParam, $(OwnedParam<$T>,)*), R, VirtualMachine> for F where F: Fn(&Py, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static, S: PyPayload, @@ -99,7 +99,7 @@ macro_rules! into_py_native_func_tuple { } } - impl PyNativeFuncInternal<(RefParam, $(OwnedParam<$T>,)*), R, VirtualMachine> for F + impl PyNativeFnInternal<(RefParam, $(OwnedParam<$T>,)*), R, VirtualMachine> for F where F: Fn(&S, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static, S: PyPayload, @@ -113,7 +113,7 @@ macro_rules! into_py_native_func_tuple { } } - impl PyNativeFuncInternal<($(OwnedParam<$T>,)*), R, ()> for F + impl PyNativeFnInternal<($(OwnedParam<$T>,)*), R, ()> for F where F: Fn($($T,)*) -> R + PyThreadingConstraint + 'static, $($T: FromArgs,)* @@ -126,7 +126,7 @@ macro_rules! into_py_native_func_tuple { } } - impl PyNativeFuncInternal<(BorrowedParam, $(OwnedParam<$T>,)*), R, ()> for F + impl PyNativeFnInternal<(BorrowedParam, $(OwnedParam<$T>,)*), R, ()> for F where F: Fn(&Py, $($T,)*) -> R + PyThreadingConstraint + 'static, S: PyPayload, @@ -140,7 +140,7 @@ macro_rules! into_py_native_func_tuple { } } - impl PyNativeFuncInternal<(RefParam, $(OwnedParam<$T>,)*), R, ()> for F + impl PyNativeFnInternal<(RefParam, $(OwnedParam<$T>,)*), R, ()> for F where F: Fn(&S, $($T,)*) -> R + PyThreadingConstraint + 'static, S: PyPayload, @@ -156,14 +156,14 @@ macro_rules! into_py_native_func_tuple { }; } -into_py_native_func_tuple!(); -into_py_native_func_tuple!((v1, T1)); -into_py_native_func_tuple!((v1, T1), (v2, T2)); -into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3)); -into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4)); -into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5)); -into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5), (v6, T6)); -into_py_native_func_tuple!( +into_py_native_fn_tuple!(); +into_py_native_fn_tuple!((v1, T1)); +into_py_native_fn_tuple!((v1, T1), (v2, T2)); +into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3)); +into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4)); +into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5)); +into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5), (v6, T6)); +into_py_native_fn_tuple!( (v1, T1), (v2, T2), (v3, T3), @@ -178,8 +178,8 @@ mod tests { use super::*; #[test] - fn test_intonativefunc_noalloc() { - let check_zst = |f: &'static PyNativeFunc| assert_eq!(std::mem::size_of_val(f), 0); + fn test_into_native_fn_noalloc() { + let check_zst = |f: &'static PyNativeFn| assert_eq!(std::mem::size_of_val(f), 0); fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 { 1 } diff --git a/vm/src/function/method.rs b/vm/src/function/method.rs new file mode 100644 index 0000000000..8c8146bbd5 --- /dev/null +++ b/vm/src/function/method.rs @@ -0,0 +1,259 @@ +use crate::{ + builtins::{ + builtin_func::{PyNativeFunction, PyNativeMethod}, + descriptor::PyMethodDescriptor, + PyType, + }, + function::{IntoPyNativeFn, PyNativeFn}, + Context, Py, PyObjectRef, PyPayload, PyRef, VirtualMachine, +}; + +bitflags::bitflags! { + // METH_XXX flags in CPython + pub struct PyMethodFlags: u32 { + // const VARARGS = 0x0001; + // const KEYWORDS = 0x0002; + // METH_NOARGS and METH_O must not be combined with the flags above. + // const NOARGS = 0x0004; + // const O = 0x0008; + + // METH_CLASS and METH_STATIC are a little different; these control + // the construction of methods for a class. These cannot be used for + // functions in modules. + const CLASS = 0x0010; + const STATIC = 0x0020; + + // METH_COEXIST allows a method to be entered even though a slot has + // already filled the entry. When defined, the flag allows a separate + // method, "__contains__" for example, to coexist with a defined + // slot like sq_contains. + // const COEXIST = 0x0040; + + // if not Py_LIMITED_API + // const FASTCALL = 0x0080; + + // This bit is preserved for Stackless Python + // const STACKLESS = 0x0100; + + // METH_METHOD means the function stores an + // additional reference to the class that defines it; + // both self and class are passed to it. + // It uses PyCMethodObject instead of PyCFunctionObject. + // May not be combined with METH_NOARGS, METH_O, METH_CLASS or METH_STATIC. + const METHOD = 0x0200; + } +} + +impl PyMethodFlags { + // FIXME: macro temp + pub const EMPTY: Self = Self::empty(); +} + +#[macro_export] +macro_rules! define_methods { + // TODO: more flexible syntax + ($($name:literal => $func:ident as $flags:ident),+) => { + vec![ $( $crate::function::PyMethodDef { + name: $name, + func: $crate::function::IntoPyNativeFn::into_func($func), + flags: $crate::function::PyMethodFlags::$flags, + doc: None, + }),+ ] + }; +} + +#[derive(Clone)] +pub struct PyMethodDef { + pub name: &'static str, // TODO: interned + pub func: &'static PyNativeFn, + pub flags: PyMethodFlags, + pub doc: Option<&'static str>, // TODO: interned +} + +impl PyMethodDef { + #[inline] + pub fn new( + name: &'static str, + func: impl IntoPyNativeFn, + flags: PyMethodFlags, + doc: Option<&'static str>, + ) -> Self { + Self { + name, + func: func.into_func(), + flags, + doc, + } + } + pub fn to_proper_method( + &'static self, + class: &'static Py, + ctx: &Context, + ) -> PyObjectRef { + if self.flags.contains(PyMethodFlags::METHOD) { + self.build_method(ctx, class).into() + } else if self.flags.contains(PyMethodFlags::CLASS) { + self.build_classmethod(ctx, class).into() + } else if self.flags.contains(PyMethodFlags::STATIC) { + self.build_staticmethod(ctx, class).into() + } else { + unreachable!(); + } + } + pub fn to_function(&'static self) -> PyNativeFunction { + PyNativeFunction { + zelf: None, + value: self, + module: None, + } + } + pub fn to_method( + &'static self, + class: &'static Py, + ctx: &Context, + ) -> PyMethodDescriptor { + PyMethodDescriptor::new(self, class, ctx) + } + pub fn to_bound_method( + &'static self, + obj: PyObjectRef, + class: &'static Py, + ) -> PyNativeMethod { + PyNativeMethod { + func: PyNativeFunction { + zelf: Some(obj), + value: self, + module: None, + }, + class, + } + } + pub fn build_function(&'static self, ctx: &Context) -> PyRef { + self.to_function().into_ref(ctx) + } + pub fn build_bound_function( + &'static self, + ctx: &Context, + obj: PyObjectRef, + ) -> PyRef { + let function = PyNativeFunction { + zelf: Some(obj), + value: self, + module: None, + }; + PyRef::new_ref( + function, + ctx.types.builtin_function_or_method_type.to_owned(), + None, + ) + } + pub fn build_method( + &'static self, + ctx: &Context, + class: &'static Py, + ) -> PyRef { + debug_assert!(self.flags.contains(PyMethodFlags::METHOD)); + PyRef::new_ref( + self.to_method(class, ctx), + ctx.types.method_descriptor_type.to_owned(), + None, + ) + } + pub fn build_bound_method( + &'static self, + ctx: &Context, + obj: PyObjectRef, + class: &'static Py, + ) -> PyRef { + PyRef::new_ref( + self.to_bound_method(obj, class), + ctx.types.builtin_method_type.to_owned(), + None, + ) + } + pub fn build_classmethod( + &'static self, + ctx: &Context, + class: &'static Py, + ) -> PyRef { + PyRef::new_ref( + self.to_method(class, ctx), + ctx.types.method_descriptor_type.to_owned(), + None, + ) + } + pub fn build_staticmethod( + &'static self, + ctx: &Context, + class: &'static Py, + ) -> PyRef { + debug_assert!(self.flags.contains(PyMethodFlags::STATIC)); + let func = self.to_function(); + PyNativeMethod { func, class }.into_ref(ctx) + } +} + +impl std::fmt::Debug for PyMethodDef { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PyMethodDef") + .field("name", &self.name) + .field( + "func", + &(unsafe { std::mem::transmute::<_, [usize; 2]>(self.func)[1] as *const u8 }), + ) + .field("flags", &self.flags) + .field("doc", &self.doc) + .finish() + } +} + +// This is not a part of CPython API. +// But useful to support dynamically generated methods +#[pyclass(name, module = false, ctx = "method_def")] +#[derive(Debug)] +pub struct HeapMethodDef { + method: PyMethodDef, +} + +impl HeapMethodDef { + pub fn new(method: PyMethodDef) -> Self { + Self { method } + } +} + +impl Py { + pub(crate) unsafe fn method(&self) -> &'static PyMethodDef { + &*(&self.method as *const _) + } + + pub fn build_function(&self, vm: &VirtualMachine) -> PyRef { + let function = unsafe { self.method() }.to_function(); + let dict = vm.ctx.new_dict(); + dict.set_item("__method_def__", self.to_owned().into(), vm) + .unwrap(); + PyRef::new_ref( + function, + vm.ctx.types.builtin_function_or_method_type.to_owned(), + Some(dict), + ) + } + + pub fn build_method( + &self, + class: &'static Py, + vm: &VirtualMachine, + ) -> PyRef { + let function = unsafe { self.method() }.to_method(class, &vm.ctx); + let dict = vm.ctx.new_dict(); + dict.set_item("__method_def__", self.to_owned().into(), vm) + .unwrap(); + PyRef::new_ref( + function, + vm.ctx.types.method_descriptor_type.to_owned(), + Some(dict), + ) + } +} + +#[pyclass] +impl HeapMethodDef {} diff --git a/vm/src/function/mod.rs b/vm/src/function/mod.rs index 0521c06e49..409e4f1dbd 100644 --- a/vm/src/function/mod.rs +++ b/vm/src/function/mod.rs @@ -5,6 +5,7 @@ mod builtin; mod either; mod fspath; mod getset; +mod method; mod number; mod protocol; @@ -15,11 +16,12 @@ pub use argument::{ pub use arithmetic::{PyArithmeticValue, PyComparisonValue}; pub use buffer::{ArgAsciiBuffer, ArgBytesLike, ArgMemoryBuffer, ArgStrOrBytesLike}; pub(self) use builtin::{BorrowedParam, OwnedParam, RefParam}; -pub use builtin::{IntoPyNativeFunc, PyNativeFunc}; +pub use builtin::{IntoPyNativeFn, PyNativeFn}; pub use either::Either; pub use fspath::FsPath; pub use getset::PySetterValue; pub(super) use getset::{IntoPyGetterFunc, IntoPySetterFunc, PyGetterFunc, PySetterFunc}; +pub use method::{HeapMethodDef, PyMethodDef, PyMethodFlags}; pub use number::{ArgIndex, ArgIntoBool, ArgIntoComplex, ArgIntoFloat, ArgPrimitiveIndex, ArgSize}; pub use protocol::{ArgCallable, ArgIterable, ArgMapping, ArgSequence}; diff --git a/vm/src/macros.rs b/vm/src/macros.rs index e92959198c..6e1953436c 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -2,7 +2,7 @@ macro_rules! extend_module { ( $vm:expr, $module:expr, { $($name:expr => $value:expr),* $(,)? }) => {{ $( - $vm.__module_set_attr($module, $name, $value).unwrap(); + $vm.__module_set_attr($module, $vm.ctx.intern_str($name), $value).unwrap(); )* }}; } @@ -218,12 +218,13 @@ macro_rules! named_function { #[allow(unused_variables)] // weird lint, something to do with paste probably let ctx: &$crate::Context = &$ctx; $crate::__exports::paste::expr! { - ctx.make_func_def( - ctx.intern_str(stringify!($func)), + ctx.new_method_def( + stringify!($func), [<$module _ $func>], + ::rustpython_vm::function::PyMethodFlags::empty(), ) - .into_function() - .with_module(ctx.new_str(stringify!($module).to_owned()).into()) + .to_function() + .with_module(ctx.intern_str(stringify!($module)).into()) .into_ref(ctx) } }}; diff --git a/vm/src/object/core.rs b/vm/src/object/core.rs index 0ca1e248f5..c5e42229d3 100644 --- a/vm/src/object/core.rs +++ b/vm/src/object/core.rs @@ -551,7 +551,12 @@ impl PyObjectRef { /// T must be the exact payload type #[inline(always)] pub unsafe fn downcast_unchecked(self) -> PyRef { - PyRef::from_obj_unchecked(self) + // PyRef::from_obj_unchecked(self) + // manual impl to avoid assertion + let obj = ManuallyDrop::new(self); + PyRef { + ptr: obj.ptr.cast(), + } } /// # Safety diff --git a/vm/src/object/payload.rs b/vm/src/object/payload.rs index 0ea79c6741..d5f3d6330b 100644 --- a/vm/src/object/payload.rs +++ b/vm/src/object/payload.rs @@ -80,3 +80,7 @@ pub trait PyObjectPayload: } impl PyObjectPayload for T {} + +pub trait SlotOffset { + fn offset() -> usize; +} diff --git a/vm/src/protocol/callable.rs b/vm/src/protocol/callable.rs index eb404d0214..ba20f2d326 100644 --- a/vm/src/protocol/callable.rs +++ b/vm/src/protocol/callable.rs @@ -16,6 +16,7 @@ impl PyObject { } /// PyObject_Call*Arg* series + #[inline] pub fn call(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult { let args = args.into_args(vm); self.call_with_args(args, vm) diff --git a/vm/src/stdlib/builtins.rs b/vm/src/stdlib/builtins.rs index a7522f097d..e14a6847bb 100644 --- a/vm/src/stdlib/builtins.rs +++ b/vm/src/stdlib/builtins.rs @@ -2,6 +2,7 @@ //! //! Implements the list of [builtin Python functions](https://docs.python.org/3/library/builtins.html). use crate::{builtins::PyModule, class::PyClassImpl, Py, VirtualMachine}; +pub(crate) use builtins::{__module_def, DOC}; pub use builtins::{ascii, print}; #[pymodule] diff --git a/vm/src/stdlib/sys.rs b/vm/src/stdlib/sys.rs index 780d7b4ef9..97eae88dbc 100644 --- a/vm/src/stdlib/sys.rs +++ b/vm/src/stdlib/sys.rs @@ -1,6 +1,6 @@ use crate::{builtins::PyModule, convert::ToPyObject, Py, PyResult, VirtualMachine}; -pub(crate) use sys::{UnraisableHookArgs, MAXSIZE, MULTIARCH}; +pub(crate) use sys::{UnraisableHookArgs, __module_def, DOC, MAXSIZE, MULTIARCH}; #[pymodule] mod sys { diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 4d2c273c5a..001e6f972e 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -3,7 +3,9 @@ use crate::{ bytecode::ComparisonOperator, common::hash::PyHash, convert::{ToPyObject, ToPyResult}, - function::{Either, FromArgs, FuncArgs, OptionalArg, PyComparisonValue, PySetterValue}, + function::{ + Either, FromArgs, FuncArgs, OptionalArg, PyComparisonValue, PyMethodDef, PySetterValue, + }, identifier, protocol::{ PyBuffer, PyIterReturn, PyMapping, PyMappingMethods, PyNumber, PyNumberMethods, @@ -62,6 +64,8 @@ pub struct PyTypeSlots { pub iter: AtomicCell>, pub iternext: AtomicCell>, + pub methods: &'static [PyMethodDef], + // Flags to define presence of optional/expanded features pub flags: PyTypeFlags, @@ -349,7 +353,7 @@ fn init_wrapper(obj: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResu Ok(()) } -fn new_wrapper(cls: PyTypeRef, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult { +pub(crate) 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()); new.call(args, vm) @@ -827,10 +831,15 @@ pub trait Callable: PyPayload { #[inline] #[pyslot] fn slot_call(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult { - let Some(zelf) = zelf.downcast_ref() else { - let err = vm.new_downcast_type_error(Self::class(&vm.ctx), zelf); - return Err(err); - }; + let zelf = zelf.downcast_ref().ok_or_else(|| { + let repr = zelf.repr(vm); + let help = if let Ok(repr) = repr.as_ref() { + repr.as_str().to_owned() + } else { + zelf.class().name().to_owned() + }; + vm.new_type_error(format!("unexpected payload for __call__ of {help}")) + })?; let args = args.bind(vm)?; Self::call(zelf, args, vm) } diff --git a/vm/src/types/zoo.rs b/vm/src/types/zoo.rs index a2d2a5c797..492b584e29 100644 --- a/vm/src/types/zoo.rs +++ b/vm/src/types/zoo.rs @@ -74,6 +74,7 @@ pub struct TypeZoo { pub zip_type: &'static Py, pub function_type: &'static Py, pub builtin_function_or_method_type: &'static Py, + pub builtin_method_type: &'static Py, pub method_descriptor_type: &'static Py, pub property_type: &'static Py, pub getset_type: &'static Py, @@ -91,6 +92,9 @@ pub struct TypeZoo { pub generic_alias_type: &'static Py, pub union_type: &'static Py, pub member_descriptor_type: &'static Py, + + // RustPython-original types + pub method_def: &'static Py, } impl TypeZoo { @@ -135,7 +139,8 @@ impl TypeZoo { async_generator_wrapped_value: asyncgenerator::PyAsyncGenWrappedValue::init_builtin_type(), bound_method_type: function::PyBoundMethod::init_builtin_type(), - builtin_function_or_method_type: builtin_func::PyBuiltinFunction::init_builtin_type(), + builtin_function_or_method_type: builtin_func::PyNativeFunction::init_builtin_type(), + builtin_method_type: builtin_func::PyNativeMethod::init_builtin_type(), bytearray_iterator_type: bytearray::PyByteArrayIterator::init_builtin_type(), bytes_iterator_type: bytes::PyBytesIterator::init_builtin_type(), callable_iterator: iter::PyCallableIterator::init_builtin_type(), @@ -172,12 +177,14 @@ impl TypeZoo { traceback_type: traceback::PyTraceback::init_builtin_type(), tuple_iterator_type: tuple::PyTupleIterator::init_builtin_type(), weakproxy_type: weakproxy::PyWeakProxy::init_builtin_type(), - method_descriptor_type: builtin_func::PyBuiltinMethod::init_builtin_type(), + method_descriptor_type: descriptor::PyMethodDescriptor::init_builtin_type(), none_type: singletons::PyNone::init_builtin_type(), not_implemented_type: singletons::PyNotImplemented::init_builtin_type(), generic_alias_type: genericalias::PyGenericAlias::init_builtin_type(), union_type: union_::PyUnion::init_builtin_type(), member_descriptor_type: descriptor::PyMemberDescriptor::init_builtin_type(), + + method_def: crate::function::HeapMethodDef::init_builtin_type(), } } diff --git a/vm/src/vm/context.rs b/vm/src/vm/context.rs index c50f2a83e9..f9982c9e9a 100644 --- a/vm/src/vm/context.rs +++ b/vm/src/vm/context.rs @@ -1,11 +1,10 @@ use crate::{ builtins::{ - builtin_func::{PyBuiltinFunction, PyBuiltinMethod, PyNativeFuncDef}, bytes, code::{self, PyCode}, descriptor::{ - DescrObject, MemberGetter, MemberKind, MemberSetter, MemberSetterFunc, PyMemberDef, - PyMemberDescriptor, + MemberGetter, MemberKind, MemberSetter, MemberSetterFunc, PyDescriptorOwned, + PyMemberDef, PyMemberDescriptor, }, getset::PyGetSet, object, pystr, @@ -17,7 +16,10 @@ use crate::{ class::{PyClassImpl, StaticType}, common::rc::PyRc, exceptions, - function::{IntoPyGetterFunc, IntoPyNativeFunc, IntoPySetterFunc}, + function::{ + HeapMethodDef, IntoPyGetterFunc, IntoPyNativeFn, IntoPySetterFunc, PyMethodDef, + PyMethodFlags, + }, intern::{InternableString, MaybeInternedString, StringPool}, object::{Py, PyObjectPayload, PyObjectRef, PyPayload, PyRef}, types::{PyTypeFlags, PyTypeSlots, TypeZoo}, @@ -45,7 +47,7 @@ pub struct Context { pub int_cache_pool: Vec, // there should only be exact objects of str in here, no non-str objects and no subclasses pub(crate) string_pool: StringPool, - pub(crate) slot_new_wrapper: PyRef, + pub(crate) slot_new_wrapper: PyMethodDef, pub names: ConstName, } @@ -287,14 +289,16 @@ impl Context { let string_pool = StringPool::default(); let names = unsafe { ConstName::new(&string_pool, &types.str_type.to_owned()) }; - let slot_new_wrapper = create_object( - PyNativeFuncDef::new(PyType::__new__.into_func(), names.__new__).into_function(), - types.builtin_function_or_method_type, - ); + let slot_new_wrapper = PyMethodDef { + name: names.__new__.as_str(), + func: PyType::__new__.into_func(), + flags: PyMethodFlags::METHOD, + doc: None, + }; let empty_str = unsafe { string_pool.intern("", types.str_type.to_owned()) }; let empty_bytes = create_object(PyBytes::from(Vec::new()), types.bytes_type); - let context = Context { + Context { true_value, false_value, none, @@ -312,10 +316,7 @@ impl Context { string_pool, slot_new_wrapper, names, - }; - TypeZoo::extend(&context); - exceptions::ExceptionZoo::extend(&context); - context + } } pub fn intern_str(&self, s: S) -> &'static PyStrInterned { @@ -484,12 +485,24 @@ impl Context { .unwrap() } - #[inline] - pub fn make_func_def(&self, name: &'static PyStrInterned, f: F) -> PyNativeFuncDef + pub fn new_method_def( + &self, + name: &'static str, + f: F, + flags: PyMethodFlags, + doc: Option<&'static str>, + ) -> PyRef where - F: IntoPyNativeFunc, + F: IntoPyNativeFn, { - PyNativeFuncDef::new(f.into_func(), name) + let def = PyMethodDef { + name, + func: f.into_func(), + flags, + doc, + }; + let payload = HeapMethodDef::new(def); + PyRef::new_ref(payload, self.types.method_def.to_owned(), None) } #[inline] @@ -509,40 +522,14 @@ impl Context { doc: None, }; let member_descriptor = PyMemberDescriptor { - common: DescrObject { + common: PyDescriptorOwned { typ: class.to_owned(), name: self.intern_str(name), qualname: PyRwLock::new(None), }, member: member_def, }; - - PyRef::new_ref( - member_descriptor, - self.types.member_descriptor_type.to_owned(), - None, - ) - } - - // #[deprecated] - pub fn new_function(&self, name: &str, f: F) -> PyRef - where - F: IntoPyNativeFunc, - { - self.make_func_def(self.intern_str(name), f) - .build_function(self) - } - - pub fn new_method( - &self, - name: &'static PyStrInterned, - class: &'static Py, - f: F, - ) -> PyRef - where - F: IntoPyNativeFunc, - { - PyBuiltinMethod::new_ref(name, class, f, self) + member_descriptor.into_ref(self) } pub fn new_readonly_getset( diff --git a/vm/src/vm/interpreter.rs b/vm/src/vm/interpreter.rs index 1a1a6cc97c..de928636a4 100644 --- a/vm/src/vm/interpreter.rs +++ b/vm/src/vm/interpreter.rs @@ -46,6 +46,8 @@ impl Interpreter { F: FnOnce(&mut VirtualMachine), { let ctx = Context::genesis(); + crate::types::TypeZoo::extend(ctx); + crate::exceptions::ExceptionZoo::extend(ctx); let mut vm = VirtualMachine::new(settings, ctx.clone()); init(&mut vm); vm.initialize(); diff --git a/vm/src/vm/mod.rs b/vm/src/vm/mod.rs index 2e546d3d6a..d993cdb938 100644 --- a/vm/src/vm/mod.rs +++ b/vm/src/vm/mod.rs @@ -116,17 +116,17 @@ impl VirtualMachine { // make a new module without access to the vm; doesn't // set __spec__, __loader__, etc. attributes - let new_module = || { + let new_module = |def| { PyRef::new_ref( - PyModule::new(), + PyModule::from_def(def), ctx.types.module_type.to_owned(), Some(ctx.new_dict()), ) }; // Hard-core modules: - let builtins = new_module(); - let sys_module = new_module(); + let builtins = new_module(stdlib::builtins::__module_def(&ctx)); + let sys_module = new_module(stdlib::sys::__module_def(&ctx)); let import_func = ctx.none(); let profile_func = RefCell::new(ctx.none()); @@ -199,11 +199,17 @@ impl VirtualMachine { let frozen = frozen::core_frozen_inits().collect(); PyRc::get_mut(&mut vm.state).unwrap().frozen = frozen; - vm.builtins - .init_module_dict(vm.ctx.intern_str("builtins"), None, &vm); - vm.sys_module - .init_module_dict(vm.ctx.intern_str("sys"), None, &vm); - + vm.builtins.init_dict( + vm.ctx.intern_str("builtins"), + Some(vm.ctx.intern_str(stdlib::builtins::DOC.unwrap()).to_owned()), + &vm, + ); + vm.sys_module.init_dict( + vm.ctx.intern_str("sys"), + Some(vm.ctx.intern_str(stdlib::sys::DOC.unwrap()).to_owned()), + &vm, + ); + // let name = vm.sys_module.get_attr("__name__", &vm).unwrap(); vm } @@ -251,7 +257,7 @@ impl VirtualMachine { self.state_mut().settings.path_list.insert(0, "".to_owned()); stdlib::builtins::init_module(self, &self.builtins); - stdlib::sys::init_module(self, self.sys_module.as_ref(), self.builtins.as_ref()); + stdlib::sys::init_module(self, &self.sys_module, &self.builtins); let mut essential_init = || -> PyResult { #[cfg(not(target_arch = "wasm32"))] @@ -802,15 +808,13 @@ impl VirtualMachine { pub fn __module_set_attr( &self, module: &Py, - attr_name: &str, + attr_name: &'static PyStrInterned, attr_value: impl Into, ) -> PyResult<()> { let val = attr_value.into(); - module.as_object().generic_setattr( - self.ctx.intern_str(attr_name), - PySetterValue::Assign(val), - self, - ) + module + .as_object() + .generic_setattr(attr_name, PySetterValue::Assign(val), self) } pub fn insert_sys_path(&self, obj: PyObjectRef) -> PyResult<()> { diff --git a/vm/src/vm/vm_new.rs b/vm/src/vm/vm_new.rs index 38b37f81f2..301c488f0c 100644 --- a/vm/src/vm/vm_new.rs +++ b/vm/src/vm/vm_new.rs @@ -1,9 +1,12 @@ use crate::{ builtins::{ + builtin_func::PyNativeFunction, + descriptor::PyMethodDescriptor, tuple::{IntoPyTuple, PyTupleRef}, PyBaseException, PyBaseExceptionRef, PyDictRef, PyModule, PyStrRef, PyType, PyTypeRef, }, convert::ToPyObject, + function::{IntoPyNativeFn, PyMethodFlags}, scope::Scope, vm::VirtualMachine, AsObject, Py, PyObject, PyObjectRef, PyRef, @@ -20,17 +23,18 @@ impl VirtualMachine { value.into_pytuple(self) } - pub fn new_module(&self, name: &str, dict: PyDictRef, doc: Option<&str>) -> PyRef { + pub fn new_module( + &self, + name: &str, + dict: PyDictRef, + doc: Option, + ) -> PyRef { let module = PyRef::new_ref( PyModule::new(), self.ctx.types.module_type.to_owned(), Some(dict), ); - module.init_module_dict( - self.ctx.intern_str(name), - doc.map(|doc| self.ctx.new_str(doc)), - self, - ); + module.init_dict(self.ctx.intern_str(name), doc, self); module } @@ -38,6 +42,31 @@ impl VirtualMachine { Scope::with_builtins(None, self.ctx.new_dict(), self) } + pub fn new_function(&self, name: &'static str, f: F) -> PyRef + where + F: IntoPyNativeFn, + { + let def = self + .ctx + .new_method_def(name, f, PyMethodFlags::empty(), None); + def.build_function(self) + } + + pub fn new_method( + &self, + name: &'static str, + class: &'static Py, + f: F, + ) -> PyRef + where + F: IntoPyNativeFn, + { + let def = self + .ctx + .new_method_def(name, f, PyMethodFlags::METHOD, None); + def.build_method(class, self) + } + /// Instantiate an exception with arguments. /// This function should only be used with builtin exception types; if a user-defined exception /// type is passed in, it may not be fully initialized; try using diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index f91f153f2e..ab2dc6da89 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -214,26 +214,25 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef { } } else if js_val.is_function() { let func = js_sys::Function::from(js_val); - vm.ctx - .new_function( - String::from(func.name()).as_str(), - move |args: FuncArgs, vm: &VirtualMachine| -> PyResult { - let this = Object::new(); - for (k, v) in args.kwargs { - Reflect::set(&this, &k.into(), &py_to_js(vm, v)) - .expect("property to be settable"); - } - let js_args = args - .args - .into_iter() - .map(|v| py_to_js(vm, v)) - .collect::(); - func.apply(&this, &js_args) - .map(|val| js_to_py(vm, val)) - .map_err(|err| js_err_to_py_err(vm, &err)) - }, - ) - .into() + vm.new_function( + vm.ctx.intern_str(String::from(func.name())).as_str(), + move |args: FuncArgs, vm: &VirtualMachine| -> PyResult { + let this = Object::new(); + for (k, v) in args.kwargs { + Reflect::set(&this, &k.into(), &py_to_js(vm, v)) + .expect("property to be settable"); + } + let js_args = args + .args + .into_iter() + .map(|v| py_to_js(vm, v)) + .collect::(); + func.apply(&this, &js_args) + .map(|val| js_to_py(vm, val)) + .map_err(|err| js_err_to_py_err(vm, &err)) + }, + ) + .into() } else if let Some(err) = js_val.dyn_ref::() { js_err_to_py_err(vm, err).into() } else if js_val.is_undefined() { diff --git a/wasm/lib/src/js_module.rs b/wasm/lib/src/js_module.rs index 7cdfd9d398..f0c5378c35 100644 --- a/wasm/lib/src/js_module.rs +++ b/wasm/lib/src/js_module.rs @@ -423,8 +423,8 @@ mod _js { }; let _ = then.call( ( - vm.ctx.new_function("resolve", resolve), - vm.ctx.new_function("reject", reject), + vm.new_function("resolve", resolve), + vm.new_function("reject", reject), ), vm, ); diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 25964efc29..0c62bb32e7 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -316,7 +316,7 @@ impl WASMVirtualMachine { let (key, value) = entry?; let key = Object::from(key).to_string(); extend_module!(vm, &py_module, { - &String::from(key) => convert::js_to_py(vm, value), + String::from(key) => convert::js_to_py(vm, value), }); } diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index 216360c1c9..59d7880af4 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -29,14 +29,14 @@ pub fn make_stdout_object( vm.ctx.types.object_type.to_owned(), {} )); - let write_method = ctx.new_method( - ctx.intern_str("write"), + let write_method = vm.new_method( + "write", cls, move |_self: PyObjectRef, data: PyStrRef, vm: &VirtualMachine| -> PyResult<()> { write_f(data.as_str(), vm) }, ); - let flush_method = ctx.new_method(ctx.intern_str("flush"), cls, |_self: PyObjectRef| {}); + let flush_method = vm.new_method("flush", cls, |_self: PyObjectRef| {}); extend_class!(ctx, cls, { "write" => write_method, "flush" => flush_method, From e73603945db23140fb540c65b7db623ac7cdd1ae Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sun, 30 Apr 2023 17:33:28 +0900 Subject: [PATCH 6/6] Rename PyTypeFlags::METHOD_{DESCR => DESCRIPTOR} --- vm/src/builtins/descriptor.rs | 2 +- vm/src/builtins/function.rs | 2 +- vm/src/types/slot.rs | 2 +- vm/src/vm/method.rs | 8 ++++++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/vm/src/builtins/descriptor.rs b/vm/src/builtins/descriptor.rs index eb4afbd2f7..6b92e4c36b 100644 --- a/vm/src/builtins/descriptor.rs +++ b/vm/src/builtins/descriptor.rs @@ -104,7 +104,7 @@ impl PyMethodDescriptor { #[pyclass( with(GetDescriptor, Callable, Constructor, Representable), - flags(METHOD_DESCR) + flags(METHOD_DESCRIPTOR) )] impl PyMethodDescriptor { #[pygetset(magic)] diff --git a/vm/src/builtins/function.rs b/vm/src/builtins/function.rs index dedec7fc9a..dc1764f48a 100644 --- a/vm/src/builtins/function.rs +++ b/vm/src/builtins/function.rs @@ -350,7 +350,7 @@ impl PyPayload for PyFunction { #[pyclass( with(GetDescriptor, Callable, Representable), - flags(HAS_DICT, METHOD_DESCR) + flags(HAS_DICT, METHOD_DESCRIPTOR) )] impl PyFunction { #[pygetset(magic)] diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 001e6f972e..ce983e278f 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -123,7 +123,7 @@ bitflags! { const IMMUTABLETYPE = 1 << 8; const HEAPTYPE = 1 << 9; const BASETYPE = 1 << 10; - const METHOD_DESCR = 1 << 17; + const METHOD_DESCRIPTOR = 1 << 17; const HAS_DICT = 1 << 40; #[cfg(debug_assertions)] diff --git a/vm/src/vm/method.rs b/vm/src/vm/method.rs index 3d09a95793..258b1e9473 100644 --- a/vm/src/vm/method.rs +++ b/vm/src/vm/method.rs @@ -33,7 +33,11 @@ impl PyMethod { let cls_attr = match interned_name.and_then(|name| cls.get_attr(name)) { Some(descr) => { let descr_cls = descr.class(); - let descr_get = if descr_cls.slots.flags.has_feature(PyTypeFlags::METHOD_DESCR) { + let descr_get = if descr_cls + .slots + .flags + .has_feature(PyTypeFlags::METHOD_DESCRIPTOR) + { is_method = true; None } else { @@ -106,7 +110,7 @@ impl PyMethod { .class() .slots .flags - .has_feature(PyTypeFlags::METHOD_DESCR) + .has_feature(PyTypeFlags::METHOD_DESCRIPTOR) { Self::Function { target: obj.to_owned(),