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