Merge pull request #3819 from qingshi163/heaptypeext

HeapTypeExt and Protocol Methods Pointer
This commit is contained in:
Jeong YunWon
2022-07-25 02:56:12 +09:00
committed by GitHub
30 changed files with 635 additions and 437 deletions

View File

@@ -564,10 +564,15 @@ where
let slot_name = slot_ident.to_string();
let tokens = {
const NON_ATOMIC_SLOTS: &[&str] = &["as_buffer"];
const POINTER_SLOTS: &[&str] = &["as_number"];
if NON_ATOMIC_SLOTS.contains(&slot_name.as_str()) {
quote_spanned! { span =>
slots.#slot_ident = Some(Self::#ident as _);
}
} else if POINTER_SLOTS.contains(&slot_name.as_str()) {
quote_spanned! { span =>
slots.#slot_ident.store(Some(PointerSlot::from(Self::#ident())));
}
} else {
quote_spanned! { span =>
slots.#slot_ident.store(Some(Self::#ident as _));

View File

@@ -25,7 +25,7 @@ mod _bisect {
vm: &VirtualMachine,
) -> PyResult<Option<isize>> {
arg.into_option()
.map(|v| vm.to_index(&v)?.try_to_primitive(vm))
.map(|v| v.try_index(vm)?.try_to_primitive(vm))
.transpose()
}

View File

@@ -199,7 +199,7 @@ mod math {
#[pyfunction]
fn isqrt(x: PyObjectRef, vm: &VirtualMachine) -> PyResult<BigInt> {
let index = vm.to_index(&x)?;
let index = x.try_index(vm)?;
let value = index.as_bigint();
if value.is_negative() {

View File

@@ -347,7 +347,7 @@ mod decl {
Some(ms) => {
let ms = if let Some(float) = ms.payload::<PyFloat>() {
float.to_f64().to_i32()
} else if let Some(int) = vm.to_index_opt(ms.clone()) {
} else if let Some(int) = ms.try_index_opt(vm) {
int?.as_bigint().to_i32()
} else {
return Err(vm.new_type_error(format!(

View File

@@ -734,8 +734,8 @@ mod _socket {
if obj.fast_isinstance(vm.ctx.types.float_type) {
return Err(vm.new_type_error("integer argument expected, got float".to_owned()));
}
let int = vm
.to_index_opt(obj)
let int = obj
.try_index_opt(vm)
.unwrap_or_else(|| Err(vm.new_type_error("an integer is required".to_owned())))?;
int.try_to_primitive::<CastFrom>(vm)
.map(|sock| sock as RawSocket)
@@ -1382,7 +1382,7 @@ mod _socket {
let (flags, address) = match arg3 {
OptionalArg::Present(arg3) => {
// should just be i32::try_from_obj but tests check for error message
let int = vm.to_index_opt(arg2).unwrap_or_else(|| {
let int = arg2.try_index_opt(vm).unwrap_or_else(|| {
Err(vm.new_type_error("an integer is required".to_owned()))
})?;
let flags = int.try_to_primitive::<i32>(vm)?;

View File

@@ -491,15 +491,15 @@ fn get_int_or_index<T>(vm: &VirtualMachine, arg: PyObjectRef) -> PyResult<T>
where
T: PrimInt + for<'a> TryFrom<&'a BigInt>,
{
match vm.to_index_opt(arg) {
Some(index) => index?
.try_to_primitive(vm)
.map_err(|_| new_struct_error(vm, "argument out of range".to_owned())),
None => Err(new_struct_error(
let index = arg.try_index_opt(vm).unwrap_or_else(|| {
Err(new_struct_error(
vm,
"required argument is not an integer".to_owned(),
)),
}
))
})?;
index
.try_to_primitive(vm)
.map_err(|_| new_struct_error(vm, "argument out of range".to_owned()))
}
make_pack_primint!(i8);

View File

@@ -3,6 +3,7 @@ use super::{
};
use crate::{
anystr::{self, AnyStr},
atomic_func,
bytesinner::{
bytes_decode, ByteInnerFindOptions, ByteInnerNewOptions, ByteInnerPaddingOptions,
ByteInnerSplitOptions, ByteInnerTranslateOptions, DecodeArgs, PyBytesInner,
@@ -608,14 +609,17 @@ impl AsSequence for PyBytes {
}
impl AsNumber for PyBytes {
const AS_NUMBER: PyNumberMethods = PyNumberMethods {
remainder: Some(|number, other, vm| {
Self::number_downcast(number)
.mod_(other.to_owned(), vm)
.to_pyresult(vm)
}),
..PyNumberMethods::NOT_IMPLEMENTED
};
fn as_number() -> &'static PyNumberMethods {
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
remainder: atomic_func!(|number, other, vm| {
PyBytes::number_downcast(number)
.mod_(other.to_owned(), vm)
.to_pyresult(vm)
}),
..PyNumberMethods::NOT_IMPLEMENTED
};
&AS_NUMBER
}
}
impl Hashable for PyBytes {

View File

@@ -1,5 +1,6 @@
use super::{float, PyStr, PyType, PyTypeRef};
use crate::{
atomic_func,
class::PyClassImpl,
convert::{ToPyObject, ToPyResult},
function::{
@@ -421,30 +422,44 @@ impl Hashable for PyComplex {
}
impl AsNumber for PyComplex {
const AS_NUMBER: PyNumberMethods = PyNumberMethods {
add: Some(|number, other, vm| Self::number_complex_op(number, other, |a, b| a + b, vm)),
subtract: Some(|number, other, vm| {
Self::number_complex_op(number, other, |a, b| a - b, vm)
}),
multiply: Some(|number, other, vm| {
Self::number_complex_op(number, other, |a, b| a * b, vm)
}),
power: Some(|number, other, vm| Self::number_general_op(number, other, inner_pow, vm)),
negative: Some(|number, vm| {
let value = Self::number_downcast(number).value;
(-value).to_pyresult(vm)
}),
positive: Some(|number, vm| Self::number_complex(number, vm).to_pyresult(vm)),
absolute: Some(|number, vm| {
let value = Self::number_downcast(number).value;
value.norm().to_pyresult(vm)
}),
boolean: Some(|number, _vm| Ok(Self::number_downcast(number).value.is_zero())),
true_divide: Some(|number, other, vm| {
Self::number_general_op(number, other, inner_div, vm)
}),
..PyNumberMethods::NOT_IMPLEMENTED
};
fn as_number() -> &'static PyNumberMethods {
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
add: atomic_func!(|number, other, vm| PyComplex::number_complex_op(
number,
other,
|a, b| a + b,
vm
)),
subtract: atomic_func!(|number, other, vm| {
PyComplex::number_complex_op(number, other, |a, b| a - b, vm)
}),
multiply: atomic_func!(|number, other, vm| {
PyComplex::number_complex_op(number, other, |a, b| a * b, vm)
}),
power: atomic_func!(|number, other, vm| PyComplex::number_general_op(
number, other, inner_pow, vm
)),
negative: atomic_func!(|number, vm| {
let value = PyComplex::number_downcast(number).value;
(-value).to_pyresult(vm)
}),
positive: atomic_func!(
|number, vm| PyComplex::number_complex(number, vm).to_pyresult(vm)
),
absolute: atomic_func!(|number, vm| {
let value = PyComplex::number_downcast(number).value;
value.norm().to_pyresult(vm)
}),
boolean: atomic_func!(|number, _vm| Ok(PyComplex::number_downcast(number)
.value
.is_zero())),
true_divide: atomic_func!(|number, other, vm| {
PyComplex::number_general_op(number, other, inner_div, vm)
}),
..PyNumberMethods::NOT_IMPLEMENTED
};
&AS_NUMBER
}
}
impl PyComplex {

View File

@@ -2,6 +2,7 @@ use super::{
try_bigint_to_f64, PyByteArray, PyBytes, PyInt, PyIntRef, PyStr, PyStrRef, PyType, PyTypeRef,
};
use crate::{
atomic_func,
class::PyClassImpl,
common::{float_ops, hash},
convert::{ToPyObject, ToPyResult},
@@ -57,16 +58,6 @@ impl From<f64> for PyFloat {
}
}
impl PyObject {
pub fn try_float_opt(&self, vm: &VirtualMachine) -> PyResult<Option<PyRef<PyFloat>>> {
PyNumber::new(self, vm).float_opt(vm)
}
pub fn try_float(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyFloat>> {
PyNumber::new(self, vm).float(vm)
}
}
pub(crate) fn to_op_float(obj: &PyObject, vm: &VirtualMachine) -> PyResult<Option<f64>> {
let v = if let Some(float) = obj.payload_if_subclass::<PyFloat>(vm) {
Some(float.value)
@@ -553,36 +544,63 @@ impl Hashable for PyFloat {
}
impl AsNumber for PyFloat {
const AS_NUMBER: PyNumberMethods = PyNumberMethods {
add: Some(|number, other, vm| Self::number_float_op(number, other, |a, b| a + b, vm)),
subtract: Some(|number, other, vm| Self::number_float_op(number, other, |a, b| a - b, vm)),
multiply: Some(|number, other, vm| Self::number_float_op(number, other, |a, b| a * b, vm)),
remainder: Some(|number, other, vm| Self::number_general_op(number, other, inner_mod, vm)),
divmod: Some(|number, other, vm| Self::number_general_op(number, other, inner_divmod, vm)),
power: Some(|number, other, vm| Self::number_general_op(number, other, float_pow, vm)),
negative: Some(|number, vm| {
let value = Self::number_downcast(number).value;
(-value).to_pyresult(vm)
}),
positive: Some(|number, vm| Self::number_float(number, vm).to_pyresult(vm)),
absolute: Some(|number, vm| {
let value = Self::number_downcast(number).value;
value.abs().to_pyresult(vm)
}),
boolean: Some(|number, _vm| Ok(Self::number_downcast(number).value.is_zero())),
int: Some(|number, vm| {
let value = Self::number_downcast(number).value;
try_to_bigint(value, vm).map(|x| vm.ctx.new_int(x))
}),
float: Some(|number, vm| Ok(Self::number_float(number, vm))),
floor_divide: Some(|number, other, vm| {
Self::number_general_op(number, other, inner_floordiv, vm)
}),
true_divide: Some(|number, other, vm| {
Self::number_general_op(number, other, inner_div, vm)
}),
..PyNumberMethods::NOT_IMPLEMENTED
};
fn as_number() -> &'static PyNumberMethods {
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
add: atomic_func!(|num, other, vm| PyFloat::number_float_op(
num,
other,
|a, b| a + b,
vm
)),
subtract: atomic_func!(|num, other, vm| PyFloat::number_float_op(
num,
other,
|a, b| a - b,
vm
)),
multiply: atomic_func!(|num, other, vm| PyFloat::number_float_op(
num,
other,
|a, b| a * b,
vm
)),
remainder: atomic_func!(|num, other, vm| PyFloat::number_general_op(
num, other, inner_mod, vm
)),
divmod: atomic_func!(|num, other, vm| PyFloat::number_general_op(
num,
other,
inner_divmod,
vm
)),
power: atomic_func!(|num, other, vm| PyFloat::number_general_op(
num, other, float_pow, vm
)),
negative: atomic_func!(|num, vm| {
let value = PyFloat::number_downcast(num).value;
(-value).to_pyresult(vm)
}),
positive: atomic_func!(|num, vm| PyFloat::number_float(num, vm).to_pyresult(vm)),
absolute: atomic_func!(|num, vm| {
let value = PyFloat::number_downcast(num).value;
value.abs().to_pyresult(vm)
}),
boolean: atomic_func!(|num, _vm| Ok(PyFloat::number_downcast(num).value.is_zero())),
int: atomic_func!(|num, vm| {
let value = PyFloat::number_downcast(num).value;
try_to_bigint(value, vm).map(|x| vm.ctx.new_int(x))
}),
float: atomic_func!(|num, vm| Ok(PyFloat::number_float(num, vm))),
floor_divide: atomic_func!(|num, other, vm| {
PyFloat::number_general_op(num, other, inner_floordiv, vm)
}),
true_divide: atomic_func!(|num, other, vm| {
PyFloat::number_general_op(num, other, inner_div, vm)
}),
..PyNumberMethods::NOT_IMPLEMENTED
};
&AS_NUMBER
}
}
impl PyFloat {

View File

@@ -1,5 +1,6 @@
use super::{float, PyByteArray, PyBytes, PyStr, PyStrRef, PyType, PyTypeRef};
use crate::{
atomic_func,
bytesinner::PyBytesInner,
class::PyClassImpl,
common::hash,
@@ -68,7 +69,7 @@ impl PyPayload for PyInt {
}
fn special_retrieve(vm: &VirtualMachine, obj: &PyObject) -> Option<PyResult<PyRef<Self>>> {
Some(vm.to_index(obj))
Some(obj.try_index(vm))
}
}
@@ -244,8 +245,8 @@ impl Constructor for PyInt {
fn py_new(cls: PyTypeRef, options: Self::Args, vm: &VirtualMachine) -> PyResult {
let value = if let OptionalArg::Present(val) = options.val_options {
if let OptionalArg::Present(base) = options.base {
let base = vm
.to_index(&base)?
let base = base
.try_index(vm)?
.as_bigint()
.to_u32()
.filter(|&v| v == 0 || (2..=36).contains(&v))
@@ -265,9 +266,7 @@ impl Constructor for PyInt {
val
};
PyNumber::new(val.as_ref(), vm)
.int(vm)
.map(|x| x.as_bigint().clone())
val.try_int(vm).map(|x| x.as_bigint().clone())
}
} else if let OptionalArg::Present(_) = options.base {
Err(vm.new_type_error("int() missing string argument".to_owned()))
@@ -756,37 +755,76 @@ impl Hashable for PyInt {
}
impl AsNumber for PyInt {
const AS_NUMBER: PyNumberMethods = PyNumberMethods {
add: Some(|number, other, vm| Self::number_int_op(number, other, |a, b| a + b, vm)),
subtract: Some(|number, other, vm| Self::number_int_op(number, other, |a, b| a - b, vm)),
multiply: Some(|number, other, vm| Self::number_int_op(number, other, |a, b| a * b, vm)),
remainder: Some(|number, other, vm| Self::number_general_op(number, other, inner_mod, vm)),
divmod: Some(|number, other, vm| Self::number_general_op(number, other, inner_divmod, vm)),
power: Some(|number, other, vm| Self::number_general_op(number, other, inner_pow, vm)),
negative: Some(|number, vm| (&Self::number_downcast(number).value).neg().to_pyresult(vm)),
positive: Some(|number, vm| Ok(Self::number_int(number, vm).into())),
absolute: Some(|number, vm| Self::number_downcast(number).value.abs().to_pyresult(vm)),
boolean: Some(|number, _vm| Ok(Self::number_downcast(number).value.is_zero())),
invert: Some(|number, vm| (&Self::number_downcast(number).value).not().to_pyresult(vm)),
lshift: Some(|number, other, vm| Self::number_general_op(number, other, inner_lshift, vm)),
rshift: Some(|number, other, vm| Self::number_general_op(number, other, inner_rshift, vm)),
and: Some(|number, other, vm| Self::number_int_op(number, other, |a, b| a & b, vm)),
xor: Some(|number, other, vm| Self::number_int_op(number, other, |a, b| a ^ b, vm)),
or: Some(|number, other, vm| Self::number_int_op(number, other, |a, b| a | b, vm)),
int: Some(|number, other| Ok(Self::number_int(number, other))),
float: Some(|number, vm| {
let zelf = Self::number_downcast(number);
try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x))
}),
floor_divide: Some(|number, other, vm| {
Self::number_general_op(number, other, inner_floordiv, vm)
}),
true_divide: Some(|number, other, vm| {
Self::number_general_op(number, other, inner_truediv, vm)
}),
index: Some(|number, vm| Ok(Self::number_int(number, vm))),
..PyNumberMethods::NOT_IMPLEMENTED
};
fn as_number() -> &'static PyNumberMethods {
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
add: atomic_func!(|num, other, vm| PyInt::number_int_op(num, other, |a, b| a + b, vm)),
subtract: atomic_func!(|num, other, vm| PyInt::number_int_op(
num,
other,
|a, b| a - b,
vm
)),
multiply: atomic_func!(|num, other, vm| PyInt::number_int_op(
num,
other,
|a, b| a * b,
vm
)),
remainder: atomic_func!(|num, other, vm| PyInt::number_general_op(
num, other, inner_mod, vm
)),
divmod: atomic_func!(|num, other, vm| PyInt::number_general_op(
num,
other,
inner_divmod,
vm
)),
power: atomic_func!(|num, other, vm| PyInt::number_general_op(
num, other, inner_pow, vm
)),
negative: atomic_func!(|num, vm| (&PyInt::number_downcast(num).value)
.neg()
.to_pyresult(vm)),
positive: atomic_func!(|num, vm| Ok(PyInt::number_int(num, vm).into())),
absolute: atomic_func!(|num, vm| PyInt::number_downcast(num)
.value
.abs()
.to_pyresult(vm)),
boolean: atomic_func!(|num, _vm| Ok(PyInt::number_downcast(num).value.is_zero())),
invert: atomic_func!(|num, vm| (&PyInt::number_downcast(num).value)
.not()
.to_pyresult(vm)),
lshift: atomic_func!(|num, other, vm| PyInt::number_general_op(
num,
other,
inner_lshift,
vm
)),
rshift: atomic_func!(|num, other, vm| PyInt::number_general_op(
num,
other,
inner_rshift,
vm
)),
and: atomic_func!(|num, other, vm| PyInt::number_int_op(num, other, |a, b| a & b, vm)),
xor: atomic_func!(|num, other, vm| PyInt::number_int_op(num, other, |a, b| a ^ b, vm)),
or: atomic_func!(|num, other, vm| PyInt::number_int_op(num, other, |a, b| a | b, vm)),
int: atomic_func!(|num, other| Ok(PyInt::number_int(num, other))),
float: atomic_func!(|num, vm| {
let zelf = PyInt::number_downcast(num);
try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x))
}),
floor_divide: atomic_func!(|num, other, vm| {
PyInt::number_general_op(num, other, inner_floordiv, vm)
}),
true_divide: atomic_func!(|num, other, vm| {
PyInt::number_general_op(num, other, inner_truediv, vm)
}),
index: atomic_func!(|num, vm| Ok(PyInt::number_int(num, vm))),
..PyNumberMethods::NOT_IMPLEMENTED
};
&AS_NUMBER
}
}
impl PyInt {

View File

@@ -1,5 +1,6 @@
use super::{PyDict, PyDictRef, PyGenericAlias, PyList, PyTuple, PyType, PyTypeRef};
use crate::{
atomic_func,
class::PyClassImpl,
convert::ToPyObject,
function::{ArgMapping, OptionalArg, PyComparisonValue},
@@ -217,11 +218,18 @@ impl AsSequence for PyMappingProxy {
}
impl AsNumber for PyMappingProxy {
const AS_NUMBER: PyNumberMethods = PyNumberMethods {
or: Some(|num, args, vm| Self::number_downcast(num).or(args.to_pyobject(vm), vm)),
inplace_or: Some(|num, args, vm| Self::number_downcast(num).ior(args.to_pyobject(vm), vm)),
..PyNumberMethods::NOT_IMPLEMENTED
};
fn as_number() -> &'static PyNumberMethods {
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
or: atomic_func!(|num, args, vm| {
PyMappingProxy::number_downcast(num).or(args.to_pyobject(vm), vm)
}),
inplace_or: atomic_func!(|num, args, vm| {
PyMappingProxy::number_downcast(num).ior(args.to_pyobject(vm), vm)
}),
..PyNumberMethods::NOT_IMPLEMENTED
};
&AS_NUMBER
}
}
impl Iterable for PyMappingProxy {

View File

@@ -904,7 +904,7 @@ impl TryFromObject for SubscriptNeedle {
Ok(Self::Index(i.try_to_primitive(vm)?))
} else if obj.payload_is::<PySlice>() {
Ok(Self::Slice(unsafe { obj.downcast_unchecked::<PySlice>() }))
} else if let Ok(i) = vm.to_index(&obj) {
} else if let Ok(i) = obj.try_index(vm) {
Ok(Self::Index(i.try_to_primitive(vm)?))
} else {
if let Some(tuple) = obj.payload::<PyTuple>() {

View File

@@ -682,7 +682,7 @@ impl TryFromObject for RangeIndex {
i @ PyInt => Ok(RangeIndex::Int(i)),
s @ PySlice => Ok(RangeIndex::Slice(s)),
obj => {
let val = vm.to_index(&obj).map_err(|_| vm.new_type_error(format!(
let val = obj.try_index(vm).map_err(|_| vm.new_type_error(format!(
"sequence indices be integers or slices or classes that override __index__ operator, not '{}'",
obj.class().name()
)))?;

View File

@@ -1,5 +1,6 @@
use super::{PyType, PyTypeRef};
use crate::{
atomic_func,
class::PyClassImpl,
convert::ToPyObject,
protocol::PyNumberMethods,
@@ -56,10 +57,13 @@ impl PyNone {
}
impl AsNumber for PyNone {
const AS_NUMBER: PyNumberMethods = PyNumberMethods {
boolean: Some(|_number, _vm| Ok(false)),
..PyNumberMethods::NOT_IMPLEMENTED
};
fn as_number() -> &'static PyNumberMethods {
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
boolean: atomic_func!(|_number, _vm| Ok(false)),
..PyNumberMethods::NOT_IMPLEMENTED
};
&AS_NUMBER
}
}
#[pyclass(module = false, name = "NotImplementedType")]

View File

@@ -12,12 +12,13 @@ use crate::{
class::{PyClassImpl, StaticType},
function::{FuncArgs, KwArgs, OptionalArg},
identifier,
protocol::PyNumberMethods,
types::{Callable, GetAttr, PyTypeFlags, PyTypeSlots, SetAttr},
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
use indexmap::{map::Entry, IndexMap};
use itertools::Itertools;
use std::{borrow::Borrow, collections::HashSet, fmt, ops::Deref};
use std::{borrow::Borrow, collections::HashSet, fmt, ops::Deref, pin::Pin, ptr::NonNull};
/// type(object_or_name, bases, dict)
/// type(object) -> the object's type
@@ -30,10 +31,62 @@ pub struct PyType {
pub subclasses: PyRwLock<Vec<PyRef<PyWeak>>>,
pub attributes: PyRwLock<PyAttributes>,
pub slots: PyTypeSlots,
pub heaptype_ext: Option<Pin<Box<HeapTypeExt>>>,
}
#[derive(Default)]
pub struct HeapTypeExt {
pub number_methods: PyNumberMethods,
}
pub struct PointerSlot<T>(NonNull<T>);
impl<T> PointerSlot<T> {
pub unsafe fn borrow_static(&self) -> &'static T {
self.0.as_ref()
}
}
impl<T> Clone for PointerSlot<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for PointerSlot<T> {}
impl<T> From<&'static T> for PointerSlot<T> {
fn from(x: &'static T) -> Self {
Self(NonNull::from(x))
}
}
impl<T> AsRef<T> for PointerSlot<T> {
fn as_ref(&self) -> &T {
unsafe { self.0.as_ref() }
}
}
impl<T> PointerSlot<T> {
pub unsafe fn from_heaptype<F>(typ: &PyType, f: F) -> Option<Self>
where
F: FnOnce(&HeapTypeExt) -> &T,
{
typ.heaptype_ext
.as_ref()
.map(|ext| Self(NonNull::from(f(ext))))
}
}
pub type PyTypeRef = PyRef<PyType>;
cfg_if::cfg_if! {
if #[cfg(feature = "threading")] {
unsafe impl Send for PyType {}
unsafe impl Sync for PyType {}
}
}
/// 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.
@@ -112,6 +165,7 @@ impl PyType {
subclasses: PyRwLock::default(),
attributes: PyRwLock::new(attrs),
slots,
heaptype_ext: Some(Pin::new(Box::new(HeapTypeExt::default()))),
},
metaclass,
None,

View File

@@ -20,7 +20,7 @@ pub fn bytes_from_object(vm: &VirtualMachine, obj: &PyObject) -> PyResult<Vec<u8
}
pub fn value_from_object(vm: &VirtualMachine, obj: &PyObject) -> PyResult<u8> {
vm.to_index(obj)?
obj.try_index(vm)?
.as_bigint()
.to_u8()
.ok_or_else(|| vm.new_value_error("byte must be in range(0, 256)".to_owned()))

View File

@@ -108,7 +108,7 @@ impl TryFromObject for std::time::Duration {
use std::time::Duration;
if let Some(float) = obj.payload::<PyFloat>() {
Ok(Duration::from_secs_f64(float.to_f64()))
} else if let Some(int) = vm.to_index_opt(obj.clone()) {
} else if let Some(int) = obj.try_index_opt(vm) {
let sec = int?
.as_bigint()
.to_u64()

View File

@@ -717,7 +717,7 @@ impl DictKey for PyObject {
}
#[inline]
fn key_as_isize(&self, vm: &VirtualMachine) -> PyResult<isize> {
vm.to_index(self)?.try_to_primitive(vm)
self.try_index(vm)?.try_to_primitive(vm)
}
}

View File

@@ -1,4 +1,4 @@
use crate::{protocol::PyNumber, AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine};
use crate::{AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine};
use num_complex::Complex64;
use std::ops::Deref;
@@ -82,7 +82,7 @@ impl Deref for ArgIntoFloat {
impl TryFromObject for ArgIntoFloat {
// Equivalent to PyFloat_AsDouble.
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let value = PyNumber::new(obj.as_ref(), vm).float(vm)?.to_f64();
let value = obj.try_float(vm)?.to_f64();
Ok(ArgIntoFloat { value })
}
}

View File

@@ -1105,6 +1105,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,
@@ -1113,6 +1114,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> {
@@ -1173,6 +1175,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);

View File

@@ -1,192 +1,48 @@
use crate::{
builtins::{int, PyByteArray, PyBytes, PyComplex, PyFloat, PyInt, PyIntRef, PyStr},
builtins::{
int, type_::PointerSlot, PyByteArray, PyBytes, PyComplex, PyFloat, PyInt, PyIntRef, PyStr,
},
function::ArgBytesLike,
stdlib::warnings,
AsObject, PyObject, PyPayload, PyRef, PyResult, TryFromBorrowedObject, VirtualMachine,
AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject,
VirtualMachine,
};
use crossbeam_utils::atomic::AtomicCell;
#[allow(clippy::type_complexity)]
#[derive(Clone)]
pub struct PyNumberMethods {
/* Number implementations must check *both*
arguments for proper type and implement the necessary conversions
in the slot functions themselves. */
pub add: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub subtract: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub multiply: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub remainder: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub divmod: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub power: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub negative: Option<fn(&PyNumber, &VirtualMachine) -> PyResult>,
pub positive: Option<fn(&PyNumber, &VirtualMachine) -> PyResult>,
pub absolute: Option<fn(&PyNumber, &VirtualMachine) -> PyResult>,
pub boolean: Option<fn(&PyNumber, &VirtualMachine) -> PyResult<bool>>,
pub invert: Option<fn(&PyNumber, &VirtualMachine) -> PyResult>,
pub lshift: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub rshift: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub and: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub xor: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub or: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub int: Option<fn(&PyNumber, &VirtualMachine) -> PyResult<PyIntRef>>,
pub float: Option<fn(&PyNumber, &VirtualMachine) -> PyResult<PyRef<PyFloat>>>,
type UnaryFunc<R = PyObjectRef> = AtomicCell<Option<fn(&PyNumber, &VirtualMachine) -> PyResult<R>>>;
type BinaryFunc<R = PyObjectRef> =
AtomicCell<Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult<R>>>;
pub inplace_add: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_subtract: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_multiply: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_remainder: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_divmod: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_power: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_lshift: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_rshift: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_and: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_xor: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_or: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub floor_divide: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub true_divide: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_floor_divide: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_true_divide: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub index: Option<fn(&PyNumber, &VirtualMachine) -> PyResult<PyIntRef>>,
pub matrix_multiply: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
pub inplace_matrix_multiply: Option<fn(&PyNumber, &PyObject, &VirtualMachine) -> PyResult>,
}
impl PyNumberMethods {
pub const NOT_IMPLEMENTED: PyNumberMethods = PyNumberMethods {
add: None,
subtract: None,
multiply: None,
remainder: None,
divmod: None,
power: None,
negative: None,
positive: None,
absolute: None,
boolean: None,
invert: None,
lshift: None,
rshift: None,
and: None,
xor: None,
or: None,
int: None,
float: None,
inplace_add: None,
inplace_subtract: None,
inplace_multiply: None,
inplace_remainder: None,
inplace_divmod: None,
inplace_power: None,
inplace_lshift: None,
inplace_rshift: None,
inplace_and: None,
inplace_xor: None,
inplace_or: None,
floor_divide: None,
true_divide: None,
inplace_floor_divide: None,
inplace_true_divide: None,
index: None,
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>,
}
impl<'a> PyNumber<'a> {
pub fn new(obj: &'a PyObject, vm: &VirtualMachine) -> Self {
Self {
obj,
methods: Self::find_methods(obj, vm),
}
}
}
impl PyNumber<'_> {
pub fn find_methods(obj: &PyObject, vm: &VirtualMachine) -> Option<&'static PyNumberMethods> {
let as_number = obj.class().mro_find_map(|x| x.slots.as_number.load());
as_number.map(|f| f(obj, vm))
impl PyObject {
#[inline]
pub fn to_number(&self) -> PyNumber<'_> {
PyNumber::from(self)
}
pub fn methods(&self) -> &'static PyNumberMethods {
self.methods.unwrap_or(&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 try_index_opt(&self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
#[allow(clippy::question_mark)]
Some(if let Some(i) = self.downcast_ref_if_exact::<PyInt>(vm) {
Ok(i.to_owned())
} else if let Some(i) = self.payload::<PyInt>() {
Ok(vm.ctx.new_bigint(i.as_bigint()))
} else if let Some(i) = self.to_number().index(vm).transpose() {
i
} else {
return None;
})
}
// PyIndex_Check
pub fn is_index(&self) -> bool {
self.methods().index.is_some()
#[inline]
pub fn try_index(&self, vm: &VirtualMachine) -> PyResult<PyIntRef> {
self.try_index_opt(vm).transpose()?.ok_or_else(|| {
vm.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",
self.class()
))
})
}
pub fn int(&self, vm: &VirtualMachine) -> PyResult<PyIntRef> {
pub fn try_int(&self, vm: &VirtualMachine) -> PyResult<PyIntRef> {
fn try_convert(obj: &PyObject, lit: &[u8], vm: &VirtualMachine) -> PyResult<PyIntRef> {
let base = 10;
match int::bytes_to_int(lit, base) {
@@ -199,11 +55,216 @@ impl PyNumber<'_> {
}
}
if let Some(i) = self.obj.downcast_ref_if_exact::<PyInt>(vm) {
if let Some(i) = self.downcast_ref_if_exact::<PyInt>(vm) {
Ok(i.to_owned())
} else if let Some(f) = self.methods().int {
} else {
let number = self.to_number();
if let Some(i) = number.int(vm)? {
Ok(i)
} else if let Some(i) = self.try_index_opt(vm) {
i
} else if let Ok(Ok(f)) =
vm.get_special_method(self.to_owned(), identifier!(vm, __trunc__))
{
// TODO: Deprecate in 3.11
// warnings::warn(
// vm.ctx.exceptions.deprecation_warning.clone(),
// "The delegation of int() to __trunc__ is deprecated.".to_owned(),
// 1,
// vm,
// )?;
let ret = f.invoke((), vm)?;
ret.try_index(vm).map_err(|_| {
vm.new_type_error(format!(
"__trunc__ returned non-Integral (type {})",
ret.class()
))
})
} else if let Some(s) = self.payload::<PyStr>() {
try_convert(self, s.as_str().as_bytes(), vm)
} else if let Some(bytes) = self.payload::<PyBytes>() {
try_convert(self, bytes, vm)
} else if let Some(bytearray) = self.payload::<PyByteArray>() {
try_convert(self, &bytearray.borrow_buf(), vm)
} else if let Ok(buffer) = ArgBytesLike::try_from_borrowed_object(vm, self) {
// TODO: replace to PyBuffer
try_convert(self, &buffer.borrow_buf(), vm)
} else {
Err(vm.new_type_error(format!(
"int() argument must be a string, a bytes-like object or a real number, not '{}'",
self.class()
)))
}
}
}
pub fn try_float_opt(&self, vm: &VirtualMachine) -> PyResult<Option<PyRef<PyFloat>>> {
let value = if let Some(float) = self.downcast_ref_if_exact::<PyFloat>(vm) {
Some(float.to_owned())
} else {
let number = self.to_number();
#[allow(clippy::manual_map)]
if let Some(f) = number.float(vm)? {
Some(f)
} else if let Some(i) = self.try_index_opt(vm) {
let value = int::try_to_float(i?.as_bigint(), vm)?;
Some(vm.ctx.new_float(value))
} else if let Some(value) = self.downcast_ref::<PyFloat>() {
Some(vm.ctx.new_float(value.to_f64()))
} else {
None
}
};
Ok(value)
}
#[inline]
pub fn try_float(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyFloat>> {
self.try_float_opt(vm)?
.ok_or_else(|| vm.new_type_error(format!("must be real number, not {}", self.class())))
}
}
#[derive(Default)]
pub struct PyNumberMethods {
/* Number implementations must check *both*
arguments for proper type and implement the necessary conversions
in the slot functions themselves. */
pub add: BinaryFunc,
pub subtract: BinaryFunc,
pub multiply: BinaryFunc,
pub remainder: BinaryFunc,
pub divmod: BinaryFunc,
pub power: BinaryFunc,
pub negative: UnaryFunc,
pub positive: UnaryFunc,
pub absolute: UnaryFunc,
pub boolean: UnaryFunc<bool>,
pub invert: UnaryFunc,
pub lshift: BinaryFunc,
pub rshift: BinaryFunc,
pub and: BinaryFunc,
pub xor: BinaryFunc,
pub or: BinaryFunc,
pub int: UnaryFunc<PyRef<PyInt>>,
pub float: UnaryFunc<PyRef<PyFloat>>,
pub inplace_add: BinaryFunc,
pub inplace_subtract: BinaryFunc,
pub inplace_multiply: BinaryFunc,
pub inplace_remainder: BinaryFunc,
pub inplace_divmod: BinaryFunc,
pub inplace_power: BinaryFunc,
pub inplace_lshift: BinaryFunc,
pub inplace_rshift: BinaryFunc,
pub inplace_and: BinaryFunc,
pub inplace_xor: BinaryFunc,
pub inplace_or: BinaryFunc,
pub floor_divide: BinaryFunc,
pub true_divide: BinaryFunc,
pub inplace_floor_divide: BinaryFunc,
pub inplace_true_divide: BinaryFunc,
pub index: UnaryFunc<PyRef<PyInt>>,
pub matrix_multiply: BinaryFunc,
pub inplace_matrix_multiply: BinaryFunc,
}
impl PyNumberMethods {
/// this is NOT a global variable
// TODO: weak order read for performance
#[allow(clippy::declare_interior_mutable_const)]
pub const NOT_IMPLEMENTED: PyNumberMethods = PyNumberMethods {
add: AtomicCell::new(None),
subtract: AtomicCell::new(None),
multiply: AtomicCell::new(None),
remainder: AtomicCell::new(None),
divmod: AtomicCell::new(None),
power: AtomicCell::new(None),
negative: AtomicCell::new(None),
positive: AtomicCell::new(None),
absolute: AtomicCell::new(None),
boolean: AtomicCell::new(None),
invert: AtomicCell::new(None),
lshift: AtomicCell::new(None),
rshift: AtomicCell::new(None),
and: AtomicCell::new(None),
xor: AtomicCell::new(None),
or: AtomicCell::new(None),
int: AtomicCell::new(None),
float: AtomicCell::new(None),
inplace_add: AtomicCell::new(None),
inplace_subtract: AtomicCell::new(None),
inplace_multiply: AtomicCell::new(None),
inplace_remainder: AtomicCell::new(None),
inplace_divmod: AtomicCell::new(None),
inplace_power: AtomicCell::new(None),
inplace_lshift: AtomicCell::new(None),
inplace_rshift: AtomicCell::new(None),
inplace_and: AtomicCell::new(None),
inplace_xor: AtomicCell::new(None),
inplace_or: AtomicCell::new(None),
floor_divide: AtomicCell::new(None),
true_divide: AtomicCell::new(None),
inplace_floor_divide: AtomicCell::new(None),
inplace_true_divide: AtomicCell::new(None),
index: AtomicCell::new(None),
matrix_multiply: AtomicCell::new(None),
inplace_matrix_multiply: AtomicCell::new(None),
};
}
pub struct PyNumber<'a> {
pub obj: &'a PyObject,
methods: &'a PyNumberMethods,
}
impl<'a> From<&'a PyObject> for PyNumber<'a> {
fn from(obj: &'a PyObject) -> Self {
static GLOBAL_NOT_IMPLEMENTED: PyNumberMethods = PyNumberMethods::NOT_IMPLEMENTED;
Self {
obj,
methods: Self::find_methods(obj)
.map_or(&GLOBAL_NOT_IMPLEMENTED, |m| unsafe { m.borrow_static() }),
}
}
}
impl PyNumber<'_> {
fn find_methods(obj: &PyObject) -> Option<PointerSlot<PyNumberMethods>> {
obj.class().mro_find_map(|x| x.slots.as_number.load())
}
pub fn methods(&self) -> &PyNumberMethods {
self.methods
}
// PyNumber_Check
pub fn check(obj: &PyObject) -> bool {
let methods = if let Some(m) = Self::find_methods(obj) {
m
} else {
return false;
};
let methods = methods.as_ref();
methods.int.load().is_some()
|| methods.index.load().is_some()
|| methods.float.load().is_some()
|| obj.payload_is::<PyComplex>()
}
// PyIndex_Check
pub fn is_index(&self) -> bool {
self.methods().index.load().is_some()
}
#[inline]
pub fn int(&self, vm: &VirtualMachine) -> PyResult<Option<PyIntRef>> {
Ok(if let Some(f) = self.methods().int.load() {
let ret = f(self, vm)?;
if !ret.class().is(PyInt::class(vm)) {
Some(if !ret.class().is(PyInt::class(vm)) {
warnings::warn(
vm.ctx.exceptions.deprecation_warning,
format!(
@@ -215,52 +276,18 @@ impl PyNumber<'_> {
1,
vm,
)?;
Ok(vm.ctx.new_bigint(ret.as_bigint()))
vm.ctx.new_bigint(ret.as_bigint())
} else {
Ok(ret)
}
} 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__))
{
// TODO: Deprecate in 3.11
// warnings::warn(
// vm.ctx.exceptions.deprecation_warning.clone(),
// "The delegation of int() to __trunc__ is deprecated.".to_owned(),
// 1,
// vm,
// )?;
let ret = f.invoke((), vm)?;
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>() {
try_convert(self.obj, bytes, vm)
} else if let Some(bytearray) = self.obj.payload::<PyByteArray>() {
try_convert(self.obj, &bytearray.borrow_buf(), vm)
} else if let Ok(buffer) = ArgBytesLike::try_from_borrowed_object(vm, self.obj) {
// TODO: replace to PyBuffer
try_convert(self.obj, &buffer.borrow_buf(), vm)
} else {
Err(vm.new_type_error(format!(
"int() argument must be a string, a bytes-like object or a real number, not '{}'",
self.obj.class()
)))
}
None
})
}
pub fn index_opt(&self, vm: &VirtualMachine) -> PyResult<Option<PyIntRef>> {
if let Some(i) = self.obj.downcast_ref_if_exact::<PyInt>(vm) {
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 {
#[inline]
pub fn index(&self, vm: &VirtualMachine) -> PyResult<Option<PyIntRef>> {
if let Some(f) = self.methods().index.load() {
let ret = f(self, vm)?;
if !ret.class().is(PyInt::class(vm)) {
warnings::warn(
@@ -283,21 +310,11 @@ impl PyNumber<'_> {
}
}
pub fn index(&self, vm: &VirtualMachine) -> PyResult<PyIntRef> {
self.index_opt(vm)?.ok_or_else(|| {
vm.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",
self.obj.class()
))
})
}
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 {
#[inline]
pub fn float(&self, vm: &VirtualMachine) -> PyResult<Option<PyRef<PyFloat>>> {
Ok(if let Some(f) = self.methods().float.load() {
let ret = f(self, vm)?;
if !ret.class().is(PyFloat::class(vm)) {
Some(if !ret.class().is(PyFloat::class(vm)) {
warnings::warn(
vm.ctx.exceptions.deprecation_warning,
format!(
@@ -309,24 +326,12 @@ impl PyNumber<'_> {
1,
vm,
)?;
Ok(Some(vm.ctx.new_float(ret.to_f64())))
vm.ctx.new_float(ret.to_f64())
} else {
Ok(Some(ret))
}
} 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)))
} else if let Some(value) = self.obj.downcast_ref::<PyFloat>() {
Ok(Some(vm.ctx.new_float(value.to_f64())))
ret
})
} else {
Ok(None)
}
}
pub fn float(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyFloat>> {
self.float_opt(vm)?.ok_or_else(|| {
vm.new_type_error(format!("must be real number, not {}", self.obj.class()))
None
})
}
}

View File

@@ -275,7 +275,7 @@ impl SequenceIndex {
.map(Self::Int)
} else if let Some(slice) = obj.payload::<PySlice>() {
slice.to_saturated(vm).map(Self::Slice)
} else if let Some(i) = vm.to_index_opt(obj.to_owned()) {
} else if let Some(i) = obj.try_index_opt(vm) {
// TODO: __index__ for indices is no more supported?
i?.try_to_primitive(vm)
.map_err(|_| {
@@ -465,7 +465,7 @@ fn to_isize_index(vm: &VirtualMachine, obj: &PyObject) -> PyResult<Option<isize>
if vm.is_none(obj) {
return Ok(None);
}
let result = vm.to_index_opt(obj.to_owned()).unwrap_or_else(|| {
let result = obj.try_index_opt(vm).unwrap_or_else(|| {
Err(vm.new_type_error(
"slice indices must be integers or None or have an __index__ method".to_owned(),
))

View File

@@ -719,7 +719,7 @@ mod builtins {
})?;
match ndigits.flatten() {
Some(obj) => {
let ndigits = vm.to_index(&obj)?;
let ndigits = obj.try_index(vm)?;
meth.invoke((ndigits,), vm)
}
None => {

View File

@@ -1336,7 +1336,7 @@ mod _io {
}
pub fn get_offset(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<Offset> {
let int = vm.to_index(&obj)?;
let int = obj.try_index(vm)?;
int.as_bigint().try_into().map_err(|_| {
vm.new_value_error(format!(
"cannot fit '{}' into an offset-sized integer",

View File

@@ -196,7 +196,7 @@ mod decl {
) -> PyResult {
let start = start.into_option().unwrap_or_else(|| vm.new_pyobj(0));
let step = step.into_option().unwrap_or_else(|| vm.new_pyobj(1));
if !PyNumber::check(&start, vm) || !PyNumber::check(&step, vm) {
if !PyNumber::check(&start) || !PyNumber::check(&step) {
return Err(vm.new_type_error("a number is required".to_owned()));
}

View File

@@ -110,7 +110,7 @@ mod _operator {
/// Return a converted to an integer. Equivalent to a.__index__().
#[pyfunction]
fn index(a: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyIntRef> {
vm.to_index(&a)
a.try_index(vm)
}
/// Return the bitwise inverse of the number obj. This is equivalent to ~obj.

View File

@@ -349,7 +349,7 @@ impl<const AVAILABLE: usize> FromArgs for DirFd<AVAILABLE> {
Some(o) if vm.is_none(&o) => DEFAULT_DIR_FD,
None => DEFAULT_DIR_FD,
Some(o) => {
let fd = vm.to_index_opt(o.clone()).unwrap_or_else(|| {
let fd = o.try_index_opt(vm).unwrap_or_else(|| {
Err(vm.new_type_error(format!(
"argument should be integer or None, not {}",
o.class().name()
@@ -1364,8 +1364,8 @@ pub(super) mod _os {
divmod.class().name()
))
})?;
let secs = vm.to_index(&div)?.try_to_primitive(vm)?;
let ns = vm.to_index(&rem)?.try_to_primitive(vm)?;
let secs = div.try_index(vm)?.try_to_primitive(vm)?;
let ns = rem.try_index(vm)?.try_to_primitive(vm)?;
Ok(Duration::new(secs, ns))
};
// TODO: do validation to make sure this doesn't.. underflow?

View File

@@ -745,7 +745,7 @@ mod _sre {
}
fn get_index(&self, group: PyObjectRef, vm: &VirtualMachine) -> Option<usize> {
let i = if let Ok(i) = vm.to_index(&group) {
let i = if let Ok(i) = group.try_index(vm) {
i
} else {
self.pattern

View File

@@ -1,6 +1,6 @@
use crate::common::{hash::PyHash, lock::PyRwLock};
use crate::{
builtins::{PyInt, PyStrInterned, PyStrRef, PyType, PyTypeRef},
builtins::{type_::PointerSlot, PyFloat, PyInt, PyStrInterned, PyStrRef, PyType, PyTypeRef},
bytecode::ComparisonOperator,
convert::ToPyResult,
function::Either,
@@ -17,6 +17,13 @@ use crossbeam_utils::atomic::AtomicCell;
use num_traits::{Signed, ToPrimitive};
use std::{borrow::Borrow, cmp::Ordering};
#[macro_export]
macro_rules! atomic_func {
($x:expr) => {
crossbeam_utils::atomic::AtomicCell::new(Some($x))
};
}
// The corresponding field in CPython is `tp_` prefixed.
// e.g. name -> tp_name
#[derive(Default)]
@@ -30,7 +37,7 @@ pub struct PyTypeSlots {
// Methods to implement standard operations
// Method suites for standard classes
pub as_number: AtomicCell<Option<AsNumberFunc>>,
pub as_number: AtomicCell<Option<PointerSlot<PyNumberMethods>>>,
pub as_sequence: AtomicCell<Option<AsSequenceFunc>>,
pub as_mapping: AtomicCell<Option<AsMappingFunc>>,
@@ -139,7 +146,6 @@ impl Default for PyTypeFlags {
pub(crate) type GenericMethod = fn(&PyObject, FuncArgs, &VirtualMachine) -> PyResult;
pub(crate) type AsMappingFunc = fn(&PyObject, &VirtualMachine) -> &'static PyMappingMethods;
pub(crate) type AsNumberFunc = fn(&PyObject, &VirtualMachine) -> &'static PyNumberMethods;
pub(crate) type HashFunc = fn(&PyObject, &VirtualMachine) -> PyResult<PyHash>;
// CallFunc = GenericMethod
pub(crate) type GetattroFunc = fn(&PyObject, PyStrRef, &VirtualMachine) -> PyResult;
@@ -205,13 +211,28 @@ fn slot_as_sequence(zelf: &PyObject, vm: &VirtualMachine) -> &'static PySequence
PySequenceMethods::generic(has_length, has_ass_item)
}
fn slot_as_number(zelf: &PyObject, vm: &VirtualMachine) -> &'static PyNumberMethods {
let (has_int, has_float, has_index) = (
zelf.class().has_attr(identifier!(vm, __int__)),
zelf.class().has_attr(identifier!(vm, __float__)),
zelf.class().has_attr(identifier!(vm, __index__)),
);
PyNumberMethods::generic(has_int, has_float, has_index)
fn int_wrapper(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 index_wrapper(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()))
})
}
fn float_wrapper(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 hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
@@ -327,21 +348,35 @@ impl PyType {
debug_assert!(name.as_str().starts_with("__"));
debug_assert!(name.as_str().ends_with("__"));
macro_rules! update_slot {
macro_rules! toggle_slot {
($name:ident, $func:expr) => {{
self.slots.$name.store(if add { Some($func) } else { None });
}};
}
macro_rules! update_slot {
($name:ident, $func:expr) => {{
self.slots.$name.store(Some($func));
}};
}
macro_rules! update_pointer_slot {
($name:ident, $pointed:ident) => {{
self.slots
.$name
.store(unsafe { PointerSlot::from_heaptype(self, |ext| &ext.$pointed) });
}};
}
match name.as_str() {
"__len__" | "__getitem__" | "__setitem__" | "__delitem__" => {
update_slot!(as_mapping, slot_as_mapping);
update_slot!(as_sequence, slot_as_sequence);
}
"__hash__" => {
update_slot!(hash, hash_wrapper);
toggle_slot!(hash, hash_wrapper);
}
"__call__" => {
update_slot!(call, call_wrapper);
toggle_slot!(call, call_wrapper);
}
"__getattr__" | "__getattribute__" => {
update_slot!(getattro, getattro_wrapper);
@@ -353,28 +388,52 @@ impl PyType {
update_slot!(richcompare, richcompare_wrapper);
}
"__iter__" => {
update_slot!(iter, iter_wrapper);
toggle_slot!(iter, iter_wrapper);
}
"__next__" => {
update_slot!(iternext, iternext_wrapper);
toggle_slot!(iternext, iternext_wrapper);
}
"__get__" => {
update_slot!(descr_get, descr_get_wrapper);
toggle_slot!(descr_get, descr_get_wrapper);
}
"__set__" | "__delete__" => {
update_slot!(descr_set, descr_set_wrapper);
}
"__init__" => {
update_slot!(init, init_wrapper);
toggle_slot!(init, init_wrapper);
}
"__new__" => {
update_slot!(new, new_wrapper);
toggle_slot!(new, new_wrapper);
}
"__del__" => {
update_slot!(del, del_wrapper);
toggle_slot!(del, del_wrapper);
}
"__int__" | "__index__" | "__float__" => {
update_slot!(as_number, slot_as_number);
"__int__" => {
self.heaptype_ext
.as_ref()
.unwrap()
.number_methods
.int
.store(Some(int_wrapper));
update_pointer_slot!(as_number, number_methods);
}
"__index__" => {
self.heaptype_ext
.as_ref()
.unwrap()
.number_methods
.index
.store(Some(index_wrapper));
update_pointer_slot!(as_number, number_methods);
}
"__float__" => {
self.heaptype_ext
.as_ref()
.unwrap()
.number_methods
.float
.store(Some(float_wrapper));
update_pointer_slot!(as_number, number_methods);
}
_ => {}
}
@@ -880,13 +939,8 @@ pub trait AsSequence: PyPayload {
#[pyimpl]
pub trait AsNumber: PyPayload {
const AS_NUMBER: PyNumberMethods;
#[inline]
#[pyslot]
fn as_number(_zelf: &PyObject, _vm: &VirtualMachine) -> &'static PyNumberMethods {
&Self::AS_NUMBER
}
fn as_number() -> &'static PyNumberMethods;
fn number_downcast<'a>(number: &'a PyNumber) -> &'a Py<Self> {
unsafe { number.obj.downcast_unchecked_ref() }

View File

@@ -3,22 +3,12 @@ use crate::{
builtins::{PyInt, PyIntRef, PyStrInterned},
function::PyArithmeticValue,
object::{AsObject, PyObject, PyObjectRef, PyResult},
protocol::{PyIterReturn, PyNumber},
protocol::PyIterReturn,
types::PyComparisonOp,
};
/// Collection of operators
impl VirtualMachine {
pub fn to_index_opt(&self, obj: PyObjectRef) -> Option<PyResult<PyIntRef>> {
PyNumber::new(obj.as_ref(), self)
.index_opt(self)
.transpose()
}
pub fn to_index(&self, obj: &PyObject) -> PyResult<PyIntRef> {
PyNumber::new(obj, self).index(self)
}
#[inline]
pub fn bool_eq(&self, a: &PyObject, b: &PyObject) -> PyResult<bool> {
a.rich_compare_bool(b, PyComparisonOp::Eq, self)