extern crate alloc;
extern crate proc_macro;
use alloc::string::String;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{self, FnArg, Ident};
mod key {
pub const __U64: &str = "u64";
pub const __U512: &str = "U512";
pub const __STRING: &str = "String";
}
#[proc_macro_attribute]
pub fn casperlabs_contract(_attr: TokenStream, input: TokenStream) -> TokenStream {
let item: syn::ItemMod = syn::parse_macro_input!(input);
let name = &item.ident;
let mut deploy_args = proc_macro2::TokenStream::new();
let mut deploy_def = proc_macro2::TokenStream::new();
let mut func_def = proc_macro2::TokenStream::new();
match item.content {
Some(module_content) => {
let (deploy_def_content, func_def_content, deploy_args_content) =
get_entry_points(name, module_content.1).unwrap();
deploy_def.extend(deploy_def_content);
func_def.extend(func_def_content);
deploy_args.extend(deploy_args_content);
}
None => println!("Empty"),
};
let gen = quote! {
fn __deploy( #deploy_args ) {
#deploy_def
}
#func_def
fn ret<T: CLTyped + ToBytes>(value: T) {
runtime::ret(CLValue::from_t(value).unwrap_or_revert())
}
};
gen.into()
}
#[proc_macro_attribute]
pub fn casperlabs_initiator(_attr: TokenStream, input: TokenStream) -> TokenStream {
let item: syn::ItemFn = syn::parse_macro_input!(input);
let func_def = quote! { #item };
let gen = quote! {
#func_def
};
gen.into()
}
#[proc_macro_attribute]
pub fn casperlabs_constructor(_attr: TokenStream, input: TokenStream) -> TokenStream {
let mut item: syn::ItemFn = syn::parse_macro_input!(input);
let mut input_strings: Vec<String> = Vec::new();
let mut declaration = proc_macro2::TokenStream::new();
let orignal_ident = item.sig.ident.clone();
let name = &orignal_ident;
let new_ident = Ident::new(&format!("__{}", name), name.span());
item.sig.ident = new_ident;
let internal = &item.sig.ident;
let constructor_def = quote! { #item };
if item.sig.inputs.is_empty() {
let gen = quote! {
#[no_mangle]
fn call() {
__deploy();
}
#constructor_def
#[no_mangle]
fn #name() {
#internal()
}
};
return gen.into();
}
for indiviual_input in item.sig.inputs {
let (dec, arg, _, _) = get_var_declaration(&indiviual_input);
declaration.extend(dec);
input_strings.push(arg);
}
let input_args = prep_input(input_strings);
let gen = quote! {
#[no_mangle]
fn call() {
#declaration
__deploy(#input_args)
}
#constructor_def
#[no_mangle]
fn #name() {
#declaration
#internal(#input_args)
}
};
gen.into()
}
#[proc_macro_attribute]
pub fn casperlabs_method(_attr: TokenStream, input: TokenStream) -> TokenStream {
let mut item: syn::ItemFn = syn::parse_macro_input!(input);
let orignal_ident = item.sig.ident.clone();
let return_type = item.sig.output.clone();
let mut return_code = proc_macro2::TokenStream::new();
let mut return_func = proc_macro2::TokenStream::new();
if let syn::ReturnType::Type(_arrow, rt) = return_type {
if let syn::Type::Path(path) = *rt {
let return_ident = &path.path.segments[0].ident;
let code = quote! { let val: #return_ident = };
return_code.extend(code);
}
}
if !return_code.is_empty() {
let runtime_return = quote! { ret(val) };
return_func.extend(runtime_return)
}
let name = &orignal_ident;
let new_ident = Ident::new(&format!("__{}", name), name.span());
item.sig.ident = new_ident;
let internal = &item.sig.ident;
let func_def = quote! { #item };
let mut declaration = proc_macro2::TokenStream::new();
let mut input_strings: Vec<String> = Vec::new();
if item.sig.inputs.is_empty() {
let gen = quote! {
#func_def
#[no_mangle]
fn #name() {
#declaration
#return_code #internal();
#return_func
}
};
return gen.into();
}
for indiviual_input in item.sig.inputs {
let (dec, arg, _, _) = get_var_declaration(&indiviual_input);
declaration.extend(dec);
input_strings.push(arg);
}
let input_args = prep_input(input_strings);
let gen = quote! {
#func_def
#[no_mangle]
fn #name() {
#declaration
#return_code #internal(#input_args);
#return_func
}
};
gen.into()
}
fn get_entry_points(
name: &syn::Ident,
funcs: Vec<syn::Item>,
) -> Option<(
proc_macro2::TokenStream,
proc_macro2::TokenStream,
proc_macro2::TokenStream,
)> {
let mut definitions: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
let mut init = quote! {
let (contract_package_hash, _) = storage::create_contract_package_at_hash();
let _constructor_access_uref: URef = storage::create_contract_user_group(
contract_package_hash,
"constructor",
1,
BTreeSet::new(),
)
.unwrap_or_revert()
.pop()
.unwrap_or_revert();
let constructor_group = Group::new("constructor");
let mut entry_points = EntryPoints::new();
};
let mut deploy_args: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
let mut contract_call: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
let constructor_ident = Ident::new("casperlabs_constructor", Span::call_site());
let member_ident = Ident::new("casperlabs_initiator", Span::call_site());
let mut init_ident: proc_macro2::TokenStream = quote! { Default::default() };
let mut access_token: proc_macro2::TokenStream;
let mut constructor_presence: bool = false;
for func in funcs {
if let syn::Item::Fn(func_body) = func {
if func_body.attrs.is_empty() {
let gen = quote! { #func_body };
definitions.extend(gen);
} else if !func_body.attrs.is_empty()
&& member_ident != func_body.attrs[0].path.segments[0].ident
{
if constructor_ident == func_body.attrs[0].path.segments[0].ident {
constructor_presence = true;
access_token = quote! { EntryPointAccess::Groups(vec![constructor_group]) };
let (call, __d_args) = get_call(&func_body);
contract_call.extend(call);
deploy_args.extend(__d_args);
} else {
access_token = quote! { EntryPointAccess::Public };
}
let name = &func_body.sig.ident;
let def = quote! { #func_body };
definitions.extend(def);
let string_name = format!("{}", name);
let mut arg: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
if !func_body.sig.inputs.is_empty() {
for input in func_body.sig.inputs {
arg.extend(get_param(input));
}
}
let params = quote! {
vec![
#arg
]
};
let gen = quote! {
entry_points.add_entry_point(EntryPoint::new(
String::from(#string_name),
#params,
CLType::Unit,
#access_token,
EntryPointType::Contract,
));
};
init.extend(gen)
} else if member_ident == func_body.attrs[0].path.segments[0].ident
&& !func_body.attrs.is_empty()
{
let init_name = func_body.sig.ident.clone();
let def = quote! { #func_body };
definitions.extend(def);
init_ident = quote! { #init_name() };
} else if func_body.attrs.is_empty() {
let def = quote! { #func_body };
definitions.extend(def);
}
}
}
if !constructor_presence {
panic!("No constructor was present");
}
let string_name = format!("{}", name);
let string_hash_name = format!("{}_hash", string_name);
let tail = quote! {
let (contract_hash, _) = storage::add_contract_version(contract_package_hash, entry_points, #init_ident);
runtime::put_key(#string_name,contract_hash.into());
let contract_hash_pack = storage::new_uref(contract_hash);
runtime::put_key(#string_hash_name, contract_hash_pack.into());
#contract_call
};
init.extend(tail);
Some((init, definitions, deploy_args))
}
fn get_call(init: &syn::ItemFn) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
let name = &init.sig.ident.clone();
let string_name = format!("{}", name);
let mut input_for_deploy: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
let mut args: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
for indiviual_input in &init.sig.inputs {
let (_, arg, arg_name, arg_type) = get_var_declaration(&indiviual_input);
let arg_ident = Ident::new(arg.as_str(), Span::call_site());
let temp = quote! { #arg => #arg_ident, };
let input_dec = quote! { #arg_name: #arg_type, };
args.extend(temp);
input_for_deploy.extend(input_dec);
}
let gen = quote! {
runtime::call_contract::<()>(
contract_hash,
#string_name,
runtime_args! {
#args
},
);
};
(gen, input_for_deploy)
}
fn get_param(arg: FnArg) -> proc_macro2::TokenStream {
let arg_name: &syn::Ident;
let arg_type: &syn::Ident;
match arg {
FnArg::Typed(ref pat_type) => {
match *pat_type.pat {
syn::Pat::Ident(ref pat_ident) => {
arg_name = &pat_ident.ident;
}
_ => panic!("Incorrect"),
}
match *pat_type.ty {
syn::Type::Path(ref path) => {
arg_type = &path.path.segments[0].ident;
}
_ => panic!("Incorrect"),
}
}
_ => panic!("Incorrect"),
}
let string_arg = format!("{}", arg_name);
match format!("{}", arg_type).as_str() {
"bool" => quote! { Parameter::new(#string_arg, CLType::Bool), },
"i32" => quote! { Parameter::new(#string_arg, CLType::I32), },
"i64" => quote! { Parameter::new(#string_arg, CLType::I64), },
"u8" => quote! { Parameter::new(#string_arg, CLType::U8), },
"u32" => quote! { Parameter::new(#string_arg, CLType::U32), },
"u64" => quote! { Parameter::new(#string_arg, CLType::U64), },
"U128" => quote! { Parameter::new(#string_arg, CLType::128), },
"U256" => quote! { Parameter::new(#string_arg, CLType::U256),},
"U512" => quote! { Parameter::new(#string_arg, CLType::U512), },
"String" => quote! { Parameter::new(#string_arg, CLType::String), },
"AccountHash" => quote! { Parameter::new(#string_arg, AccountHash::cl_type()), },
_ => panic!("Currently unsupported type: {}", arg_name),
}
}
fn prep_input(input_strings: Vec<String>) -> proc_macro2::TokenStream {
let first_arg = Ident::new(&input_strings[0], Span::call_site());
let mut args = quote! { #first_arg, };
for input_string in input_strings.iter().take(input_strings.len() - 1).skip(1) {
let ident = Ident::new(&input_string, Span::call_site());
let temp = quote! { #ident, };
args.extend(temp);
}
if input_strings.len() == 1 {
return args;
}
let last_ident = Ident::new(&input_strings[input_strings.len() - 1], Span::call_site());
let last_arg = quote! { #last_ident };
args.extend(last_arg);
args
}
fn get_var_declaration(
arg: &FnArg,
) -> (proc_macro2::TokenStream, String, &syn::Ident, &syn::Ident) {
let arg_name: &syn::Ident;
let arg_type: &syn::Ident;
match arg {
FnArg::Typed(ref pat_type) => {
match *pat_type.pat {
syn::Pat::Ident(ref pat_ident) => {
arg_name = &pat_ident.ident;
}
_ => panic!("Incorrect"),
}
match *pat_type.ty {
syn::Type::Path(ref path) => {
arg_type = &path.path.segments[0].ident;
}
_ => panic!("Incorrect"),
}
}
_ => panic!("Incorrect"),
}
let string_arg = format!("{}", arg_name);
let dec = quote! {let #arg_name: #arg_type = runtime::get_named_arg(#string_arg); };
let arg = format!("{}", arg_name);
(dec, arg, arg_name, arg_type)
}