From d05645ea585a6195499a6a3f039daa323f2a6cc6 Mon Sep 17 00:00:00 2001 From: Jeong Yunwon <jeong@youknowone.org> Date: Fri, 27 May 2022 15:43:27 +0900 Subject: [PATCH 1/9] update syn-ext --- derive/Cargo.toml | 2 +- derive/src/pyclass.rs | 21 +++++++++++--------- stdlib/src/array.rs | 10 ++-------- vm/src/builtins/bytearray.rs | 34 ++++++++++++++------------------- vm/src/builtins/bytes.rs | 16 +++++----------- vm/src/builtins/dict.rs | 32 ++++++++++++++----------------- vm/src/builtins/genericalias.rs | 10 ++-------- vm/src/builtins/list.rs | 30 ++++++++++++----------------- vm/src/builtins/mappingproxy.rs | 10 ++-------- vm/src/builtins/memory.rs | 10 ++-------- vm/src/builtins/range.rs | 18 +++++++---------- vm/src/builtins/str.rs | 8 +------- vm/src/builtins/tuple.rs | 10 ++-------- vm/src/builtins/union.rs | 10 +++------- vm/src/stdlib/sre.rs | 10 +++------- 15 files changed, 82 insertions(+), 149 deletions(-) diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 576f221e4f..afe1de788e 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -12,7 +12,7 @@ proc-macro = true [dependencies] syn = { version = "1.0.91", features = ["full", "extra-traits"] } -syn-ext = { version = "0.3.1", features = ["full"] } +syn-ext = { version = "0.4.0", features = ["full"] } quote = "1.0.18" proc-macro2 = "1.0.37" rustpython-compiler = { path = "../compiler/porcelain", version = "0.1.1" } diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index c394556609..d940f7611f 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -548,25 +548,28 @@ where Item: ItemLike + ToTokens + GetIdent, { fn gen_impl_item(&self, args: ImplItemArgs<'_, Item>) -> Result<()> { - let func = args - .item - .function_or_method() - .map_err(|_| self.new_syn_error(args.item.span(), "can only be on a method"))?; - let ident = &func.sig().ident; + let (ident, span) = if let Ok(c) = args.item.constant() { + (c.ident(), c.span()) + } else if let Ok(f) = args.item.function_or_method() { + (&f.sig().ident, f.span()) + } else { + return Err(self.new_syn_error(args.item.span(), "can only be on a method")); + }; let item_attr = args.attrs.remove(self.index()); let item_meta = SlotItemMeta::from_attr(ident.clone(), &item_attr)?; let slot_ident = item_meta.slot_name()?; + let slot_ident = Ident::new(&slot_ident.to_string().to_lowercase(), slot_ident.span()); let slot_name = slot_ident.to_string(); let tokens = { const NON_ATOMIC_SLOTS: &[&str] = &["as_buffer"]; if NON_ATOMIC_SLOTS.contains(&slot_name.as_str()) { - quote_spanned! { func.span() => + quote_spanned! { span => slots.#slot_ident = Some(Self::#ident as _); } } else { - quote_spanned! { func.span() => + quote_spanned! { span => slots.#slot_ident.store(Some(Self::#ident as _)); } } @@ -599,11 +602,11 @@ where Ok(py_name) }; let (ident, py_name, tokens) = - if args.item.function_or_method().is_ok() || args.item.is_const() { + if args.item.function_or_method().is_ok() || args.item.constant().is_ok() { let ident = args.item.get_ident().unwrap(); let py_name = get_py_name(&attr, ident)?; - let value = if args.item.is_const() { + let value = if args.item.constant().is_ok() { // TODO: ctx.new_value quote_spanned!(ident.span() => ctx.new_int(Self::#ident).into()) } else { diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs index 3b6fe0a3a7..ee9bef2a82 100644 --- a/stdlib/src/array.rs +++ b/stdlib/src/array.rs @@ -1238,8 +1238,8 @@ mod array { }, }; - impl PyArray { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { + impl AsMapping for PyArray { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), subscript: Some(|mapping, needle, vm| { Self::mapping_downcast(mapping)._getitem(needle, vm) @@ -1255,12 +1255,6 @@ mod array { }; } - impl AsMapping for PyArray { - fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } - } - impl Iterable for PyArray { fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult { Ok(PyArrayIter { diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 04762df510..1a6d314463 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -690,23 +690,6 @@ impl PyByteArray { } } -impl PyByteArray { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { - length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), - subscript: Some(|mapping, needle, vm| { - Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) - }), - ass_subscript: Some(|mapping, needle, value, vm| { - let zelf = Self::mapping_downcast(mapping); - if let Some(value) = value { - Self::setitem(zelf.to_owned(), needle.to_owned(), value, vm) - } else { - zelf.delitem(needle.to_owned(), vm) - } - }), - }; -} - impl Constructor for PyByteArray { type Args = FuncArgs; @@ -784,9 +767,20 @@ impl<'a> BufferResizeGuard<'a> for PyByteArray { } impl AsMapping for PyByteArray { - fn as_mapping(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } + const AS_MAPPING: PyMappingMethods = PyMappingMethods { + length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), + subscript: Some(|mapping, needle, vm| { + Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) + }), + ass_subscript: Some(|mapping, needle, value, vm| { + let zelf = Self::mapping_downcast(mapping); + if let Some(value) = value { + Self::setitem(zelf.to_owned(), needle.to_owned(), value, vm) + } else { + zelf.delitem(needle.to_owned(), vm) + } + }), + }; } impl AsSequence for PyByteArray { diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index ccfdb88f80..e680eded4c 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -550,14 +550,6 @@ impl PyBytes { } } -impl PyBytes { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { - length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), - subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)), - ass_subscript: None, - }; -} - static BUFFER_METHODS: BufferMethods = BufferMethods { obj_bytes: |buffer| buffer.obj_as::<PyBytes>().as_bytes().into(), obj_bytes_mut: |_| panic!(), @@ -577,9 +569,11 @@ impl AsBuffer for PyBytes { } impl AsMapping for PyBytes { - fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } + const AS_MAPPING: PyMappingMethods = PyMappingMethods { + length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), + subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)), + ass_subscript: None, + }; } impl AsSequence for PyBytes { diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index cd5066057b..dd2d3c6947 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -212,21 +212,6 @@ impl PyDict { pub fn size(&self) -> dictdatatype::DictSize { self.entries.size() } - - pub(crate) const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { - length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), - subscript: Some(|mapping, needle, vm| { - Self::mapping_downcast(mapping).inner_getitem(needle, vm) - }), - ass_subscript: Some(|mapping, needle, value, vm| { - let zelf = Self::mapping_downcast(mapping); - if let Some(value) = value { - zelf.inner_setitem(needle, value, vm) - } else { - zelf.inner_delitem(needle, vm) - } - }), - }; } // Python dict methods: @@ -476,9 +461,20 @@ impl Initializer for PyDict { } impl AsMapping for PyDict { - fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } + const AS_MAPPING: PyMappingMethods = PyMappingMethods { + length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), + subscript: Some(|mapping, needle, vm| { + Self::mapping_downcast(mapping).inner_getitem(needle, vm) + }), + ass_subscript: Some(|mapping, needle, value, vm| { + let zelf = Self::mapping_downcast(mapping); + if let Some(value) = value { + zelf.inner_setitem(needle, value, vm) + } else { + zelf.inner_delitem(needle, vm) + } + }), + }; } impl AsSequence for PyDict { diff --git a/vm/src/builtins/genericalias.rs b/vm/src/builtins/genericalias.rs index 9c585124c9..4f1d6fedde 100644 --- a/vm/src/builtins/genericalias.rs +++ b/vm/src/builtins/genericalias.rs @@ -306,8 +306,8 @@ pub fn subs_parameters<F: Fn(&VirtualMachine) -> PyResult<String>>( Ok(PyTuple::new_ref(new_args, &vm.ctx)) } -impl PyGenericAlias { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { +impl AsMapping for PyGenericAlias { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: None, subscript: Some(|mapping, needle, vm| { Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) @@ -316,12 +316,6 @@ impl PyGenericAlias { }; } -impl AsMapping for PyGenericAlias { - fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - impl Callable for PyGenericAlias { type Args = FuncArgs; fn call(zelf: &crate::Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index f6187ad9e0..46ceaac074 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -352,21 +352,6 @@ impl PyList { } } -impl PyList { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { - length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), - subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)), - ass_subscript: Some(|mapping, needle, value, vm| { - let zelf = Self::mapping_downcast(mapping); - if let Some(value) = value { - zelf._setitem(needle, value, vm) - } else { - zelf._delitem(needle, vm) - } - }), - }; -} - impl<'a> MutObjectSequenceOp<'a> for PyList { type Guard = PyMappedRwLockReadGuard<'a, [PyObjectRef]>; @@ -404,9 +389,18 @@ impl Initializer for PyList { } impl AsMapping for PyList { - fn as_mapping(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } + const AS_MAPPING: PyMappingMethods = PyMappingMethods { + length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), + subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)), + ass_subscript: Some(|mapping, needle, value, vm| { + let zelf = Self::mapping_downcast(mapping); + if let Some(value) = value { + zelf._setitem(needle, value, vm) + } else { + zelf._delitem(needle, vm) + } + }), + }; } impl AsSequence for PyList { diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs index 4ab71a1dff..d5ebeec0ea 100644 --- a/vm/src/builtins/mappingproxy.rs +++ b/vm/src/builtins/mappingproxy.rs @@ -157,8 +157,8 @@ impl PyMappingProxy { } } -impl PyMappingProxy { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { +impl AsMapping for PyMappingProxy { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: None, subscript: Some(|mapping, needle, vm| { Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) @@ -167,12 +167,6 @@ impl PyMappingProxy { }; } -impl AsMapping for PyMappingProxy { - fn as_mapping(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - impl AsSequence for PyMappingProxy { fn as_sequence( _zelf: &crate::Py<Self>, diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index 14bcbca790..cba6789f5b 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -957,8 +957,8 @@ impl Drop for PyMemoryView { } } -impl PyMemoryView { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { +impl AsMapping for PyMemoryView { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: Some(|mapping, vm| Self::mapping_downcast(mapping).len(vm)), subscript: Some(|mapping, needle, vm| { let zelf = Self::mapping_downcast(mapping); @@ -975,12 +975,6 @@ impl PyMemoryView { }; } -impl AsMapping for PyMemoryView { - fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - impl AsSequence for PyMemoryView { fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { Cow::Borrowed(&Self::SEQUENCE_METHODS) diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs index 1348d7654a..5de3d78fae 100644 --- a/vm/src/builtins/range.rs +++ b/vm/src/builtins/range.rs @@ -390,14 +390,6 @@ impl PyRange { .map(|x| x as usize) } - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { - length: Some(|mapping, vm| Self::mapping_downcast(mapping).protocol_length(vm)), - subscript: Some(|mapping, needle, vm| { - Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) - }), - ass_subscript: None, - }; - const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods { length: Some(|seq, vm| Self::sequence_downcast(seq).protocol_length(vm)), item: Some(|seq, i, vm| { @@ -414,9 +406,13 @@ impl PyRange { } impl AsMapping for PyRange { - fn as_mapping(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } + const AS_MAPPING: PyMappingMethods = PyMappingMethods { + length: Some(|mapping, vm| Self::mapping_downcast(mapping).protocol_length(vm)), + subscript: Some(|mapping, needle, vm| { + Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) + }), + ass_subscript: None, + }; } impl AsSequence for PyRange { diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index 8a58f680d2..0562bcd548 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -1301,13 +1301,7 @@ impl Iterable for PyStr { } impl AsMapping for PyStr { - fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - -impl PyStr { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)), ass_subscript: None, diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs index 84865a0916..976a5aaefb 100644 --- a/vm/src/builtins/tuple.rs +++ b/vm/src/builtins/tuple.rs @@ -345,20 +345,14 @@ impl PyTuple { } } -impl PyTuple { - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { +impl AsMapping for PyTuple { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: Some(|mapping, _vm| Ok(Self::mapping_downcast(mapping).len())), subscript: Some(|mapping, needle, vm| Self::mapping_downcast(mapping)._getitem(needle, vm)), ass_subscript: None, }; } -impl AsMapping for PyTuple { - fn as_mapping(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - impl AsSequence for PyTuple { fn as_sequence( _zelf: &crate::Py<Self>, diff --git a/vm/src/builtins/union.rs b/vm/src/builtins/union.rs index e2072a3425..813974b53c 100644 --- a/vm/src/builtins/union.rs +++ b/vm/src/builtins/union.rs @@ -224,8 +224,10 @@ impl PyUnion { Ok(res) } +} - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { +impl AsMapping for PyUnion { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: None, subscript: Some(|mapping, needle, vm| { Self::mapping_downcast(mapping).getitem(needle.to_owned(), vm) @@ -234,12 +236,6 @@ impl PyUnion { }; } -impl AsMapping for PyUnion { - fn as_mapping(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } -} - impl Comparable for PyUnion { fn cmp( zelf: &crate::Py<Self>, diff --git a/vm/src/stdlib/sre.rs b/vm/src/stdlib/sre.rs index 7ffbb35dca..2b6b88d9f2 100644 --- a/vm/src/stdlib/sre.rs +++ b/vm/src/stdlib/sre.rs @@ -780,8 +780,10 @@ mod _sre { fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { PyGenericAlias::new(cls, args, vm) } + } - const MAPPING_METHODS: PyMappingMethods = PyMappingMethods { + impl AsMapping for Match { + const AS_MAPPING: PyMappingMethods = PyMappingMethods { length: None, subscript: Some(|mapping, needle, vm| { Self::mapping_downcast(mapping) @@ -792,12 +794,6 @@ mod _sre { }; } - impl AsMapping for Match { - fn as_mapping(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyMappingMethods { - Self::MAPPING_METHODS - } - } - #[pyattr] #[pyclass(name = "SRE_Scanner")] #[derive(Debug, PyPayload)] From 97c2d187f68c5939278e0cce47d1073c4610fc2b Mon Sep 17 00:00:00 2001 From: Jeong Yunwon <jeong@youknowone.org> Date: Sat, 28 May 2022 01:38:30 +0900 Subject: [PATCH 2/9] AsMapping::AS_MAPPING --- Cargo.lock | 13 +++++++++++-- vm/src/function/protocol.rs | 3 ++- vm/src/types/slot.rs | 9 ++++----- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb986773c8..f376072567 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1518,7 +1518,7 @@ dependencies = [ "proc-macro2", "quote", "syn", - "syn-ext", + "syn-ext 0.3.1", ] [[package]] @@ -1645,7 +1645,7 @@ dependencies = [ "rustpython-compiler", "rustpython-doc", "syn", - "syn-ext", + "syn-ext 0.4.0", "textwrap 0.15.0", ] @@ -2120,6 +2120,15 @@ dependencies = [ "syn", ] +[[package]] +name = "syn-ext" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b86cb2b68c5b3c078cac02588bc23f3c04bb828c5d3aedd17980876ec6a7be6" +dependencies = [ + "syn", +] + [[package]] name = "system-configuration" version = "0.5.0" diff --git a/vm/src/function/protocol.rs b/vm/src/function/protocol.rs index de18517ce3..02c179b4af 100644 --- a/vm/src/function/protocol.rs +++ b/vm/src/function/protocol.rs @@ -4,6 +4,7 @@ use crate::{ convert::ToPyObject, identifier, protocol::{PyIter, PyIterIter, PyMapping, PyMappingMethods}, + types::AsMapping, AsObject, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, }; use std::{borrow::Borrow, marker::PhantomData}; @@ -110,7 +111,7 @@ impl ArgMapping { pub fn from_dict_exact(dict: PyDictRef) -> Self { Self { obj: dict.into(), - mapping_methods: PyDict::MAPPING_METHODS, + mapping_methods: PyDict::AS_MAPPING, } } diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index b626327e91..d7bbaed1f1 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -896,15 +896,14 @@ pub trait AsBuffer: PyPayload { #[pyimpl] pub trait AsMapping: PyPayload { + const AS_MAPPING: PyMappingMethods; + #[inline] #[pyslot] - fn slot_as_mapping(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods { - let zelf = unsafe { zelf.downcast_unchecked_ref::<Self>() }; - Self::as_mapping(zelf, vm) + fn as_mapping(_zelf: &PyObject, _vm: &VirtualMachine) -> PyMappingMethods { + Self::AS_MAPPING } - fn as_mapping(zelf: &Py<Self>, vm: &VirtualMachine) -> PyMappingMethods; - fn mapping_downcast<'a>(mapping: &'a PyMapping) -> &'a Py<Self> { unsafe { mapping.obj.downcast_unchecked_ref() } } From 6857384c338a0741b7f8448c7b12f0b9ce918c64 Mon Sep 17 00:00:00 2001 From: Jeong Yunwon <jeong@youknowone.org> Date: Sat, 28 May 2022 23:36:10 +0900 Subject: [PATCH 3/9] new_mapping_wrapper as static slice --- vm/src/types/slot.rs | 76 ++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index d7bbaed1f1..f68b99c294 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -174,8 +174,8 @@ macro_rules! then_some_closure { }; } -fn length_wrapper(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> { - let ret = vm.call_special_method(obj, identifier!(vm, __len__), ())?; +fn length_wrapper(obj: &PyObject, vm: &VirtualMachine) -> PyResult<usize> { + let ret = vm.call_special_method(obj.to_owned(), identifier!(vm, __len__), ())?; let len = ret.payload::<PyInt>().ok_or_else(|| { vm.new_type_error(format!( "'{}' object cannot be interpreted as an integer", @@ -192,26 +192,30 @@ fn length_wrapper(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> { Ok(len as usize) } -fn as_mapping_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods { +const fn new_as_mapping_wrapper( + has_length: bool, + has_subscript: bool, + has_ass_subscript: bool, +) -> PyMappingMethods { PyMappingMethods { - length: then_some_closure!( - zelf.class().has_attr(identifier!(vm, __len__)), - |mapping, vm| { length_wrapper(mapping.obj.to_owned(), vm) } - ), - subscript: then_some_closure!( - zelf.class().has_attr(identifier!(vm, __getitem__)), - |mapping, needle, vm| { + length: if has_length { + Some(|mapping, vm| length_wrapper(&mapping.obj, vm)) + } else { + None + }, + subscript: if has_subscript { + Some(|mapping, needle, vm| { vm.call_special_method( mapping.obj.to_owned(), identifier!(vm, __getitem__), (needle.to_owned(),), ) - } - ), - ass_subscript: then_some_closure!( - zelf.class().has_attr(identifier!(vm, __setitem__)) - | zelf.class().has_attr(identifier!(vm, __delitem__)), - |mapping, needle, value, vm| match value { + }) + } else { + None + }, + ass_subscript: if has_ass_subscript { + Some(|mapping, needle, value, vm| match value { Some(value) => vm .call_special_method( mapping.obj.to_owned(), @@ -223,12 +227,44 @@ fn as_mapping_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods .call_special_method( mapping.obj.to_owned(), identifier!(vm, __delitem__), - (needle.to_owned(),) + (needle.to_owned(),), ) .map(|_| Ok(()))?, - } - ), + }) + } else { + None + }, + } +} + +fn as_mapping_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods { + static MAPPING_METHODS: &[PyMappingMethods] = &[ + new_as_mapping_wrapper(false, false, false), + new_as_mapping_wrapper(true, false, false), + new_as_mapping_wrapper(false, true, false), + new_as_mapping_wrapper(true, true, false), + new_as_mapping_wrapper(false, false, true), + new_as_mapping_wrapper(true, false, true), + new_as_mapping_wrapper(false, true, true), + new_as_mapping_wrapper(true, true, true), + ]; + const fn bool_int(v: bool) -> usize { + if v { + 1 + } else { + 0 + } } + let (has_length, has_subscript, has_ass_subscript) = ( + zelf.class().has_attr(identifier!(vm, __len__)), + zelf.class().has_attr(identifier!(vm, __getitem__)), + zelf.class().has_attr(identifier!(vm, __setitem__)) + | zelf.class().has_attr(identifier!(vm, __delitem__)), + ); + let key = (bool_int(has_length) << 0) + | (bool_int(has_subscript) << 1) + | (bool_int(has_ass_subscript) << 2); + MAPPING_METHODS[key].clone() } fn as_sequence_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { @@ -239,7 +275,7 @@ fn as_sequence_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PyS Cow::Owned(PySequenceMethods { length: then_some_closure!( zelf.class().has_attr(identifier!(vm, __len__)), - |seq, vm| { length_wrapper(seq.obj.to_owned(), vm) } + |seq, vm| { length_wrapper(&seq.obj, vm) } ), item: Some(|seq, i, vm| { vm.call_special_method( From 83a146d8c83b5342d02327dec62483ac7d594a3f Mon Sep 17 00:00:00 2001 From: Jeong Yunwon <jeong@youknowone.org> Date: Sun, 29 May 2022 00:26:49 +0900 Subject: [PATCH 4/9] AsMapping only with static reference --- vm/src/builtins/mappingproxy.rs | 2 +- vm/src/function/protocol.rs | 6 +- vm/src/protocol/mapping.rs | 78 ++++++++---------- vm/src/protocol/object.rs | 29 ++++--- vm/src/protocol/sequence.rs | 4 +- vm/src/types/slot.rs | 140 ++++++++++++++++++-------------- 6 files changed, 137 insertions(+), 122 deletions(-) diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs index d5ebeec0ea..fae4c55890 100644 --- a/vm/src/builtins/mappingproxy.rs +++ b/vm/src/builtins/mappingproxy.rs @@ -39,7 +39,7 @@ impl Constructor for PyMappingProxy { type Args = PyObjectRef; fn py_new(cls: PyTypeRef, mapping: Self::Args, vm: &VirtualMachine) -> PyResult { - if !PyMapping::from(mapping.as_ref()).check(vm) + if !PyMapping::check(mapping.as_ref(), vm) || mapping.payload_if_subclass::<PyList>(vm).is_some() || mapping.payload_if_subclass::<PyTuple>(vm).is_some() { diff --git a/vm/src/function/protocol.rs b/vm/src/function/protocol.rs index 02c179b4af..30816e11f9 100644 --- a/vm/src/function/protocol.rs +++ b/vm/src/function/protocol.rs @@ -103,7 +103,7 @@ where #[derive(Clone)] pub struct ArgMapping { obj: PyObjectRef, - mapping_methods: PyMappingMethods, + mapping_methods: &'static PyMappingMethods, } impl ArgMapping { @@ -111,7 +111,7 @@ impl ArgMapping { pub fn from_dict_exact(dict: PyDictRef) -> Self { Self { obj: dict.into(), - mapping_methods: PyDict::AS_MAPPING, + mapping_methods: &PyDict::AS_MAPPING, } } @@ -152,7 +152,7 @@ impl ToPyObject for ArgMapping { impl TryFromObject for ArgMapping { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> { let mapping = PyMapping::try_protocol(&obj, vm)?; - let mapping_methods = *mapping.methods(vm); + let mapping_methods = mapping.methods; Ok(Self { obj, mapping_methods, diff --git a/vm/src/protocol/mapping.rs b/vm/src/protocol/mapping.rs index a9389db473..84ff355472 100644 --- a/vm/src/protocol/mapping.rs +++ b/vm/src/protocol/mapping.rs @@ -3,7 +3,6 @@ use crate::{ dict::{PyDictItems, PyDictKeys, PyDictValues}, PyDict, PyStrInterned, }, - common::lock::OnceCell, convert::ToPyResult, AsObject, PyObject, PyObjectRef, PyResult, VirtualMachine, }; @@ -11,7 +10,6 @@ use crate::{ // Mapping protocol // https://docs.python.org/3/c-api/mapping.html #[allow(clippy::type_complexity)] -#[derive(Default, Copy, Clone)] pub struct PyMappingMethods { pub length: Option<fn(&PyMapping, &VirtualMachine) -> PyResult<usize>>, pub subscript: Option<fn(&PyMapping, &PyObject, &VirtualMachine) -> PyResult>, @@ -19,20 +17,16 @@ pub struct PyMappingMethods { Option<fn(&PyMapping, &PyObject, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>>, } +impl PyMappingMethods { + fn check(&self) -> bool { + self.subscript.is_some() + } +} + #[derive(Clone)] pub struct PyMapping<'a> { pub obj: &'a PyObject, - methods: OnceCell<PyMappingMethods>, -} - -impl<'a> From<&'a PyObject> for PyMapping<'a> { - #[inline(always)] - fn from(obj: &'a PyObject) -> Self { - Self { - obj, - methods: OnceCell::new(), - } - } + pub methods: &'static PyMappingMethods, } impl AsRef<PyObject> for PyMapping<'_> { @@ -43,47 +37,47 @@ impl AsRef<PyObject> for PyMapping<'_> { } impl<'a> PyMapping<'a> { + #[inline] + pub fn new(obj: &'a PyObject, vm: &VirtualMachine) -> Option<Self> { + let methods = Self::find_methods(obj, vm)?; + Some(Self { obj, methods }) + } + #[inline(always)] - pub fn with_methods(obj: &'a PyObject, methods: PyMappingMethods) -> Self { - Self { - obj, - methods: OnceCell::from(methods), - } + pub fn with_methods(obj: &'a PyObject, methods: &'static PyMappingMethods) -> Self { + Self { obj, methods } } pub fn try_protocol(obj: &'a PyObject, vm: &VirtualMachine) -> PyResult<Self> { - let zelf = Self::from(obj); - if zelf.check(vm) { - Ok(zelf) - } else { - Err(vm.new_type_error(format!("{} is not a mapping object", zelf.obj.class()))) + if let Some(methods) = Self::find_methods(obj, vm) { + if methods.check() { + return Ok(Self::with_methods(obj, methods)); + } } + + Err(vm.new_type_error(format!("{} is not a mapping object", obj.class()))) } } impl PyMapping<'_> { // PyMapping::Check #[inline] - pub fn check(&self, vm: &VirtualMachine) -> bool { - self.methods(vm).subscript.is_some() - } - - pub fn methods(&self, vm: &VirtualMachine) -> &PyMappingMethods { - self.methods.get_or_init(|| { - if let Some(f) = self - .obj - .class() - .mro_find_map(|cls| cls.slots.as_mapping.load()) - { - f(self.obj, vm) - } else { - PyMappingMethods::default() - } - }) + pub fn check(obj: &PyObject, vm: &VirtualMachine) -> bool { + Self::find_methods(obj, vm) + .and_then(|m| m.subscript) + .is_some() + } + + pub fn find_methods(obj: &PyObject, vm: &VirtualMachine) -> Option<&'static PyMappingMethods> { + if let Some(f) = obj.class().mro_find_map(|cls| cls.slots.as_mapping.load()) { + Some(f(obj, vm)) + } else { + None + } } pub fn length_opt(&self, vm: &VirtualMachine) -> Option<PyResult<usize>> { - self.methods(vm).length.map(|f| f(self, vm)) + self.methods.length.map(|f| f(self, vm)) } pub fn length(&self, vm: &VirtualMachine) -> PyResult<usize> { @@ -110,7 +104,7 @@ impl PyMapping<'_> { fn _subscript(&self, needle: &PyObject, vm: &VirtualMachine) -> PyResult { let f = self - .methods(vm) + .methods .subscript .ok_or_else(|| vm.new_type_error(format!("{} is not a mapping", self.obj.class())))?; f(self, needle, vm) @@ -122,7 +116,7 @@ impl PyMapping<'_> { value: Option<PyObjectRef>, vm: &VirtualMachine, ) -> PyResult<()> { - let f = self.methods(vm).ass_subscript.ok_or_else(|| { + let f = self.methods.ass_subscript.ok_or_else(|| { vm.new_type_error(format!( "'{}' object does not support item assignment", self.obj.class() diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index d4ed959eea..31cf76b47b 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -524,7 +524,7 @@ impl PyObject { pub fn length_opt(&self, vm: &VirtualMachine) -> Option<PyResult<usize>> { PySequence::from(self) .length_opt(vm) - .or_else(|| PyMapping::from(self).length_opt(vm)) + .or_else(|| PyMapping::new(self, vm).and_then(|mapping| mapping.length_opt(vm))) } pub fn length(&self, vm: &VirtualMachine) -> PyResult<usize> { @@ -570,13 +570,15 @@ impl PyObject { return dict.set_item(needle, value, vm); } - let mapping = PyMapping::from(self); - let seq = PySequence::from(self); + if let Some(mapping) = PyMapping::new(self, vm) { + if let Some(f) = mapping.methods.ass_subscript { + let needle = needle.to_pyobject(vm); + return f(&mapping, &needle, Some(value), vm); + } + } - if let Some(f) = mapping.methods(vm).ass_subscript { - let needle = needle.to_pyobject(vm); - f(&mapping, &needle, Some(value), vm) - } else if let Some(f) = seq.methods(vm).ass_item { + let seq = PySequence::from(self); + if let Some(f) = seq.methods(vm).ass_item { let i = needle.key_as_isize(vm)?; f(&seq, i, Some(value), vm) } else { @@ -592,13 +594,16 @@ impl PyObject { return dict.del_item(needle, vm); } - let mapping = PyMapping::from(self); + if let Some(mapping) = PyMapping::new(self, vm) { + if let Some(f) = mapping.methods.ass_subscript { + let needle = needle.to_pyobject(vm); + return f(&mapping, &needle, None, vm); + } + } + let seq = PySequence::from(self); - if let Some(f) = mapping.methods(vm).ass_subscript { - let needle = needle.to_pyobject(vm); - f(&mapping, &needle, None, vm) - } else if let Some(f) = seq.methods(vm).ass_item { + if let Some(f) = seq.methods(vm).ass_item { let i = needle.key_as_isize(vm)?; f(&seq, i, None, vm) } else { diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs index ac15be3e06..3af7a0bf98 100644 --- a/vm/src/protocol/sequence.rs +++ b/vm/src/protocol/sequence.rs @@ -242,8 +242,8 @@ impl PySequence<'_> { value: Option<PyObjectRef>, vm: &VirtualMachine, ) -> PyResult<()> { - let mapping = PyMapping::from(self.obj); - if let Some(f) = mapping.methods(vm).ass_subscript { + let mapping = PyMapping::new(self.obj, vm).unwrap(); + if let Some(f) = mapping.methods.ass_subscript { let slice = PySlice { start: Some(start.to_pyobject(vm)), stop: stop.to_pyobject(vm), diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index f68b99c294..0a112c7d7e 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -140,7 +140,7 @@ impl Default for PyTypeFlags { } pub(crate) type GenericMethod = fn(&PyObject, FuncArgs, &VirtualMachine) -> PyResult; -pub(crate) type AsMappingFunc = fn(&PyObject, &VirtualMachine) -> PyMappingMethods; +pub(crate) type AsMappingFunc = fn(&PyObject, &VirtualMachine) -> &'static PyMappingMethods; pub(crate) type HashFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyHash>; // CallFunc = GenericMethod pub(crate) type GetattroFunc = fn(&PyObject, PyStrRef, &VirtualMachine) -> PyResult; @@ -192,79 +192,94 @@ fn length_wrapper(obj: &PyObject, vm: &VirtualMachine) -> PyResult<usize> { Ok(len as usize) } -const fn new_as_mapping_wrapper( +const fn bool_int(v: bool) -> usize { + if v { + 1 + } else { + 0 + } +} + +pub(crate) fn static_as_mapping_generic( has_length: bool, has_subscript: bool, has_ass_subscript: bool, -) -> PyMappingMethods { - PyMappingMethods { - length: if has_length { - Some(|mapping, vm| length_wrapper(&mapping.obj, vm)) - } else { - None - }, - subscript: if has_subscript { - Some(|mapping, needle, vm| { - vm.call_special_method( +) -> &'static PyMappingMethods { + static METHODS: &[PyMappingMethods] = &[ + new_generic(false, false, false), + new_generic(true, false, false), + new_generic(false, true, false), + new_generic(true, true, false), + new_generic(false, false, true), + new_generic(true, false, true), + new_generic(false, true, true), + new_generic(true, true, true), + ]; + + fn length(mapping: &PyMapping, vm: &VirtualMachine) -> PyResult<usize> { + length_wrapper(mapping.obj, vm) + } + fn subscript(mapping: &PyMapping, needle: &PyObject, vm: &VirtualMachine) -> PyResult { + vm.call_special_method( + mapping.obj.to_owned(), + identifier!(vm, __getitem__), + (needle.to_owned(),), + ) + } + fn ass_subscript( + mapping: &PyMapping, + needle: &PyObject, + value: Option<PyObjectRef>, + vm: &VirtualMachine, + ) -> PyResult<()> { + match value { + Some(value) => vm + .call_special_method( mapping.obj.to_owned(), - identifier!(vm, __getitem__), + identifier!(vm, __setitem__), + (needle.to_owned(), value), + ) + .map(|_| Ok(()))?, + None => vm + .call_special_method( + mapping.obj.to_owned(), + identifier!(vm, __delitem__), (needle.to_owned(),), ) - }) - } else { - None - }, - ass_subscript: if has_ass_subscript { - Some(|mapping, needle, value, vm| match value { - Some(value) => vm - .call_special_method( - mapping.obj.to_owned(), - identifier!(vm, __setitem__), - (needle.to_owned(), value), - ) - .map(|_| Ok(()))?, - None => vm - .call_special_method( - mapping.obj.to_owned(), - identifier!(vm, __delitem__), - (needle.to_owned(),), - ) - .map(|_| Ok(()))?, - }) - } else { - None - }, + .map(|_| Ok(()))?, + } } -} -fn as_mapping_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyMappingMethods { - static MAPPING_METHODS: &[PyMappingMethods] = &[ - new_as_mapping_wrapper(false, false, false), - new_as_mapping_wrapper(true, false, false), - new_as_mapping_wrapper(false, true, false), - new_as_mapping_wrapper(true, true, false), - new_as_mapping_wrapper(false, false, true), - new_as_mapping_wrapper(true, false, true), - new_as_mapping_wrapper(false, true, true), - new_as_mapping_wrapper(true, true, true), - ]; - const fn bool_int(v: bool) -> usize { - if v { - 1 - } else { - 0 + const fn new_generic( + has_length: bool, + has_subscript: bool, + has_ass_subscript: bool, + ) -> PyMappingMethods { + PyMappingMethods { + length: if has_length { Some(length) } else { None }, + subscript: if has_subscript { Some(subscript) } else { None }, + ass_subscript: if has_ass_subscript { + Some(ass_subscript) + } else { + None + }, } } + + let key = + bool_int(has_length) | (bool_int(has_subscript) << 1) | (bool_int(has_ass_subscript) << 2); + + &METHODS[key] +} + +fn as_mapping_generic(zelf: &PyObject, vm: &VirtualMachine) -> &'static PyMappingMethods { let (has_length, has_subscript, has_ass_subscript) = ( zelf.class().has_attr(identifier!(vm, __len__)), zelf.class().has_attr(identifier!(vm, __getitem__)), zelf.class().has_attr(identifier!(vm, __setitem__)) | zelf.class().has_attr(identifier!(vm, __delitem__)), ); - let key = (bool_int(has_length) << 0) - | (bool_int(has_subscript) << 1) - | (bool_int(has_ass_subscript) << 2); - MAPPING_METHODS[key].clone() + static_as_mapping_generic(has_length, has_subscript, has_ass_subscript) } fn as_sequence_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { @@ -275,7 +290,7 @@ fn as_sequence_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PyS Cow::Owned(PySequenceMethods { length: then_some_closure!( zelf.class().has_attr(identifier!(vm, __len__)), - |seq, vm| { length_wrapper(&seq.obj, vm) } + |seq, vm| { length_wrapper(seq.obj, vm) } ), item: Some(|seq, i, vm| { vm.call_special_method( @@ -420,7 +435,7 @@ impl PyType { } match name.as_str() { "__len__" | "__getitem__" | "__setitem__" | "__delitem__" => { - update_slot!(as_mapping, as_mapping_wrapper); + update_slot!(as_mapping, as_mapping_generic); update_slot!(as_sequence, as_sequence_wrapper); } "__hash__" => { @@ -936,10 +951,11 @@ pub trait AsMapping: PyPayload { #[inline] #[pyslot] - fn as_mapping(_zelf: &PyObject, _vm: &VirtualMachine) -> PyMappingMethods { - Self::AS_MAPPING + fn as_mapping(_zelf: &PyObject, _vm: &VirtualMachine) -> &'static PyMappingMethods { + &Self::AS_MAPPING } + #[inline] fn mapping_downcast<'a>(mapping: &'a PyMapping) -> &'a Py<Self> { unsafe { mapping.obj.downcast_unchecked_ref() } } From 6d0abe6915c2c00228d80318abec71703423238e Mon Sep 17 00:00:00 2001 From: Jeong Yunwon <jeong@youknowone.org> Date: Sun, 29 May 2022 03:40:01 +0900 Subject: [PATCH 5/9] mapping proxy holds PyMapping --- vm/src/builtins/mappingproxy.rs | 79 +++++++++++++-------------------- vm/src/function/protocol.rs | 30 ++++++++----- vm/src/protocol/mapping.rs | 6 +++ 3 files changed, 57 insertions(+), 58 deletions(-) diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs index fae4c55890..507375de86 100644 --- a/vm/src/builtins/mappingproxy.rs +++ b/vm/src/builtins/mappingproxy.rs @@ -2,7 +2,7 @@ use super::{PyDict, PyGenericAlias, PyList, PyTuple, PyType, PyTypeRef}; use crate::{ class::PyClassImpl, convert::ToPyObject, - function::OptionalArg, + function::{ArgMapping, OptionalArg}, protocol::{PyMapping, PyMappingMethods, PySequence, PySequenceMethods}, types::{AsMapping, AsSequence, Constructor, Iterable}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, @@ -18,7 +18,7 @@ pub struct PyMappingProxy { #[derive(Debug)] enum MappingProxyInner { Class(PyTypeRef), - Dict(PyObjectRef), + Mapping(ArgMapping), } impl PyPayload for PyMappingProxy { @@ -39,21 +39,21 @@ impl Constructor for PyMappingProxy { type Args = PyObjectRef; fn py_new(cls: PyTypeRef, mapping: Self::Args, vm: &VirtualMachine) -> PyResult { - if !PyMapping::check(mapping.as_ref(), vm) - || mapping.payload_if_subclass::<PyList>(vm).is_some() - || mapping.payload_if_subclass::<PyTuple>(vm).is_some() - { - Err(vm.new_type_error(format!( - "mappingproxy() argument must be a mapping, not {}", - mapping.class() - ))) - } else { - Self { - mapping: MappingProxyInner::Dict(mapping), + if let Some(methods) = PyMapping::find_methods(&mapping, vm) { + if mapping.payload_if_subclass::<PyList>(vm).is_none() + && mapping.payload_if_subclass::<PyTuple>(vm).is_none() + { + return Self { + mapping: MappingProxyInner::Mapping(ArgMapping::with_methods(mapping, methods)), + } + .into_ref_with_type(vm, cls) + .map(Into::into); } - .into_ref_with_type(vm, cls) - .map(Into::into) } + Err(vm.new_type_error(format!( + "mappingproxy() argument must be a mapping, not {}", + mapping.class() + ))) } } @@ -64,7 +64,7 @@ impl PyMappingProxy { MappingProxyInner::Class(class) => key .as_interned_str(vm) .and_then(|key| class.attributes.read().get(key).cloned()), - MappingProxyInner::Dict(obj) => obj.get_item(&*key, vm).ok(), + MappingProxyInner::Mapping(mapping) => mapping.mapping().subscript(&*key, vm).ok(), }; Ok(opt) } @@ -92,7 +92,7 @@ impl PyMappingProxy { MappingProxyInner::Class(class) => Ok(key .as_interned_str(vm) .map_or(false, |key| class.attributes.read().contains_key(key))), - MappingProxyInner::Dict(obj) => PySequence::from(obj.as_ref()).contains(key, vm), + MappingProxyInner::Mapping(obj) => PySequence::from(obj.as_ref()).contains(key, vm), } } @@ -101,40 +101,34 @@ impl PyMappingProxy { self._contains(&key, vm) } - #[pymethod] - pub fn items(&self, vm: &VirtualMachine) -> PyResult { - let obj = match &self.mapping { - MappingProxyInner::Dict(d) => d.clone(), + fn to_object(&self, vm: &VirtualMachine) -> PyResult { + Ok(match &self.mapping { + MappingProxyInner::Mapping(d) => d.as_ref().to_owned(), MappingProxyInner::Class(c) => { PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm) } - }; + }) + } + + #[pymethod] + pub fn items(&self, vm: &VirtualMachine) -> PyResult { + let obj = self.to_object(vm)?; vm.call_method(&obj, identifier!(vm, items).as_str(), ()) } #[pymethod] pub fn keys(&self, vm: &VirtualMachine) -> PyResult { - let obj = match &self.mapping { - MappingProxyInner::Dict(d) => d.clone(), - MappingProxyInner::Class(c) => { - PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm) - } - }; + let obj = self.to_object(vm)?; vm.call_method(&obj, identifier!(vm, keys).as_str(), ()) } #[pymethod] pub fn values(&self, vm: &VirtualMachine) -> PyResult { - let obj = match &self.mapping { - MappingProxyInner::Dict(d) => d.clone(), - MappingProxyInner::Class(c) => { - PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm) - } - }; + let obj = self.to_object(vm)?; vm.call_method(&obj, identifier!(vm, values).as_str(), ()) } #[pymethod] pub fn copy(&self, vm: &VirtualMachine) -> PyResult { match &self.mapping { - MappingProxyInner::Dict(d) => vm.call_method(d, identifier!(vm, copy).as_str(), ()), + MappingProxyInner::Mapping(d) => vm.call_method(d, identifier!(vm, copy).as_str(), ()), MappingProxyInner::Class(c) => { Ok(PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm)) } @@ -142,12 +136,7 @@ impl PyMappingProxy { } #[pymethod(magic)] fn repr(&self, vm: &VirtualMachine) -> PyResult<String> { - let obj = match &self.mapping { - MappingProxyInner::Dict(d) => d.clone(), - MappingProxyInner::Class(c) => { - PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm) - } - }; + let obj = self.to_object(vm)?; Ok(format!("mappingproxy({})", obj.repr(vm)?)) } @@ -185,13 +174,7 @@ impl PyMappingProxy { impl Iterable for PyMappingProxy { fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult { - let obj = match &zelf.mapping { - MappingProxyInner::Dict(d) => d.clone(), - MappingProxyInner::Class(c) => { - // TODO: something that's much more efficient than this - PyDict::from_attributes(c.attributes.read().clone(), vm)?.to_pyobject(vm) - } - }; + let obj = zelf.to_object(vm)?; let iter = obj.get_iter(vm)?; Ok(iter.into()) } diff --git a/vm/src/function/protocol.rs b/vm/src/function/protocol.rs index 30816e11f9..b6035ff968 100644 --- a/vm/src/function/protocol.rs +++ b/vm/src/function/protocol.rs @@ -7,7 +7,7 @@ use crate::{ types::AsMapping, AsObject, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, }; -use std::{borrow::Borrow, marker::PhantomData}; +use std::{borrow::Borrow, marker::PhantomData, ops::Deref}; #[derive(Clone, Debug)] pub struct ArgCallable { @@ -100,24 +100,29 @@ where } } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct ArgMapping { obj: PyObjectRef, - mapping_methods: &'static PyMappingMethods, + methods: &'static PyMappingMethods, } impl ArgMapping { + #[inline] + pub fn with_methods(obj: PyObjectRef, methods: &'static PyMappingMethods) -> Self { + Self { obj, methods } + } + #[inline(always)] pub fn from_dict_exact(dict: PyDictRef) -> Self { Self { obj: dict.into(), - mapping_methods: &PyDict::AS_MAPPING, + methods: &PyDict::AS_MAPPING, } } #[inline(always)] pub fn mapping(&self) -> PyMapping { - PyMapping::with_methods(&self.obj, self.mapping_methods) + PyMapping::with_methods(&self.obj, self.methods) } } @@ -135,6 +140,14 @@ impl AsRef<PyObject> for ArgMapping { } } +impl Deref for ArgMapping { + type Target = PyObject; + #[inline(always)] + fn deref(&self) -> &PyObject { + &self.obj + } +} + impl From<ArgMapping> for PyObjectRef { #[inline(always)] fn from(value: ArgMapping) -> PyObjectRef { @@ -152,11 +165,8 @@ impl ToPyObject for ArgMapping { impl TryFromObject for ArgMapping { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> { let mapping = PyMapping::try_protocol(&obj, vm)?; - let mapping_methods = mapping.methods; - Ok(Self { - obj, - mapping_methods, - }) + let methods = mapping.methods; + Ok(Self { obj, methods }) } } diff --git a/vm/src/protocol/mapping.rs b/vm/src/protocol/mapping.rs index 84ff355472..b19818cc12 100644 --- a/vm/src/protocol/mapping.rs +++ b/vm/src/protocol/mapping.rs @@ -17,6 +17,12 @@ pub struct PyMappingMethods { Option<fn(&PyMapping, &PyObject, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>>, } +impl std::fmt::Debug for PyMappingMethods { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "mapping methods") + } +} + impl PyMappingMethods { fn check(&self) -> bool { self.subscript.is_some() From 386057cdf55bbb711e4797b6fc7d39ff325fced6 Mon Sep 17 00:00:00 2001 From: Jeong Yunwon <jeong@youknowone.org> Date: Sun, 29 May 2022 02:05:19 +0900 Subject: [PATCH 6/9] Add fannkuch.py --- benches/benchmarks/fannkuch.py | 55 ++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 benches/benchmarks/fannkuch.py diff --git a/benches/benchmarks/fannkuch.py b/benches/benchmarks/fannkuch.py new file mode 100644 index 0000000000..6b4a5dc113 --- /dev/null +++ b/benches/benchmarks/fannkuch.py @@ -0,0 +1,55 @@ +""" +The Computer Language Benchmarks Game +http://benchmarksgame.alioth.debian.org/ + +Contributed by Sokolov Yura, modified by Tupteq. +""" + +# import pyperf + + +DEFAULT_ARG = 9 + + +def fannkuch(n): + count = list(range(1, n + 1)) + max_flips = 0 + m = n - 1 + r = n + perm1 = list(range(n)) + perm = list(range(n)) + perm1_ins = perm1.insert + perm1_pop = perm1.pop + + while 1: + while r != 1: + count[r - 1] = r + r -= 1 + + if perm1[0] != 0 and perm1[m] != m: + perm = perm1[:] + flips_count = 0 + k = perm[0] + while k: + perm[:k + 1] = perm[k::-1] + flips_count += 1 + k = perm[0] + + if flips_count > max_flips: + max_flips = flips_count + + while r != n: + perm1_ins(r, perm1_pop(0)) + count[r] -= 1 + if count[r] > 0: + break + r += 1 + else: + return max_flips + + +if __name__ == "__main__": + #runner = pyperf.Runner() + arg = DEFAULT_ARG + #runner.bench_func('fannkuch', fannkuch, arg) + fannkuch(arg) From 38a36d75935a30af894f655bf4be5636b9428c32 Mon Sep 17 00:00:00 2001 From: Jeong Yunwon <jeong@youknowone.org> Date: Sun, 29 May 2022 02:22:34 +0900 Subject: [PATCH 7/9] Simplify AsSequence --- vm/src/builtins/bytearray.rs | 10 ++-------- vm/src/builtins/bytes.rs | 10 ++-------- vm/src/builtins/dict.rs | 31 +++++------------------------ vm/src/builtins/list.rs | 12 ++--------- vm/src/builtins/mappingproxy.rs | 12 +---------- vm/src/builtins/memory.rs | 10 ++-------- vm/src/builtins/range.rs | 35 +++++++++++++-------------------- vm/src/builtins/set.rs | 23 ++-------------------- vm/src/builtins/str.rs | 11 +---------- vm/src/builtins/tuple.rs | 13 ++---------- vm/src/stdlib/collections.rs | 11 +---------- vm/src/types/slot.rs | 9 ++++----- 12 files changed, 38 insertions(+), 149 deletions(-) diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 1a6d314463..94fcdb5401 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -36,7 +36,7 @@ use crate::{ TryFromBorrowedObject, TryFromObject, VirtualMachine, }; use bstr::ByteSlice; -use std::{borrow::Cow, mem::size_of}; +use std::mem::size_of; #[pyclass(module = false, name = "bytearray")] #[derive(Debug, Default)] @@ -784,13 +784,7 @@ impl AsMapping for PyByteArray { } impl AsSequence for PyByteArray { - fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PyByteArray { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), concat: Some(|seq, other, vm| { Self::sequence_downcast(seq) diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index e680eded4c..3ba95683b2 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -25,7 +25,7 @@ use crate::{ TryFromBorrowedObject, TryFromObject, VirtualMachine, }; use bstr::ByteSlice; -use std::{borrow::Cow, mem::size_of, ops::Deref}; +use std::{mem::size_of, ops::Deref}; #[pyclass(module = false, name = "bytes")] #[derive(Clone, Debug)] @@ -577,13 +577,7 @@ impl AsMapping for PyBytes { } impl AsSequence for PyBytes { - fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PyBytes { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), concat: Some(|seq, other, vm| { Self::sequence_downcast(seq) diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index dd2d3c6947..9d88d19187 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -25,7 +25,7 @@ use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, }; use rustpython_common::lock::PyMutex; -use std::{borrow::Cow, fmt}; +use std::fmt; pub type DictContentType = dictdatatype::Dict; @@ -478,13 +478,7 @@ impl AsMapping for PyDict { } impl AsSequence for PyDict { - fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PyDict { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { contains: Some(|seq, target, vm| Self::sequence_downcast(seq).entries.contains(vm, target)), ..*PySequenceMethods::not_implemented() }; @@ -1054,12 +1048,7 @@ impl Comparable for PyDictKeys { } impl AsSequence for PyDictKeys { - fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} -impl PyDictKeys { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), contains: Some(|seq, target, vm| { Self::sequence_downcast(seq) @@ -1108,12 +1097,7 @@ impl Comparable for PyDictItems { } impl AsSequence for PyDictItems { - fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} -impl PyDictItems { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), contains: Some(|seq, target, vm| { Self::sequence_downcast(seq) @@ -1130,12 +1114,7 @@ impl PyDictValues {} impl Unconstructible for PyDictValues {} impl AsSequence for PyDictValues { - fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} -impl PyDictValues { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), ..*PySequenceMethods::not_implemented() }; diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index 46ceaac074..139b358fcf 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -19,7 +19,7 @@ use crate::{ vm::VirtualMachine, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, }; -use std::{borrow::Cow, fmt, ops::DerefMut}; +use std::{fmt, ops::DerefMut}; /// Built-in mutable sequence. /// @@ -404,15 +404,7 @@ impl AsMapping for PyList { } impl AsSequence for PyList { - fn as_sequence( - _zelf: &crate::Py<Self>, - _vm: &VirtualMachine, - ) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHDOS) - } -} -impl PyList { - const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), concat: Some(|seq, other, vm| { Self::sequence_downcast(seq) diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs index 507375de86..d9760c798f 100644 --- a/vm/src/builtins/mappingproxy.rs +++ b/vm/src/builtins/mappingproxy.rs @@ -7,7 +7,6 @@ use crate::{ types::{AsMapping, AsSequence, Constructor, Iterable}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; -use std::borrow::Cow; #[pyclass(module = false, name = "mappingproxy")] #[derive(Debug)] @@ -157,16 +156,7 @@ impl AsMapping for PyMappingProxy { } impl AsSequence for PyMappingProxy { - fn as_sequence( - _zelf: &crate::Py<Self>, - _vm: &VirtualMachine, - ) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PyMappingProxy { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { contains: Some(|seq, target, vm| Self::sequence_downcast(seq)._contains(target, vm)), ..*PySequenceMethods::not_implemented() }; diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index cba6789f5b..cd5ba4ec91 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -24,7 +24,7 @@ use crate::{ }; use crossbeam_utils::atomic::AtomicCell; use itertools::Itertools; -use std::{borrow::Cow, cmp::Ordering, fmt::Debug, mem::ManuallyDrop, ops::Range}; +use std::{cmp::Ordering, fmt::Debug, mem::ManuallyDrop, ops::Range}; #[derive(FromArgs)] pub struct PyMemoryViewNewArgs { @@ -976,13 +976,7 @@ impl AsMapping for PyMemoryView { } impl AsSequence for PyMemoryView { - fn as_sequence(_zelf: &Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PyMemoryView { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, vm| { let zelf = Self::sequence_downcast(seq); zelf.try_not_released(vm)?; diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs index 5de3d78fae..04fa9a213c 100644 --- a/vm/src/builtins/range.rs +++ b/vm/src/builtins/range.rs @@ -16,7 +16,7 @@ use crossbeam_utils::atomic::AtomicCell; use num_bigint::{BigInt, Sign}; use num_integer::Integer; use num_traits::{One, Signed, ToPrimitive, Zero}; -use std::{borrow::Cow, cmp::max}; +use std::cmp::max; // Search flag passed to iter_search enum SearchType { @@ -389,20 +389,6 @@ impl PyRange { .try_to_primitive::<isize>(vm) .map(|x| x as usize) } - - const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods { - length: Some(|seq, vm| Self::sequence_downcast(seq).protocol_length(vm)), - item: Some(|seq, i, vm| { - Self::sequence_downcast(seq) - .get(&i.into()) - .map(|x| PyInt::from(x).into_ref(vm).into()) - .ok_or_else(|| vm.new_index_error("index out of range".to_owned())) - }), - contains: Some(|seq, needle, vm| { - Ok(Self::sequence_downcast(seq).contains(needle.to_owned(), vm)) - }), - ..*PySequenceMethods::not_implemented() - }; } impl AsMapping for PyRange { @@ -416,12 +402,19 @@ impl AsMapping for PyRange { } impl AsSequence for PyRange { - fn as_sequence( - _zelf: &crate::Py<Self>, - _vm: &VirtualMachine, - ) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHDOS) - } + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { + length: Some(|seq, vm| Self::sequence_downcast(seq).protocol_length(vm)), + item: Some(|seq, i, vm| { + Self::sequence_downcast(seq) + .get(&i.into()) + .map(|x| PyInt::from(x).into_ref(vm).into()) + .ok_or_else(|| vm.new_index_error("index out of range".to_owned())) + }), + contains: Some(|seq, needle, vm| { + Ok(Self::sequence_downcast(seq).contains(needle.to_owned(), vm)) + }), + ..*PySequenceMethods::not_implemented() + }; } impl Hashable for PyRange { diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index eac707c3ba..3c9ec107af 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -20,7 +20,6 @@ use crate::{ vm::VirtualMachine, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, }; -use std::borrow::Cow; use std::{fmt, ops::Deref}; pub type SetContentType = dictdatatype::Dict<()>; @@ -663,16 +662,7 @@ impl Initializer for PySet { } impl AsSequence for PySet { - fn as_sequence( - _zelf: &crate::Py<Self>, - _vm: &VirtualMachine, - ) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PySet { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), contains: Some(|seq, needle, vm| Self::sequence_downcast(seq).inner.contains(needle, vm)), ..*PySequenceMethods::not_implemented() @@ -904,16 +894,7 @@ impl PyFrozenSet { } impl AsSequence for PyFrozenSet { - fn as_sequence( - _zelf: &crate::Py<Self>, - _vm: &VirtualMachine, - ) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHODS) - } -} - -impl PyFrozenSet { - const SEQUENCE_METHODS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), contains: Some(|seq, needle, vm| Self::sequence_downcast(seq).inner.contains(needle, vm)), ..*PySequenceMethods::not_implemented() diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index 0562bcd548..a6da111a71 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -1309,16 +1309,7 @@ impl AsMapping for PyStr { } impl AsSequence for PyStr { - fn as_sequence( - _zelf: &Py<Self>, - _vm: &VirtualMachine, - ) -> std::borrow::Cow<'static, PySequenceMethods> { - std::borrow::Cow::Borrowed(&Self::SEQUENCE_METHDOS) - } -} - -impl PyStr { - const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), concat: Some(|seq, other, vm| { let zelf = Self::sequence_downcast(seq); diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs index 976a5aaefb..acf7a692eb 100644 --- a/vm/src/builtins/tuple.rs +++ b/vm/src/builtins/tuple.rs @@ -17,7 +17,7 @@ use crate::{ vm::VirtualMachine, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, }; -use std::{borrow::Cow, fmt, marker::PhantomData}; +use std::{fmt, marker::PhantomData}; /// tuple() -> empty tuple /// tuple(iterable) -> tuple initialized from iterable's items @@ -354,16 +354,7 @@ impl AsMapping for PyTuple { } impl AsSequence for PyTuple { - fn as_sequence( - _zelf: &crate::Py<Self>, - _vm: &VirtualMachine, - ) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::SEQUENCE_METHDOS) - } -} - -impl PyTuple { - const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), concat: Some(|seq, other, vm| { let zelf = Self::sequence_downcast(seq); diff --git a/vm/src/stdlib/collections.rs b/vm/src/stdlib/collections.rs index 508f1b4119..59efa9a674 100644 --- a/vm/src/stdlib/collections.rs +++ b/vm/src/stdlib/collections.rs @@ -518,16 +518,7 @@ mod _collections { } impl AsSequence for PyDeque { - fn as_sequence( - _zelf: &crate::Py<Self>, - _vm: &VirtualMachine, - ) -> std::borrow::Cow<'static, PySequenceMethods> { - std::borrow::Cow::Borrowed(&Self::SEQUENCE_METHDOS) - } - } - - impl PyDeque { - const SEQUENCE_METHDOS: PySequenceMethods = PySequenceMethods { + const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), concat: Some(|seq, other, vm| { Self::sequence_downcast(seq) diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 0a112c7d7e..287f0d6b69 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -963,15 +963,14 @@ pub trait AsMapping: PyPayload { #[pyimpl] pub trait AsSequence: PyPayload { + const AS_SEQUENCE: PySequenceMethods; + #[inline] #[pyslot] - fn slot_as_sequence(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - let zelf = unsafe { zelf.downcast_unchecked_ref::<Self>() }; - Self::as_sequence(zelf, vm) + fn as_sequence(_zelf: &PyObject, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { + Cow::Borrowed(&Self::AS_SEQUENCE) } - fn as_sequence(zelf: &Py<Self>, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods>; - fn sequence_downcast<'a>(seq: &'a PySequence) -> &'a Py<Self> { unsafe { seq.obj.downcast_unchecked_ref() } } From 4d02fe0aa66022242b36743f76601ecabdf26812 Mon Sep 17 00:00:00 2001 From: Jeong Yunwon <jeong@youknowone.org> Date: Sun, 29 May 2022 02:26:45 +0900 Subject: [PATCH 8/9] static_as_sequence_generic --- vm/src/builtins/bytes.rs | 2 +- vm/src/builtins/dict.rs | 8 +-- vm/src/builtins/mappingproxy.rs | 2 +- vm/src/builtins/memory.rs | 2 +- vm/src/builtins/range.rs | 2 +- vm/src/builtins/set.rs | 4 +- vm/src/builtins/str.rs | 2 +- vm/src/builtins/tuple.rs | 2 +- vm/src/protocol/sequence.rs | 26 +++---- vm/src/types/slot.rs | 116 ++++++++++++++++++-------------- 10 files changed, 90 insertions(+), 76 deletions(-) diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index 3ba95683b2..faa8dc5ac0 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -602,7 +602,7 @@ impl AsSequence for PyBytes { let other = <Either<PyBytesInner, PyIntRef>>::try_from_object(vm, other.to_owned())?; Self::sequence_downcast(seq).contains(other, vm) }), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index 9d88d19187..8be379ee1e 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -480,7 +480,7 @@ impl AsMapping for PyDict { impl AsSequence for PyDict { const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { contains: Some(|seq, target, vm| Self::sequence_downcast(seq).entries.contains(vm, target)), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } @@ -1056,7 +1056,7 @@ impl AsSequence for PyDictKeys { .entries .contains(vm, target) }), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } @@ -1105,7 +1105,7 @@ impl AsSequence for PyDictItems { .entries .contains(vm, target) }), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } @@ -1116,7 +1116,7 @@ impl Unconstructible for PyDictValues {} impl AsSequence for PyDictValues { const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs index d9760c798f..18495155ba 100644 --- a/vm/src/builtins/mappingproxy.rs +++ b/vm/src/builtins/mappingproxy.rs @@ -158,7 +158,7 @@ impl AsMapping for PyMappingProxy { impl AsSequence for PyMappingProxy { const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { contains: Some(|seq, target, vm| Self::sequence_downcast(seq)._contains(target, vm)), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index cd5ba4ec91..61b28e3bc5 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -987,7 +987,7 @@ impl AsSequence for PyMemoryView { zelf.try_not_released(vm)?; zelf.getitem_by_idx(i, vm) }), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs index 04fa9a213c..312a46efe0 100644 --- a/vm/src/builtins/range.rs +++ b/vm/src/builtins/range.rs @@ -413,7 +413,7 @@ impl AsSequence for PyRange { contains: Some(|seq, needle, vm| { Ok(Self::sequence_downcast(seq).contains(needle.to_owned(), vm)) }), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index 3c9ec107af..a637b3b6ef 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -665,7 +665,7 @@ impl AsSequence for PySet { const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), contains: Some(|seq, needle, vm| Self::sequence_downcast(seq).inner.contains(needle, vm)), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } @@ -897,7 +897,7 @@ impl AsSequence for PyFrozenSet { const AS_SEQUENCE: PySequenceMethods = PySequenceMethods { length: Some(|seq, _vm| Ok(Self::sequence_downcast(seq).len())), contains: Some(|seq, needle, vm| Self::sequence_downcast(seq).inner.contains(needle, vm)), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index a6da111a71..1700795d6a 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -1325,7 +1325,7 @@ impl AsSequence for PyStr { .map(|x| zelf.new_substr(x.to_string()).into_ref(vm).into()) }), contains: Some(|seq, needle, vm| Self::sequence_downcast(seq)._contains(needle, vm)), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs index acf7a692eb..59ea9a6447 100644 --- a/vm/src/builtins/tuple.rs +++ b/vm/src/builtins/tuple.rs @@ -378,7 +378,7 @@ impl AsSequence for PyTuple { let zelf = Self::sequence_downcast(seq); zelf._contains(needle, vm) }), - ..*PySequenceMethods::not_implemented() + ..PySequenceMethods::NOT_IMPLEMENTED }; } diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs index 3af7a0bf98..331d788695 100644 --- a/vm/src/protocol/sequence.rs +++ b/vm/src/protocol/sequence.rs @@ -30,9 +30,16 @@ pub struct PySequenceMethods { } impl PySequenceMethods { - pub const fn not_implemented() -> &'static Self { - &NOT_IMPLEMENTED - } + pub const NOT_IMPLEMENTED: PySequenceMethods = PySequenceMethods { + length: None, + concat: None, + repeat: None, + item: None, + ass_item: None, + contains: None, + inplace_concat: None, + inplace_repeat: None, + }; } impl Debug for PySequenceMethods { @@ -101,7 +108,7 @@ impl PySequence<'_> { return f(self.obj, vm); } } - Cow::Borrowed(PySequenceMethods::not_implemented()) + Cow::Borrowed(&PySequenceMethods::NOT_IMPLEMENTED) }) } @@ -393,14 +400,3 @@ impl PySequence<'_> { } } } - -const NOT_IMPLEMENTED: PySequenceMethods = PySequenceMethods { - length: None, - concat: None, - repeat: None, - item: None, - ass_item: None, - contains: None, - inplace_concat: None, - inplace_repeat: None, -}; diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 287f0d6b69..d8b5bd67c8 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -164,16 +164,6 @@ pub(crate) type InitFunc = fn(PyObjectRef, FuncArgs, &VirtualMachine) -> PyResul pub(crate) type DelFunc = fn(&PyObject, &VirtualMachine) -> PyResult<()>; pub(crate) type AsSequenceFunc = fn(&PyObject, &VirtualMachine) -> Cow<'static, PySequenceMethods>; -macro_rules! then_some_closure { - ($cond:expr, $closure:expr) => { - if $cond { - Some($closure) - } else { - None - } - }; -} - fn length_wrapper(obj: &PyObject, vm: &VirtualMachine) -> PyResult<usize> { let ret = vm.call_special_method(obj.to_owned(), identifier!(vm, __len__), ())?; let len = ret.payload::<PyInt>().ok_or_else(|| { @@ -282,45 +272,73 @@ fn as_mapping_generic(zelf: &PyObject, vm: &VirtualMachine) -> &'static PyMappin static_as_mapping_generic(has_length, has_subscript, has_ass_subscript) } -fn as_sequence_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { +pub(crate) fn static_as_sequence_generic( + has_length: bool, + has_ass_item: bool, +) -> &'static PySequenceMethods { + static METHODS: &[PySequenceMethods] = &[ + new_generic(false, false), + new_generic(true, false), + new_generic(false, true), + new_generic(true, true), + ]; + + fn length(seq: &PySequence, vm: &VirtualMachine) -> PyResult<usize> { + length_wrapper(seq.obj, vm) + } + fn item(seq: &PySequence, i: isize, vm: &VirtualMachine) -> PyResult { + vm.call_special_method(seq.obj.to_owned(), identifier!(vm, __getitem__), (i,)) + } + fn ass_item( + seq: &PySequence, + i: isize, + value: Option<PyObjectRef>, + vm: &VirtualMachine, + ) -> PyResult<()> { + match value { + Some(value) => vm + .call_special_method( + seq.obj.to_owned(), + identifier!(vm, __setitem__), + (i.to_pyobject(vm), value), + ) + .map(|_| Ok(()))?, + None => vm + .call_special_method( + seq.obj.to_owned(), + identifier!(vm, __delitem__), + (i.to_pyobject(vm),), + ) + .map(|_| Ok(()))?, + } + } + + const fn new_generic(has_length: bool, has_ass_item: bool) -> PySequenceMethods { + PySequenceMethods { + length: if has_length { Some(length) } else { None }, + item: Some(item), + ass_item: if has_ass_item { Some(ass_item) } else { None }, + ..PySequenceMethods::NOT_IMPLEMENTED + } + } + + let key = bool_int(has_length) | (bool_int(has_ass_item) << 1); + + &METHODS[key] +} + +fn as_sequence_generic(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { if !zelf.class().has_attr(identifier!(vm, __getitem__)) { - return Cow::Borrowed(PySequenceMethods::not_implemented()); - } - - Cow::Owned(PySequenceMethods { - length: then_some_closure!( - zelf.class().has_attr(identifier!(vm, __len__)), - |seq, vm| { length_wrapper(seq.obj, vm) } - ), - item: Some(|seq, i, vm| { - vm.call_special_method( - seq.obj.to_owned(), - identifier!(vm, __getitem__), - (i.to_pyobject(vm),), - ) - }), - ass_item: then_some_closure!( - zelf.class().has_attr(identifier!(vm, __setitem__)) - | zelf.class().has_attr(identifier!(vm, __delitem__)), - |seq, i, value, vm| match value { - Some(value) => vm - .call_special_method( - seq.obj.to_owned(), - identifier!(vm, __setitem__), - (i.to_pyobject(vm), value), - ) - .map(|_| Ok(()))?, - None => vm - .call_special_method( - seq.obj.to_owned(), - identifier!(vm, __delitem__), - (i.to_pyobject(vm),) - ) - .map(|_| Ok(()))?, - } - ), - ..Default::default() - }) + return Cow::Borrowed(&PySequenceMethods::NOT_IMPLEMENTED); + } + + let (has_length, has_ass_item) = ( + zelf.class().has_attr(identifier!(vm, __len__)), + zelf.class().has_attr(identifier!(vm, __setitem__)) + | zelf.class().has_attr(identifier!(vm, __delitem__)), + ); + + Cow::Borrowed(static_as_sequence_generic(has_length, has_ass_item)) } fn hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> { @@ -436,7 +454,7 @@ impl PyType { match name.as_str() { "__len__" | "__getitem__" | "__setitem__" | "__delitem__" => { update_slot!(as_mapping, as_mapping_generic); - update_slot!(as_sequence, as_sequence_wrapper); + update_slot!(as_sequence, as_sequence_generic); } "__hash__" => { update_slot!(hash, hash_wrapper); From 4546661105a353ee4f9908b9502bc66272908068 Mon Sep 17 00:00:00 2001 From: Jeong Yunwon <jeong@youknowone.org> Date: Sun, 29 May 2022 03:40:08 +0900 Subject: [PATCH 9/9] &'static PySequenceMethods --- vm/src/builtins/iter.rs | 10 +- vm/src/builtins/list.rs | 31 ++++++- vm/src/builtins/mappingproxy.rs | 2 +- vm/src/protocol/mapping.rs | 4 +- vm/src/protocol/object.rs | 39 ++++---- vm/src/protocol/sequence.rs | 159 +++++++++++++------------------- vm/src/types/slot.rs | 17 ++-- 7 files changed, 123 insertions(+), 139 deletions(-) diff --git a/vm/src/builtins/iter.rs b/vm/src/builtins/iter.rs index e1d588dfb3..98bdb4a414 100644 --- a/vm/src/builtins/iter.rs +++ b/vm/src/builtins/iter.rs @@ -14,7 +14,6 @@ use rustpython_common::{ lock::{PyMutex, PyRwLock, PyRwLockUpgradableReadGuard}, static_cell, }; -use std::borrow::Cow; /// Marks status of iterator. #[derive(Debug, Clone)] @@ -163,7 +162,7 @@ pub fn builtins_reversed(vm: &VirtualMachine) -> &PyObject { #[derive(Debug)] pub struct PySequenceIterator { // cached sequence methods - seq_methods: Cow<'static, PySequenceMethods>, + seq_methods: &'static PySequenceMethods, internal: PyMutex<PositionIterInternal<PyObjectRef>>, } @@ -177,9 +176,8 @@ impl PyPayload for PySequenceIterator { impl PySequenceIterator { pub fn new(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> { let seq = PySequence::try_protocol(obj.as_ref(), vm)?; - let seq_methods = seq.methods_cow(vm).clone(); Ok(Self { - seq_methods, + seq_methods: seq.methods, internal: PyMutex::new(PositionIterInternal::new(obj, 0)), }) } @@ -188,7 +186,7 @@ impl PySequenceIterator { fn length_hint(&self, vm: &VirtualMachine) -> PyObjectRef { let internal = self.internal.lock(); if let IterStatus::Active(obj) = &internal.status { - let seq = PySequence::with_methods(obj, self.seq_methods.clone()); + let seq = PySequence::with_methods(obj, self.seq_methods); seq.length(vm) .map(|x| PyInt::from(x).into_pyobject(vm)) .unwrap_or_else(|_| vm.ctx.not_implemented()) @@ -212,7 +210,7 @@ impl IterNextIterable for PySequenceIterator {} impl IterNext for PySequenceIterator { fn next(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> { zelf.internal.lock().next(|obj, pos| { - let seq = PySequence::with_methods(obj, zelf.seq_methods.clone()); + let seq = PySequence::with_methods(obj, zelf.seq_methods); PyIterReturn::from_getitem_result(seq.get_item(pos as isize, vm), vm) }) } diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index 139b358fcf..27a22ea284 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -139,7 +139,7 @@ impl PyList { } fn inplace_concat(zelf: &Py<Self>, other: &PyObject, vm: &VirtualMachine) -> PyObjectRef { - if let Ok(mut seq) = PySequence::from(other).extract_cloned(Ok, vm) { + if let Ok(mut seq) = extract_cloned(other, Ok, vm) { zelf.borrow_vec_mut().append(&mut seq); zelf.to_owned().into() } else { @@ -149,7 +149,7 @@ impl PyList { #[pymethod(magic)] fn iadd(zelf: PyRef<Self>, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - if let Ok(mut seq) = PySequence::from(other.as_ref()).extract_cloned(Ok, vm) { + if let Ok(mut seq) = extract_cloned(&*other, Ok, vm) { zelf.borrow_vec_mut().append(&mut seq); zelf.into() } else { @@ -215,7 +215,7 @@ impl PyList { match SequenceIndex::try_from_borrowed_object(vm, needle)? { SequenceIndex::Int(index) => self.borrow_vec_mut().set_item_by_index(vm, index, value), SequenceIndex::Slice(slice) => { - let sec = PySequence::from(value.as_ref()).extract_cloned(Ok, vm)?; + let sec = extract_cloned(&*value, Ok, vm)?; self.borrow_vec_mut().set_item_by_slice(vm, slice, &sec) } } @@ -352,6 +352,31 @@ impl PyList { } } +fn extract_cloned<F, R>(obj: &PyObject, mut f: F, vm: &VirtualMachine) -> PyResult<Vec<R>> +where + F: FnMut(PyObjectRef) -> PyResult<R>, +{ + use crate::builtins::PyTuple; + if let Some(tuple) = obj.payload_if_exact::<PyTuple>(vm) { + tuple.iter().map(|x| f(x.clone())).collect() + } else if let Some(list) = obj.payload_if_exact::<PyList>(vm) { + list.borrow_vec().iter().map(|x| f(x.clone())).collect() + } else { + let iter = obj.to_owned().get_iter(vm)?; + let iter = iter.iter::<PyObjectRef>(vm)?; + let len = PySequence::new(obj, vm) + .and_then(|seq| seq.length_opt(vm)) + .transpose()? + .unwrap_or(0); + let mut v = Vec::with_capacity(len); + for x in iter { + v.push(f(x?)?); + } + v.shrink_to_fit(); + Ok(v) + } +} + impl<'a> MutObjectSequenceOp<'a> for PyList { type Guard = PyMappedRwLockReadGuard<'a, [PyObjectRef]>; diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs index 18495155ba..d603bca740 100644 --- a/vm/src/builtins/mappingproxy.rs +++ b/vm/src/builtins/mappingproxy.rs @@ -91,7 +91,7 @@ impl PyMappingProxy { MappingProxyInner::Class(class) => Ok(key .as_interned_str(vm) .map_or(false, |key| class.attributes.read().contains_key(key))), - MappingProxyInner::Mapping(obj) => PySequence::from(obj.as_ref()).contains(key, vm), + MappingProxyInner::Mapping(mapping) => PySequence::contains(&*mapping, key, vm), } } diff --git a/vm/src/protocol/mapping.rs b/vm/src/protocol/mapping.rs index b19818cc12..48663e7a8f 100644 --- a/vm/src/protocol/mapping.rs +++ b/vm/src/protocol/mapping.rs @@ -69,9 +69,7 @@ impl PyMapping<'_> { // PyMapping::Check #[inline] pub fn check(obj: &PyObject, vm: &VirtualMachine) -> bool { - Self::find_methods(obj, vm) - .and_then(|m| m.subscript) - .is_some() + Self::find_methods(obj, vm).map_or(false, PyMappingMethods::check) } pub fn find_methods(obj: &PyObject, vm: &VirtualMachine) -> Option<&'static PyMappingMethods> { diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index 31cf76b47b..b475b78f02 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -522,8 +522,8 @@ impl PyObject { // int PyObject_TypeCheck(PyObject *o, PyTypeObject *type) pub fn length_opt(&self, vm: &VirtualMachine) -> Option<PyResult<usize>> { - PySequence::from(self) - .length_opt(vm) + PySequence::new(self, vm) + .and_then(|seq| seq.length_opt(vm)) .or_else(|| PyMapping::new(self, vm).and_then(|mapping| mapping.length_opt(vm))) } @@ -576,17 +576,17 @@ impl PyObject { return f(&mapping, &needle, Some(value), vm); } } - - let seq = PySequence::from(self); - if let Some(f) = seq.methods(vm).ass_item { - let i = needle.key_as_isize(vm)?; - f(&seq, i, Some(value), vm) - } else { - Err(vm.new_type_error(format!( - "'{}' does not support item assignment", - self.class() - ))) + if let Some(seq) = PySequence::new(self, vm) { + if let Some(f) = seq.methods.ass_item { + let i = needle.key_as_isize(vm)?; + return f(&seq, i, Some(value), vm); + } } + + Err(vm.new_type_error(format!( + "'{}' does not support item assignment", + self.class() + ))) } pub fn del_item<K: DictKey + ?Sized>(&self, needle: &K, vm: &VirtualMachine) -> PyResult<()> { @@ -600,14 +600,13 @@ impl PyObject { return f(&mapping, &needle, None, vm); } } - - let seq = PySequence::from(self); - - if let Some(f) = seq.methods(vm).ass_item { - let i = needle.key_as_isize(vm)?; - f(&seq, i, None, vm) - } else { - Err(vm.new_type_error(format!("'{}' does not support item deletion", self.class()))) + if let Some(seq) = PySequence::new(self, vm) { + if let Some(f) = seq.methods.ass_item { + let i = needle.key_as_isize(vm)?; + return f(&seq, i, None, vm); + } } + + Err(vm.new_type_error(format!("'{}' does not support item deletion", self.class()))) } } diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs index 331d788695..fd87c8a1ff 100644 --- a/vm/src/protocol/sequence.rs +++ b/vm/src/protocol/sequence.rs @@ -1,22 +1,17 @@ use crate::{ builtins::{PyList, PyListRef, PySlice, PyTuple, PyTupleRef}, - common::lock::OnceCell, convert::ToPyObject, function::PyArithmeticValue, protocol::PyMapping, AsObject, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; use itertools::Itertools; -use std::{ - borrow::{Borrow, Cow}, - fmt::Debug, -}; +use std::fmt::Debug; // Sequence Protocol // https://docs.python.org/3/c-api/sequence.html #[allow(clippy::type_complexity)] -#[derive(Default, Clone)] pub struct PySequenceMethods { pub length: Option<fn(&PySequence, &VirtualMachine) -> PyResult<usize>>, pub concat: Option<fn(&PySequence, &PyObject, &VirtualMachine) -> PyResult>, @@ -40,6 +35,10 @@ impl PySequenceMethods { inplace_concat: None, inplace_repeat: None, }; + + pub fn check(&self) -> bool { + self.item.is_some() + } } impl Debug for PySequenceMethods { @@ -59,61 +58,50 @@ impl Debug for PySequenceMethods { pub struct PySequence<'a> { pub obj: &'a PyObject, - // some function don't need it, so lazy initialize - methods: OnceCell<Cow<'static, PySequenceMethods>>, + pub methods: &'static PySequenceMethods, } -impl<'a> From<&'a PyObject> for PySequence<'a> { - fn from(obj: &'a PyObject) -> Self { - Self { - obj, - methods: OnceCell::new(), - } +impl<'a> PySequence<'a> { + #[inline] + pub fn new(obj: &'a PyObject, vm: &VirtualMachine) -> Option<Self> { + let methods = Self::find_methods(obj, vm)?; + Some(Self { obj, methods }) } -} -impl<'a> PySequence<'a> { - pub fn with_methods(obj: &'a PyObject, methods: Cow<'static, PySequenceMethods>) -> Self { - Self { - obj, - methods: OnceCell::from(methods), - } + #[inline] + pub fn with_methods(obj: &'a PyObject, methods: &'static PySequenceMethods) -> Self { + Self { obj, methods } } pub fn try_protocol(obj: &'a PyObject, vm: &VirtualMachine) -> PyResult<Self> { - let zelf = Self::from(obj); - if zelf.check(vm) { - Ok(zelf) - } else { - Err(vm.new_type_error(format!("'{}' is not a sequence", obj.class()))) + if let Some(methods) = Self::find_methods(obj, vm) { + if methods.check() { + return Ok(Self::with_methods(obj, methods)); + } } + + Err(vm.new_type_error(format!("'{}' is not a sequence", obj.class()))) } } impl PySequence<'_> { // PySequence_Check - pub fn check(&self, vm: &VirtualMachine) -> bool { - self.methods(vm).item.is_some() + pub fn check(obj: &PyObject, vm: &VirtualMachine) -> bool { + Self::find_methods(obj, vm).map_or(false, PySequenceMethods::check) } - pub fn methods(&self, vm: &VirtualMachine) -> &PySequenceMethods { - self.methods_cow(vm).borrow() - } - - pub fn methods_cow(&self, vm: &VirtualMachine) -> &Cow<'static, PySequenceMethods> { - self.methods.get_or_init(|| { - let cls = self.obj.class(); - if !cls.is(vm.ctx.types.dict_type) { - if let Some(f) = cls.mro_find_map(|x| x.slots.as_sequence.load()) { - return f(self.obj, vm); - } + pub fn find_methods(obj: &PyObject, vm: &VirtualMachine) -> Option<&'static PySequenceMethods> { + let cls = obj.class(); + if !cls.is(vm.ctx.types.dict_type) { + if let Some(f) = cls.mro_find_map(|x| x.slots.as_sequence.load()) { + return Some(f(obj, vm)); } - Cow::Borrowed(&PySequenceMethods::NOT_IMPLEMENTED) - }) + } + None } pub fn length_opt(&self, vm: &VirtualMachine) -> Option<PyResult<usize>> { - self.methods(vm).length.map(|f| f(self, vm)) + self.methods.length.map(|f| f(self, vm)) } pub fn length(&self, vm: &VirtualMachine) -> PyResult<usize> { @@ -126,12 +114,12 @@ impl PySequence<'_> { } pub fn concat(&self, other: &PyObject, vm: &VirtualMachine) -> PyResult { - if let Some(f) = self.methods(vm).concat { + if let Some(f) = self.methods.concat { return f(self, other, vm); } // if both arguments apear to be sequences, try fallback to __add__ - if self.check(vm) && PySequence::from(other).check(vm) { + if PySequence::check(other, vm) { let ret = vm._add(self.obj, other)?; if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { return Ok(ret); @@ -144,30 +132,29 @@ impl PySequence<'_> { } pub fn repeat(&self, n: usize, vm: &VirtualMachine) -> PyResult { - if let Some(f) = self.methods(vm).repeat { + if let Some(f) = self.methods.repeat { return f(self, n, vm); } // try fallback to __mul__ - if self.check(vm) { - let ret = vm._mul(self.obj, &n.to_pyobject(vm))?; - if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { - return Ok(ret); - } + let ret = vm._mul(self.obj, &n.to_pyobject(vm))?; + if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { + return Ok(ret); } + Err(vm.new_type_error(format!("'{}' object can't be repeated", self.obj.class()))) } pub fn inplace_concat(&self, other: &PyObject, vm: &VirtualMachine) -> PyResult { - if let Some(f) = self.methods(vm).inplace_concat { + if let Some(f) = self.methods.inplace_concat { return f(self, other, vm); } - if let Some(f) = self.methods(vm).concat { + if let Some(f) = self.methods.concat { return f(self, other, vm); } // if both arguments apear to be sequences, try fallback to __iadd__ - if self.check(vm) && PySequence::from(other).check(vm) { + if PySequence::check(other, vm) { let ret = vm._iadd(self.obj, other)?; if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { return Ok(ret); @@ -180,24 +167,23 @@ impl PySequence<'_> { } pub fn inplace_repeat(&self, n: usize, vm: &VirtualMachine) -> PyResult { - if let Some(f) = self.methods(vm).inplace_repeat { + if let Some(f) = self.methods.inplace_repeat { return f(self, n, vm); } - if let Some(f) = self.methods(vm).repeat { + if let Some(f) = self.methods.repeat { return f(self, n, vm); } - if self.check(vm) { - let ret = vm._imul(self.obj, &n.to_pyobject(vm))?; - if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { - return Ok(ret); - } + let ret = vm._imul(self.obj, &n.to_pyobject(vm))?; + if let PyArithmeticValue::Implemented(ret) = PyArithmeticValue::from_object(vm, ret) { + return Ok(ret); } + Err(vm.new_type_error(format!("'{}' object can't be repeated", self.obj.class()))) } pub fn get_item(&self, i: isize, vm: &VirtualMachine) -> PyResult { - if let Some(f) = self.methods(vm).item { + if let Some(f) = self.methods.item { return f(self, i, vm); } Err(vm.new_type_error(format!( @@ -207,7 +193,7 @@ impl PySequence<'_> { } fn _ass_item(&self, i: isize, value: Option<PyObjectRef>, vm: &VirtualMachine) -> PyResult<()> { - if let Some(f) = self.methods(vm).ass_item { + if let Some(f) = self.methods.ass_item { return f(self, i, value, vm); } Err(vm.new_type_error(format!( @@ -301,23 +287,6 @@ impl PySequence<'_> { Ok(list) } - pub fn contains(&self, target: &PyObject, vm: &VirtualMachine) -> PyResult<bool> { - if let Some(f) = self.methods(vm).contains { - return f(self, target, vm); - } - - let iter = self.obj.to_owned().get_iter(vm)?; - let iter = iter.iter::<PyObjectRef>(vm)?; - - for elem in iter { - let elem = elem?; - if vm.bool_eq(&elem, target)? { - return Ok(true); - } - } - Ok(false) - } - pub fn count(&self, target: &PyObject, vm: &VirtualMachine) -> PyResult<usize> { let mut n = 0; @@ -379,24 +348,22 @@ impl PySequence<'_> { } } - pub fn extract_cloned<F, R>(&self, mut f: F, vm: &VirtualMachine) -> PyResult<Vec<R>> - where - F: FnMut(PyObjectRef) -> PyResult<R>, - { - if let Some(tuple) = self.obj.payload_if_exact::<PyTuple>(vm) { - tuple.iter().map(|x| f(x.clone())).collect() - } else if let Some(list) = self.obj.payload_if_exact::<PyList>(vm) { - list.borrow_vec().iter().map(|x| f(x.clone())).collect() - } else { - let iter = self.obj.to_owned().get_iter(vm)?; - let iter = iter.iter::<PyObjectRef>(vm)?; - let len = self.length(vm).unwrap_or(0); - let mut v = Vec::with_capacity(len); - for x in iter { - v.push(f(x?)?); + pub fn contains(zelf: &PyObject, target: &PyObject, vm: &VirtualMachine) -> PyResult<bool> { + if let Some(seq) = PySequence::new(zelf, vm) { + if let Some(contains) = seq.methods.contains { + return contains(&seq, target, vm); + } + } + + let iter = zelf.to_owned().get_iter(vm)?; + let iter = iter.iter::<PyObjectRef>(vm)?; + + for elem in iter { + let elem = elem?; + if vm.bool_eq(&elem, target)? { + return Ok(true); } - v.shrink_to_fit(); - Ok(v) } + Ok(false) } } diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index d8b5bd67c8..245e7a81c8 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -14,10 +14,7 @@ use crate::{ }; use crossbeam_utils::atomic::AtomicCell; use num_traits::{Signed, ToPrimitive}; -use std::{ - borrow::{Borrow, Cow}, - cmp::Ordering, -}; +use std::{borrow::Borrow, cmp::Ordering}; // The corresponding field in CPython is `tp_` prefixed. // e.g. name -> tp_name @@ -162,7 +159,7 @@ pub(crate) type DescrSetFunc = pub(crate) type NewFunc = fn(PyTypeRef, FuncArgs, &VirtualMachine) -> PyResult; pub(crate) type InitFunc = fn(PyObjectRef, FuncArgs, &VirtualMachine) -> PyResult<()>; pub(crate) type DelFunc = fn(&PyObject, &VirtualMachine) -> PyResult<()>; -pub(crate) type AsSequenceFunc = fn(&PyObject, &VirtualMachine) -> Cow<'static, PySequenceMethods>; +pub(crate) type AsSequenceFunc = fn(&PyObject, &VirtualMachine) -> &'static PySequenceMethods; fn length_wrapper(obj: &PyObject, vm: &VirtualMachine) -> PyResult<usize> { let ret = vm.call_special_method(obj.to_owned(), identifier!(vm, __len__), ())?; @@ -327,9 +324,9 @@ pub(crate) fn static_as_sequence_generic( &METHODS[key] } -fn as_sequence_generic(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { +fn as_sequence_generic(zelf: &PyObject, vm: &VirtualMachine) -> &'static PySequenceMethods { if !zelf.class().has_attr(identifier!(vm, __getitem__)) { - return Cow::Borrowed(&PySequenceMethods::NOT_IMPLEMENTED); + return &PySequenceMethods::NOT_IMPLEMENTED; } let (has_length, has_ass_item) = ( @@ -338,7 +335,7 @@ fn as_sequence_generic(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PyS | zelf.class().has_attr(identifier!(vm, __delitem__)), ); - Cow::Borrowed(static_as_sequence_generic(has_length, has_ass_item)) + static_as_sequence_generic(has_length, has_ass_item) } fn hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> { @@ -985,8 +982,8 @@ pub trait AsSequence: PyPayload { #[inline] #[pyslot] - fn as_sequence(_zelf: &PyObject, _vm: &VirtualMachine) -> Cow<'static, PySequenceMethods> { - Cow::Borrowed(&Self::AS_SEQUENCE) + fn as_sequence(_zelf: &PyObject, _vm: &VirtualMachine) -> &'static PySequenceMethods { + &Self::AS_SEQUENCE } fn sequence_downcast<'a>(seq: &'a PySequence) -> &'a Py<Self> {