use std::any::Any;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::fmt;
use std::marker::PhantomData;
use std::ops::Deref;
use std::rc::Rc;
use indexmap::IndexMap;
use num_bigint::BigInt;
use num_complex::Complex64;
use num_traits::{One, ToPrimitive, Zero};
use crate::bytecode;
use crate::dictdatatype::DictKey;
use crate::exceptions::{self, PyBaseExceptionRef};
use crate::function::{IntoPyNativeFunc, PyFuncArgs};
use crate::obj::objbuiltinfunc::{PyBuiltinFunction, PyBuiltinMethod};
use crate::obj::objbytearray;
use crate::obj::objbytes;
use crate::obj::objclassmethod::PyClassMethod;
use crate::obj::objcode;
use crate::obj::objcode::PyCodeRef;
use crate::obj::objcomplex::PyComplex;
use crate::obj::objdict::{PyDict, PyDictRef};
use crate::obj::objfloat::PyFloat;
use crate::obj::objfunction::{PyBoundMethod, PyFunction};
use crate::obj::objgetset::{IntoPyGetterFunc, IntoPySetterFunc, PyGetSet};
use crate::obj::objint::{PyInt, PyIntRef};
use crate::obj::objiter;
use crate::obj::objlist::PyList;
use crate::obj::objnamespace::PyNamespace;
use crate::obj::objnone::{PyNone, PyNoneRef};
use crate::obj::objobject;
use crate::obj::objset::PySet;
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;
pub type PyObjectRef = Rc<PyObject<dyn PyObjectPayload>>;
pub type PyResult<T = PyObjectRef> = Result<T, PyBaseExceptionRef>;
pub type PyAttributes = HashMap<String, PyObjectRef>;
impl fmt::Display for PyObject<dyn PyObjectPayload> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(PyClass { ref name, .. }) = self.payload::<PyClass>() {
let type_name = self.class().name.clone();
if type_name == "type" {
return write!(f, "type object '{}'", name);
} else {
return write!(f, "'{}' object", type_name);
}
}
write!(f, "'{}' object", self.class().name)
}
}
const INT_CACHE_POOL_MIN: i32 = -5;
const INT_CACHE_POOL_MAX: i32 = 256;
#[derive(Debug)]
pub struct PyContext {
pub true_value: PyIntRef,
pub false_value: PyIntRef,
pub none: PyNoneRef,
pub empty_tuple: PyTupleRef,
pub ellipsis_type: PyClassRef,
pub ellipsis: PyEllipsisRef,
pub not_implemented: PyNotImplementedRef,
pub types: TypeZoo,
pub exceptions: exceptions::ExceptionZoo,
pub int_cache_pool: Vec<PyObjectRef>,
}
pub type PyNotImplementedRef = PyRef<PyNotImplemented>;
#[derive(Debug)]
pub struct PyNotImplemented;
impl PyValue for PyNotImplemented {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.ctx.not_implemented().class()
}
}
pub type PyEllipsisRef = PyRef<PyEllipsis>;
#[derive(Debug)]
pub struct PyEllipsis;
impl PyValue for PyEllipsis {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.ctx.ellipsis_type.clone()
}
}
impl PyContext {
pub fn new() -> Self {
flame_guard!("init PyContext");
let types = TypeZoo::new();
let exceptions = exceptions::ExceptionZoo::new(&types.type_type, &types.object_type);
fn create_object<T: PyObjectPayload + PyValue>(payload: T, cls: &PyClassRef) -> PyRef<T> {
PyRef::new_ref_unchecked(PyObject::new(payload, cls.clone(), None))
}
let none_type = create_type("NoneType", &types.type_type, &types.object_type);
let none = create_object(PyNone, &none_type);
let ellipsis_type = create_type("EllipsisType", &types.type_type, &types.object_type);
let ellipsis = create_object(PyEllipsis, &ellipsis_type);
let not_implemented_type =
create_type("NotImplementedType", &types.type_type, &types.object_type);
let not_implemented = create_object(PyNotImplemented, ¬_implemented_type);
let int_cache_pool = (INT_CACHE_POOL_MIN..=INT_CACHE_POOL_MAX)
.map(|v| create_object(PyInt::new(BigInt::from(v)), &types.int_type).into_object())
.collect();
let true_value = create_object(PyInt::new(BigInt::one()), &types.bool_type);
let false_value = create_object(PyInt::new(BigInt::zero()), &types.bool_type);
let empty_tuple = create_object(PyTuple::from(vec![]), &types.tuple_type);
let context = PyContext {
true_value,
false_value,
not_implemented,
none,
empty_tuple,
ellipsis,
ellipsis_type,
types,
exceptions,
int_cache_pool,
};
initialize_types(&context);
exceptions::init(&context);
context
}
pub fn bytearray_type(&self) -> PyClassRef {
self.types.bytearray_type.clone()
}
pub fn bytearrayiterator_type(&self) -> PyClassRef {
self.types.bytearrayiterator_type.clone()
}
pub fn bytes_type(&self) -> PyClassRef {
self.types.bytes_type.clone()
}
pub fn bytesiterator_type(&self) -> PyClassRef {
self.types.bytesiterator_type.clone()
}
pub fn code_type(&self) -> PyClassRef {
self.types.code_type.clone()
}
pub fn complex_type(&self) -> PyClassRef {
self.types.complex_type.clone()
}
pub fn dict_type(&self) -> PyClassRef {
self.types.dict_type.clone()
}
pub fn float_type(&self) -> PyClassRef {
self.types.float_type.clone()
}
pub fn frame_type(&self) -> PyClassRef {
self.types.frame_type.clone()
}
pub fn int_type(&self) -> PyClassRef {
self.types.int_type.clone()
}
pub fn list_type(&self) -> PyClassRef {
self.types.list_type.clone()
}
pub fn listiterator_type(&self) -> PyClassRef {
self.types.listiterator_type.clone()
}
pub fn listreverseiterator_type(&self) -> PyClassRef {
self.types.listreverseiterator_type.clone()
}
pub fn striterator_type(&self) -> PyClassRef {
self.types.striterator_type.clone()
}
pub fn strreverseiterator_type(&self) -> PyClassRef {
self.types.strreverseiterator_type.clone()
}
pub fn module_type(&self) -> PyClassRef {
self.types.module_type.clone()
}
pub fn namespace_type(&self) -> PyClassRef {
self.types.namespace_type.clone()
}
pub fn set_type(&self) -> PyClassRef {
self.types.set_type.clone()
}
pub fn range_type(&self) -> PyClassRef {
self.types.range_type.clone()
}
pub fn rangeiterator_type(&self) -> PyClassRef {
self.types.rangeiterator_type.clone()
}
pub fn slice_type(&self) -> PyClassRef {
self.types.slice_type.clone()
}
pub fn frozenset_type(&self) -> PyClassRef {
self.types.frozenset_type.clone()
}
pub fn bool_type(&self) -> PyClassRef {
self.types.bool_type.clone()
}
pub fn memoryview_type(&self) -> PyClassRef {
self.types.memoryview_type.clone()
}
pub fn tuple_type(&self) -> PyClassRef {
self.types.tuple_type.clone()
}
pub fn tupleiterator_type(&self) -> PyClassRef {
self.types.tupleiterator_type.clone()
}
pub fn iter_type(&self) -> PyClassRef {
self.types.iter_type.clone()
}
pub fn enumerate_type(&self) -> PyClassRef {
self.types.enumerate_type.clone()
}
pub fn filter_type(&self) -> PyClassRef {
self.types.filter_type.clone()
}
pub fn map_type(&self) -> PyClassRef {
self.types.map_type.clone()
}
pub fn zip_type(&self) -> PyClassRef {
self.types.zip_type.clone()
}
pub fn str_type(&self) -> PyClassRef {
self.types.str_type.clone()
}
pub fn super_type(&self) -> PyClassRef {
self.types.super_type.clone()
}
pub fn function_type(&self) -> PyClassRef {
self.types.function_type.clone()
}
pub fn builtin_function_or_method_type(&self) -> PyClassRef {
self.types.builtin_function_or_method_type.clone()
}
pub fn method_descriptor_type(&self) -> PyClassRef {
self.types.method_descriptor_type.clone()
}
pub fn property_type(&self) -> PyClassRef {
self.types.property_type.clone()
}
pub fn readonly_property_type(&self) -> PyClassRef {
self.types.readonly_property_type.clone()
}
pub fn getset_type(&self) -> PyClassRef {
self.types.getset_type.clone()
}
pub fn classmethod_type(&self) -> PyClassRef {
self.types.classmethod_type.clone()
}
pub fn staticmethod_type(&self) -> PyClassRef {
self.types.staticmethod_type.clone()
}
pub fn generator_type(&self) -> PyClassRef {
self.types.generator_type.clone()
}
pub fn bound_method_type(&self) -> PyClassRef {
self.types.bound_method_type.clone()
}
pub fn weakref_type(&self) -> PyClassRef {
self.types.weakref_type.clone()
}
pub fn weakproxy_type(&self) -> PyClassRef {
self.types.weakproxy_type.clone()
}
pub fn traceback_type(&self) -> PyClassRef {
self.types.traceback_type.clone()
}
pub fn type_type(&self) -> PyClassRef {
self.types.type_type.clone()
}
pub fn none(&self) -> PyObjectRef {
self.none.clone().into_object()
}
pub fn ellipsis(&self) -> PyObjectRef {
self.ellipsis.clone().into_object()
}
pub fn not_implemented(&self) -> PyObjectRef {
self.not_implemented.clone().into_object()
}
pub fn object(&self) -> PyClassRef {
self.types.object_type.clone()
}
#[inline]
pub fn new_int<T: Into<BigInt> + ToPrimitive>(&self, i: T) -> PyObjectRef {
if let Some(i) = i.to_i32() {
if i >= INT_CACHE_POOL_MIN && i <= INT_CACHE_POOL_MAX {
let inner_idx = (i - INT_CACHE_POOL_MIN) as usize;
return self.int_cache_pool[inner_idx].clone();
}
}
PyObject::new(PyInt::new(i), self.int_type(), None)
}
#[inline]
pub fn new_bigint(&self, i: &BigInt) -> PyObjectRef {
if let Some(i) = i.to_i32() {
if i >= INT_CACHE_POOL_MIN && i <= INT_CACHE_POOL_MAX {
let inner_idx = (i - INT_CACHE_POOL_MIN) as usize;
return self.int_cache_pool[inner_idx].clone();
}
}
PyObject::new(PyInt::new(i.clone()), self.int_type(), None)
}
pub fn new_float(&self, value: f64) -> PyObjectRef {
PyObject::new(PyFloat::from(value), self.float_type(), None)
}
pub fn new_complex(&self, value: Complex64) -> PyObjectRef {
PyObject::new(PyComplex::from(value), self.complex_type(), None)
}
pub fn new_str(&self, s: String) -> PyObjectRef {
PyObject::new(objstr::PyString::from(s), self.str_type(), None)
}
pub fn new_bytes(&self, data: Vec<u8>) -> PyObjectRef {
PyObject::new(objbytes::PyBytes::new(data), self.bytes_type(), None)
}
pub fn new_bytearray(&self, data: Vec<u8>) -> PyObjectRef {
PyObject::new(
objbytearray::PyByteArray::new(data),
self.bytearray_type(),
None,
)
}
#[inline]
pub fn new_bool(&self, b: bool) -> PyObjectRef {
let value = if b {
&self.true_value
} else {
&self.false_value
};
value.clone().into_object()
}
pub fn new_tuple(&self, elements: Vec<PyObjectRef>) -> PyObjectRef {
if elements.is_empty() {
self.empty_tuple.clone().into_object()
} else {
PyObject::new(PyTuple::from(elements), self.tuple_type(), None)
}
}
pub fn new_list(&self, elements: Vec<PyObjectRef>) -> PyObjectRef {
PyObject::new(PyList::from(elements), self.list_type(), None)
}
pub fn new_set(&self) -> PyObjectRef {
PyObject::new(PySet::default(), self.set_type(), None)
}
pub fn new_dict(&self) -> PyDictRef {
PyObject::new(PyDict::default(), self.dict_type(), None)
.downcast()
.unwrap()
}
pub fn new_class(&self, name: &str, base: PyClassRef) -> PyClassRef {
create_type(name, &self.type_type(), &base)
}
pub fn new_namespace(&self) -> PyObjectRef {
PyObject::new(PyNamespace, self.namespace_type(), Some(self.new_dict()))
}
pub fn new_function<F, T, R, VM>(&self, f: F) -> PyObjectRef
where
F: IntoPyNativeFunc<T, R, VM>,
{
PyObject::new(
PyBuiltinFunction::new(f.into_func()),
self.builtin_function_or_method_type(),
None,
)
}
pub fn new_method<F, T, R, VM>(&self, f: F) -> PyObjectRef
where
F: IntoPyNativeFunc<T, R, VM>,
{
PyObject::new(
PyBuiltinMethod::new(f.into_func()),
self.method_descriptor_type(),
None,
)
}
pub fn new_classmethod<F, T, R, VM>(&self, f: F) -> PyObjectRef
where
F: IntoPyNativeFunc<T, R, VM>,
{
PyObject::new(
PyClassMethod::new(self.new_method(f)),
self.classmethod_type(),
None,
)
}
pub fn new_readonly_getset<F, T>(&self, name: impl Into<String>, f: F) -> PyObjectRef
where
F: IntoPyGetterFunc<T>,
{
PyObject::new(PyGetSet::with_get(name.into(), f), self.getset_type(), None)
}
pub fn new_getset<G, S, T, U>(&self, name: impl Into<String>, g: G, s: S) -> PyObjectRef
where
G: IntoPyGetterFunc<T>,
S: IntoPySetterFunc<U>,
{
PyObject::new(
PyGetSet::with_get_set(name.into(), g, s),
self.getset_type(),
None,
)
}
pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyCodeRef {
PyObject::new(objcode::PyCode::new(code), self.code_type(), None)
.downcast()
.unwrap()
}
pub fn new_pyfunction(
&self,
code_obj: PyCodeRef,
scope: Scope,
defaults: Option<PyTupleRef>,
kw_only_defaults: Option<PyDictRef>,
) -> PyObjectRef {
PyObject::new(
PyFunction::new(code_obj, scope, defaults, kw_only_defaults),
self.function_type(),
Some(self.new_dict()),
)
}
pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef {
PyObject::new(
PyBoundMethod::new(object, function),
self.bound_method_type(),
None,
)
}
pub fn new_base_object(&self, class: PyClassRef, dict: Option<PyDictRef>) -> PyObjectRef {
PyObject {
typ: class,
dict: dict.map(RefCell::new),
payload: objobject::PyBaseObject,
}
.into_ref()
}
pub fn unwrap_constant(&self, value: &bytecode::Constant) -> PyObjectRef {
match *value {
bytecode::Constant::Integer { ref value } => self.new_bigint(value),
bytecode::Constant::Float { ref value } => self.new_float(*value),
bytecode::Constant::Complex { ref value } => self.new_complex(*value),
bytecode::Constant::String { ref value } => self.new_str(value.clone()),
bytecode::Constant::Bytes { ref value } => self.new_bytes(value.clone()),
bytecode::Constant::Boolean { ref value } => self.new_bool(value.clone()),
bytecode::Constant::Code { ref code } => {
self.new_code_object(*code.clone()).into_object()
}
bytecode::Constant::Tuple { ref elements } => {
let elements = elements
.iter()
.map(|value| self.unwrap_constant(value))
.collect();
self.new_tuple(elements)
}
bytecode::Constant::None => self.none(),
bytecode::Constant::Ellipsis => self.ellipsis(),
}
}
}
impl Default for PyContext {
fn default() -> Self {
PyContext::new()
}
}
pub struct PyObject<T>
where
T: ?Sized + PyObjectPayload,
{
pub typ: PyClassRef,
pub dict: Option<RefCell<PyDictRef>>,
pub payload: T,
}
impl PyObject<dyn PyObjectPayload> {
pub fn downcast<T: PyObjectPayload>(self: Rc<Self>) -> Result<PyRef<T>, PyObjectRef> {
if self.payload_is::<T>() {
Ok({
PyRef {
obj: self,
_payload: PhantomData,
}
})
} else {
Err(self)
}
}
}
#[derive(Debug)]
pub struct PyRef<T> {
obj: PyObjectRef,
_payload: PhantomData<T>,
}
impl<T> Clone for PyRef<T> {
fn clone(&self) -> Self {
Self {
obj: self.obj.clone(),
_payload: PhantomData,
}
}
}
impl<T: PyValue> PyRef<T> {
fn new_ref(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<Self> {
if obj.payload_is::<T>() {
Ok(Self::new_ref_unchecked(obj))
} else {
Err(vm.new_exception_msg(
vm.ctx.exceptions.runtime_error.clone(),
format!("Unexpected payload for type {:?}", obj.class().name),
))
}
}
pub(crate) fn new_ref_unchecked(obj: PyObjectRef) -> Self {
PyRef {
obj,
_payload: PhantomData,
}
}
pub fn as_object(&self) -> &PyObjectRef {
&self.obj
}
pub fn into_object(self) -> PyObjectRef {
self.obj
}
pub fn typ(&self) -> PyClassRef {
self.obj.class()
}
}
impl<T> Deref for PyRef<T>
where
T: PyValue,
{
type Target = T;
fn deref(&self) -> &T {
self.obj.payload().expect("unexpected payload for type")
}
}
impl<T> TryFromObject for PyRef<T>
where
T: PyValue,
{
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
if objtype::isinstance(&obj, &T::class(vm)) {
PyRef::new_ref(obj, vm)
} else {
let class = T::class(vm);
let expected_type = vm.to_pystr(&class)?;
let actual_type = vm.to_pystr(&obj.class())?;
Err(vm.new_type_error(format!(
"Expected type {}, not {}",
expected_type, actual_type,
)))
}
}
}
impl<'a, T: PyValue> From<&'a PyRef<T>> for &'a PyObjectRef {
fn from(obj: &'a PyRef<T>) -> Self {
obj.as_object()
}
}
impl<T: PyValue> From<PyRef<T>> for PyObjectRef {
fn from(obj: PyRef<T>) -> Self {
obj.into_object()
}
}
impl<T: fmt::Display> fmt::Display for PyRef<T>
where
T: PyValue + fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let value: &T = self.obj.payload().expect("unexpected payload for type");
fmt::Display::fmt(value, f)
}
}
#[derive(Clone, Debug)]
pub struct PyCallable {
obj: PyObjectRef,
}
impl PyCallable {
#[inline]
pub fn invoke(&self, args: impl Into<PyFuncArgs>, vm: &VirtualMachine) -> PyResult {
vm.invoke(&self.obj, args)
}
#[inline]
pub fn into_object(self) -> PyObjectRef {
self.obj
}
}
impl TryFromObject for PyCallable {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
if vm.is_callable(&obj) {
Ok(PyCallable { obj })
} else {
Err(vm.new_type_error(format!("'{}' object is not callable", obj.class().name)))
}
}
}
pub trait IdProtocol {
fn get_id(&self) -> usize;
fn is<T>(&self, other: &T) -> bool
where
T: IdProtocol,
{
self.get_id() == other.get_id()
}
}
#[derive(Debug)]
enum Never {}
impl PyValue for Never {
fn class(_vm: &VirtualMachine) -> PyClassRef {
unreachable!()
}
}
impl<T: ?Sized + PyObjectPayload> IdProtocol for PyObject<T> {
fn get_id(&self) -> usize {
self as *const _ as *const PyObject<Never> as usize
}
}
impl<T: ?Sized + IdProtocol> IdProtocol for Rc<T> {
fn get_id(&self) -> usize {
(**self).get_id()
}
}
impl<T: PyObjectPayload> IdProtocol for PyRef<T> {
fn get_id(&self) -> usize {
self.obj.get_id()
}
}
pub trait TypeProtocol {
fn class(&self) -> PyClassRef;
}
impl TypeProtocol for PyObjectRef {
fn class(&self) -> PyClassRef {
(**self).class()
}
}
impl<T> TypeProtocol for PyObject<T>
where
T: ?Sized + PyObjectPayload,
{
fn class(&self) -> PyClassRef {
self.typ.clone()
}
}
impl<T> TypeProtocol for PyRef<T> {
fn class(&self) -> PyClassRef {
self.obj.typ.clone()
}
}
impl<T: TypeProtocol> TypeProtocol for &'_ T {
fn class(&self) -> PyClassRef {
(&**self).class()
}
}
pub trait ItemProtocol {
fn get_item<T: IntoPyObject + DictKey + Copy>(&self, key: T, vm: &VirtualMachine) -> PyResult;
fn set_item<T: IntoPyObject + DictKey + Copy>(
&self,
key: T,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult;
fn del_item<T: IntoPyObject + DictKey + Copy>(&self, key: T, vm: &VirtualMachine) -> PyResult;
}
impl ItemProtocol for PyObjectRef {
fn get_item<T: IntoPyObject>(&self, key: T, vm: &VirtualMachine) -> PyResult {
vm.call_method(self, "__getitem__", key.into_pyobject(vm)?)
}
fn set_item<T: IntoPyObject>(
&self,
key: T,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult {
vm.call_method(self, "__setitem__", vec![key.into_pyobject(vm)?, value])
}
fn del_item<T: IntoPyObject>(&self, key: T, vm: &VirtualMachine) -> PyResult {
vm.call_method(self, "__delitem__", key.into_pyobject(vm)?)
}
}
pub trait BufferProtocol {
fn readonly(&self) -> bool;
}
impl BufferProtocol for PyObjectRef {
fn readonly(&self) -> bool {
match self.class().name.as_str() {
"bytes" => false,
"bytearray" | "memoryview" => true,
_ => panic!("Bytes-Like type expected not {:?}", self),
}
}
}
impl fmt::Debug for PyObject<dyn PyObjectPayload> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[PyObj {:?}]", &self.payload)
}
}
pub struct PyIterable<T = PyObjectRef> {
method: PyObjectRef,
_item: std::marker::PhantomData<T>,
}
impl<T> PyIterable<T> {
pub fn iter<'a>(&self, vm: &'a VirtualMachine) -> PyResult<PyIterator<'a, T>> {
let method = &self.method;
let iter_obj = vm.invoke(
method,
PyFuncArgs {
args: vec![],
kwargs: IndexMap::new(),
},
)?;
Ok(PyIterator {
vm,
obj: iter_obj,
_item: std::marker::PhantomData,
})
}
}
pub struct PyIterator<'a, T> {
vm: &'a VirtualMachine,
obj: PyObjectRef,
_item: std::marker::PhantomData<T>,
}
impl<'a, T> Iterator for PyIterator<'a, T>
where
T: TryFromObject,
{
type Item = PyResult<T>;
fn next(&mut self) -> Option<Self::Item> {
match self.vm.call_method(&self.obj, "__next__", vec![]) {
Ok(value) => Some(T::try_from_object(self.vm, value)),
Err(err) => {
if objtype::isinstance(&err, &self.vm.ctx.exceptions.stop_iteration) {
None
} else {
Some(Err(err))
}
}
}
}
}
impl<T> TryFromObject for PyIterable<T>
where
T: TryFromObject,
{
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
if let Some(method_or_err) = vm.get_method(obj.clone(), "__iter__") {
let method = method_or_err?;
Ok(PyIterable {
method,
_item: std::marker::PhantomData,
})
} else {
vm.get_method_or_type_error(obj.clone(), "__getitem__", || {
format!("'{}' object is not iterable", obj.class().name)
})?;
Self::try_from_object(
vm,
objiter::PySequenceIterator {
position: Cell::new(0),
obj: obj.clone(),
reversed: false,
}
.into_ref(vm)
.into_object(),
)
}
}
}
impl TryFromObject for PyObjectRef {
#[inline]
fn try_from_object(_vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
Ok(obj)
}
}
impl<T: TryFromObject> TryFromObject for Option<T> {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
if vm.get_none().is(&obj) {
Ok(None)
} else {
T::try_from_object(vm, obj).map(Some)
}
}
}
pub trait TryIntoRef<T> {
fn try_into_ref(self, vm: &VirtualMachine) -> PyResult<PyRef<T>>;
}
impl<T> TryIntoRef<T> for PyRef<T> {
fn try_into_ref(self, _vm: &VirtualMachine) -> PyResult<PyRef<T>> {
Ok(self)
}
}
impl<T> TryIntoRef<T> for PyObjectRef
where
T: PyValue,
{
fn try_into_ref(self, vm: &VirtualMachine) -> PyResult<PyRef<T>> {
TryFromObject::try_from_object(vm, self)
}
}
pub trait TryFromObject: Sized {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self>;
}
pub trait IntoPyObject {
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult;
}
impl<T> IntoPyObject for PyRef<T> {
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
Ok(self.obj)
}
}
impl IntoPyObject for PyCallable {
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
Ok(self.into_object())
}
}
impl IntoPyObject for PyObjectRef {
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
Ok(self)
}
}
impl IntoPyObject for &PyObjectRef {
fn into_pyobject(self, _vm: &VirtualMachine) -> PyResult {
Ok(self.clone())
}
}
impl<T> IntoPyObject for PyResult<T>
where
T: IntoPyObject,
{
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult {
self.and_then(|res| T::into_pyobject(res, vm))
}
}
impl<T> IntoPyObject for T
where
T: PyValue + Sized,
{
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult {
Ok(PyObject::new(self, T::class(vm), None))
}
}
impl<T> PyObject<T>
where
T: Sized + PyObjectPayload,
{
#[allow(clippy::new_ret_no_self)]
pub fn new(payload: T, typ: PyClassRef, dict: Option<PyDictRef>) -> PyObjectRef {
PyObject {
typ,
dict: dict.map(RefCell::new),
payload,
}
.into_ref()
}
pub fn into_ref(self) -> PyObjectRef {
Rc::new(self)
}
}
impl PyObject<dyn PyObjectPayload> {
#[inline]
pub fn payload<T: PyObjectPayload>(&self) -> Option<&T> {
self.payload.as_any().downcast_ref()
}
#[inline]
pub fn payload_is<T: PyObjectPayload>(&self) -> bool {
self.payload.as_any().is::<T>()
}
#[inline]
pub fn payload_if_subclass<T: PyObjectPayload + PyValue>(
&self,
vm: &VirtualMachine,
) -> Option<&T> {
if objtype::issubclass(&self.class(), &T::class(vm)) {
self.payload()
} else {
None
}
}
}
pub trait PyValue: fmt::Debug + Sized + 'static {
const HAVE_DICT: bool = false;
fn class(vm: &VirtualMachine) -> PyClassRef;
fn into_ref(self, vm: &VirtualMachine) -> PyRef<Self> {
self.into_ref_with_type_unchecked(Self::class(vm), None)
}
fn into_ref_with_type(self, vm: &VirtualMachine, cls: PyClassRef) -> PyResult<PyRef<Self>> {
let class = Self::class(vm);
if objtype::issubclass(&cls, &class) {
let dict = if !Self::HAVE_DICT && cls.is(&class) {
None
} else {
Some(vm.ctx.new_dict())
};
PyRef::new_ref(PyObject::new(self, cls, dict), vm)
} else {
let subtype = vm.to_str(&cls.obj)?;
let basetype = vm.to_str(&class.obj)?;
Err(vm.new_type_error(format!("{} is not a subtype of {}", subtype, basetype)))
}
}
fn into_ref_with_type_unchecked(self, cls: PyClassRef, dict: Option<PyDictRef>) -> PyRef<Self> {
PyRef::new_ref_unchecked(PyObject::new(self, cls, dict))
}
}
pub trait PyObjectPayload: Any + fmt::Debug + 'static {
fn as_any(&self) -> &dyn Any;
}
impl<T: PyValue + 'static> PyObjectPayload for T {
#[inline]
fn as_any(&self) -> &dyn Any {
self
}
}
pub enum Either<A, B> {
A(A),
B(B),
}
impl<A: PyValue, B: PyValue> Either<PyRef<A>, PyRef<B>> {
pub fn into_object(self) -> PyObjectRef {
match self {
Either::A(a) => a.into_object(),
Either::B(b) => b.into_object(),
}
}
}
impl<A, B> TryFromObject for Either<A, B>
where
A: TryFromObject,
B: TryFromObject,
{
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
A::try_from_object(vm, obj.clone())
.map(Either::A)
.or_else(|_| B::try_from_object(vm, obj.clone()).map(Either::B))
.map_err(|_| vm.new_type_error(format!("unexpected type {}", obj.class())))
}
}
pub trait PyClassDef {
const NAME: &'static str;
const DOC: Option<&'static str> = None;
}
impl<T> PyClassDef for PyRef<T>
where
T: PyClassDef,
{
const NAME: &'static str = T::NAME;
const DOC: Option<&'static str> = T::DOC;
}
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()));
}
}
fn make_class(ctx: &PyContext) -> PyClassRef {
Self::make_class_with_base(ctx, ctx.object())
}
fn make_class_with_base(ctx: &PyContext, base: PyClassRef) -> PyClassRef {
let py_class = ctx.new_class(Self::NAME, base);
Self::extend_class(ctx, &py_class);
py_class
}
}
impl TryFromObject for std::time::Duration {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
use std::time::Duration;
u64::try_from_object(vm, obj.clone())
.map(Duration::from_secs)
.or_else(|_| f64::try_from_object(vm, obj.clone()).map(Duration::from_secs_f64))
.map_err(|_| {
vm.new_type_error(format!(
"expected an int or float for duration, got {}",
obj.class()
))
})
}
}
result_like::option_like!(pub PyArithmaticValue, Implemented, NotImplemented);
impl<T> IntoPyObject for PyArithmaticValue<T>
where
T: IntoPyObject,
{
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult {
match self {
PyArithmaticValue::Implemented(v) => v.into_pyobject(vm),
PyArithmaticValue::NotImplemented => Ok(vm.ctx.not_implemented()),
}
}
}
pub type PyComparisonValue = PyArithmaticValue<bool>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_type_type() {
PyContext::new();
}
}