mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-09 22:49:57 +09:00
1083 lines
34 KiB
Rust
1083 lines
34 KiB
Rust
use super::bytecode;
|
|
use super::exceptions;
|
|
use super::frame::Frame;
|
|
use super::obj::objbool;
|
|
use super::obj::objbytearray;
|
|
use super::obj::objbytes;
|
|
use super::obj::objcode;
|
|
use super::obj::objcomplex;
|
|
use super::obj::objdict;
|
|
use super::obj::objfilter;
|
|
use super::obj::objfloat;
|
|
use super::obj::objframe;
|
|
use super::obj::objfunction;
|
|
use super::obj::objgenerator;
|
|
use super::obj::objint;
|
|
use super::obj::objiter;
|
|
use super::obj::objlist;
|
|
use super::obj::objmap;
|
|
use super::obj::objmemory;
|
|
use super::obj::objobject;
|
|
use super::obj::objproperty;
|
|
use super::obj::objrange;
|
|
use super::obj::objset;
|
|
use super::obj::objstr;
|
|
use super::obj::objsuper;
|
|
use super::obj::objtuple;
|
|
use super::obj::objtype;
|
|
use super::vm::VirtualMachine;
|
|
use num_bigint::BigInt;
|
|
use num_bigint::ToBigInt;
|
|
use num_complex::Complex64;
|
|
use num_traits::{One, Zero};
|
|
use std::cell::RefCell;
|
|
use std::collections::HashMap;
|
|
use std::fmt;
|
|
use std::rc::{Rc, Weak};
|
|
|
|
/* Python objects and references.
|
|
|
|
Okay, so each python object itself is an class itself (PyObject). Each
|
|
python object can have several references to it (PyObjectRef). These
|
|
references are Rc (reference counting) rust smart pointers. So when
|
|
all references are destroyed, the object itself also can be cleaned up.
|
|
Basically reference counting, but then done by rust.
|
|
|
|
*/
|
|
|
|
/*
|
|
* Good reference: https://github.com/ProgVal/pythonvm-rust/blob/master/src/objects/mod.rs
|
|
*/
|
|
|
|
/*
|
|
The PyRef type implements
|
|
https://doc.rust-lang.org/std/cell/index.html#introducing-mutability-inside-of-something-immutable
|
|
*/
|
|
pub type PyRef<T> = Rc<RefCell<T>>;
|
|
|
|
/// The `PyObjectRef` is one of the most used types. It is a reference to a
|
|
/// python object. A single python object can have multiple references, and
|
|
/// this reference counting is accounted for by this type. Use the `.clone()`
|
|
/// method to create a new reference and increment the amount of references
|
|
/// to the python object by 1.
|
|
pub type PyObjectRef = PyRef<PyObject>;
|
|
|
|
/// Same as PyObjectRef, except for being a weak reference.
|
|
pub type PyObjectWeakRef = Weak<RefCell<PyObject>>;
|
|
|
|
/// Use this type for function which return a python object or and exception.
|
|
/// Both the python object and the python exception are `PyObjectRef` types
|
|
/// since exceptions are also python objects.
|
|
pub type PyResult = Result<PyObjectRef, PyObjectRef>; // A valid value, or an exception
|
|
|
|
/// For attributes we do not use a dict, but a hashmap. This is probably
|
|
/// faster, unordered, and only supports strings as keys.
|
|
pub type PyAttributes = HashMap<String, PyObjectRef>;
|
|
|
|
impl fmt::Display for PyObject {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
use self::TypeProtocol;
|
|
match &self.payload {
|
|
PyObjectPayload::Module { name, .. } => write!(f, "module '{}'", name),
|
|
PyObjectPayload::Class { name, .. } => {
|
|
let type_name = objtype::get_type_name(&self.typ());
|
|
// We don't have access to a vm, so just assume that if its parent's name
|
|
// is type, it's a type
|
|
if type_name == "type" {
|
|
write!(f, "type object '{}'", name)
|
|
} else {
|
|
write!(f, "'{}' object", type_name)
|
|
}
|
|
}
|
|
_ => write!(f, "'{}' object", objtype::get_type_name(&self.typ())),
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Idea: implement the iterator trait upon PyObjectRef
|
|
impl Iterator for (VirtualMachine, PyObjectRef) {
|
|
type Item = char;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
// call method ("_next__")
|
|
}
|
|
}
|
|
*/
|
|
|
|
#[derive(Debug)]
|
|
pub struct PyContext {
|
|
pub bytes_type: PyObjectRef,
|
|
pub bytearray_type: PyObjectRef,
|
|
pub bool_type: PyObjectRef,
|
|
pub classmethod_type: PyObjectRef,
|
|
pub code_type: PyObjectRef,
|
|
pub dict_type: PyObjectRef,
|
|
pub filter_type: PyObjectRef,
|
|
pub float_type: PyObjectRef,
|
|
pub frame_type: PyObjectRef,
|
|
pub frozenset_type: PyObjectRef,
|
|
pub generator_type: PyObjectRef,
|
|
pub int_type: PyObjectRef,
|
|
pub iter_type: PyObjectRef,
|
|
pub complex_type: PyObjectRef,
|
|
pub true_value: PyObjectRef,
|
|
pub false_value: PyObjectRef,
|
|
pub list_type: PyObjectRef,
|
|
pub map_type: PyObjectRef,
|
|
pub memoryview_type: PyObjectRef,
|
|
pub none: PyObjectRef,
|
|
pub tuple_type: PyObjectRef,
|
|
pub set_type: PyObjectRef,
|
|
pub staticmethod_type: PyObjectRef,
|
|
pub super_type: PyObjectRef,
|
|
pub str_type: PyObjectRef,
|
|
pub range_type: PyObjectRef,
|
|
pub type_type: PyObjectRef,
|
|
pub function_type: PyObjectRef,
|
|
pub property_type: PyObjectRef,
|
|
pub module_type: PyObjectRef,
|
|
pub bound_method_type: PyObjectRef,
|
|
pub member_descriptor_type: PyObjectRef,
|
|
pub object: PyObjectRef,
|
|
pub exceptions: exceptions::ExceptionZoo,
|
|
}
|
|
|
|
/*
|
|
* So a scope is a linked list of scopes.
|
|
* When a name is looked up, it is check in its scope.
|
|
*/
|
|
#[derive(Debug)]
|
|
pub struct Scope {
|
|
pub locals: PyObjectRef, // Variables
|
|
pub parent: Option<PyObjectRef>, // Parent scope
|
|
}
|
|
|
|
fn _nothing() -> PyObjectRef {
|
|
PyObject {
|
|
payload: PyObjectPayload::None,
|
|
typ: None,
|
|
}
|
|
.into_ref()
|
|
}
|
|
|
|
pub fn create_type(
|
|
name: &str,
|
|
type_type: &PyObjectRef,
|
|
base: &PyObjectRef,
|
|
_dict_type: &PyObjectRef,
|
|
) -> PyObjectRef {
|
|
let dict = PyAttributes::new();
|
|
objtype::new(type_type.clone(), name, vec![base.clone()], dict).unwrap()
|
|
}
|
|
|
|
// Basic objects:
|
|
impl PyContext {
|
|
pub fn new() -> Self {
|
|
let type_type = _nothing();
|
|
let object_type = _nothing();
|
|
let dict_type = _nothing();
|
|
|
|
objtype::create_type(type_type.clone(), object_type.clone(), dict_type.clone());
|
|
objobject::create_object(type_type.clone(), object_type.clone(), dict_type.clone());
|
|
objdict::create_type(type_type.clone(), object_type.clone(), dict_type.clone());
|
|
|
|
let module_type = create_type("module", &type_type, &object_type, &dict_type);
|
|
let classmethod_type = create_type("classmethod", &type_type, &object_type, &dict_type);
|
|
let staticmethod_type = create_type("staticmethod", &type_type, &object_type, &dict_type);
|
|
let function_type = create_type("function", &type_type, &object_type, &dict_type);
|
|
let property_type = create_type("property", &type_type, &object_type, &dict_type);
|
|
let super_type = create_type("super", &type_type, &object_type, &dict_type);
|
|
let generator_type = create_type("generator", &type_type, &object_type, &dict_type);
|
|
let bound_method_type = create_type("method", &type_type, &object_type, &dict_type);
|
|
let member_descriptor_type =
|
|
create_type("member_descriptor", &type_type, &object_type, &dict_type);
|
|
let str_type = create_type("str", &type_type, &object_type, &dict_type);
|
|
let list_type = create_type("list", &type_type, &object_type, &dict_type);
|
|
let set_type = create_type("set", &type_type, &object_type, &dict_type);
|
|
let frozenset_type = create_type("frozenset", &type_type, &object_type, &dict_type);
|
|
let int_type = create_type("int", &type_type, &object_type, &dict_type);
|
|
let float_type = create_type("float", &type_type, &object_type, &dict_type);
|
|
let frame_type = create_type("frame", &type_type, &object_type, &dict_type);
|
|
let complex_type = create_type("complex", &type_type, &object_type, &dict_type);
|
|
let bytes_type = create_type("bytes", &type_type, &object_type, &dict_type);
|
|
let bytearray_type = create_type("bytearray", &type_type, &object_type, &dict_type);
|
|
let tuple_type = create_type("tuple", &type_type, &object_type, &dict_type);
|
|
let iter_type = create_type("iter", &type_type, &object_type, &dict_type);
|
|
let filter_type = create_type("filter", &type_type, &object_type, &dict_type);
|
|
let map_type = create_type("map", &type_type, &object_type, &dict_type);
|
|
let bool_type = create_type("bool", &type_type, &int_type, &dict_type);
|
|
let memoryview_type = create_type("memoryview", &type_type, &object_type, &dict_type);
|
|
let code_type = create_type("code", &type_type, &int_type, &dict_type);
|
|
let range_type = create_type("range", &type_type, &object_type, &dict_type);
|
|
let exceptions = exceptions::ExceptionZoo::new(&type_type, &object_type, &dict_type);
|
|
|
|
let none = PyObject::new(
|
|
PyObjectPayload::None,
|
|
create_type("NoneType", &type_type, &object_type, &dict_type),
|
|
);
|
|
|
|
let true_value = PyObject::new(
|
|
PyObjectPayload::Integer { value: One::one() },
|
|
bool_type.clone(),
|
|
);
|
|
let false_value = PyObject::new(
|
|
PyObjectPayload::Integer {
|
|
value: Zero::zero(),
|
|
},
|
|
bool_type.clone(),
|
|
);
|
|
let context = PyContext {
|
|
bool_type,
|
|
memoryview_type,
|
|
bytearray_type,
|
|
bytes_type,
|
|
code_type,
|
|
complex_type,
|
|
classmethod_type,
|
|
int_type,
|
|
float_type,
|
|
frame_type,
|
|
staticmethod_type,
|
|
list_type,
|
|
set_type,
|
|
frozenset_type,
|
|
true_value,
|
|
false_value,
|
|
tuple_type,
|
|
iter_type,
|
|
filter_type,
|
|
map_type,
|
|
dict_type,
|
|
none,
|
|
str_type,
|
|
range_type,
|
|
object: object_type,
|
|
function_type,
|
|
super_type,
|
|
property_type,
|
|
generator_type,
|
|
module_type,
|
|
bound_method_type,
|
|
member_descriptor_type,
|
|
type_type,
|
|
exceptions,
|
|
};
|
|
objtype::init(&context);
|
|
objlist::init(&context);
|
|
objset::init(&context);
|
|
objtuple::init(&context);
|
|
objobject::init(&context);
|
|
objdict::init(&context);
|
|
objfunction::init(&context);
|
|
objgenerator::init(&context);
|
|
objint::init(&context);
|
|
objfloat::init(&context);
|
|
objcomplex::init(&context);
|
|
objbytes::init(&context);
|
|
objbytearray::init(&context);
|
|
objproperty::init(&context);
|
|
objmemory::init(&context);
|
|
objstr::init(&context);
|
|
objrange::init(&context);
|
|
objsuper::init(&context);
|
|
objtuple::init(&context);
|
|
objiter::init(&context);
|
|
objfilter::init(&context);
|
|
objmap::init(&context);
|
|
objbool::init(&context);
|
|
objcode::init(&context);
|
|
objframe::init(&context);
|
|
exceptions::init(&context);
|
|
context
|
|
}
|
|
|
|
pub fn bytearray_type(&self) -> PyObjectRef {
|
|
self.bytearray_type.clone()
|
|
}
|
|
|
|
pub fn bytes_type(&self) -> PyObjectRef {
|
|
self.bytes_type.clone()
|
|
}
|
|
|
|
pub fn code_type(&self) -> PyObjectRef {
|
|
self.code_type.clone()
|
|
}
|
|
|
|
pub fn complex_type(&self) -> PyObjectRef {
|
|
self.complex_type.clone()
|
|
}
|
|
|
|
pub fn dict_type(&self) -> PyObjectRef {
|
|
self.dict_type.clone()
|
|
}
|
|
|
|
pub fn float_type(&self) -> PyObjectRef {
|
|
self.float_type.clone()
|
|
}
|
|
|
|
pub fn frame_type(&self) -> PyObjectRef {
|
|
self.frame_type.clone()
|
|
}
|
|
|
|
pub fn int_type(&self) -> PyObjectRef {
|
|
self.int_type.clone()
|
|
}
|
|
|
|
pub fn list_type(&self) -> PyObjectRef {
|
|
self.list_type.clone()
|
|
}
|
|
|
|
pub fn set_type(&self) -> PyObjectRef {
|
|
self.set_type.clone()
|
|
}
|
|
|
|
pub fn range_type(&self) -> PyObjectRef {
|
|
self.range_type.clone()
|
|
}
|
|
|
|
pub fn frozenset_type(&self) -> PyObjectRef {
|
|
self.frozenset_type.clone()
|
|
}
|
|
|
|
pub fn bool_type(&self) -> PyObjectRef {
|
|
self.bool_type.clone()
|
|
}
|
|
|
|
pub fn memoryview_type(&self) -> PyObjectRef {
|
|
self.memoryview_type.clone()
|
|
}
|
|
|
|
pub fn tuple_type(&self) -> PyObjectRef {
|
|
self.tuple_type.clone()
|
|
}
|
|
|
|
pub fn iter_type(&self) -> PyObjectRef {
|
|
self.iter_type.clone()
|
|
}
|
|
|
|
pub fn filter_type(&self) -> PyObjectRef {
|
|
self.filter_type.clone()
|
|
}
|
|
|
|
pub fn map_type(&self) -> PyObjectRef {
|
|
self.map_type.clone()
|
|
}
|
|
|
|
pub fn str_type(&self) -> PyObjectRef {
|
|
self.str_type.clone()
|
|
}
|
|
|
|
pub fn super_type(&self) -> PyObjectRef {
|
|
self.super_type.clone()
|
|
}
|
|
|
|
pub fn function_type(&self) -> PyObjectRef {
|
|
self.function_type.clone()
|
|
}
|
|
|
|
pub fn property_type(&self) -> PyObjectRef {
|
|
self.property_type.clone()
|
|
}
|
|
|
|
pub fn classmethod_type(&self) -> PyObjectRef {
|
|
self.classmethod_type.clone()
|
|
}
|
|
|
|
pub fn staticmethod_type(&self) -> PyObjectRef {
|
|
self.staticmethod_type.clone()
|
|
}
|
|
|
|
pub fn generator_type(&self) -> PyObjectRef {
|
|
self.generator_type.clone()
|
|
}
|
|
|
|
pub fn bound_method_type(&self) -> PyObjectRef {
|
|
self.bound_method_type.clone()
|
|
}
|
|
pub fn member_descriptor_type(&self) -> PyObjectRef {
|
|
self.member_descriptor_type.clone()
|
|
}
|
|
pub fn type_type(&self) -> PyObjectRef {
|
|
self.type_type.clone()
|
|
}
|
|
|
|
pub fn none(&self) -> PyObjectRef {
|
|
self.none.clone()
|
|
}
|
|
pub fn object(&self) -> PyObjectRef {
|
|
self.object.clone()
|
|
}
|
|
|
|
pub fn new_object(&self) -> PyObjectRef {
|
|
self.new_instance(self.object(), None)
|
|
}
|
|
|
|
pub fn new_int<T: ToBigInt>(&self, i: T) -> PyObjectRef {
|
|
PyObject::new(
|
|
PyObjectPayload::Integer {
|
|
value: i.to_bigint().unwrap(),
|
|
},
|
|
self.int_type(),
|
|
)
|
|
}
|
|
|
|
pub fn new_float(&self, i: f64) -> PyObjectRef {
|
|
PyObject::new(PyObjectPayload::Float { value: i }, self.float_type())
|
|
}
|
|
|
|
pub fn new_complex(&self, i: Complex64) -> PyObjectRef {
|
|
PyObject::new(PyObjectPayload::Complex { value: i }, self.complex_type())
|
|
}
|
|
|
|
pub fn new_str(&self, s: String) -> PyObjectRef {
|
|
PyObject::new(PyObjectPayload::String { value: s }, self.str_type())
|
|
}
|
|
|
|
pub fn new_bytes(&self, data: Vec<u8>) -> PyObjectRef {
|
|
PyObject::new(PyObjectPayload::Bytes { value: data }, self.bytes_type())
|
|
}
|
|
|
|
pub fn new_bytearray(&self, data: Vec<u8>) -> PyObjectRef {
|
|
PyObject::new(
|
|
PyObjectPayload::Bytes { value: data },
|
|
self.bytearray_type(),
|
|
)
|
|
}
|
|
|
|
pub fn new_bool(&self, b: bool) -> PyObjectRef {
|
|
if b {
|
|
self.true_value.clone()
|
|
} else {
|
|
self.false_value.clone()
|
|
}
|
|
}
|
|
|
|
pub fn new_tuple(&self, elements: Vec<PyObjectRef>) -> PyObjectRef {
|
|
PyObject::new(PyObjectPayload::Sequence { elements }, self.tuple_type())
|
|
}
|
|
|
|
pub fn new_list(&self, elements: Vec<PyObjectRef>) -> PyObjectRef {
|
|
PyObject::new(PyObjectPayload::Sequence { elements }, self.list_type())
|
|
}
|
|
|
|
pub fn new_set(&self, elements: Vec<PyObjectRef>) -> PyObjectRef {
|
|
let elements = objset::sequence_to_hashmap(&elements);
|
|
PyObject::new(PyObjectPayload::Set { elements }, self.set_type())
|
|
}
|
|
|
|
pub fn new_dict(&self) -> PyObjectRef {
|
|
PyObject::new(
|
|
PyObjectPayload::Dict {
|
|
elements: HashMap::new(),
|
|
},
|
|
self.dict_type(),
|
|
)
|
|
}
|
|
|
|
pub fn new_class(&self, name: &str, base: PyObjectRef) -> PyObjectRef {
|
|
objtype::new(self.type_type(), name, vec![base], PyAttributes::new()).unwrap()
|
|
}
|
|
|
|
pub fn new_scope(&self, parent: Option<PyObjectRef>) -> PyObjectRef {
|
|
let locals = self.new_dict();
|
|
let scope = Scope { locals, parent };
|
|
PyObject {
|
|
payload: PyObjectPayload::Scope { scope },
|
|
typ: None,
|
|
}
|
|
.into_ref()
|
|
}
|
|
|
|
pub fn new_module(&self, name: &str, scope: PyObjectRef) -> PyObjectRef {
|
|
PyObject::new(
|
|
PyObjectPayload::Module {
|
|
name: name.to_string(),
|
|
dict: scope.clone(),
|
|
},
|
|
self.module_type.clone(),
|
|
)
|
|
}
|
|
|
|
pub fn new_rustfunc<F: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>(
|
|
&self,
|
|
function: F,
|
|
) -> PyObjectRef {
|
|
PyObject::new(
|
|
PyObjectPayload::RustFunction {
|
|
function: Box::new(function),
|
|
},
|
|
self.function_type(),
|
|
)
|
|
}
|
|
|
|
pub fn new_rustfunc_from_box(
|
|
&self,
|
|
function: Box<Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>,
|
|
) -> PyObjectRef {
|
|
PyObject::new(
|
|
PyObjectPayload::RustFunction { function },
|
|
self.function_type(),
|
|
)
|
|
}
|
|
|
|
pub fn new_frame(&self, frame: Frame) -> PyObjectRef {
|
|
PyObject::new(PyObjectPayload::Frame { frame }, self.frame_type())
|
|
}
|
|
|
|
pub fn new_property<F: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>(
|
|
&self,
|
|
function: F,
|
|
) -> PyObjectRef {
|
|
let fget = self.new_rustfunc(function);
|
|
let py_obj = self.new_instance(self.property_type(), None);
|
|
self.set_attr(&py_obj, "fget", fget.clone());
|
|
py_obj
|
|
}
|
|
|
|
pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyObjectRef {
|
|
PyObject::new(PyObjectPayload::Code { code }, self.code_type())
|
|
}
|
|
|
|
pub fn new_function(
|
|
&self,
|
|
code_obj: PyObjectRef,
|
|
scope: PyObjectRef,
|
|
defaults: PyObjectRef,
|
|
) -> PyObjectRef {
|
|
PyObject::new(
|
|
PyObjectPayload::Function {
|
|
code: code_obj,
|
|
scope,
|
|
defaults,
|
|
},
|
|
self.function_type(),
|
|
)
|
|
}
|
|
|
|
pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef {
|
|
PyObject::new(
|
|
PyObjectPayload::BoundMethod { function, object },
|
|
self.bound_method_type(),
|
|
)
|
|
}
|
|
|
|
pub fn new_member_descriptor<F: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>(
|
|
&self,
|
|
function: F,
|
|
) -> PyObjectRef {
|
|
let mut dict = PyAttributes::new();
|
|
dict.insert("function".to_string(), self.new_rustfunc(function));
|
|
self.new_instance(self.member_descriptor_type(), Some(dict))
|
|
}
|
|
|
|
pub fn new_instance(&self, class: PyObjectRef, dict: Option<PyAttributes>) -> PyObjectRef {
|
|
let dict = if let Some(dict) = dict {
|
|
dict
|
|
} else {
|
|
PyAttributes::new()
|
|
};
|
|
PyObject::new(
|
|
PyObjectPayload::Instance {
|
|
dict: RefCell::new(dict),
|
|
},
|
|
class,
|
|
)
|
|
}
|
|
|
|
// Item set/get:
|
|
pub fn set_item(&self, obj: &PyObjectRef, key: &str, v: PyObjectRef) {
|
|
match obj.borrow_mut().payload {
|
|
PyObjectPayload::Dict { ref mut elements } => {
|
|
let key = self.new_str(key.to_string());
|
|
objdict::set_item_in_content(elements, &key, &v);
|
|
}
|
|
PyObjectPayload::Module { ref mut dict, .. } => self.set_item(dict, key, v),
|
|
PyObjectPayload::Scope { ref mut scope } => {
|
|
self.set_item(&scope.locals, key, v);
|
|
}
|
|
ref k => panic!("TODO {:?}", k),
|
|
};
|
|
}
|
|
|
|
pub fn get_attr(&self, obj: &PyObjectRef, attr_name: &str) -> Option<PyObjectRef> {
|
|
// This does not need to be on the PyContext.
|
|
// We do not require to make a new key as string for this function
|
|
// (yet)...
|
|
obj.get_attr(attr_name)
|
|
}
|
|
|
|
pub fn set_attr(&self, obj: &PyObjectRef, attr_name: &str, value: PyObjectRef) {
|
|
match obj.borrow().payload {
|
|
PyObjectPayload::Module { ref dict, .. } => self.set_item(dict, attr_name, value),
|
|
PyObjectPayload::Instance { ref dict } | PyObjectPayload::Class { ref dict, .. } => {
|
|
dict.borrow_mut().insert(attr_name.to_string(), value);
|
|
}
|
|
ref payload => unimplemented!("set_attr unimplemented for: {:?}", payload),
|
|
};
|
|
}
|
|
}
|
|
|
|
/// This is an actual python object. It consists of a `typ` which is the
|
|
/// python class, and carries some rust payload optionally. This rust
|
|
/// payload can be a rust float or rust int in case of float and int objects.
|
|
pub struct PyObject {
|
|
pub payload: PyObjectPayload,
|
|
pub typ: Option<PyObjectRef>,
|
|
// pub dict: HashMap<String, PyObjectRef>, // __dict__ member
|
|
}
|
|
|
|
pub trait IdProtocol {
|
|
fn get_id(&self) -> usize;
|
|
fn is(&self, other: &PyObjectRef) -> bool;
|
|
}
|
|
|
|
impl IdProtocol for PyObjectRef {
|
|
fn get_id(&self) -> usize {
|
|
self.as_ptr() as usize
|
|
}
|
|
|
|
fn is(&self, other: &PyObjectRef) -> bool {
|
|
self.get_id() == other.get_id()
|
|
}
|
|
}
|
|
|
|
pub trait FromPyObjectRef {
|
|
fn from_pyobj(obj: &PyObjectRef) -> Self;
|
|
}
|
|
|
|
pub trait TypeProtocol {
|
|
fn typ(&self) -> PyObjectRef;
|
|
}
|
|
|
|
impl TypeProtocol for PyObjectRef {
|
|
fn typ(&self) -> PyObjectRef {
|
|
self.borrow().typ()
|
|
}
|
|
}
|
|
|
|
impl TypeProtocol for PyObject {
|
|
fn typ(&self) -> PyObjectRef {
|
|
match self.typ {
|
|
Some(ref typ) => typ.clone(),
|
|
None => panic!("Object {:?} doesn't have a type!", self),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait ParentProtocol {
|
|
fn has_parent(&self) -> bool;
|
|
fn get_parent(&self) -> PyObjectRef;
|
|
}
|
|
|
|
impl ParentProtocol for PyObjectRef {
|
|
fn has_parent(&self) -> bool {
|
|
match self.borrow().payload {
|
|
PyObjectPayload::Scope { ref scope } => scope.parent.is_some(),
|
|
_ => panic!("Only scopes have parent (not {:?}", self),
|
|
}
|
|
}
|
|
|
|
fn get_parent(&self) -> PyObjectRef {
|
|
match self.borrow().payload {
|
|
PyObjectPayload::Scope { ref scope } => match scope.parent {
|
|
Some(ref value) => value.clone(),
|
|
None => panic!("OMG"),
|
|
},
|
|
_ => panic!("TODO"),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait AttributeProtocol {
|
|
fn get_attr(&self, attr_name: &str) -> Option<PyObjectRef>;
|
|
fn has_attr(&self, attr_name: &str) -> bool;
|
|
}
|
|
|
|
fn class_get_item(class: &PyObjectRef, attr_name: &str) -> Option<PyObjectRef> {
|
|
let class = class.borrow();
|
|
match class.payload {
|
|
PyObjectPayload::Class { ref dict, .. } => dict.borrow().get(attr_name).map(|v| v.clone()),
|
|
_ => panic!("Only classes should be in MRO!"),
|
|
}
|
|
}
|
|
|
|
fn class_has_item(class: &PyObjectRef, attr_name: &str) -> bool {
|
|
let class = class.borrow();
|
|
match class.payload {
|
|
PyObjectPayload::Class { ref dict, .. } => dict.borrow().contains_key(attr_name),
|
|
_ => panic!("Only classes should be in MRO!"),
|
|
}
|
|
}
|
|
|
|
impl AttributeProtocol for PyObjectRef {
|
|
fn get_attr(&self, attr_name: &str) -> Option<PyObjectRef> {
|
|
let obj = self.borrow();
|
|
match obj.payload {
|
|
PyObjectPayload::Module { ref dict, .. } => dict.get_item(attr_name),
|
|
PyObjectPayload::Class { ref mro, .. } => {
|
|
if let Some(item) = class_get_item(self, attr_name) {
|
|
return Some(item);
|
|
}
|
|
for class in mro {
|
|
if let Some(item) = class_get_item(class, attr_name) {
|
|
return Some(item);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
PyObjectPayload::Instance { ref dict } => {
|
|
dict.borrow().get(attr_name).map(|v| v.clone())
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn has_attr(&self, attr_name: &str) -> bool {
|
|
let obj = self.borrow();
|
|
match obj.payload {
|
|
PyObjectPayload::Module { ref dict, .. } => dict.contains_key(attr_name),
|
|
PyObjectPayload::Class { ref mro, .. } => {
|
|
class_has_item(self, attr_name) || mro.iter().any(|d| class_has_item(d, attr_name))
|
|
}
|
|
PyObjectPayload::Instance { ref dict } => dict.borrow().contains_key(attr_name),
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait DictProtocol {
|
|
fn contains_key(&self, k: &str) -> bool;
|
|
fn get_item(&self, k: &str) -> Option<PyObjectRef>;
|
|
fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)>;
|
|
}
|
|
|
|
impl DictProtocol for PyObjectRef {
|
|
fn contains_key(&self, k: &str) -> bool {
|
|
match self.borrow().payload {
|
|
PyObjectPayload::Dict { ref elements } => {
|
|
objdict::content_contains_key_str(elements, k)
|
|
}
|
|
PyObjectPayload::Module { ref dict, .. } => dict.contains_key(k),
|
|
PyObjectPayload::Scope { ref scope } => scope.locals.contains_key(k),
|
|
ref payload => unimplemented!("TODO {:?}", payload),
|
|
}
|
|
}
|
|
|
|
fn get_item(&self, k: &str) -> Option<PyObjectRef> {
|
|
match self.borrow().payload {
|
|
PyObjectPayload::Dict { ref elements } => objdict::content_get_key_str(elements, k),
|
|
PyObjectPayload::Module { ref dict, .. } => dict.get_item(k),
|
|
PyObjectPayload::Scope { ref scope } => scope.locals.get_item(k),
|
|
_ => panic!("TODO"),
|
|
}
|
|
}
|
|
|
|
fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> {
|
|
match self.borrow().payload {
|
|
PyObjectPayload::Dict { .. } => objdict::get_key_value_pairs(self),
|
|
PyObjectPayload::Module { ref dict, .. } => dict.get_key_value_pairs(),
|
|
PyObjectPayload::Scope { ref scope } => scope.locals.get_key_value_pairs(),
|
|
_ => panic!("TODO"),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait BufferProtocol {
|
|
fn readonly(&self) -> bool;
|
|
}
|
|
|
|
impl BufferProtocol for PyObjectRef {
|
|
fn readonly(&self) -> bool {
|
|
match objtype::get_type_name(&self.typ()).as_ref() {
|
|
"bytes" => false,
|
|
"bytearray" | "memoryview" => true,
|
|
_ => panic!("Bytes-Like type expected not {:?}", self),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for PyObject {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f, "[PyObj {:?}]", self.payload)
|
|
}
|
|
}
|
|
|
|
/// The `PyFuncArgs` struct is one of the most used structs then creating
|
|
/// a rust function that can be called from python. It holds both positional
|
|
/// arguments, as well as keyword arguments passed to the function.
|
|
#[derive(Debug, Default, Clone)]
|
|
pub struct PyFuncArgs {
|
|
pub args: Vec<PyObjectRef>,
|
|
pub kwargs: Vec<(String, PyObjectRef)>,
|
|
}
|
|
|
|
impl PyFuncArgs {
|
|
pub fn new(mut args: Vec<PyObjectRef>, kwarg_names: Vec<String>) -> PyFuncArgs {
|
|
let mut kwargs = vec![];
|
|
for name in kwarg_names.iter().rev() {
|
|
kwargs.push((name.clone(), args.pop().unwrap()));
|
|
}
|
|
PyFuncArgs { args, kwargs }
|
|
}
|
|
|
|
pub fn insert(&self, item: PyObjectRef) -> PyFuncArgs {
|
|
let mut args = PyFuncArgs {
|
|
args: self.args.clone(),
|
|
kwargs: self.kwargs.clone(),
|
|
};
|
|
args.args.insert(0, item);
|
|
args
|
|
}
|
|
|
|
pub fn shift(&mut self) -> PyObjectRef {
|
|
self.args.remove(0)
|
|
}
|
|
|
|
pub fn get_kwarg(&self, key: &str, default: PyObjectRef) -> PyObjectRef {
|
|
for (arg_name, arg_value) in self.kwargs.iter() {
|
|
if arg_name == key {
|
|
return arg_value.clone();
|
|
}
|
|
}
|
|
default.clone()
|
|
}
|
|
|
|
pub fn get_optional_kwarg(&self, key: &str) -> Option<PyObjectRef> {
|
|
for (arg_name, arg_value) in self.kwargs.iter() {
|
|
if arg_name == key {
|
|
return Some(arg_value.clone());
|
|
}
|
|
}
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Rather than determining the type of a python object, this enum is more
|
|
/// a holder for the rust payload of a python object. It is more a carrier
|
|
/// of rust data for a particular python object. Determine the python type
|
|
/// by using for example the `.typ()` method on a python object.
|
|
pub enum PyObjectPayload {
|
|
String {
|
|
value: String,
|
|
},
|
|
Integer {
|
|
value: BigInt,
|
|
},
|
|
Float {
|
|
value: f64,
|
|
},
|
|
Complex {
|
|
value: Complex64,
|
|
},
|
|
Bytes {
|
|
value: Vec<u8>,
|
|
},
|
|
Sequence {
|
|
elements: Vec<PyObjectRef>,
|
|
},
|
|
Dict {
|
|
elements: objdict::DictContentType,
|
|
},
|
|
Set {
|
|
elements: HashMap<usize, PyObjectRef>,
|
|
},
|
|
Iterator {
|
|
position: usize,
|
|
iterated_obj: PyObjectRef,
|
|
},
|
|
FilterIterator {
|
|
predicate: PyObjectRef,
|
|
iterator: PyObjectRef,
|
|
},
|
|
MapIterator {
|
|
mapper: PyObjectRef,
|
|
iterators: Vec<PyObjectRef>,
|
|
},
|
|
Slice {
|
|
start: Option<i32>,
|
|
stop: Option<i32>,
|
|
step: Option<i32>,
|
|
},
|
|
Range {
|
|
range: objrange::RangeType,
|
|
},
|
|
MemoryView {
|
|
obj: PyObjectRef,
|
|
},
|
|
Code {
|
|
code: bytecode::CodeObject,
|
|
},
|
|
Frame {
|
|
frame: Frame,
|
|
},
|
|
Function {
|
|
code: PyObjectRef,
|
|
scope: PyObjectRef,
|
|
defaults: PyObjectRef,
|
|
},
|
|
Generator {
|
|
frame: Frame,
|
|
},
|
|
BoundMethod {
|
|
function: PyObjectRef,
|
|
object: PyObjectRef,
|
|
},
|
|
Scope {
|
|
scope: Scope,
|
|
},
|
|
Module {
|
|
name: String,
|
|
dict: PyObjectRef,
|
|
},
|
|
None,
|
|
Class {
|
|
name: String,
|
|
dict: RefCell<PyAttributes>,
|
|
mro: Vec<PyObjectRef>,
|
|
},
|
|
WeakRef {
|
|
referent: PyObjectWeakRef,
|
|
},
|
|
Instance {
|
|
dict: RefCell<PyAttributes>,
|
|
},
|
|
RustFunction {
|
|
function: Box<Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>,
|
|
},
|
|
}
|
|
|
|
impl fmt::Debug for PyObjectPayload {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match self {
|
|
PyObjectPayload::String { ref value } => write!(f, "str \"{}\"", value),
|
|
PyObjectPayload::Integer { ref value } => write!(f, "int {}", value),
|
|
PyObjectPayload::Float { ref value } => write!(f, "float {}", value),
|
|
PyObjectPayload::Complex { ref value } => write!(f, "complex {}", value),
|
|
PyObjectPayload::Bytes { ref value } => write!(f, "bytes/bytearray {:?}", value),
|
|
PyObjectPayload::MemoryView { ref obj } => write!(f, "bytes/bytearray {:?}", obj),
|
|
PyObjectPayload::Sequence { .. } => write!(f, "list or tuple"),
|
|
PyObjectPayload::Dict { .. } => write!(f, "dict"),
|
|
PyObjectPayload::Set { .. } => write!(f, "set"),
|
|
PyObjectPayload::WeakRef { .. } => write!(f, "weakref"),
|
|
PyObjectPayload::Range { .. } => write!(f, "range"),
|
|
PyObjectPayload::Iterator { .. } => write!(f, "iterator"),
|
|
PyObjectPayload::FilterIterator { .. } => write!(f, "filter"),
|
|
PyObjectPayload::MapIterator { .. } => write!(f, "map"),
|
|
PyObjectPayload::Slice { .. } => write!(f, "slice"),
|
|
PyObjectPayload::Code { ref code } => write!(f, "code: {:?}", code),
|
|
PyObjectPayload::Function { .. } => write!(f, "function"),
|
|
PyObjectPayload::Generator { .. } => write!(f, "generator"),
|
|
PyObjectPayload::BoundMethod {
|
|
ref function,
|
|
ref object,
|
|
} => write!(f, "bound-method: {:?} of {:?}", function, object),
|
|
PyObjectPayload::Module { .. } => write!(f, "module"),
|
|
PyObjectPayload::Scope { .. } => write!(f, "scope"),
|
|
PyObjectPayload::None => write!(f, "None"),
|
|
PyObjectPayload::Class { ref name, .. } => write!(f, "class {:?}", name),
|
|
PyObjectPayload::Instance { .. } => write!(f, "instance"),
|
|
PyObjectPayload::RustFunction { .. } => write!(f, "rust function"),
|
|
PyObjectPayload::Frame { .. } => write!(f, "frame"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PyObject {
|
|
pub fn new(
|
|
payload: PyObjectPayload,
|
|
/* dict: PyObjectRef,*/ typ: PyObjectRef,
|
|
) -> PyObjectRef {
|
|
PyObject {
|
|
payload,
|
|
typ: Some(typ),
|
|
// dict: HashMap::new(), // dict,
|
|
}
|
|
.into_ref()
|
|
}
|
|
|
|
/// Deprecated method, please call `vm.to_pystr`
|
|
pub fn str(&self) -> String {
|
|
match self.payload {
|
|
PyObjectPayload::String { ref value } => value.clone(),
|
|
PyObjectPayload::Integer { ref value } => format!("{:?}", value),
|
|
PyObjectPayload::Float { ref value } => format!("{:?}", value),
|
|
PyObjectPayload::Complex { ref value } => format!("{:?}", value),
|
|
PyObjectPayload::Bytes { ref value } => format!("b'{:?}'", value),
|
|
PyObjectPayload::MemoryView { ref obj } => format!("b'{:?}'", obj),
|
|
PyObjectPayload::Sequence { ref elements } => format!(
|
|
"(/[{}]/)",
|
|
elements
|
|
.iter()
|
|
.map(|elem| elem.borrow().str())
|
|
.collect::<Vec<_>>()
|
|
.join(", ")
|
|
),
|
|
PyObjectPayload::Dict { ref elements } => format!(
|
|
"{{ {} }}",
|
|
elements
|
|
.iter()
|
|
.map(|elem| format!("{}: ...", elem.0))
|
|
.collect::<Vec<_>>()
|
|
.join(", ")
|
|
),
|
|
PyObjectPayload::Set { ref elements } => format!(
|
|
"{{ {} }}",
|
|
elements
|
|
.iter()
|
|
.map(|elem| elem.1.borrow().str())
|
|
.collect::<Vec<_>>()
|
|
.join(", ")
|
|
),
|
|
PyObjectPayload::WeakRef { .. } => String::from("weakref"),
|
|
PyObjectPayload::None => String::from("None"),
|
|
PyObjectPayload::Class {
|
|
ref name,
|
|
dict: ref _dict,
|
|
..
|
|
} => format!("<class '{}'>", name),
|
|
PyObjectPayload::Instance { .. } => "<instance>".to_string(),
|
|
PyObjectPayload::Code { .. } => "<code>".to_string(),
|
|
PyObjectPayload::Function { .. } => "<func>".to_string(),
|
|
PyObjectPayload::Generator { .. } => "<generator>".to_string(),
|
|
PyObjectPayload::Frame { .. } => "<frame>".to_string(),
|
|
PyObjectPayload::BoundMethod { .. } => "<bound-method>".to_string(),
|
|
PyObjectPayload::RustFunction { .. } => "<rustfunc>".to_string(),
|
|
PyObjectPayload::Module { ref name, .. } => format!("<module '{}'>", name),
|
|
PyObjectPayload::Scope { ref scope } => format!("<scope '{:?}'>", scope),
|
|
PyObjectPayload::Slice {
|
|
ref start,
|
|
ref stop,
|
|
ref step,
|
|
} => format!("<slice '{:?}:{:?}:{:?}'>", start, stop, step),
|
|
PyObjectPayload::Range { ref range } => format!("<range '{:?}'>", range),
|
|
PyObjectPayload::Iterator {
|
|
ref position,
|
|
ref iterated_obj,
|
|
} => format!(
|
|
"<iter pos {} in {}>",
|
|
position,
|
|
iterated_obj.borrow_mut().str()
|
|
),
|
|
PyObjectPayload::FilterIterator { .. } => format!("<filter>"),
|
|
PyObjectPayload::MapIterator { .. } => format!("<map>"),
|
|
}
|
|
}
|
|
|
|
// Move this object into a reference object, transferring ownership.
|
|
pub fn into_ref(self) -> PyObjectRef {
|
|
Rc::new(RefCell::new(self))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::PyContext;
|
|
|
|
#[test]
|
|
fn test_type_type() {
|
|
// TODO: Write this test
|
|
PyContext::new();
|
|
}
|
|
}
|