Skip to content

Commit

Permalink
WIP: make it work with syn 2.0 only
Browse files Browse the repository at this point in the history
  • Loading branch information
DCNick3 committed Aug 21, 2023
1 parent 20ef28e commit cd093fa
Show file tree
Hide file tree
Showing 18 changed files with 1,797 additions and 583 deletions.
2 changes: 0 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions ffi/derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ categories = ["development-tools::ffi"]
proc-macro = true

[dependencies]
syn = { workspace = true, features = ["full", "visit", "visit-mut", "extra-traits"] }
syn2 = { workspace = true, features = ["full", "visit", "visit-mut", "extra-traits"] }
quote = { workspace = true }
proc-macro2 = { workspace = true }
manyhow = { workspace = true, features = ["syn1"] }
manyhow = { workspace = true }
darling = { workspace = true }
rustc-hash = { workspace = true }

Expand Down
147 changes: 147 additions & 0 deletions ffi/derive/examples/aboba.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#![feature(trivial_bounds)]

use getset::Getters;
use iroha_ffi::decl_ffi_fns;
use iroha_ffi_derive::{ffi, ffi_export, ffi_import};

ffi! {
#[derive(Getters)]
#[getset(get = "pub")]
#[ffi_type(opaque)]
pub struct A {
a: i32,
}
}

