Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

experiment with multilevel inheritance model #205

Closed
wants to merge 39 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8c33f1b
++
qalisander Jun 28, 2024
e263ef6
++
qalisander Jun 28, 2024
88bde2d
++
qalisander Jun 28, 2024
fe91512
++
qalisander Jun 29, 2024
bd6b124
++
qalisander Jun 29, 2024
9c2a8e4
++
qalisander Jun 29, 2024
8caf123
++
qalisander Jun 29, 2024
02335c7
++
qalisander Jun 29, 2024
440bc7c
++
qalisander Jun 29, 2024
e096255
++
qalisander Jun 29, 2024
089698a
++
qalisander Jun 29, 2024
3df4ad6
++
qalisander Jul 11, 2024
0a7298c
++
qalisander Jul 11, 2024
2290304
++
qalisander Jul 12, 2024
ef49b39
++
qalisander Jul 12, 2024
a3b781f
++
qalisander Jul 12, 2024
848d4c2
++
qalisander Jul 12, 2024
a06f022
use Super and This keywords
qalisander Jul 12, 2024
d814341
enforce macro to burnable and virtual
qalisander Jul 12, 2024
541a0d9
it works
qalisander Jul 12, 2024
be5cd88
r#virtual -> r#override
qalisander Jul 12, 2024
a8e7952
add r#virtual attribute for virtual trait declaration
qalisander Jul 12, 2024
8f71cf9
++
qalisander Jul 12, 2024
9e2a5de
++
qalisander Jul 12, 2024
c1ffd63
++
qalisander Jul 12, 2024
35de810
++
qalisander Jul 12, 2024
4a3a7aa
add impl_items
qalisander Jul 12, 2024
f3ee10a
refactor tests
qalisander Jul 13, 2024
4ec8ead
Merge remote-tracking branch 'refs/remotes/origin/main' into function…
qalisander Jul 14, 2024
6ced4b7
use function overrides in enumerable
qalisander Jul 14, 2024
7a77bf1
++
qalisander Jul 14, 2024
5eb8956
use function overrides in metadata
qalisander Jul 14, 2024
174b32b
remove derive_virtual
qalisander Jul 14, 2024
2c494cb
Merge remote-tracking branch 'refs/remotes/origin/main' into function…
qalisander Jul 18, 2024
ba4e493
++
qalisander Jul 18, 2024
e356962
++
qalisander Jul 18, 2024
1e10726
++
qalisander Jul 18, 2024
c169267
++
qalisander Jul 18, 2024
cdf82e3
++
qalisander Jul 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 100 additions & 89 deletions Cargo.lock

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"contracts",
"contracts-proc",
"lib/crypto",
"lib/motsu",
"lib/motsu-proc",
Expand Down Expand Up @@ -101,3 +102,17 @@ incremental = false

[profile.dev]
panic = "abort"

[patch.crates-io.stylus-sdk]
git = "https://github.com/qalisander/stylus-sdk-rs"
branch = "toplevelstorage-dependency-injection"

[patch.crates-io.stylus-proc]
git = "https://github.com/qalisander/stylus-sdk-rs"
branch = "toplevelstorage-dependency-injection"

#[patch.crates-io.stylus-sdk]
#path = "../stylus-sdk-rs/stylus-sdk"
#
#[patch.crates-io.stylus-proc]
#path = "../stylus-sdk-rs/stylus-proc"
16 changes: 16 additions & 0 deletions contracts-proc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "openzeppelin-stylus-proc"
description = "Macros for contracts"
edition.workspace = true
keywords.workspace = true
license.workspace = true
repository.workspace = true
version = "0.1.0"

[dependencies]
proc-macro2.workspace = true
quote.workspace = true
syn.workspace = true

[lib]
proc-macro = true
46 changes: 46 additions & 0 deletions contracts-proc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse::Parse, parse_macro_input, punctuated::Punctuated};

mod r#virtual;
#[proc_macro_attribute]
pub fn r#override(attr: TokenStream, input: TokenStream) -> TokenStream {
r#virtual::r#override(attr, input)
}

#[proc_macro_attribute]
pub fn r#virtual(attr: TokenStream, input: TokenStream) -> TokenStream {
r#virtual::r#virtual(attr, input)
}

#[proc_macro]
pub fn inherit(input: TokenStream) -> TokenStream {
let override_types = parse_macro_input!(input as OverrideTypes);
create_complex_type_rec(&override_types.0).into()
}

fn create_complex_type_rec(
override_types: &[syn::Type],
) -> proc_macro2::TokenStream {
if override_types.len() == 1 {
let base_override = &override_types[0];
quote! { #base_override }
} else {
let child = &override_types[0];
let parent = create_complex_type_rec(&override_types[1..]);
quote! {
#child < #parent >
}
}
}

struct OverrideTypes(Vec<syn::Type>);

impl Parse for OverrideTypes {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let args: Punctuated<syn::Type, syn::Token![,]> =
Punctuated::parse_terminated(input)?;
Ok(OverrideTypes(args.into_iter().collect()))
}
}
233 changes: 233 additions & 0 deletions contracts-proc/src/virtual.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
use std::mem;

use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse::Parse, parse_macro_input, punctuated::Punctuated, token::Comma,
FnArg, GenericParam, ImplItem, ItemImpl, Path, Token, Type,
};

use crate::create_complex_type_rec;

pub fn r#virtual(_attr: TokenStream, input: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(input as ItemImpl);
let trait_path = trait_path(&input);
let self_ty = input.self_ty.clone();
let mut output = quote! {};

