forked from Rust-related/RustPython
Merge pull request #1712 from youknowone/basetype
Add Py_TPFLAGS_BASETYPE support to restrict subclass
This commit is contained in:
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -59,7 +59,7 @@ impl PyBuiltinDescriptor for PyClassMethod {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(PyBuiltinDescriptor))]
|
||||
#[pyimpl(with(PyBuiltinDescriptor), flags(BASETYPE))]
|
||||
impl PyClassMethod {
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -23,7 +23,7 @@ impl PyValue for PyFilter {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
#[pyimpl(flags(BASETYPE))]
|
||||
impl PyFilter {
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -22,7 +22,7 @@ impl PyValue for PyMap {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
#[pyimpl(flags(BASETYPE))]
|
||||
impl PyMap {
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>> {
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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![]);
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()))
|
||||
|
||||
@@ -28,7 +28,7 @@ impl PyBuiltinDescriptor for PyStaticMethod {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(PyBuiltinDescriptor))]
|
||||
#[pyimpl(with(PyBuiltinDescriptor), flags(BASETYPE))]
|
||||
impl PyStaticMethod {
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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![
|
||||
|
||||
@@ -41,7 +41,7 @@ impl PyBuiltinCallable for PyWeak {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl(with(PyBuiltinCallable))]
|
||||
#[pyimpl(with(PyBuiltinCallable), flags(BASETYPE))]
|
||||
impl PyWeak {
|
||||
// TODO callbacks
|
||||
#[pyslot]
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -37,7 +37,7 @@ impl PyDeque {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
#[pyimpl(flags(BASETYPE))]
|
||||
impl PyDeque {
|
||||
#[pyslot]
|
||||
fn tp_new(
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)?
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
23
vm/src/vm.rs
23
vm/src/vm.rs
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user