pyo3/pyo3cls/src/py_class.rs

280 lines
9.5 KiB
Rust
Raw Normal View History

2017-05-16 05:24:06 +00:00
// Copyright (c) 2017-present PyO3 Project and Contributors
use syn;
use quote::{Tokens, ToTokens};
pub fn build_py_class(ast: &mut syn::DeriveInput) -> Tokens {
2017-05-17 06:43:39 +00:00
let base = syn::Ident::from("pyo3::PyObject");
2017-05-16 05:24:06 +00:00
let mut tokens = Tokens::new();
2017-05-17 06:43:39 +00:00
match ast.body {
syn::Body::Struct(syn::VariantData::Struct(ref mut data)) => {
impl_storage(&ast.ident, &base, data).to_tokens(&mut tokens);
let tt = quote! {
struct Test {
_unsafe_inner: PyObject
}
};
let t = syn::parse_item(tt.as_str()).unwrap();
match t.node {
syn::ItemKind::Struct(syn::VariantData::Struct(fields), _) => {
data.clear();
data.extend(fields);
}
_ => panic!("something is worng"),
}
},
_ =>
panic!("#[class] can only be used with notmal structs"),
}
2017-05-16 05:24:06 +00:00
impl_to_py_object(&ast.ident).to_tokens(&mut tokens);
impl_from_py_object(&ast.ident).to_tokens(&mut tokens);
impl_python_object(&ast.ident).to_tokens(&mut tokens);
impl_checked_downcast(&ast.ident).to_tokens(&mut tokens);
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_CLS_{}", ast.ident));
quote! {
#[feature(specialization)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const #dummy_const: () = {
extern crate pyo3;
use std;
2017-05-17 06:43:39 +00:00
use pyo3::class::BaseObject;
use pyo3::{ffi, Python, PyObject, PyType, PyResult, PyModule};
2017-05-16 05:24:06 +00:00
#tokens
};
}
}
2017-05-17 06:43:39 +00:00
fn impl_storage(cls: &syn::Ident, base: &syn::Ident, fields: &Vec<syn::Field>) -> Tokens {
let names: Vec<syn::Ident> = fields.iter()
.map(|f| f.ident.as_ref().unwrap().clone()).collect();
let values: Vec<syn::Ident> = fields.iter()
.map(|f| f.ident.as_ref().unwrap().clone()).collect();
//let types: Vec<syn::Ty> = fields.iter().map(|f| f.ty.clone()).collect();
let mut accessors = Tokens::new();
for field in fields.iter() {
let name = &field.ident.as_ref().unwrap();
let name_mut = syn::Ident::from(format!("{}_mut", name.as_ref()));
let ty = &field.ty;
let accessor = quote!{
impl #cls {
fn #name<'a>(&'a self, py: Python<'a>) -> &'a #ty {
unsafe {
let ptr = (self._unsafe_inner.as_ptr() as *const u8)
.offset(base_offset() as isize) as *const Storage;
&(*ptr).#name
}
}
fn #name_mut<'a>(&'a self, py: Python<'a>) -> &'a mut #ty {
unsafe {
let ptr = (self._unsafe_inner.as_ptr() as *const u8)
.offset(base_offset() as isize) as *mut Storage;
&mut (*ptr).#name
}
}
}
};
accessor.to_tokens(&mut accessors);
}
quote! {
struct Storage {
#(#fields),*
}
impl #cls {
fn create_instance(py: Python, #(#fields),*) -> PyResult<#cls> {
let obj = try!(unsafe {
<#cls as BaseObject>::alloc(
py, &py.get_type::<#cls>(),
Storage { #(#names: #values),*})});
return Ok(#cls { _unsafe_inner: obj });
}
}
#accessors
impl pyo3::PythonObjectWithTypeObject for #cls {
#[inline]
fn type_object(py: Python) -> PyType {
unsafe { #cls::initialized(py, None) }
}
}
impl pyo3::class::PyTypeObject for #cls {
fn add_to_module(py: Python, module: &PyModule) -> PyResult<()> {
let ty = unsafe { #cls::initialized(py, module.name(py).ok()) };
module.add(py, stringify!(#cls), ty)
}
#[inline]
unsafe fn type_obj() -> &'static mut ffi::PyTypeObject {
static mut TYPE_OBJECT: ffi::PyTypeObject = ffi::PyTypeObject_INIT;
&mut TYPE_OBJECT
}
unsafe fn initialized(py: Python, module_name: Option<&str>) -> PyType {
let mut ty = #cls::type_obj();
if (ty.tp_flags & ffi::Py_TPFLAGS_READY) != 0 {
PyType::from_type_ptr(py, ty)
} else {
// automatically initialize the class on-demand
pyo3::class::typeob::initialize_type::<#cls>(
py, module_name, ty).expect(
concat!("An error occurred while initializing class ",
stringify!(#cls)));
PyType::from_type_ptr(py, ty)
}
}
}
#[inline]
fn base_offset() -> usize {
let align = std::mem::align_of::<Storage>();
let bs = <#base as BaseObject>::size();
// round base_size up to next multiple of align
(bs + align - 1) / align * align
}
impl BaseObject for #cls {
type Type = Storage;
#[inline]
fn size() -> usize {
base_offset() + std::mem::size_of::<Self::Type>()
}
unsafe fn alloc(py: Python, ty: &PyType, value: Self::Type) -> PyResult<PyObject>
{
let obj = try!(<#base as BaseObject>::alloc(py, ty, ()));
let ptr = (obj.as_ptr() as *mut u8)
.offset(base_offset() as isize) as *mut Self::Type;
std::ptr::write(ptr, value);
Ok(obj)
}
unsafe fn dealloc(py: Python, obj: *mut ffi::PyObject) {
let ptr = (obj as *mut u8).offset(base_offset() as isize) as *mut Self::Type;
std::ptr::drop_in_place(ptr);
<#base as BaseObject>::dealloc(py, obj)
}
}
}
}
2017-05-16 05:24:06 +00:00
fn impl_to_py_object(cls: &syn::Ident) -> Tokens {
quote! {
/// Identity conversion: allows using existing `PyObject` instances where
/// `T: ToPyObject` is expected.
impl pyo3::ToPyObject for #cls where #cls: pyo3::PythonObject {
#[inline]
fn to_py_object(&self, py: pyo3::Python) -> pyo3::PyObject {
pyo3::PyClone::clone_ref(self, py).into_object()
}
#[inline]
fn into_py_object(self, _py: pyo3::Python) -> pyo3::PyObject {
self.into_object()
}
#[inline]
fn with_borrowed_ptr<F, R>(&self, _py: pyo3::Python, f: F) -> R
where F: FnOnce(*mut pyo3::ffi::PyObject) -> R
{
f(pyo3::PythonObject::as_object(self).as_ptr())
}
}
}
}
fn impl_from_py_object(cls: &syn::Ident) -> Tokens {
quote! {
impl <'source> pyo3::FromPyObject<'source> for #cls {
#[inline]
fn extract(py: pyo3::Python, obj: &'source pyo3::PyObject)
-> pyo3::PyResult<#cls> {
Ok(obj.clone_ref(py).cast_into::<#cls>(py)?)
}
}
impl <'source> pyo3::FromPyObject<'source> for &'source #cls {
#[inline]
fn extract(py: pyo3::Python, obj: &'source pyo3::PyObject)
-> pyo3::PyResult<&'source #cls> {
Ok(obj.cast_as::<#cls>(py)?)
}
}
}
}
fn impl_python_object(cls: &syn::Ident) -> Tokens {
quote! {
impl pyo3::PythonObject for #cls {
#[inline]
fn as_object(&self) -> &pyo3::PyObject {
&self._unsafe_inner
}
#[inline]
fn into_object(self) -> pyo3::PyObject {
self._unsafe_inner
}
/// Unchecked downcast from PyObject to Self.
/// Undefined behavior if the input object does not have the expected type.
#[inline]
unsafe fn unchecked_downcast_from(obj: pyo3::PyObject) -> Self {
#cls { _unsafe_inner: obj }
}
/// Unchecked downcast from PyObject to Self.
/// Undefined behavior if the input object does not have the expected type.
#[inline]
unsafe fn unchecked_downcast_borrow_from<'a>(obj: &'a pyo3::PyObject) -> &'a Self {
std::mem::transmute(obj)
}
}
}
}
fn impl_checked_downcast(cls: &syn::Ident) -> Tokens {
quote! {
impl pyo3::PythonObjectWithCheckedDowncast for #cls {
#[inline]
fn downcast_from<'p>(py: pyo3::Python<'p>, obj: pyo3::PyObject)
-> Result<#cls, pyo3::PythonObjectDowncastError<'p>> {
if py.get_type::<#cls>().is_instance(py, &obj) {
Ok(#cls { _unsafe_inner: obj })
} else {
Err(pyo3::PythonObjectDowncastError(py))
}
}
#[inline]
fn downcast_borrow_from<'a, 'p>(py: pyo3::Python<'p>, obj: &'a pyo3::PyObject)
-> Result<&'a #cls, pyo3::PythonObjectDowncastError<'p>> {
if py.get_type::<#cls>().is_instance(py, obj) {
unsafe { Ok(std::mem::transmute(obj)) }
} else {
Err(pyo3::PythonObjectDowncastError(py))
}
}
}
}
}