Merge pull request #1712 from youknowone/basetype

Add Py_TPFLAGS_BASETYPE support to restrict subclass
This commit is contained in:
Jeong YunWon
2020-01-30 17:21:12 +09:00
committed by GitHub
39 changed files with 529 additions and 349 deletions

16
Cargo.lock generated
View File

@@ -152,7 +152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.2.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -279,7 +279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -291,7 +291,7 @@ name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -788,7 +788,7 @@ name = "nix"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -800,7 +800,7 @@ name = "nix"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1291,7 +1291,7 @@ name = "rustpython-bytecode"
version = "0.1.1"
dependencies = [
"bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lz4-compress 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num-bigint 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-complex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1344,7 +1344,7 @@ dependencies = [
"adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"arr_macro 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"caseless 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2212,7 +2212,7 @@ dependencies = [
"checksum bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8ab639324e3ee8774d296864fbc0dbbb256cf1a41c490b94cba90c082915f92"
"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80"
"checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb"
"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330"
"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182"
"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"

View File

@@ -187,8 +187,7 @@ impl<'a> ClassItemMeta<'a> {
} else {
let sig_name = self.sig.ident.to_string();
let name = if setter {
if sig_name.starts_with("set_") {
let name = &sig_name["set_".len()..];
if let Some(name) = strip_prefix(&sig_name, "set_") {
if name.is_empty() {
bail_span!(
&self.sig.ident,
@@ -300,9 +299,8 @@ impl Class {
}
let slot_ident = if nesteds.is_empty() {
let ident_str = sig.ident.to_string();
if ident_str.starts_with("tp_") {
let slot_name = &ident_str[3..];
proc_macro2::Ident::new(slot_name, sig.ident.span())
if let Some(stripped) = strip_prefix(&ident_str, "tp_") {
proc_macro2::Ident::new(stripped, sig.ident.span())
} else {
sig.ident.clone()
}
@@ -373,10 +371,7 @@ struct ItemSig<'a> {
sig: &'a Signature,
}
fn extract_impl_items(
attr: AttributeArgs,
mut items: Vec<ItemSig>,
) -> Result<TokenStream2, Diagnostic> {
fn extract_impl_items(mut items: Vec<ItemSig>) -> Result<TokenStream2, Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = Vec::new();
let mut class = Class::default();
@@ -470,33 +465,68 @@ fn extract_impl_items(
Diagnostic::from_vec(diagnostics)?;
Ok(quote! {
#(#methods)*
#(#properties)*
})
}
fn extract_impl_attrs(attr: AttributeArgs) -> Result<(TokenStream2, TokenStream2), Diagnostic> {
let mut withs = Vec::new();
let mut flags = vec![quote! { ::rustpython_vm::slots::PyTpFlags::DEFAULT.bits() }];
for attr in attr {
match attr {
NestedMeta::Meta(Meta::List(syn::MetaList { path, nested, .. }))
if path_eq(&path, "with") =>
{
for meta in nested {
match meta {
NestedMeta::Meta(Meta::Path(path)) => {
withs.push(quote! {
<Self as #path>::__extend_py_class(ctx, class);
});
NestedMeta::Meta(Meta::List(syn::MetaList { path, nested, .. })) => {
if path_eq(&path, "with") {
for meta in nested {
match meta {
NestedMeta::Meta(Meta::Path(path)) => {
withs.push(quote! {
<Self as #path>::__extend_py_class(ctx, class);
});
}
meta => {
bail_span!(meta, "#[pyimpl(with(...))] arguments should be paths")
}
}
meta => bail_span!(meta, "#[pyimpl(with(...))] arguments should be paths"),
}
} else if path_eq(&path, "flags") {
for meta in nested {
match meta {
NestedMeta::Meta(Meta::Path(path)) => {
if let Some(ident) = path.get_ident() {
flags.push(quote! {
| ::rustpython_vm::slots::PyTpFlags::#ident.bits()
});
} else {
bail_span!(
path,
"#[pyimpl(flags(...))] arguments should be ident"
)
}
}
meta => {
bail_span!(meta, "#[pyimpl(flags(...))] arguments should be ident")
}
}
}
} else {
bail_span!(path, "Unknown pyimpl attribute")
}
}
attr => bail_span!(attr, "Unknown pyimpl attribute"),
}
}
Ok(quote! {
#(#methods)*
#(#properties)*
#(#withs)*
})
Ok((
quote! {
#(#withs)*
},
quote! {
#(#flags)*
},
))
}
pub fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diagnostic> {
@@ -512,16 +542,20 @@ pub fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diag
_ => None,
})
.collect();
let extend_impl = extract_impl_items(attr, items)?;
let extend_impl = extract_impl_items(items)?;
let (with_impl, flags) = extract_impl_attrs(attr)?;
let ty = &imp.self_ty;
let ret = quote! {
#imp
impl ::rustpython_vm::pyobject::PyClassImpl for #ty {
const TP_FLAGS: ::rustpython_vm::slots::PyTpFlags = ::rustpython_vm::slots::PyTpFlags::from_bits_truncate(#flags);
fn impl_extend_class(
ctx: &::rustpython_vm::pyobject::PyContext,
class: &::rustpython_vm::obj::objtype::PyClassRef,
) {
#extend_impl
#with_impl
}
}
};
@@ -538,7 +572,7 @@ pub fn impl_pyimpl(attr: AttributeArgs, item: Item) -> Result<TokenStream2, Diag
_ => None,
})
.collect();
let extend_impl = extract_impl_items(attr, items)?;
let extend_impl = extract_impl_items(items)?;
let item = parse_quote! {
fn __extend_py_class(
ctx: &::rustpython_vm::pyobject::PyContext,
@@ -703,3 +737,11 @@ pub fn impl_pystruct_sequence(attr: AttributeArgs, item: Item) -> Result<TokenSt
};
Ok(ret)
}
fn strip_prefix<'a>(s: &'a str, prefix: &str) -> Option<&'a str> {
if s.starts_with(prefix) {
Some(&s[prefix.len()..])
} else {
None
}
}

View File

@@ -60,7 +60,7 @@ unicode-casing = "0.1"
unic = "0.9"
unic-common = "0.9"
maplit = "1.0"
bitflags = "1.1"
bitflags = "1.2.1"
libc = "0.2"
nix = "0.16.0"
arr_macro = "0.1.2"

View File

@@ -9,6 +9,7 @@ use crate::pyobject::{
PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
TypeProtocol,
};
use crate::slots::PyTpFlags;
use crate::types::create_type;
use crate::VirtualMachine;
use itertools::Itertools;
@@ -43,7 +44,7 @@ impl PyValue for PyBaseException {
}
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyBaseException {
pub(crate) fn new(args: Vec<PyObjectRef>, vm: &VirtualMachine) -> PyBaseException {
PyBaseException {
@@ -446,73 +447,78 @@ pub struct ExceptionZoo {
impl ExceptionZoo {
pub fn new(type_type: &PyClassRef, object_type: &PyClassRef) -> Self {
let create_exception_type = |name: &str, base: &PyClassRef| {
let typ = create_type(name, type_type, base);
typ.slots.borrow_mut().flags |= PyTpFlags::BASETYPE;
typ
};
// Sorted By Hierarchy then alphabetized.
let base_exception_type = create_type("BaseException", &type_type, &object_type);
let exception_type = create_type("Exception", &type_type, &base_exception_type);
let arithmetic_error = create_type("ArithmeticError", &type_type, &exception_type);
let assertion_error = create_type("AssertionError", &type_type, &exception_type);
let attribute_error = create_type("AttributeError", &type_type, &exception_type);
let import_error = create_type("ImportError", &type_type, &exception_type);
let lookup_error = create_type("LookupError", &type_type, &exception_type);
let index_error = create_type("IndexError", &type_type, &lookup_error);
let key_error = create_type("KeyError", &type_type, &lookup_error);
let name_error = create_type("NameError", &type_type, &exception_type);
let runtime_error = create_type("RuntimeError", &type_type, &exception_type);
let reference_error = create_type("ReferenceError", &type_type, &exception_type);
let stop_iteration = create_type("StopIteration", &type_type, &exception_type);
let stop_async_iteration = create_type("StopAsyncIteration", &type_type, &exception_type);
let syntax_error = create_type("SyntaxError", &type_type, &exception_type);
let system_error = create_type("SystemError", &type_type, &exception_type);
let type_error = create_type("TypeError", &type_type, &exception_type);
let value_error = create_type("ValueError", &type_type, &exception_type);
let overflow_error = create_type("OverflowError", &type_type, &arithmetic_error);
let zero_division_error = create_type("ZeroDivisionError", &type_type, &arithmetic_error);
let module_not_found_error = create_type("ModuleNotFoundError", &type_type, &import_error);
let not_implemented_error = create_type("NotImplementedError", &type_type, &runtime_error);
let recursion_error = create_type("RecursionError", &type_type, &runtime_error);
let eof_error = create_type("EOFError", &type_type, &exception_type);
let indentation_error = create_type("IndentationError", &type_type, &syntax_error);
let tab_error = create_type("TabError", &type_type, &indentation_error);
let unicode_error = create_type("UnicodeError", &type_type, &value_error);
let unicode_decode_error = create_type("UnicodeDecodeError", &type_type, &unicode_error);
let unicode_encode_error = create_type("UnicodeEncodeError", &type_type, &unicode_error);
let base_exception_type = create_exception_type("BaseException", &object_type);
let exception_type = create_exception_type("Exception", &base_exception_type);
let arithmetic_error = create_exception_type("ArithmeticError", &exception_type);
let assertion_error = create_exception_type("AssertionError", &exception_type);
let attribute_error = create_exception_type("AttributeError", &exception_type);
let import_error = create_exception_type("ImportError", &exception_type);
let lookup_error = create_exception_type("LookupError", &exception_type);
let index_error = create_exception_type("IndexError", &lookup_error);
let key_error = create_exception_type("KeyError", &lookup_error);
let name_error = create_exception_type("NameError", &exception_type);
let runtime_error = create_exception_type("RuntimeError", &exception_type);
let reference_error = create_exception_type("ReferenceError", &exception_type);
let stop_iteration = create_exception_type("StopIteration", &exception_type);
let stop_async_iteration = create_exception_type("StopAsyncIteration", &exception_type);
let syntax_error = create_exception_type("SyntaxError", &exception_type);
let system_error = create_exception_type("SystemError", &exception_type);
let type_error = create_exception_type("TypeError", &exception_type);
let value_error = create_exception_type("ValueError", &exception_type);
let overflow_error = create_exception_type("OverflowError", &arithmetic_error);
let zero_division_error = create_exception_type("ZeroDivisionError", &arithmetic_error);
let module_not_found_error = create_exception_type("ModuleNotFoundError", &import_error);
let not_implemented_error = create_exception_type("NotImplementedError", &runtime_error);
let recursion_error = create_exception_type("RecursionError", &runtime_error);
let eof_error = create_exception_type("EOFError", &exception_type);
let indentation_error = create_exception_type("IndentationError", &syntax_error);
let tab_error = create_exception_type("TabError", &indentation_error);
let unicode_error = create_exception_type("UnicodeError", &value_error);
let unicode_decode_error = create_exception_type("UnicodeDecodeError", &unicode_error);
let unicode_encode_error = create_exception_type("UnicodeEncodeError", &unicode_error);
let unicode_translate_error =
create_type("UnicodeTranslateError", &type_type, &unicode_error);
let memory_error = create_type("MemoryError", &type_type, &exception_type);
create_exception_type("UnicodeTranslateError", &unicode_error);
let memory_error = create_exception_type("MemoryError", &exception_type);
// os errors
let os_error = create_type("OSError", &type_type, &exception_type);
let os_error = create_exception_type("OSError", &exception_type);
let file_not_found_error = create_type("FileNotFoundError", &type_type, &os_error);
let permission_error = create_type("PermissionError", &type_type, &os_error);
let file_exists_error = create_type("FileExistsError", &type_type, &os_error);
let blocking_io_error = create_type("BlockingIOError", &type_type, &os_error);
let interrupted_error = create_type("InterruptedError", &type_type, &os_error);
let connection_error = create_type("ConnectionError", &type_type, &os_error);
let file_not_found_error = create_exception_type("FileNotFoundError", &os_error);
let permission_error = create_exception_type("PermissionError", &os_error);
let file_exists_error = create_exception_type("FileExistsError", &os_error);
let blocking_io_error = create_exception_type("BlockingIOError", &os_error);
let interrupted_error = create_exception_type("InterruptedError", &os_error);
let connection_error = create_exception_type("ConnectionError", &os_error);
let connection_reset_error =
create_type("ConnectionResetError", &type_type, &connection_error);
create_exception_type("ConnectionResetError", &connection_error);
let connection_refused_error =
create_type("ConnectionRefusedError", &type_type, &connection_error);
create_exception_type("ConnectionRefusedError", &connection_error);
let connection_aborted_error =
create_type("ConnectionAbortedError", &type_type, &connection_error);
let broken_pipe_error = create_type("BrokenPipeError", &type_type, &connection_error);
create_exception_type("ConnectionAbortedError", &connection_error);
let broken_pipe_error = create_exception_type("BrokenPipeError", &connection_error);
let warning = create_type("Warning", &type_type, &exception_type);
let bytes_warning = create_type("BytesWarning", &type_type, &warning);
let unicode_warning = create_type("UnicodeWarning", &type_type, &warning);
let deprecation_warning = create_type("DeprecationWarning", &type_type, &warning);
let warning = create_exception_type("Warning", &exception_type);
let bytes_warning = create_exception_type("BytesWarning", &warning);
let unicode_warning = create_exception_type("UnicodeWarning", &warning);
let deprecation_warning = create_exception_type("DeprecationWarning", &warning);
let pending_deprecation_warning =
create_type("PendingDeprecationWarning", &type_type, &warning);
let future_warning = create_type("FutureWarning", &type_type, &warning);
let import_warning = create_type("ImportWarning", &type_type, &warning);
let syntax_warning = create_type("SyntaxWarning", &type_type, &warning);
let resource_warning = create_type("ResourceWarning", &type_type, &warning);
let runtime_warning = create_type("RuntimeWarning", &type_type, &warning);
let user_warning = create_type("UserWarning", &type_type, &warning);
create_exception_type("PendingDeprecationWarning", &warning);
let future_warning = create_exception_type("FutureWarning", &warning);
let import_warning = create_exception_type("ImportWarning", &warning);
let syntax_warning = create_exception_type("SyntaxWarning", &warning);
let resource_warning = create_exception_type("ResourceWarning", &warning);
let runtime_warning = create_exception_type("RuntimeWarning", &warning);
let user_warning = create_exception_type("UserWarning", &warning);
let keyboard_interrupt = create_type("KeyboardInterrupt", &type_type, &base_exception_type);
let generator_exit = create_type("GeneratorExit", &type_type, &base_exception_type);
let system_exit = create_type("SystemExit", &type_type, &base_exception_type);
let keyboard_interrupt = create_exception_type("KeyboardInterrupt", &base_exception_type);
let generator_exit = create_exception_type("GeneratorExit", &base_exception_type);
let system_exit = create_exception_type("SystemExit", &base_exception_type);
ExceptionZoo {
arithmetic_error,

View File

@@ -72,7 +72,7 @@ mod pyhash;
pub mod pyobject;
pub mod scope;
mod sequence;
mod slots;
pub mod slots;
pub mod stdlib;
mod sysmodule;
pub mod types;

View File

@@ -139,6 +139,8 @@ macro_rules! py_class {
( $ctx:expr, $class_name:expr, $class_base:expr, { $($name:tt => $value:expr),* $(,)* }) => {
{
let py_class = $ctx.new_class($class_name, $class_base);
// FIXME: setting flag here probably wrong
py_class.slots.borrow_mut().flags |= $crate::slots::PyTpFlags::BASETYPE;
$crate::extend_class!($ctx, &py_class, { $($name => $value),* });
py_class
}

View File

@@ -87,7 +87,7 @@ pub(crate) fn init(context: &PyContext) {
PyByteArrayIterator::extend_class(context, &context.types.bytearrayiterator_type);
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyByteArray {
#[pyslot]
fn tp_new(

View File

@@ -89,7 +89,7 @@ pub(crate) fn init(context: &PyContext) {
PyBytesIterator::extend_class(context, &context.types.bytesiterator_type);
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyBytes {
#[pyslot]
fn tp_new(

View File

@@ -59,7 +59,7 @@ impl PyBuiltinDescriptor for PyClassMethod {
}
}
#[pyimpl(with(PyBuiltinDescriptor))]
#[pyimpl(with(PyBuiltinDescriptor), flags(BASETYPE))]
impl PyClassMethod {
#[pyslot]
fn tp_new(

View File

@@ -54,7 +54,7 @@ fn try_complex(value: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<Comp
Ok(r)
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyComplex {
#[pyproperty(name = "real")]
fn real(&self, _vm: &VirtualMachine) -> f64 {

View File

@@ -17,6 +17,7 @@ use std::mem::size_of;
pub type DictContentType = dictdatatype::Dict;
#[pyclass]
#[derive(Default)]
pub struct PyDict {
entries: RefCell<DictContentType>,
@@ -37,8 +38,10 @@ impl PyValue for PyDict {
}
// Python dict methods:
#[pyimpl(flags(BASETYPE))]
impl PyDictRef {
fn new(
#[pyslot]
fn tp_new(
class: PyClassRef,
dict_obj: OptionalArg<PyObjectRef>,
kwargs: KwArgs,
@@ -100,6 +103,7 @@ impl PyDictRef {
Ok(())
}
#[pyclassmethod]
fn fromkeys(
class: PyClassRef,
iterable: PyIterable,
@@ -116,6 +120,7 @@ impl PyDictRef {
PyDict { entries }.into_ref_with_type(vm, class)
}
#[pymethod(magic)]
fn bool(self, _vm: &VirtualMachine) -> bool {
!self.entries.borrow().is_empty()
}
@@ -142,6 +147,7 @@ impl PyDictRef {
Ok(true)
}
#[pymethod(magic)]
fn eq(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if let Some(other) = other.payload::<PyDict>() {
let eq = self.inner_eq(other, vm)?;
@@ -151,6 +157,7 @@ impl PyDictRef {
}
}
#[pymethod(magic)]
fn ne(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if let Some(other) = other.payload::<PyDict>() {
let neq = !self.inner_eq(other, vm)?;
@@ -160,14 +167,17 @@ impl PyDictRef {
}
}
#[pymethod(magic)]
fn len(self, _vm: &VirtualMachine) -> usize {
self.entries.borrow().len()
}
#[pymethod(magic)]
fn sizeof(self, _vm: &VirtualMachine) -> usize {
size_of::<Self>() + self.entries.borrow().sizeof()
}
#[pymethod(magic)]
fn repr(self, vm: &VirtualMachine) -> PyResult<String> {
let s = if let Some(_guard) = ReprGuard::enter(self.as_object()) {
let mut str_parts = vec![];
@@ -184,40 +194,43 @@ impl PyDictRef {
Ok(s)
}
#[pymethod(magic)]
fn contains(self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
self.entries.borrow().contains(vm, &key)
}
fn inner_delitem(self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
#[pymethod(magic)]
fn delitem(self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
self.entries.borrow_mut().delete(vm, &key)
}
#[pymethod]
fn clear(self, _vm: &VirtualMachine) {
self.entries.borrow_mut().clear()
}
#[pymethod(magic)]
fn iter(self, _vm: &VirtualMachine) -> PyDictKeyIterator {
PyDictKeyIterator::new(self)
}
#[pymethod]
fn keys(self, _vm: &VirtualMachine) -> PyDictKeys {
PyDictKeys::new(self)
}
#[pymethod]
fn values(self, _vm: &VirtualMachine) -> PyDictValues {
PyDictValues::new(self)
}
#[pymethod]
fn items(self, _vm: &VirtualMachine) -> PyDictItems {
PyDictItems::new(self)
}
fn inner_setitem(
self,
key: PyObjectRef,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
#[pymethod(magic)]
fn setitem(self, key: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
self.inner_setitem_fast(&key, value, vm)
}
@@ -232,8 +245,9 @@ impl PyDictRef {
self.entries.borrow_mut().insert(vm, key, value)
}
#[pymethod(magic)]
#[cfg_attr(feature = "flame-it", flame("PyDictRef"))]
fn inner_getitem(self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult {
fn getitem(self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult {
if let Some(value) = self.inner_getitem_option(&key, vm)? {
Ok(value)
} else {
@@ -259,6 +273,7 @@ impl PyDictRef {
}
}
#[pymethod]
fn get(
self,
key: PyObjectRef,
@@ -271,6 +286,7 @@ impl PyDictRef {
}
}
#[pymethod]
fn setdefault(
self,
key: PyObjectRef,
@@ -288,12 +304,14 @@ impl PyDictRef {
}
}
#[pymethod]
pub fn copy(self, _vm: &VirtualMachine) -> PyDict {
PyDict {
entries: self.entries.clone(),
}
}
#[pymethod]
fn update(
self,
dict_obj: OptionalArg<PyObjectRef>,
@@ -303,6 +321,7 @@ impl PyDictRef {
PyDictRef::merge(&self.entries, dict_obj, kwargs, vm)
}
#[pymethod]
fn pop(
self,
key: PyObjectRef,
@@ -318,6 +337,7 @@ impl PyDictRef {
}
}
#[pymethod]
fn popitem(self, vm: &VirtualMachine) -> PyResult {
let mut entries = self.entries.borrow_mut();
if let Some((key, value)) = entries.pop_front() {
@@ -349,6 +369,7 @@ impl PyDictRef {
Ok(PyDict { entries }.into_ref(vm))
}
#[pymethod(magic)]
fn hash(self, vm: &VirtualMachine) -> PyResult<()> {
Err(vm.new_type_error("unhashable type".to_string()))
}
@@ -594,34 +615,8 @@ dict_iterator! {
vm.ctx.new_tuple(vec![key.clone(), value.clone()])
}
pub fn init(context: &PyContext) {
extend_class!(context, &context.types.dict_type, {
"__bool__" => context.new_method(PyDictRef::bool),
"__len__" => context.new_method(PyDictRef::len),
"__sizeof__" => context.new_method(PyDictRef::sizeof),
"__contains__" => context.new_method(PyDictRef::contains),
"__delitem__" => context.new_method(PyDictRef::inner_delitem),
"__eq__" => context.new_method(PyDictRef::eq),
"__ne__" => context.new_method(PyDictRef::ne),
"__getitem__" => context.new_method(PyDictRef::inner_getitem),
"__iter__" => context.new_method(PyDictRef::iter),
(slot new) => PyDictRef::new,
"__repr__" => context.new_method(PyDictRef::repr),
"__setitem__" => context.new_method(PyDictRef::inner_setitem),
"__hash__" => context.new_method(PyDictRef::hash),
"clear" => context.new_method(PyDictRef::clear),
"values" => context.new_method(PyDictRef::values),
"items" => context.new_method(PyDictRef::items),
"keys" => context.new_method(PyDictRef::keys),
"fromkeys" => context.new_classmethod(PyDictRef::fromkeys),
"get" => context.new_method(PyDictRef::get),
"setdefault" => context.new_method(PyDictRef::setdefault),
"copy" => context.new_method(PyDictRef::copy),
"update" => context.new_method(PyDictRef::update),
"pop" => context.new_method(PyDictRef::pop),
"popitem" => context.new_method(PyDictRef::popitem),
});
pub(crate) fn init(context: &PyContext) {
PyDictRef::extend_class(context, &context.types.dict_type);
PyDictKeys::extend_class(context, &context.types.dictkeys_type);
PyDictKeyIterator::extend_class(context, &context.types.dictkeyiterator_type);
PyDictValues::extend_class(context, &context.types.dictvalues_type);

View File

@@ -23,7 +23,7 @@ impl PyValue for PyFilter {
}
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyFilter {
#[pyslot]
fn tp_new(

View File

@@ -173,7 +173,7 @@ fn int_eq(value: f64, other: &BigInt) -> bool {
}
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
#[allow(clippy::trivially_copy_pass_by_ref)]
impl PyFloat {
#[pyslot]

View File

@@ -211,7 +211,7 @@ fn inner_truediv(i1: &BigInt, i2: &BigInt, vm: &VirtualMachine) -> PyResult {
}
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyInt {
#[pyslot]
fn tp_new(cls: PyClassRef, options: IntOptions, vm: &VirtualMachine) -> PyResult<PyIntRef> {

View File

@@ -146,7 +146,7 @@ struct SortOptions {
pub type PyListRef = PyRef<PyList>;
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyList {
#[pymethod]
pub(crate) fn append(&self, x: PyObjectRef, _vm: &VirtualMachine) {

View File

@@ -22,7 +22,7 @@ impl PyValue for PyMap {
}
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyMap {
#[pyslot]
fn tp_new(

View File

@@ -1,7 +1,7 @@
use super::objdict::PyDictRef;
use super::objiter;
use super::objstr::PyStringRef;
use super::objtype::{self, PyClassRef};
use super::objtype::PyClassRef;
use crate::function::OptionalArg;
use crate::pyobject::{
ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
@@ -48,7 +48,7 @@ impl PyMappingProxy {
let opt = match &self.mapping {
MappingProxyInner::Class(class) => {
let key = PyStringRef::try_from_object(vm, key)?;
objtype::class_get_attr(&class, key.as_str())
class.get_attr(key.as_str())
}
MappingProxyInner::Dict(obj) => obj.get_item(&key, vm).ok(),
};
@@ -75,7 +75,7 @@ impl PyMappingProxy {
match &self.mapping {
MappingProxyInner::Class(class) => {
let key = PyStringRef::try_from_object(vm, key)?;
Ok(vm.new_bool(objtype::class_has_attr(&class, key.as_str())))
Ok(vm.new_bool(class.has_attr(key.as_str())))
}
MappingProxyInner::Dict(obj) => vm._membership(obj.clone(), key),
}

View File

@@ -2,9 +2,12 @@ use super::objdict::PyDictRef;
use super::objstr::{PyString, PyStringRef};
use super::objtype::PyClassRef;
use crate::function::OptionalOption;
use crate::pyobject::{ItemProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
use crate::pyobject::{
ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
};
use crate::vm::VirtualMachine;
#[pyclass]
#[derive(Debug)]
pub struct PyModule {}
pub type PyModuleRef = PyRef<PyModule>;
@@ -40,8 +43,10 @@ pub fn init_module_dict(
.expect("Failed to set __spec__ on module");
}
#[pyimpl(flags(BASETYPE))]
impl PyModuleRef {
fn new(
#[pyslot]
fn tp_new(
cls: PyClassRef,
name: PyStringRef,
doc: OptionalOption<PyStringRef>,
@@ -67,6 +72,7 @@ impl PyModuleRef {
.and_then(|obj| obj.payload::<PyString>().map(|s| s.as_str().to_owned()))
}
#[pymethod(magic)]
fn getattribute(self, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
vm.generic_getattribute(self.as_object().clone(), name.clone())?
.ok_or_else(|| {
@@ -81,6 +87,7 @@ impl PyModuleRef {
})
}
#[pymethod(magic)]
fn repr(self, vm: &VirtualMachine) -> PyResult {
let importlib = vm.import("_frozen_importlib", &[], 0)?;
let module_repr = vm.get_attribute(importlib, "_module_repr")?;
@@ -88,10 +95,6 @@ impl PyModuleRef {
}
}
pub fn init(context: &PyContext) {
extend_class!(&context, &context.types.module_type, {
(slot new) => PyModuleRef::new,
"__getattribute__" => context.new_method(PyModuleRef::getattribute),
"__repr__" => context.new_method(PyModuleRef::repr),
});
pub(crate) fn init(context: &PyContext) {
PyModuleRef::extend_class(&context, &context.types.module_type);
}

View File

@@ -17,7 +17,7 @@ impl PyValue for PyNamespace {
}
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyNamespace {
#[pyslot]
fn tp_new(cls: PyClassRef, kwargs: KwArgs, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {

View File

@@ -1,6 +1,6 @@
use super::objproperty::PyPropertyRef;
use super::objstr::PyStringRef;
use super::objtype::{class_get_attr, class_has_attr, PyClassRef};
use super::objtype::PyClassRef;
use crate::pyobject::{
IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
TypeProtocol,
@@ -79,10 +79,10 @@ impl PyNone {
}
}
if let Some(attr) = class_get_attr(&cls, name.as_str()) {
if let Some(attr) = cls.get_attr(name.as_str()) {
let attr_class = attr.class();
if class_has_attr(&attr_class, "__set__") {
if let Some(get_func) = class_get_attr(&attr_class, "__get__") {
if attr_class.has_attr("__set__") {
if let Some(get_func) = attr_class.get_attr("__get__") {
return call_descriptor(
attr,
get_func,
@@ -98,14 +98,14 @@ impl PyNone {
// if let Some(obj_attr) = zelf.as_object().get_attr(name.as_str()) {
// Ok(obj_attr)
// } else
if let Some(attr) = class_get_attr(&cls, name.as_str()) {
if let Some(attr) = cls.get_attr(name.as_str()) {
let attr_class = attr.class();
if let Some(get_func) = class_get_attr(&attr_class, "__get__") {
if let Some(get_func) = attr_class.get_attr("__get__") {
call_descriptor(attr, get_func, zelf.into_object(), cls.into_object(), vm)
} else {
Ok(attr)
}
} else if let Some(getter) = class_get_attr(&cls, "__getattr__") {
} else if let Some(getter) = cls.get_attr("__getattr__") {
vm.invoke(&getter, vec![zelf.into_object(), name.into_object()])
} else {
Err(vm.new_attribute_error(format!("{} has no attribute '{}'", zelf.as_object(), name)))

View File

@@ -10,6 +10,7 @@ use crate::pyobject::{
IdProtocol, ItemProtocol, PyArithmaticValue::*, PyAttributes, PyComparisonValue, PyContext,
PyObject, PyObjectRef, PyResult, PyValue, TryFromObject, TypeProtocol,
};
use crate::slots::PyTpFlags;
use crate::vm::VirtualMachine;
#[derive(Debug)]
@@ -86,8 +87,8 @@ pub(crate) fn object_setattr(
vm_trace!("object.__setattr__({:?}, {}, {:?})", obj, attr_name, value);
let cls = obj.class();
if let Some(attr) = objtype::class_get_attr(&cls, attr_name.as_str()) {
if let Some(descriptor) = objtype::class_get_attr(&attr.class(), "__set__") {
if let Some(attr) = cls.get_attr(attr_name.as_str()) {
if let Some(descriptor) = attr.class().get_attr("__set__") {
return vm
.invoke(&descriptor, vec![attr, obj.clone(), value])
.map(|_| ());
@@ -109,8 +110,8 @@ pub(crate) fn object_setattr(
fn object_delattr(obj: PyObjectRef, attr_name: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
let cls = obj.class();
if let Some(attr) = objtype::class_get_attr(&cls, attr_name.as_str()) {
if let Some(descriptor) = objtype::class_get_attr(&attr.class(), "__delete__") {
if let Some(attr) = cls.get_attr(attr_name.as_str()) {
if let Some(descriptor) = attr.class().get_attr("__delete__") {
return vm.invoke(&descriptor, vec![attr, obj.clone()]).map(|_| ());
}
}
@@ -140,7 +141,7 @@ fn object_subclasshook(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult {
}
pub fn object_dir(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyList> {
let attributes: PyAttributes = objtype::get_attributes(obj.class());
let attributes: PyAttributes = obj.class().get_attributes();
let dict = PyDictRef::from_attributes(attributes, vm)?;
@@ -173,6 +174,8 @@ pub fn init(context: &PyContext) {
let object = &context.types.object_type;
let object_doc = "The most base type";
object.slots.borrow_mut().flags |= PyTpFlags::BASETYPE;
extend_class!(context, object, {
(slot new) => new_instance,
// yeah, it's `type_new`, but we're putting here so it's available on every object
@@ -262,9 +265,8 @@ fn object_reduce(obj: PyObjectRef, proto: OptionalArg<usize>, vm: &VirtualMachin
fn object_reduce_ex(obj: PyObjectRef, proto: usize, vm: &VirtualMachine) -> PyResult {
let cls = obj.class();
if let Some(reduce) = objtype::class_get_attr(&cls, "__reduce__") {
let object_reduce =
objtype::class_get_attr(&vm.ctx.types.object_type, "__reduce__").unwrap();
if let Some(reduce) = cls.get_attr("__reduce__") {
let object_reduce = vm.ctx.types.object_type.get_attr("__reduce__").unwrap();
if !reduce.is(&object_reduce) {
return vm.invoke(&reduce, vec![]);
}

View File

@@ -129,7 +129,7 @@ impl PyBuiltinDescriptor for PyProperty {
}
}
#[pyimpl(with(PyBuiltinDescriptor))]
#[pyimpl(with(PyBuiltinDescriptor), flags(BASETYPE))]
impl PyProperty {
#[pyslot]
fn tp_new(cls: PyClassRef, args: PropertyArgs, vm: &VirtualMachine) -> PyResult<PyPropertyRef> {

View File

@@ -328,7 +328,7 @@ macro_rules! try_set_cmp {
};
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PySet {
#[pyslot]
fn tp_new(
@@ -584,7 +584,7 @@ impl PySet {
}
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyFrozenSet {
#[pyslot]
fn tp_new(

View File

@@ -1,5 +1,5 @@
use super::objint::PyInt;
use super::objtype::{class_has_attr, PyClassRef};
use super::objtype::PyClassRef;
use crate::function::{OptionalArg, PyFuncArgs};
use crate::pyobject::{
IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryIntoRef,
@@ -327,7 +327,7 @@ fn to_index_value(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<Option<Big
Ok(Some(val.as_bigint().clone()))
} else {
let cls = obj.class();
if class_has_attr(&cls, "__index__") {
if cls.has_attr("__index__") {
let index_result = vm.call_method(obj, "__index__", vec![])?;
if let Some(val) = index_result.payload::<PyInt>() {
Ok(Some(val.as_bigint().clone()))

View File

@@ -28,7 +28,7 @@ impl PyBuiltinDescriptor for PyStaticMethod {
}
}
#[pyimpl(with(PyBuiltinDescriptor))]
#[pyimpl(with(PyBuiltinDescriptor), flags(BASETYPE))]
impl PyStaticMethod {
#[pyslot]
fn tp_new(

View File

@@ -184,7 +184,7 @@ struct SplitLineArgs {
keepends: OptionalArg<bool>,
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyString {
#[pyslot]
fn tp_new(cls: PyClassRef, args: StrArgs, vm: &VirtualMachine) -> PyResult<PyStringRef> {

View File

@@ -76,7 +76,7 @@ pub(crate) fn get_value(obj: &PyObjectRef) -> &[PyObjectRef] {
obj.payload::<PyTuple>().unwrap().as_slice()
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyTuple {
#[inline]
fn cmp<F>(&self, other: PyObjectRef, op: F, vm: &VirtualMachine) -> PyResult<PyComparisonValue>

View File

@@ -11,12 +11,16 @@ use super::objtuple::PyTuple;
use super::objweakref::PyWeak;
use crate::function::PyFuncArgs;
use crate::pyobject::{
IdProtocol, PyAttributes, PyContext, PyIterable, PyObject, PyObjectRef, PyRef, PyResult,
PyValue, TypeProtocol,
IdProtocol, PyAttributes, PyClassImpl, PyContext, PyIterable, PyObject, PyObjectRef, PyRef,
PyResult, PyValue, TypeProtocol,
};
use crate::slots::PyClassSlots;
use crate::slots::{PyClassSlots, PyTpFlags};
use crate::vm::VirtualMachine;
/// type(object_or_name, bases, dict)
/// type(object) -> the object's type
/// type(name, bases, dict) -> a new type
#[pyclass(name = "type")]
#[derive(Debug)]
pub struct PyClass {
pub name: String,
@@ -67,6 +71,7 @@ impl<'a> Iterator for IterMro<'a> {
}
}
#[pyimpl(flags(BASETYPE))]
impl PyClassRef {
fn iter_mro(&self) -> IterMro {
IterMro {
@@ -75,23 +80,25 @@ impl PyClassRef {
}
}
fn mro(self, _vm: &VirtualMachine) -> PyTuple {
fn _mro(self, _vm: &VirtualMachine) -> PyTuple {
let elements: Vec<PyObjectRef> =
_mro(&self).iter().map(|x| x.as_object().clone()).collect();
PyTuple::from(elements)
}
fn set_mro(self, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
fn _set_mro(self, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
Err(vm.new_attribute_error("read-only attribute".to_string()))
}
#[pyproperty(magic)]
fn bases(self, vm: &VirtualMachine) -> PyObjectRef {
vm.ctx
.new_tuple(self.bases.iter().map(|x| x.as_object().clone()).collect())
}
#[pymethod(magic)]
fn dir(self, vm: &VirtualMachine) -> PyList {
let attributes = get_attributes(self);
let attributes = self.get_attributes();
let attributes: Vec<PyObjectRef> = attributes
.keys()
.map(|k| vm.ctx.new_str(k.to_string()))
@@ -99,22 +106,27 @@ impl PyClassRef {
PyList::from(attributes)
}
fn instance_check(self, obj: PyObjectRef, _vm: &VirtualMachine) -> bool {
#[pymethod(magic)]
fn instancecheck(self, obj: PyObjectRef, _vm: &VirtualMachine) -> bool {
isinstance(&obj, &self)
}
fn subclass_check(self, subclass: PyClassRef, _vm: &VirtualMachine) -> bool {
#[pymethod(magic)]
fn subclasscheck(self, subclass: PyClassRef, _vm: &VirtualMachine) -> bool {
issubclass(&subclass, &self)
}
#[pyproperty(magic)]
fn name(self, _vm: &VirtualMachine) -> String {
self.name.clone()
}
#[pymethod(magic)]
fn repr(self, _vm: &VirtualMachine) -> String {
format!("<class '{}'>", self.name)
}
#[pyproperty(magic)]
fn qualname(self, vm: &VirtualMachine) -> PyObjectRef {
self.attributes
.borrow()
@@ -123,6 +135,7 @@ impl PyClassRef {
.unwrap_or_else(|| vm.ctx.new_str(self.name.clone()))
}
#[pyproperty(magic)]
fn module(self, vm: &VirtualMachine) -> PyObjectRef {
// TODO: Implement getting the actual module a builtin type is from
self.attributes
@@ -132,19 +145,21 @@ impl PyClassRef {
.unwrap_or_else(|| vm.ctx.new_str("builtins".to_owned()))
}
#[pymethod(magic)]
fn prepare(_name: PyStringRef, _bases: PyObjectRef, vm: &VirtualMachine) -> PyDictRef {
vm.ctx.new_dict()
}
#[pymethod(magic)]
fn getattribute(self, name_ref: PyStringRef, vm: &VirtualMachine) -> PyResult {
let name = name_ref.as_str();
vm_trace!("type.__getattribute__({:?}, {:?})", self, name);
let mcl = self.class();
if let Some(attr) = class_get_attr(&mcl, &name) {
if let Some(attr) = mcl.get_attr(&name) {
let attr_class = attr.class();
if class_has_attr(&attr_class, "__set__") {
if let Some(ref descriptor) = class_get_attr(&attr_class, "__get__") {
if attr_class.has_attr("__set__") {
if let Some(ref descriptor) = attr_class.get_attr("__get__") {
return vm.invoke(
descriptor,
vec![attr, self.into_object(), mcl.into_object()],
@@ -153,32 +168,33 @@ impl PyClassRef {
}
}
if let Some(attr) = class_get_attr(&self, &name) {
if let Some(attr) = self.get_attr(&name) {
let attr_class = attr.class();
if let Some(ref descriptor) = class_get_attr(&attr_class, "__get__") {
if let Some(ref descriptor) = attr_class.get_attr("__get__") {
return vm.invoke(descriptor, vec![attr, vm.get_none(), self.into_object()]);
}
}
if let Some(cls_attr) = class_get_attr(&self, &name) {
if let Some(cls_attr) = self.get_attr(&name) {
Ok(cls_attr)
} else if let Some(attr) = class_get_attr(&mcl, &name) {
} else if let Some(attr) = mcl.get_attr(&name) {
vm.call_get_descriptor(attr, self.into_object())
} else if let Some(ref getter) = class_get_attr(&self, "__getattr__") {
} else if let Some(ref getter) = self.get_attr("__getattr__") {
vm.invoke(getter, vec![mcl.into_object(), name_ref.into_object()])
} else {
Err(vm.new_attribute_error(format!("{} has no attribute '{}'", self, name)))
}
}
fn set_attr(
#[pymethod(magic)]
fn setattr(
self,
attr_name: PyStringRef,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
if let Some(attr) = class_get_attr(&self.class(), attr_name.as_str()) {
if let Some(ref descriptor) = class_get_attr(&attr.class(), "__set__") {
if let Some(attr) = self.class().get_attr(attr_name.as_str()) {
if let Some(ref descriptor) = attr.class().get_attr("__set__") {
vm.invoke(descriptor, vec![attr, self.into_object(), value])?;
return Ok(());
}
@@ -190,16 +206,17 @@ impl PyClassRef {
Ok(())
}
fn del_attr(self, attr_name: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
if let Some(attr) = class_get_attr(&self.class(), attr_name.as_str()) {
if let Some(ref descriptor) = class_get_attr(&attr.class(), "__delete__") {
#[pymethod(magic)]
fn delattr(self, attr_name: PyStringRef, vm: &VirtualMachine) -> PyResult<()> {
if let Some(attr) = self.class().get_attr(attr_name.as_str()) {
if let Some(ref descriptor) = attr.class().get_attr("__delete__") {
return vm
.invoke(descriptor, vec![attr, self.into_object()])
.map(|_| ());
}
}
if class_get_attr(&self, attr_name.as_str()).is_some() {
if self.get_attr(attr_name.as_str()).is_some() {
self.attributes.borrow_mut().remove(attr_name.as_str());
Ok(())
} else {
@@ -214,6 +231,7 @@ impl PyClassRef {
.insert(attr_name.to_string(), value.into());
}
#[pymethod(magic)]
fn subclasses(self, _vm: &VirtualMachine) -> PyList {
let mut subclasses = self.subclasses.borrow_mut();
subclasses.retain(|x| x.upgrade().is_some());
@@ -224,52 +242,103 @@ impl PyClassRef {
.collect::<Vec<_>>(),
)
}
}
fn type_mro(cls: PyClassRef, vm: &VirtualMachine) -> PyObjectRef {
let mut mro = vec![cls.clone().into_object()];
mro.extend(cls.mro.iter().map(|x| x.clone().into_object()));
vm.ctx.new_list(mro)
#[pymethod]
fn mro(self, vm: &VirtualMachine) -> PyObjectRef {
let mut mro = vec![self.clone().into_object()];
mro.extend(self.mro.iter().map(|x| x.clone().into_object()));
vm.ctx.new_list(mro)
}
#[pyslot]
fn tp_new(metatype: PyClassRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
vm_trace!("type.__new__ {:?}", args);
if metatype.is(&vm.ctx.types.type_type) {
if args.args.len() == 1 && args.kwargs.is_empty() {
return Ok(args.args[0].class().into_object());
}
if args.args.len() != 3 {
return Err(vm.new_type_error("type() takes 1 or 3 arguments".to_string()));
}
}
let (name, bases, dict): (PyStringRef, PyIterable<PyClassRef>, PyDictRef) =
args.clone().bind(vm)?;
let bases: Vec<PyClassRef> = bases.iter(vm)?.collect::<Result<Vec<_>, _>>()?;
let (metatype, base, bases) = if bases.is_empty() {
let base = vm.ctx.object();
(metatype, base.clone(), vec![base])
} else {
// TODO
// for base in &bases {
// if PyType_Check(base) { continue; }
// _PyObject_LookupAttrId(base, PyId___mro_entries__, &base)?
// Err(new_type_error( "type() doesn't support MRO entry resolution; "
// "use types.new_class()"))
// }
// Search the bases for the proper metatype to deal with this:
let winner = calculate_meta_class(metatype.clone(), &bases, vm)?;
let metatype = if !winner.is(&metatype) {
if let Some(ref tp_new) = winner.clone().slots.borrow().new {
// Pass it to the winner
return tp_new(vm, args.insert(winner.into_object()));
}
winner
} else {
metatype
};
let base = best_base(&bases, vm)?;
(metatype, base, bases)
};
let attributes = dict.to_attributes();
let typ = new(metatype, name.as_str(), base.clone(), bases, attributes)?;
typ.slots.borrow_mut().flags = base.slots.borrow().flags;
Ok(typ.into())
}
#[pyslot]
#[pymethod(magic)]
fn call(self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
vm_trace!("type_call: {:?}", self);
let new = vm.get_attribute(self.as_object().clone(), "__new__")?;
let new_args = args.insert(self.into_object());
let obj = vm.invoke(&new, new_args)?;
if let Some(init_method_or_err) = vm.get_method(obj.clone(), "__init__") {
let init_method = init_method_or_err?;
let res = vm.invoke(&init_method, args)?;
if !res.is(&vm.get_none()) {
return Err(vm.new_type_error("__init__ must return None".to_string()));
}
}
Ok(obj)
}
}
/*
* The magical type type
*/
pub fn init(ctx: &PyContext) {
let type_doc = "type(object_or_name, bases, dict)\n\
type(object) -> the object's type\n\
type(name, bases, dict) -> a new type";
pub(crate) fn init(ctx: &PyContext) {
PyClassRef::extend_class(ctx, &ctx.types.type_type);
extend_class!(&ctx, &ctx.types.type_type, {
"mro" => ctx.new_method(type_mro),
"__call__" => ctx.new_method(type_call),
(slot call) => type_call,
"__dict__" =>
PropertyBuilder::new(ctx)
.add_getter(type_dict)
.add_setter(type_dict_setter)
.create(),
(slot new) => type_new_slot,
"__mro__" =>
PropertyBuilder::new(ctx)
.add_getter(PyClassRef::mro)
.add_setter(PyClassRef::set_mro)
.add_getter(PyClassRef::_mro)
.add_setter(PyClassRef::_set_mro)
.create(),
"__bases__" => ctx.new_property(PyClassRef::bases),
"__name__" => ctx.new_property(PyClassRef::name),
"__repr__" => ctx.new_method(PyClassRef::repr),
"__qualname__" => ctx.new_property(PyClassRef::qualname),
"__module__" => ctx.new_property(PyClassRef::module),
"__prepare__" => ctx.new_method(PyClassRef::prepare),
"__getattribute__" => ctx.new_method(PyClassRef::getattribute),
"__setattr__" => ctx.new_method(PyClassRef::set_attr),
"__delattr__" => ctx.new_method(PyClassRef::del_attr),
"__subclasses__" => ctx.new_method(PyClassRef::subclasses),
"__instancecheck__" => ctx.new_method(PyClassRef::instance_check),
"__subclasscheck__" => ctx.new_method(PyClassRef::subclass_check),
"__doc__" => ctx.new_str(type_doc.to_string()),
"__dir__" => ctx.new_method(PyClassRef::dir),
});
}
@@ -292,49 +361,6 @@ pub fn issubclass(subclass: &PyClassRef, cls: &PyClassRef) -> bool {
subclass.is(cls) || mro.iter().any(|c| c.is(cls.as_object()))
}
fn type_new_slot(metatype: PyClassRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
vm_trace!("type.__new__ {:?}", args);
if metatype.is(&vm.ctx.types.type_type) {
if args.args.len() == 1 && args.kwargs.is_empty() {
return Ok(args.args[0].class().into_object());
}
if args.args.len() != 3 {
return Err(vm.new_type_error("type() takes 1 or 3 arguments".to_string()));
}
}
let (name, bases, dict): (PyStringRef, PyIterable<PyClassRef>, PyDictRef) = args.bind(vm)?;
let bases: Vec<PyClassRef> = bases.iter(vm)?.collect::<Result<Vec<_>, _>>()?;
let bases = if bases.is_empty() {
vec![vm.ctx.object()]
} else {
bases
};
let attributes = dict.to_attributes();
let mut winner = metatype;
for base in &bases {
let base_type = base.class();
if issubclass(&winner, &base_type) {
continue;
} else if issubclass(&base_type, &winner) {
winner = base_type.clone();
continue;
}
return Err(vm.new_type_error(
"metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass \
of the metaclasses of all its bases"
.to_string(),
));
}
new(winner, name.as_str(), bases, attributes).map(Into::into)
}
pub fn type_new(
zelf: PyClassRef,
cls: PyClassRef,
@@ -365,22 +391,6 @@ pub fn type_new(
new(vm, args.insert(cls.into_object()))
}
pub fn type_call(class: PyClassRef, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult {
vm_trace!("type_call: {:?}", class);
let new = vm.get_attribute(class.as_object().clone(), "__new__")?;
let new_args = args.insert(class.into_object());
let obj = vm.invoke(&new, new_args)?;
if let Some(init_method_or_err) = vm.get_method(obj.clone(), "__init__") {
let init_method = init_method_or_err?;
let res = vm.invoke(&init_method, args)?;
if !res.is(&vm.get_none()) {
return Err(vm.new_type_error("__init__ must return None".to_string()));
}
}
Ok(obj)
}
fn type_dict(class: PyClassRef, _vm: &VirtualMachine) -> PyMappingProxy {
PyMappingProxy::new(class)
}
@@ -395,48 +405,48 @@ fn type_dict_setter(
))
}
/// This is the internal get_attr implementation for fast lookup on a class.
pub fn class_get_attr(class: &PyClassRef, attr_name: &str) -> Option<PyObjectRef> {
flame_guard!(format!("class_get_attr({:?})", attr_name));
impl PyClassRef {
/// This is the internal get_attr implementation for fast lookup on a class.
pub fn get_attr(&self, attr_name: &str) -> Option<PyObjectRef> {
flame_guard!(format!("class_get_attr({:?})", attr_name));
class
.attributes
.borrow()
.get(attr_name)
.cloned()
.or_else(|| class_get_super_attr(class, attr_name))
}
pub fn class_get_super_attr(class: &PyClassRef, attr_name: &str) -> Option<PyObjectRef> {
class
.mro
.iter()
.find_map(|class| class.attributes.borrow().get(attr_name).cloned())
}
// This is the internal has_attr implementation for fast lookup on a class.
pub fn class_has_attr(class: &PyClassRef, attr_name: &str) -> bool {
class.attributes.borrow().contains_key(attr_name)
|| class
.mro
.iter()
.any(|c| c.attributes.borrow().contains_key(attr_name))
}
pub fn get_attributes(cls: PyClassRef) -> PyAttributes {
// Gather all members here:
let mut attributes = PyAttributes::new();
let mut base_classes: Vec<&PyClassRef> = cls.iter_mro().collect();
base_classes.reverse();
for bc in base_classes {
for (name, value) in bc.attributes.borrow().iter() {
attributes.insert(name.to_string(), value.clone());
}
self.attributes
.borrow()
.get(attr_name)
.cloned()
.or_else(|| self.get_super_attr(attr_name))
}
attributes
pub fn get_super_attr(&self, attr_name: &str) -> Option<PyObjectRef> {
self.mro
.iter()
.find_map(|class| class.attributes.borrow().get(attr_name).cloned())
}
// This is the internal has_attr implementation for fast lookup on a class.
pub fn has_attr(&self, attr_name: &str) -> bool {
self.attributes.borrow().contains_key(attr_name)
|| self
.mro
.iter()
.any(|c| c.attributes.borrow().contains_key(attr_name))
}
pub fn get_attributes(self) -> PyAttributes {
// Gather all members here:
let mut attributes = PyAttributes::new();
let mut base_classes: Vec<&PyClassRef> = self.iter_mro().collect();
base_classes.reverse();
for bc in base_classes {
for (name, value) in bc.attributes.borrow().iter() {
attributes.insert(name.to_string(), value.clone());
}
}
attributes
}
}
fn take_next_base(mut bases: Vec<Vec<PyClassRef>>) -> Option<(PyClassRef, Vec<Vec<PyClassRef>>)> {
@@ -481,6 +491,7 @@ fn linearise_mro(mut bases: Vec<Vec<PyClassRef>>) -> Option<Vec<PyClassRef>> {
pub fn new(
typ: PyClassRef,
name: &str,
_base: PyClassRef,
bases: Vec<PyClassRef>,
dict: HashMap<String, PyObjectRef>,
) -> PyResult<PyClassRef> {
@@ -511,6 +522,79 @@ pub fn new(
Ok(new_type)
}
fn calculate_meta_class(
metatype: PyClassRef,
bases: &[PyClassRef],
vm: &VirtualMachine,
) -> PyResult<PyClassRef> {
// = _PyType_CalculateMetaclass
let mut winner = metatype;
for base in bases {
let base_type = base.class();
if issubclass(&winner, &base_type) {
continue;
} else if issubclass(&base_type, &winner) {
winner = base_type.clone();
continue;
}
return Err(vm.new_type_error(
"metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass \
of the metaclasses of all its bases"
.to_string(),
));
}
Ok(winner)
}
fn best_base<'a>(bases: &'a [PyClassRef], vm: &VirtualMachine) -> PyResult<PyClassRef> {
// let mut base = None;
// let mut winner = None;
for base_i in bases {
// base_proto = PyTuple_GET_ITEM(bases, i);
// if (!PyType_Check(base_proto)) {
// PyErr_SetString(
// PyExc_TypeError,
// "bases must be types");
// return NULL;
// }
// base_i = (PyTypeObject *)base_proto;
// if (base_i->tp_dict == NULL) {
// if (PyType_Ready(base_i) < 0)
// return NULL;
// }
if !base_i.slots.borrow().flags.has_feature(PyTpFlags::BASETYPE) {
return Err(vm.new_type_error(format!(
"type '{}' is not an acceptable base type",
base_i.name
)));
}
// candidate = solid_base(base_i);
// if (winner == NULL) {
// winner = candidate;
// base = base_i;
// }
// else if (PyType_IsSubtype(winner, candidate))
// ;
// else if (PyType_IsSubtype(candidate, winner)) {
// winner = candidate;
// base = base_i;
// }
// else {
// PyErr_SetString(
// PyExc_TypeError,
// "multiple bases have "
// "instance lay-out conflict");
// return NULL;
// }
}
// FIXME: Ok(base.unwrap()) is expected
Ok(bases[0].clone())
}
#[cfg(test)]
mod tests {
use super::{linearise_mro, new};
@@ -529,8 +613,22 @@ mod tests {
let object: PyClassRef = context.object();
let type_type = &context.types.type_type;
let a = new(type_type.clone(), "A", vec![object.clone()], HashMap::new()).unwrap();
let b = new(type_type.clone(), "B", vec![object.clone()], HashMap::new()).unwrap();
let a = new(
type_type.clone(),
"A",
object.clone(),
vec![object.clone()],
HashMap::new(),
)
.unwrap();
let b = new(
type_type.clone(),
"B",
object.clone(),
vec![object.clone()],
HashMap::new(),
)
.unwrap();
assert_eq!(
map_ids(linearise_mro(vec![

View File

@@ -41,7 +41,7 @@ impl PyBuiltinCallable for PyWeak {
}
}
#[pyimpl(with(PyBuiltinCallable))]
#[pyimpl(with(PyBuiltinCallable), flags(BASETYPE))]
impl PyWeak {
// TODO callbacks
#[pyslot]

View File

@@ -18,7 +18,7 @@ impl PyValue for PyZip {
}
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyZip {
#[pyslot]
fn tp_new(cls: PyClassRef, iterables: Args, vm: &VirtualMachine) -> PyResult<PyZipRef> {

View File

@@ -37,6 +37,7 @@ use crate::obj::objstr;
use crate::obj::objtuple::{PyTuple, PyTupleRef};
use crate::obj::objtype::{self, PyClass, PyClassRef};
use crate::scope::Scope;
use crate::slots::PyTpFlags;
use crate::types::{create_type, initialize_types, TypeZoo};
use crate::vm::VirtualMachine;
@@ -458,7 +459,7 @@ impl PyContext {
}
pub fn new_class(&self, name: &str, base: PyClassRef) -> PyClassRef {
objtype::new(self.type_type(), name, vec![base], PyAttributes::new()).unwrap()
create_type(name, &self.type_type(), &base)
}
pub fn new_namespace(&self) -> PyObjectRef {
@@ -1196,10 +1197,13 @@ where
}
pub trait PyClassImpl: PyClassDef {
const TP_FLAGS: PyTpFlags = PyTpFlags::DEFAULT;
fn impl_extend_class(ctx: &PyContext, class: &PyClassRef);
fn extend_class(ctx: &PyContext, class: &PyClassRef) {
Self::impl_extend_class(ctx, class);
class.slots.borrow_mut().flags = Self::TP_FLAGS;
if let Some(doc) = Self::DOC {
class.set_str_attr("__doc__", ctx.new_str(doc.into()));
}

View File

@@ -2,8 +2,30 @@ use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc};
use crate::pyobject::{IdProtocol, PyObjectRef, PyRef, PyResult, PyValue};
use crate::VirtualMachine;
bitflags! {
pub struct PyTpFlags: u64 {
const BASETYPE = 1 << 10;
}
}
impl PyTpFlags {
// CPython default: Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | Py_TPFLAGS_HAVE_VERSION_TAG
pub const DEFAULT: Self = Self::from_bits_truncate(0);
pub fn has_feature(self, flag: Self) -> bool {
self.contains(flag)
}
}
impl Default for PyTpFlags {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Default)]
pub struct PyClassSlots {
pub flags: PyTpFlags,
pub new: Option<PyNativeFunc>,
pub call: Option<PyNativeFunc>,
pub descr_get: Option<PyNativeFunc>,

View File

@@ -175,7 +175,7 @@ def_array_enum!(
(Double, f64, 'd'),
);
#[pyclass]
#[pyclass(name = "array")]
#[derive(Debug)]
pub struct PyArray {
array: RefCell<ArrayContentType>,
@@ -188,7 +188,7 @@ impl PyValue for PyArray {
}
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyArray {
#[pyslot]
fn tp_new(

View File

@@ -37,7 +37,7 @@ impl PyDeque {
}
}
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PyDeque {
#[pyslot]
fn tp_new(

View File

@@ -944,7 +944,7 @@ pub fn io_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
//IOBase the abstract base class of the IO Module
// IOBase the abstract base class of the IO Module
let io_base = py_class!(ctx, "_IOBase", ctx.object(), {
"__enter__" => ctx.new_method(io_base_cm_enter),
"__exit__" => ctx.new_method(io_base_cm_exit),

View File

@@ -777,7 +777,7 @@ impl PyItertoolsTee {
) -> PyResult<PyRef<PyTuple>> {
let n = n.unwrap_or(2);
let copyable = if objtype::class_has_attr(&iterable.class(), "__copy__") {
let copyable = if iterable.class().has_attr("__copy__") {
vm.call_method(&iterable, "__copy__", PyFuncArgs::from(vec![]))?
} else {
PyItertoolsTee::from_iter(iterable, vm)?

View File

@@ -84,7 +84,7 @@ impl PyValue for PySocket {
pub type PySocketRef = PyRef<PySocket>;
#[pyimpl]
#[pyimpl(flags(BASETYPE))]
impl PySocket {
fn sock(&self) -> Ref<Socket> {
self.sock.borrow()

View File

@@ -233,7 +233,14 @@ impl TypeZoo {
pub fn create_type(name: &str, type_type: &PyClassRef, base: &PyClassRef) -> PyClassRef {
let dict = PyAttributes::new();
objtype::new(type_type.clone(), name, vec![base.clone()], dict).unwrap()
objtype::new(
type_type.clone(),
name,
base.clone(),
vec![base.clone()],
dict,
)
.unwrap()
}
fn init_type_hierarchy() -> (PyClassRef, PyClassRef) {

View File

@@ -624,7 +624,7 @@ impl VirtualMachine {
if let Some(descr_get) = slots.borrow().descr_get.as_ref() {
let cls = obj.class();
descr_get(self, vec![attr, obj.clone(), cls.into_object()].into())
} else if let Some(ref descriptor) = objtype::class_get_attr(&attr_class, "__get__") {
} else if let Some(ref descriptor) = attr_class.get_attr("__get__") {
let cls = obj.class();
self.invoke(descriptor, vec![attr, obj.clone(), cls.into_object()])
} else {
@@ -640,7 +640,7 @@ impl VirtualMachine {
// This is only used in the vm for magic methods, which use a greatly simplified attribute lookup.
let cls = obj.class();
match objtype::class_get_attr(&cls, method_name) {
match cls.get_attr(method_name) {
Some(func) => {
vm_trace!(
"vm.call_method {:?} {:?} {:?} -> {:?}",
@@ -666,7 +666,7 @@ impl VirtualMachine {
let result = slot_call(self, args);
self.trace_event(TraceEvent::Return)?;
result
} else if objtype::class_has_attr(&class, "__call__") {
} else if class.has_attr("__call__") {
let result = self.call_method(&callable, "__call__", args);
result
} else {
@@ -781,7 +781,7 @@ impl VirtualMachine {
F: FnOnce() -> String,
{
let cls = obj.class();
match objtype::class_get_attr(&cls, method_name) {
match cls.get_attr(method_name) {
Some(method) => self.call_get_descriptor(method, obj.clone()),
None => Err(self.new_type_error(err_msg())),
}
@@ -790,7 +790,7 @@ impl VirtualMachine {
/// May return exception, if `__get__` descriptor raises one
pub fn get_method(&self, obj: PyObjectRef, method_name: &str) -> Option<PyResult> {
let cls = obj.class();
let method = objtype::class_get_attr(&cls, method_name)?;
let method = cls.get_attr(method_name)?;
Some(self.call_get_descriptor(method, obj.clone()))
}
@@ -851,10 +851,10 @@ impl VirtualMachine {
let name = name_str.as_str();
let cls = obj.class();
if let Some(attr) = objtype::class_get_attr(&cls, &name) {
if let Some(attr) = cls.get_attr(&name) {
let attr_class = attr.class();
if objtype::class_has_attr(&attr_class, "__set__") {
if let Some(descriptor) = objtype::class_get_attr(&attr_class, "__get__") {
if attr_class.has_attr("__set__") {
if let Some(descriptor) = attr_class.get_attr("__get__") {
return self
.invoke(&descriptor, vec![attr, obj, cls.into_object()])
.map(Some);
@@ -870,9 +870,9 @@ impl VirtualMachine {
if let Some(obj_attr) = attr {
Ok(Some(obj_attr))
} else if let Some(attr) = objtype::class_get_attr(&cls, &name) {
} else if let Some(attr) = cls.get_attr(&name) {
self.call_get_descriptor(attr, obj).map(Some)
} else if let Some(getter) = objtype::class_get_attr(&cls, "__getattr__") {
} else if let Some(getter) = cls.get_attr("__getattr__") {
self.invoke(&getter, vec![obj, name_str.into_object()])
.map(Some)
} else {
@@ -881,8 +881,7 @@ impl VirtualMachine {
}
pub fn is_callable(&self, obj: &PyObjectRef) -> bool {
obj.class().slots.borrow().call.is_some()
|| objtype::class_has_attr(&obj.class(), "__call__")
obj.class().slots.borrow().call.is_some() || obj.class().has_attr("__call__")
}
#[inline]