Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions extra_tests/snippets/builtin_type.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import unittest.mock
from testutils import assert_raises


Expand Down Expand Up @@ -550,3 +551,12 @@ def my_repr_func():
pass

assert repr(my_repr_func).startswith('<function my_repr_func at 0x')

class GenLike:
def __iter__(self): pass
def __next__(self): pass

# gen = GenLike()
gen = unittest.mock.MagicMock(GenLike)
gen.__iter__ = lambda gen: gen
assert gen is iter(gen)
25 changes: 8 additions & 17 deletions vm/src/builtins/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,28 +252,19 @@ impl Constructor for PyInt {
.ok_or_else(|| {
vm.new_value_error("int() base must be >= 2 and <= 36, or 0".to_owned())
})?;
try_int_radix(&val, base, vm)
try_int_radix(&val, base, vm)?
} else {
let val = if cls.is(vm.ctx.types.int_type) {
match val.downcast_exact::<PyInt>(vm) {
Ok(i) => {
return Ok(i.to_pyobject(vm));
}
Err(val) => val,
}
} else {
val
};
if cls.is(vm.ctx.types.int_type) && val.class().is(vm.ctx.types.int_type) {
return Ok(val);
}

PyNumber::new(val.as_ref(), vm)
.int(vm)
.map(|x| x.as_bigint().clone())
PyNumber::new(val.as_ref(), vm).int(vm)?.as_bigint().clone()
}
} else if let OptionalArg::Present(_) = options.base {
Err(vm.new_type_error("int() missing string argument".to_owned()))
return Err(vm.new_type_error("int() missing string argument".to_owned()));
} else {
Ok(Zero::zero())
}?;
Zero::zero()
};