// #[repr(transparent)]
// pub struct A(*mut iroha_ffi::Extern) where A: iroha_ffi::Handle;
// #[derive(Clone, Copy)]
// #[repr(transparent)]
// pub struct RefA<'a> (*const iroha_ffi::Extern, core::marker::PhantomData<&'a ()>) where A: iroha_ffi::Handle;
// #[repr(transparent)]
// pub struct RefMutA<'a> (*mut iroha_ffi::Extern, core::marker::PhantomData<&'a mut ()>) where A: iroha_ffi::Handle;
// impl Drop for A where A: iroha_ffi::Handle {
// fn drop(&mut self) {
// let handle_id = iroha_ffi::FfiConvert::into_ffi(<A as iroha_ffi::Handle>::ID, &mut ());
// let drop_result = unsafe { crate::__drop(handle_id, self.0) };
// if drop_result != iroha_ffi::FfiReturn::Ok { panic!("Drop returned: {}", drop_result); }
// }
// }
// impl A where A: iroha_ffi::Handle { fn from_extern_ptr(opaque_ptr: *mut iroha_ffi::Extern) -> Self { Self(opaque_ptr) } }
// impl<'a> A where A: iroha_ffi::Handle {
// fn as_ref(&self) -> RefA<'a> { RefA(self.0, core::marker::PhantomData) }
// fn as_mut(&mut self) -> RefMutA<'a> where A: iroha_ffi::Handle { RefMutA(self.0, core::marker::PhantomData) }
// }
// impl<'a> core::ops::Deref for RefA<'a> where A: iroha_ffi::Handle {
// type Target = A;
// fn deref(&self) -> &Self::Target { unsafe { &*(&self.0 as *const *const iroha_ffi::Extern).cast() } }
// }
// impl<'a> core::ops::Deref for RefMutA<'a> where A: iroha_ffi::Handle {
// type Target = RefA<'a>;
// fn deref(&self) -> &Self::Target { unsafe { &*(&self.0 as *const *mut iroha_ffi::Extern).cast() } }
// }
// impl<'a> core::ops::DerefMut for RefMutA<'a> where A: iroha_ffi::Handle { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { &mut *(&mut self.0 as *mut *mut iroha_ffi::Extern).cast() } } }
// unsafe impl iroha_ffi::ir::External for A where A: iroha_ffi::Handle {
// type RefType<'a> = RefA<'a>;
// type RefMutType<'a> = RefMutA<'a>;
// fn as_extern_ptr(&self) -> *const iroha_ffi::Extern { self.0 }
// fn as_extern_ptr_mut(&mut self) -> *mut iroha_ffi::Extern { self.0 }
// unsafe fn from_extern_ptr(opaque_ptr: *mut iroha_ffi::Extern) -> Self { Self::from_extern_ptr(opaque_ptr) }
// }
// unsafe impl iroha_ffi::ir::Transmute for A where A: iroha_ffi::Handle {
// type Target = *mut iroha_ffi::Extern;
// #[inline]
// unsafe fn is_valid(target: &Self::Target) -> bool { !target.is_null() }
// }
// impl iroha_ffi::ir::Ir for A where A: iroha_ffi::Handle { type Type = Self; }
// impl iroha_ffi::repr_c::CType<Self> for A where A: iroha_ffi::Handle { type ReprC = *mut iroha_ffi::Extern; }
// impl iroha_ffi::repr_c::CTypeConvert<'_, Self, *mut iroha_ffi::Extern> for A where A: iroha_ffi::Handle {
// type RustStore = ();
// type FfiStore = ();
// fn into_repr_c(self, _: &mut ()) -> *mut iroha_ffi::Extern { core::mem::ManuallyDrop::new(self).0 }
// unsafe fn try_from_repr_c(source: *mut iroha_ffi::Extern, _: &mut ()) -> iroha_ffi::Result<Self> {
// if source.is_null() { return Err(iroha_ffi::FfiReturn::ArgIsNull); }
// Ok(Self::from_extern_ptr(source))
// }
// }
// impl iroha_ffi::repr_c::CWrapperType<Self> for A where A: iroha_ffi::Handle {
// type InputType = Self;
// type ReturnType = Self;
// }
// impl iroha_ffi::repr_c::COutPtr<Self> for A where A: iroha_ffi::Handle { type OutPtr = Self::ReprC; }
// impl iroha_ffi::repr_c::COutPtrRead<Self> for A where A: iroha_ffi::Handle { unsafe fn try_read_out(out_ptr: Self::OutPtr) -> iroha_ffi::Result<Self> { iroha_ffi::repr_c::read_non_local::<_, Self>(out_ptr) } }
// impl iroha_ffi::ir::IrTypeFamily for A where A: iroha_ffi::Handle {
// type Ref<'a> = &'a iroha_ffi::Extern where;
// type RefMut<'a> = &'a mut iroha_ffi::Extern where;
// type Box = Box<iroha_ffi::Extern>;
// type SliceRef<'a> = &'a [iroha_ffi::ir::Transparent] where;
// type SliceRefMut<'a> = &'a mut [iroha_ffi::ir::Transparent] where;
// type Vec = Vec<iroha_ffi::ir::Transparent>;
// type Arr<const N: usize> = iroha_ffi::ir::Transparent;
// }
// unsafe impl iroha_ffi::repr_c::NonLocal<Self> for A where A: iroha_ffi::Handle {}
// impl<'a> ::iroha_ffi::ir::Ir for RefA<'a> where A: iroha_ffi::Handle {
// type Type = ::iroha_ffi::ir::Transparent;
// }
// unsafe impl<'a> ::iroha_ffi::ir::Transmute for RefA<'a> where A: iroha_ffi::Handle {
// type Target = *const iroha_ffi::Extern;
//
// #[inline]
// unsafe fn is_valid(target: &Self::Target) -> bool {
// (|target: &*const iroha_ffi::Extern| !target.is_null())(target)
// }
// }
// impl<'a> ::iroha_ffi::option::Niche<'_> for RefA<'a> where A: iroha_ffi::Handle {
// const NICHE_VALUE: <*const iroha_ffi::Extern as ::iroha_ffi::FfiType>::ReprC = (core::ptr::null());
// }
// impl<'a> ::iroha_ffi::ir::Ir for RefMutA<'a> where A: iroha_ffi::Handle {
// type Type = ::iroha_ffi::ir::Transparent;
// }
// unsafe impl<'a> ::iroha_ffi::ir::Transmute for RefMutA<'a> where A: iroha_ffi::Handle {
// type Target = *mut iroha_ffi::Extern;
//
// #[inline]
// unsafe fn is_valid(target: &Self::Target) -> bool {
// (|target: &*mut iroha_ffi::Extern| !target.is_null())(target)
// }
// }
// impl<'a> ::iroha_ffi::option::Niche<'_> for RefMutA<'a> where A: iroha_ffi::Handle {
// const NICHE_VALUE: <*mut iroha_ffi::Extern as ::iroha_ffi::FfiType>::ReprC = (core::ptr::null_mut());
// }
// unsafe impl iroha_ffi::ir::InfallibleTransmute for A where A: iroha_ffi::Handle {}
// unsafe impl<'a> iroha_ffi::ir::InfallibleTransmute for RefA<'a> where A: iroha_ffi::Handle {}
// unsafe impl<'a> iroha_ffi::ir::InfallibleTransmute for RefMutA<'a> where A: iroha_ffi::Handle {}
// impl iroha_ffi::WrapperTypeOf<Self> for A where A: iroha_ffi::Handle { type Type = Self; }
// impl<'a> iroha_ffi::WrapperTypeOf<&'a A> for RefA<'a> where A: iroha_ffi::Handle { type Type = Self; }
// impl<'a> iroha_ffi::WrapperTypeOf<&'a mut A> for RefMutA<'a> where A: iroha_ffi::Handle { type Type = Self; }
// impl iroha_ffi::option::Niche<'_> for A where A: iroha_ffi::Handle { const NICHE_VALUE: *mut iroha_ffi::Extern = core::ptr::null_mut(); }
// impl A {
// pub fn a(&self: <&Self as iroha_ffi::FfiWrapperType>::InputType) -> <&i32 as iroha_ffi::FfiWrapperType>::ReturnType { self.as_ref().a() }
// }
// impl<'a> RefA<'a> {
// pub fn a(&self: <&Self as iroha_ffi::FfiWrapperType>::InputType) -> <&'a i32 as iroha_ffi::FfiWrapperType>::ReturnType {
// let __handle = self;
// let __handle = __handle.0;
// let mut __handle_store = Default::default();
// let __handle = iroha_ffi::FfiConvert::into_ffi(__handle, &mut __handle_store);
// let mut a = core::mem::MaybeUninit::uninit();
// unsafe {
// let __ffi_return = A__a(__handle, a.as_mut_ptr());
// match __ffi_return {
// iroha_ffi::FfiReturn::Ok => {}
// _ => panic!(concat!(stringify!(A__a ), " returned {}" ), __ffi_return)
// }
// let a = a.assume_init();
// let a = iroha_ffi::FfiOutPtrRead::try_read_out(a).expect("Invalid out-pointer value returned");
// a
// }
// }
// }
// extern {
// #[doc = " FFI function equivalent of [`A::a`]\n \n # Safety\n \n All of the given pointers must be valid"]
// fn A__a(__handle: <<&A as iroha_ffi::FfiWrapperType>::InputType as iroha_ffi::FfiType>::ReprC, a: *mut <<&i32 as iroha_ffi::FfiWrapperType>::ReturnType as iroha_ffi::FfiOutPtr>::OutPtr) -> iroha_ffi::FfiReturn;
// }
//
// iroha_ffi::decl_ffi_fns! { link_prefix="iroha_aboba" Drop, Clone, Eq, Ord, Default }