let trait_declr_items = trait_declr_items(&mut input);
output.extend(quote! {
pub trait #trait_path: 'static {
type Base: #trait_path;

#trait_declr_items
}
});

let trait_impl_items = trait_impl_items(&mut input);
output.extend(quote! {
impl #trait_path for #self_ty {
type Base = Self;

#trait_impl_items
}

pub struct #self_ty;
});

if !_attr.is_empty() {
let name_ty = parse_macro_input!(_attr as Type);
let impl_items = impl_items(&mut input);
output.extend(quote! {
impl<This: #trait_path> #name_ty<This> {
#impl_items
}
})
};

output.into()
}

fn impl_items(input: &mut ItemImpl) -> proc_macro2::TokenStream {
let mut impl_items = Vec::new();
for item in input.items.iter_mut() {
let ImplItem::Fn(func) = item else {
continue;
};
let name = func.sig.ident.clone();
let return_ty = func.sig.output.clone();
let args = func.sig.inputs.clone();
let input_args = input_args(&args);
let attrs = func.attrs.clone();
impl_items.push(quote! {
#(#attrs)*
fn #name ( #args ) #return_ty
{
<This>::#name::<This> ( #input_args )
}
});
}

quote! {
#(#impl_items)*
}
}

fn trait_declr_items(input: &mut ItemImpl) -> proc_macro2::TokenStream {
let trait_path = trait_path(input);
let mut trait_declr_items = Vec::new();
for item in input.items.iter_mut() {
let ImplItem::Fn(func) = item else {
trait_declr_items.push(quote! {
#item
});
continue;
};
let name = func.sig.ident.clone();
let return_ty = func.sig.output.clone();
let args = func.sig.inputs.clone();
let input_args = input_args(&args);
let attrs = func.attrs.clone();
let generics = func.sig.generics.clone();

if generics.params.is_empty() {
trait_declr_items.push(quote! {
#(#attrs)*
fn #name <This: #trait_path> ( #args ) #return_ty
{
Self::Base::#name::<This> ( #input_args )
}
});
} else {
let generic_idents =
func.sig.generics.params.iter().map(|gp| match gp {
GenericParam::Type(ty) => ty.ident.clone(),
_ => {
panic!("functions should not contain lifetimes or const generics")
}
});
trait_declr_items.push(quote! {
#(#attrs)*
fn #name #generics ( #args ) #return_ty
{
Self::Base::#name::<#(#generic_idents)*> ( #input_args )
}
});
}
}

quote! {
#(#trait_declr_items)*
}
}

fn input_args(args: &Punctuated<FnArg, Comma>) -> proc_macro2::TokenStream {
let input_args = args.iter().map(|arg| match arg {
FnArg::Receiver(_) => {
panic!("functions should not contain `&self` or `&mut self`")
}
FnArg::Typed(arg) => arg.pat.clone(),
});
quote! {
#(#input_args,)*
}
}

pub fn r#override(_attr: TokenStream, input: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(input as ItemImpl);
let trait_path = trait_path(&input);
let self_ty = input.self_ty.clone();
let mut output = quote! {};
let override_alias = override_alias(&mut input);

let trait_impl_items = trait_impl_items(&mut input);

output.extend(quote! {
impl<Super: #trait_path> #trait_path for #self_ty<Super> {
type Base = Super;

#trait_impl_items
}

#override_alias

pub struct #self_ty<Super: #trait_path>(Super);
});
output.into()
}

fn trait_impl_items(input: &mut ItemImpl) -> proc_macro2::TokenStream {
let trait_path = trait_path(input);
let mut items = Vec::new();
for item in input.items.iter_mut() {
let ImplItem::Fn(func) = item else {
items.push(quote! {
#item
});
continue;
};
if !func.sig.generics.params.is_empty() {
let sig = func.sig.clone();
let block = func.block.clone();
items.push(quote! {
#sig
#block
});
continue;
}
let name = func.sig.ident.clone();
let return_ty = func.sig.output.clone();
let args = func.sig.inputs.clone();
let block = func.block.clone();
items.push(quote! {
fn #name <This: #trait_path> ( #args ) #return_ty
#block
});
}

quote! {
#(#items)*
}
}

fn override_alias(input: &mut ItemImpl) -> proc_macro2::TokenStream {
let mut inherits = vec![];
for attr in mem::take(&mut input.attrs) {
if !attr.path().is_ident("inherit") {
input.attrs.push(attr);
continue;
}
let contents: InheritsAttr = match attr.parse_args() {
Ok(contents) => contents,
Err(err) => return err.to_compile_error(),
};
for ty in contents.types {
inherits.push(ty);
}
}
if inherits.is_empty() {
quote! {}
} else {
let override_ty = create_complex_type_rec(&inherits);
quote! {
type Override = #override_ty;
}
}
}

fn trait_path(input: &ItemImpl) -> Path {
let (_, trait_path, _) =
input.trait_.clone().expect("should contain trait implementation");
trait_path
}

struct InheritsAttr {
types: Punctuated<Type, Token![,]>,
}

impl Parse for InheritsAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let types = Punctuated::parse_separated_nonempty(input)?;
Ok(Self { types })
}
}
1 change: 1 addition & 0 deletions contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ alloy-sol-types.workspace = true
stylus-sdk.workspace = true
stylus-proc.workspace = true
mini-alloc.workspace = true
openzeppelin-stylus-proc = { path = "../contracts-proc"}

[dev-dependencies]
alloy-primitives = { workspace = true, features = ["arbitrary"] }
Expand Down
Loading
Loading