1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
use proc_macro2::TokenStream as TokenStream2;
use syn::{ItemImpl, ImplItem, FnArg};
use quote::{quote};
use syn::Visibility;
use syn::spanned::Spanned;
use crate::common::is_tokens_empty;
/// 在deploy时需要调用`new`构造出对象
pub (crate) fn generate_deploy(items: &ItemImpl) -> TokenStream2 {
// get Impl name
let i_name = items.self_ty.as_ref();
// get new method
let functions = items.items.iter()
.filter_map(|item| {
match item {
ImplItem::Method(m) => Some(m),
_ => None
}
})
.filter(|method| {
method.sig.ident.to_string().eq("new")
})
.map(|method| {
let mt = &method.sig.ident;
let mut n: usize = 0usize;
let (names, in_types): (Vec<_>, Vec<_>) = method.sig.inputs.iter()
.map(|param| match param {
FnArg::Typed(tp) => {
n += 1;
let index = format!("p_{}", n);
let index_ident = syn::Ident::new(&index, index.span());
(quote! {#index_ident}, &tp.ty)
}
_ => { unreachable!("{}-th error input params for constructor function `new`", n) }
})
.unzip();
let padding_str = if in_types.len() > 0 {
quote! {let _ = <String as scale::Decode>::decode(input).unwrap();}
} else {
quote! {}
};
quote! {
#[no_mangle]
fn deploy() {
// 1. fetch input
let input_raw = fvm_std::advance::input();
let input = &mut &input_raw[..];
// 2. decode input
// add the padding string for deploy and invoke params format uniform
// with deploy: "N" + deploy_params
// with invoke: invoke_method + invoke_params
#padding_str
let (#(#names,) *) = (#(<#in_types as scale::Decode>::decode(input).unwrap(),)*);
if input.len() != 0 { // check the input correct
fvm_std::advance::revert("deploy param error");
}
// scale::Encode::encode(&inst.#m_name(#(#names), *))
// 3. call new method
<#i_name>::#mt(#(#names), *);
// 4. store to ledger
// instance.write_ledger("".as_bytes(), "deploy".as_bytes())
}
}
}).collect::<Vec<_>>();
quote! {
#(#functions)*
}
}
pub (crate) fn generate_invoke(items: &ItemImpl) -> TokenStream2 {
// 1. 解析每个方法的签名
// 2. 为每个方法的参数的类型实现decode方法
// 3. 生成call方法
// get Impl name
let i_name = items.self_ty.as_ref();
//为每个方法生成调用的Token
let method_list = items.items.iter()
.filter_map(|item| {
match item {
ImplItem::Method(m) => Some(m),
_ => None
}
})
.filter_map(|method| {
match &method.vis {
Visibility::Public(_) => Some(method),
_ => None
}
})
.map(|method| {
// ignore not `pub` methods
let m_name = &method.sig.ident;
let m_name_str = format!("{}", &method.sig.ident);
// 这里需要排除self参数
let mut n: usize = 0usize;
let (names, in_types): (Vec<_>, Vec<_>) = method.sig.inputs.iter()
.filter(
|param| match param {
FnArg::Typed(_) => { true }
_ => { false }
})
.map(|param| match param {
FnArg::Typed(tp) => {
n += 1;
let index = format!("p_{}", n);
let index_ident = syn::Ident::new(&index, index.span());
(quote! {#index_ident}, &tp.ty)
}
_ => { unreachable!("unreachable") }
})
.unzip();
quote! {
#m_name_str => {
let (#(#names,) *) = (#(<#in_types as scale::Decode>::decode(input).unwrap(),)*);
if input.len() != 0 { // check the input correct
fvm_std::advance::revert("invoke param error");
}
scale::Encode::encode(&#i_name::#m_name(#(#names), *))
}
}
}).collect::<Vec<_>>();
if is_tokens_empty(&method_list) {
quote! {
#[no_mangle]
fn invoke() {
panic!("no method to call");
}
}
} else {
quote! {
#[no_mangle]
fn invoke() {
// 2. fetch input and resolve call method name
let input_raw = fvm_std::advance::input();
let input = &mut &input_raw[..];
let m_name = <String as ::scale::Decode>::decode(input).unwrap();
// 3. deliver method and call
let res = match m_name.as_str() {
#(#method_list)*
_ => {
// fvm_std::advance::revert(format!("no such method to call: {}", m_name).as_str());
fvm_std::advance::revert("no such method to call");
}
};
// 5. return call result
fvm_std::advance::ret(&*res);
}
}
}
}
#[cfg(test)]
mod test {
use quote::quote;
use crate::common::is_tokens_empty;
#[test]
fn test_is_tokens_empty() {
let token_list = vec![quote! {}];
assert!(is_tokens_empty(&token_list));
let token_list2 = vec![quote! {}, quote! {use quote::quote;}];
assert_eq!(is_tokens_empty(&token_list2), false)
}
}