fn main() {}
184 changes: 184 additions & 0 deletions ffi/derive/src/attr_parse/derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
//! This module provides parsing of `#[derive(...)]` attributes

use darling::FromAttributes;
use quote::ToTokens;
use syn2::{punctuated::Punctuated, Attribute, Token};

use super::getset::GetSetDerive;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RustcDerive {
Eq,
PartialEq,
Ord,
PartialOrd,
Clone,
Copy,
Hash,
Default,
Debug,
}

impl RustcDerive {
fn try_from_path(path: &syn2::Path) -> Option<Self> {
let Some(ident) = path.get_ident() else {
return None;
};

match ident.to_string().as_str() {
"Eq" => Some(Self::Eq),
"PartialEq" => Some(Self::PartialEq),
"Ord" => Some(Self::Ord),
"PartialOrd" => Some(Self::PartialOrd),
"Clone" => Some(Self::Clone),
"Copy" => Some(Self::Copy),
"Hash" => Some(Self::Hash),
"Default" => Some(Self::Default),
"Debug" => Some(Self::Debug),
_ => None,
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Derive {
Rustc(RustcDerive),
GetSet(GetSetDerive),
Other(String),
}

/// Represents a collection of all `#[derive(...)]` attributes placed on the item
///
/// NOTE: strictly speaking, correctly parsing this is impossible, since it requires
/// us to resolve the paths in the attributes, which is not possible in a proc-macro context.
///
/// We just __hope__ that the user refers to the derives by their canonical names (no aliases).
///
/// This, however, will mistakingly thing that `derive_more` derives are actually rustc's built-in ones.
///
/// Care should be taken, and it should be documented in the macro APIs that use this.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DeriveAttr {
pub derives: Vec<Derive>,
}

impl FromAttributes for DeriveAttr {
fn from_attributes(attrs: &[Attribute]) -> darling::Result<Self> {
let mut derives = Vec::new();
let mut accumulator = darling::error::Accumulator::default();

for attr in attrs {
if attr.path().is_ident("derive") {
let Some(list) = accumulator.handle(attr.meta.require_list().map_err(Into::into)) else {
continue
};
let Some(paths) = accumulator.handle(
list.parse_args_with(Punctuated::<syn2::Path, Token![,]>::parse_terminated).map_err(Into::into)
) else {
continue
};

for path in paths {
let derive = if let Some(derive) = RustcDerive::try_from_path(&path) {
Derive::Rustc(derive)
} else if let Some(derive) = GetSetDerive::try_from_path(&path) {
Derive::GetSet(derive)
} else {
Derive::Other(path.to_token_stream().to_string())
};

// I __think__ it's an error to use the same derive twice
// I don't think we care in this case though
derives.push(derive);
}
}
}

accumulator.finish_with(Self { derives })
}
}

#[cfg(test)]
mod test {
use darling::FromAttributes;
use proc_macro2::TokenStream;
use quote::quote;
use syn2::parse::ParseStream;

use super::{Derive, DeriveAttr, GetSetDerive, RustcDerive};

fn parse_derives(attrs: TokenStream) -> darling::Result<DeriveAttr> {
struct Attributes(Vec<syn2::Attribute>);

impl syn2::parse::Parse for Attributes {
fn parse(input: ParseStream) -> syn2::Result<Self> {
Ok(Self(input.call(syn2::Attribute::parse_outer)?))
}
}

let attrs = syn2::parse2::<Attributes>(attrs)
.expect("Failed to parse tokens as outer attributes")
.0;
DeriveAttr::from_attributes(&attrs)
}

macro_rules! assert_derive_ok {
($( #[$meta:meta] )*,
$expected:expr
) => {
assert_eq!(parse_derives(quote!(
$( #[$meta] )*
)).unwrap(),
$expected
)
};
}

#[test]
fn derive_rustc() {
assert_derive_ok!(
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash, Default, Debug)],
DeriveAttr {
derives: vec![
RustcDerive::Eq,
RustcDerive::PartialEq,
RustcDerive::Ord,
RustcDerive::PartialOrd,
RustcDerive::Clone,
RustcDerive::Copy,
RustcDerive::Hash,
RustcDerive::Default,
RustcDerive::Debug,
].into_iter().map(Derive::Rustc).collect(),
}
)
}

#[test]
fn derive_getset() {
assert_derive_ok!(
#[derive(Getters, Setters, MutGetters, CopyGetters)],
DeriveAttr {
derives: vec![
GetSetDerive::Getters,
GetSetDerive::Setters,
GetSetDerive::MutGetters,
GetSetDerive::CopyGetters,
].into_iter().map(Derive::GetSet).collect(),
}
)
}

#[test]
fn derive_unknown() {
assert_derive_ok!(
#[derive(Aboba, Kek)],
DeriveAttr {
derives: vec![
"Aboba".to_string(),
"Kek".to_string(),
].into_iter().map(Derive::Other).collect(),
}
)
}
}
18 changes: 18 additions & 0 deletions ffi/derive/src/attr_parse/doc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use darling::FromAttributes;
use syn2::Attribute;

pub struct DocAttrs {
pub attrs: Vec<Attribute>,
}

impl FromAttributes for DocAttrs {
fn from_attributes(attrs: &[Attribute]) -> darling::Result<Self> {
let mut docs = Vec::new();
for attr in attrs {
if attr.path().is_ident("doc") {
docs.push(attr.clone());
}
}
Ok(DocAttrs { attrs: docs })
}
}
Loading

0 comments on commit cd093fa

Please sign in to comment.