Self::with_value(cls, value, vm).to_pyresult(vm)
}
Expand Down
62 changes: 36 additions & 26 deletions vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ use super::{
mappingproxy::PyMappingProxy, object, union_, PyClassMethod, PyDictRef, PyList, PyStaticMethod,
PyStr, PyStrInterned, PyStrRef, PyTuple, PyTupleRef, PyWeak,
};
use crate::common::{
ascii,
borrow::BorrowedValue,
lock::{PyRwLock, PyRwLockReadGuard},
};
use crate::{
builtins::PyBaseExceptionRef,
class::{PyClassImpl, StaticType},
common::{
ascii,
borrow::BorrowedValue,
lock::{PyRwLock, PyRwLockReadGuard},
},
function::{FuncArgs, KwArgs, OptionalArg},
identifier,
protocol::PyNumberMethods,
types::{Callable, GetAttr, PyTypeFlags, PyTypeSlots, SetAttr},
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
Expand All @@ -30,10 +31,16 @@ pub struct PyType {
pub subclasses: PyRwLock<Vec<PyRef<PyWeak>>>,
pub attributes: PyRwLock<PyAttributes>,
pub slots: PyTypeSlots,
pub heaptype_ext: Option<Box<HeapTypeExt>>,
}

pub type PyTypeRef = PyRef<PyType>;

#[derive(Default)]
pub struct HeapTypeExt {
pub number_methods: PyNumberMethods,
}

/// For attributes we do not use a dict, but an IndexMap, which is an Hash Table
/// that maintains order and is compatible with the standard HashMap This is probably
/// faster and only supports strings as keys.
Expand Down Expand Up @@ -104,25 +111,27 @@ impl PyType {

*slots.name.get_mut() = Some(String::from(name));

let new_type = PyRef::new_ref(
PyType {
base: Some(base),
bases,
mro,
subclasses: PyRwLock::default(),
attributes: PyRwLock::new(attrs),
slots,
},
metaclass,
None,
);

for attr_name in new_type.attributes.read().keys() {
if attr_name.as_str().starts_with("__") && attr_name.as_str().ends_with("__") {
new_type.update_slot(attr_name, true);
}
}
let mut new_type = PyType {
base: Some(base),
bases,
mro,
subclasses: PyRwLock::default(),
attributes: PyRwLock::new(attrs),
slots,
heaptype_ext: Some(Box::new(HeapTypeExt::default())),
};
// metaclass,
// None,
// );

new_type.update_slots();
// for attr_name in new_type.attributes.read().keys() {
// if attr_name.as_str().starts_with("__") && attr_name.as_str().ends_with("__") {
// new_type.update_slot(attr_name, true);
// }
// }
let weakref_type = super::PyWeak::static_type();
let new_type = PyRef::new_ref(new_type, metaclass, None);
for base in &new_type.bases {
base.subclasses.write().push(
new_type
Expand Down Expand Up @@ -727,9 +736,10 @@ impl SetAttr for PyType {
));
}
}
if attr_name.as_str().starts_with("__") && attr_name.as_str().ends_with("__") {
zelf.update_slot(attr_name, assign);
}
// TODO
// if attr_name.as_str().starts_with("__") && attr_name.as_str().ends_with("__") {
// zelf.update_slot(attr_name, assign);
// }
Ok(())
}
}
Expand Down
3 changes: 3 additions & 0 deletions vm/src/object/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,7 @@ pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef, PyTypeRef) {
subclasses: PyRwLock::default(),
attributes: PyRwLock::new(Default::default()),
slots: PyType::make_slots(),
heaptype_ext: None,
};
let object_payload = PyType {
base: None,
Expand All @@ -1112,6 +1113,7 @@ pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef, PyTypeRef) {
subclasses: PyRwLock::default(),
attributes: PyRwLock::new(Default::default()),
slots: object::PyBaseObject::make_slots(),
heaptype_ext: None,
};
let type_type_ptr = Box::into_raw(Box::new(partially_init!(
PyInner::<PyType> {
Expand Down Expand Up @@ -1172,6 +1174,7 @@ pub(crate) fn init_type_hierarchy() -> (PyTypeRef, PyTypeRef, PyTypeRef) {
subclasses: PyRwLock::default(),
attributes: PyRwLock::default(),
slots: PyWeak::make_slots(),
heaptype_ext: None,
};
let weakref_type = PyRef::new_ref(weakref_type, type_type.clone(), None);

Expand Down
117 changes: 40 additions & 77 deletions vm/src/protocol/number.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use crate::{
builtins::{int, PyByteArray, PyBytes, PyComplex, PyFloat, PyInt, PyIntRef, PyStr},
common::borrow::BorrowedValue,
function::ArgBytesLike,
stdlib::warnings,
AsObject, PyObject, PyPayload, PyRef, PyResult, TryFromBorrowedObject, VirtualMachine,
};

#[allow(clippy::type_complexity)]
#[derive(Clone)]
#[derive(Default, Clone)]
pub struct PyNumberMethods {
/* Number implementations must check *both*
arguments for proper type and implement the necessary conversions
Expand Down Expand Up @@ -92,64 +93,11 @@ impl PyNumberMethods {
matrix_multiply: None,
inplace_matrix_multiply: None,
};

pub(crate) fn generic(
has_int: bool,
has_float: bool,
has_index: bool,
) -> &'static PyNumberMethods {
static METHODS: &[PyNumberMethods] = &[
new_generic(false, false, false),
new_generic(true, false, false),
new_generic(false, true, false),
new_generic(true, true, false),
new_generic(false, false, true),
new_generic(true, false, true),
new_generic(false, true, true),
new_generic(true, true, true),
];

fn int(num: &PyNumber, vm: &VirtualMachine) -> PyResult<PyRef<PyInt>> {
let ret = vm.call_special_method(num.obj.to_owned(), identifier!(vm, __int__), ())?;
ret.downcast::<PyInt>().map_err(|obj| {
vm.new_type_error(format!("__int__ returned non-int (type {})", obj.class()))
})
}
fn float(num: &PyNumber, vm: &VirtualMachine) -> PyResult<PyRef<PyFloat>> {
let ret = vm.call_special_method(num.obj.to_owned(), identifier!(vm, __float__), ())?;
ret.downcast::<PyFloat>().map_err(|obj| {
vm.new_type_error(format!(
"__float__ returned non-float (type {})",
obj.class()
))
})
}
fn index(num: &PyNumber, vm: &VirtualMachine) -> PyResult<PyRef<PyInt>> {
let ret = vm.call_special_method(num.obj.to_owned(), identifier!(vm, __index__), ())?;
ret.downcast::<PyInt>().map_err(|obj| {
vm.new_type_error(format!("__index__ returned non-int (type {})", obj.class()))
})
}

const fn new_generic(has_int: bool, has_float: bool, has_index: bool) -> PyNumberMethods {
PyNumberMethods {
int: if has_int { Some(int) } else { None },
float: if has_float { Some(float) } else { None },
index: if has_index { Some(index) } else { None },
..PyNumberMethods::NOT_IMPLEMENTED
}
}

let key = (has_int as usize) | ((has_float as usize) << 1) | ((has_index as usize) << 2);

&METHODS[key]
}
}

pub struct PyNumber<'a> {
pub obj: &'a PyObject,
// some fast path do not need methods, so we do lazy initialize
pub methods: Option<&'static PyNumberMethods>,
pub methods: BorrowedValue<'a, PyNumberMethods>,
}

impl<'a> PyNumber<'a> {
Expand All @@ -159,32 +107,31 @@ impl<'a> PyNumber<'a> {
methods: Self::find_methods(obj, vm),
}
}
}

impl PyNumber<'_> {
pub fn find_methods(obj: &PyObject, vm: &VirtualMachine) -> Option<&'static PyNumberMethods> {
pub fn find_methods(
obj: &'a PyObject,
vm: &VirtualMachine,
) -> BorrowedValue<'a, PyNumberMethods> {
obj.class()
.mro_find_map(|x| x.slots.as_number.load())
.map(|f| f(obj, vm))
}

pub fn methods(&self) -> &'static PyNumberMethods {
self.methods.unwrap_or(&PyNumberMethods::NOT_IMPLEMENTED)
.unwrap_or(BorrowedValue::Ref(&PyNumberMethods::NOT_IMPLEMENTED))
}

// PyNumber_Check
pub fn check(obj: &PyObject, vm: &VirtualMachine) -> bool {
Self::find_methods(obj, vm).map_or(false, |methods| {
methods.int.is_some()
|| methods.index.is_some()
|| methods.float.is_some()
|| obj.payload_is::<PyComplex>()
})
pub fn check(obj: &'a PyObject, vm: &VirtualMachine) -> bool {
let methods = Self::find_methods(obj, vm);
methods.int.is_some()
|| methods.index.is_some()
|| methods.float.is_some()
|| obj.payload_is::<PyComplex>()
}
}

impl PyNumber<'_> {
// PyIndex_Check
pub fn is_index(&self) -> bool {
self.methods().index.is_some()
self.methods.index.is_some()
}

pub fn int(&self, vm: &VirtualMachine) -> PyResult<PyIntRef> {
Expand All @@ -202,7 +149,7 @@ impl PyNumber<'_> {

if let Some(i) = self.obj.downcast_ref_if_exact::<PyInt>(vm) {
Ok(i.to_owned())
} else if let Some(f) = self.methods().int {
} else if let Some(f) = self.methods.int {
let ret = f(self, vm)?;
if !ret.class().is(PyInt::class(vm)) {
warnings::warn(
Expand All @@ -220,7 +167,7 @@ impl PyNumber<'_> {
} else {
Ok(ret)
}
} else if self.methods().index.is_some() {
} else if self.methods.index.is_some() {
self.index(vm)
} else if let Ok(Ok(f)) =
vm.get_special_method(self.obj.to_owned(), identifier!(vm, __trunc__))
Expand All @@ -233,12 +180,13 @@ impl PyNumber<'_> {
// vm,
// )?;
let ret = f.invoke((), vm)?;
PyNumber::new(ret.as_ref(), vm).index(vm).map_err(|_| {
let ret = PyNumber::new(ret.as_ref(), vm).index(vm).map_err(|_| {
vm.new_type_error(format!(
"__trunc__ returned non-Integral (type {})",
ret.class()
))
})
});
ret
} else if let Some(s) = self.obj.payload::<PyStr>() {
try_convert(self.obj, s.as_str().as_bytes(), vm)
} else if let Some(bytes) = self.obj.payload::<PyBytes>() {
Expand All @@ -261,7 +209,7 @@ impl PyNumber<'_> {
Ok(Some(i.to_owned()))
} else if let Some(i) = self.obj.payload::<PyInt>() {
Ok(Some(vm.ctx.new_bigint(i.as_bigint())))
} else if let Some(f) = self.methods().index {
} else if let Some(f) = self.methods.index {
let ret = f(self, vm)?;
if !ret.class().is(PyInt::class(vm)) {
warnings::warn(
Expand Down Expand Up @@ -296,7 +244,7 @@ impl PyNumber<'_> {
pub fn float_opt(&self, vm: &VirtualMachine) -> PyResult<Option<PyRef<PyFloat>>> {
if let Some(float) = self.obj.downcast_ref_if_exact::<PyFloat>(vm) {
Ok(Some(float.to_owned()))
} else if let Some(f) = self.methods().float {
} else if let Some(f) = self.methods.float {
let ret = f(self, vm)?;
if !ret.class().is(PyFloat::class(vm)) {
warnings::warn(
Expand All @@ -314,7 +262,7 @@ impl PyNumber<'_> {
} else {
Ok(Some(ret))
}
} else if self.methods().index.is_some() {
} else if self.methods.index.is_some() {
let i = self.index(vm)?;
let value = int::try_to_float(i.as_bigint(), vm)?;
Ok(Some(vm.ctx.new_float(value)))
Expand All @@ -330,4 +278,19 @@ impl PyNumber<'_> {
vm.new_type_error(format!("must be real number, not {}", self.obj.class()))
})
}

// pub fn add(&self, other: &PyObject, vm: &VirtualMachine) -> PyResult {
// let slotv = self.methods.add;
// if !other.class().is(&self.obj.class()) && other.class().fast_issubclass(&self.obj.class()) {
// // fallback?
// let other = PyNumber::new(other, vm);
// let slotw = other.methods.add;
// if let Some(slotw) = slotw {
// let ret = slotw(self, other.obj, vm)?;
// if !ret.is(&vm.ctx.not_implemented) {
// return Ok(ret);
// }
// }
// }
// }
}
Loading