1+ #![ recursion_limit="128" ]
2+ extern crate eng_wasm;
3+ extern crate proc_macro2;
4+ #[ macro_use] extern crate quote;
5+ extern crate proc_macro;
6+ #[ macro_use]
7+ extern crate syn;
8+ extern crate serde_json;
9+ extern crate ethabi;
10+
11+ use eng_wasm:: * ;
12+ use std:: fs:: File ;
13+ use std:: string:: ToString ;
14+ //use std::io::prelude::*;
15+ use std:: convert:: * ;
16+ use ethabi:: { Contract , ParamType } ;
17+
18+ fn generate_eng_wasm_aux_functions ( ) -> proc_macro2:: TokenStream {
19+ quote ! {
20+ #[ no_mangle]
21+ pub fn function_name( ) -> String {
22+
23+ let length_result = unsafe { eng_wasm:: external:: fetch_function_name_length( ) } ;
24+
25+ match length_result {
26+ 0 => "" . to_string( ) ,
27+ length => {
28+ let mut data = Vec :: with_capacity( length as usize ) ;
29+ for _ in 0 ..length{
30+ data. push( 0 ) ;
31+ }
32+
33+ unsafe {
34+ eng_wasm:: external:: fetch_function_name( data. as_mut_ptr( ) ) ;
35+ }
36+ from_utf8( & data) . unwrap( ) . to_string( )
37+ }
38+ }
39+ }
40+ #[ no_mangle]
41+ pub fn args( ) -> String {
42+ let length_result = unsafe { external:: fetch_args_length( ) } ;
43+
44+ match length_result {
45+ 0 => "" . to_string( ) ,
46+ length => {
47+ let mut data = Vec :: with_capacity( length as usize ) ;
48+ for _ in 0 ..length{
49+ data. push( 0 ) ;
50+ }
51+
52+ unsafe {
53+ external:: fetch_args( data. as_mut_ptr( ) ) ;
54+ }
55+ from_utf8( & data) . unwrap( ) . to_string( )
56+ }
57+ }
58+ }
59+
60+ }
61+ }
62+
63+ fn generate_dispatch ( input : syn:: Item ) -> proc_macro2:: TokenStream {
64+ let v;
65+ let functions= match input {
66+ syn:: Item :: Trait ( input) => {
67+ v = input. items ;
68+ v. iter ( )
69+ } ,
70+ _ => panic ! ( ) ,
71+ } ;
72+
73+ let it: Vec < proc_macro2:: TokenStream > = functions. filter_map ( |item| {
74+ match item {
75+ syn:: TraitItem :: Method ( item) => {
76+ let func = item. sig . ident . clone ( ) ;
77+ let arg_types: Vec < proc_macro2:: TokenStream > = item. sig . decl . inputs . iter ( ) . filter_map ( |arg| match arg {
78+ // Argument captured by a name
79+ syn:: FnArg :: Captured ( arg_captured) => {
80+ let ty = & arg_captured. ty ;
81+ Some ( quote ! { #ty} )
82+ } ,
83+ // Argument without a name
84+ syn:: FnArg :: Ignored ( type_only) => {
85+ Some ( quote ! { #type_only} )
86+ } ,
87+ _ => None ,
88+ } ) . collect ( ) ;
89+
90+ let name = & func. to_string ( ) ;
91+ //println!("METHOD {:#?} TYPES {:#?}", item, arg_types);
92+ Some ( quote ! {
93+ #name => {
94+ let mut stream = pwasm_abi:: eth:: Stream :: new( args. as_bytes( ) ) ;
95+
96+ Contract :: #func( #( stream. pop:: <#arg_types>( ) . expect( "argument decoding failed" ) ) , * ) ;
97+
98+ }
99+ } )
100+ } ,
101+ _ => None ,
102+ }
103+ } ) . collect ( ) ;
104+
105+
106+ quote ! {
107+ pub fn dispatch( name: & str , args: & str ) {
108+ match name{
109+ #( #it, ) *
110+ _=>panic!( ) ,
111+ }
112+ }
113+ }
114+ }
115+
116+ #[ proc_macro_attribute]
117+ #[ allow( unused_variables, unused_mut) ]
118+ pub fn dispatch ( args : proc_macro:: TokenStream , input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
119+ let input_tokens = parse_macro_input ! ( input as syn:: Item ) ;
120+ let disp = generate_dispatch ( input_tokens. clone ( ) ) ;
121+ let eng_wasm_aux = generate_eng_wasm_aux_functions ( ) ;
122+ let result = quote ! {
123+ #eng_wasm_aux
124+ #input_tokens
125+ #disp
126+
127+ #[ no_mangle]
128+ pub fn call( ) {
129+ dispatch( & function_name( ) , & args( ) ) ;
130+ }
131+ } ;
132+ proc_macro:: TokenStream :: from ( result)
133+ }
134+
135+ //--------------------------------------------------------------------------------------------------
136+
137+ trait Write {
138+ fn write ( & self ) -> String ;
139+ fn error ( & self ) -> String ;
140+ }
141+
142+ impl Write for ParamType {
143+ /// Returns string which is a formatted represenation of param.
144+ fn write ( & self ) -> String {
145+ match * self {
146+ ParamType :: Address => "Address" . to_owned ( ) ,
147+ ParamType :: Bytes => "Vec<u8>" . to_owned ( ) ,
148+ ParamType :: FixedBytes ( len) => format ! ( "u8[{}]" , len) ,
149+ ParamType :: Int ( len) => match len{
150+ 32 | 64 => format ! ( "i{}" , len) ,
151+ _ => panic ! ( "{}" , self . error( ) ) ,
152+ } ,
153+ ParamType :: Uint ( len) => match len{
154+ 32 | 64 => format ! ( "i{}" , len) ,
155+ 256 => "U256" . to_owned ( ) ,
156+ _ => panic ! ( "{}" , self . error( ) ) ,
157+ } ,
158+ ParamType :: Bool => "bool" . to_owned ( ) ,
159+ ParamType :: String => "String" . to_owned ( ) ,
160+ ParamType :: FixedArray ( ref param, len) => format ! ( "{}[{}]" , param. write( ) , len) ,
161+ ParamType :: Array ( ref param) => format ! ( "Vec<{}>" , param. write( ) ) ,
162+ }
163+ }
164+ fn error ( & self ) -> String {
165+ format ! ( "The type {} is not supported" , self . to_string( ) )
166+ }
167+ }
168+
169+ #[ derive( Debug ) ]
170+ struct FunctionAst {
171+ name : syn:: Ident ,
172+ args_ast_types : Vec < syn:: Type > ,
173+ args_types : Vec < ParamType > ,
174+ }
175+
176+ fn read_contract_file ( file_path : String ) -> Result < Box < File > , EngWasmError > {
177+ let file = File :: open ( file_path) ?;
178+ let contents = Box :: new ( file) ;
179+ Ok ( contents)
180+ }
181+
182+ fn generate_eth_functions ( contract : & Contract ) -> Result < Box < Vec < proc_macro2:: TokenStream > > , EngWasmError > {
183+ let mut functions: Vec < FunctionAst > = Vec :: new ( ) ;
184+ for function in & contract. functions {
185+ let mut args_ast_types = Vec :: new ( ) ;
186+ for input in & function. 1 . inputs {
187+ let arg_type: syn:: Type = syn:: parse_str ( & input. kind . clone ( ) . write ( ) ) ?;
188+ args_ast_types. push ( arg_type) ;
189+ }
190+ let args_types = function. 1 . inputs . iter ( ) . map ( |input| {
191+ input. kind . clone ( )
192+ } ) . collect ( ) ;
193+
194+ let name = syn:: Ident :: new ( & function. 1 . name , proc_macro2:: Span :: call_site ( ) ) ;
195+ functions. push ( FunctionAst { name, args_types, args_ast_types} )
196+ }
197+
198+ let result: Vec < proc_macro2:: TokenStream > = functions. iter ( ) . map ( |function| {
199+ let function_name = & function. name ;
200+ let args_ast_types = function. args_ast_types . clone ( ) ;
201+ let sig_u32 = short_signature ( & function_name. to_string ( ) , & function. args_types ) ;
202+ let sig = syn:: Lit :: Int ( syn:: LitInt :: new ( sig_u32 as u64 , syn:: IntSuffix :: U32 , proc_macro2:: Span :: call_site ( ) ) ) ;
203+ let args_number = syn:: Lit :: Int ( syn:: LitInt :: new ( args_ast_types. len ( ) as u64 ,
204+ syn:: IntSuffix :: Usize ,
205+ proc_macro2:: Span :: call_site ( ) ) ) ;
206+ let args_names: Vec < syn:: Ident > = function. args_ast_types . iter ( ) . enumerate ( ) . map ( |item| {
207+ let mut arg = String :: from ( "arg" ) ;
208+ arg. push_str ( item. 0 . to_string ( ) . as_str ( ) ) ;
209+ syn:: Ident :: new ( & arg, proc_macro2:: Span :: call_site ( ) )
210+ } ) . collect ( ) ;
211+ let args_names_copy = args_names. clone ( ) ;
212+ quote ! {
213+ fn #function_name( & self , #( #args_names: #args_ast_types) , * ) {
214+ #![ allow( unused_mut) ]
215+ #![ allow( unused_variables) ]
216+ let mut payload = Vec :: with_capacity( 4 + #args_number * 32 ) ;
217+ payload. push( ( #sig >> 24 ) as u8 ) ;
218+ payload. push( ( #sig >> 16 ) as u8 ) ;
219+ payload. push( ( #sig >> 8 ) as u8 ) ;
220+ payload. push( #sig as u8 ) ;
221+
222+ let mut sink = pwasm_abi:: eth:: Sink :: new( #args_number) ;
223+ #( sink. push( #args_names_copy) ; ) *
224+ sink. drain_to( & mut payload) ;
225+ write_ethereum_payload( payload) ;
226+ write_ethereum_contract_addr( & self . addr) ;
227+ }
228+ }
229+ } ) . collect ( ) ;
230+
231+ Ok ( Box :: new ( result) )
232+ }
233+
234+ #[ proc_macro_attribute]
235+ #[ allow( unused_variables, unused_mut) ]
236+ pub fn eth_contract ( args : proc_macro:: TokenStream , input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
237+ let input_tokens = parse_macro_input ! ( input as syn:: ItemStruct ) ;
238+ let struct_name = input_tokens. ident ;
239+ let file_path = parse_macro_input ! ( args as syn:: LitStr ) ;
240+ let mut contents: Box < File > = read_contract_file ( file_path. value ( ) ) . expect ( "Bad contract file" ) ;
241+ let contract = Contract :: load ( contents) . unwrap ( ) ;
242+ let it: Vec < proc_macro2:: TokenStream > = * generate_eth_functions ( & contract) . unwrap ( ) ;
243+
244+ let result = quote ! {
245+ struct #struct_name{
246+ addr: [ u8 ; 20 ] ,
247+ }
248+ impl EthContract {
249+ fn new( addr_str: /*Address*/ & str ) -> Self {
250+ EthContract { addr: From :: from( Address :: from( addr_str. as_bytes( ) ) ) }
251+ }
252+ #( #it) *
253+ }
254+ } ;
255+ proc_macro:: TokenStream :: from ( result)
256+ }
0 commit comments