flapigen/java_jni/
find_cache.rs1use rustc_hash::FxHashMap;
2use syn::{
3 parse::{Parse, ParseStream},
4 parse_quote,
5 spanned::Spanned,
6 visit::Visit,
7 LitStr, Macro, Token,
8};
9
10#[derive(Default)]
11pub struct JniCacheMacroCalls {
12 pub calls: FxHashMap<String, JniFindClass>,
13}
14
15impl JniCacheMacroCalls {
16 pub fn global_vars(&self) -> Vec<syn::Item> {
17 let mut ret = Vec::with_capacity(self.calls.len());
18 for find_class in self.calls.values() {
19 let id = &find_class.id;
20 ret.push(parse_quote! {
21 static mut #id: jclass = ::std::ptr::null_mut();
22 });
23 for sub_id in &find_class.methods {
24 let id = &sub_id.id;
25 ret.push(parse_quote! {
26 static mut #id: jmethodID = ::std::ptr::null_mut();
27 });
28 }
29 for sub_id in &find_class.static_methods {
30 let id = &sub_id.id;
31 ret.push(parse_quote! {
32 static mut #id: jmethodID = ::std::ptr::null_mut();
33 });
34 }
35 for sub_id in &find_class.static_fields {
36 let id = &sub_id.id;
37 ret.push(parse_quote! {
38 static mut #id: jfieldID = ::std::ptr::null_mut();
39 });
40 }
41 for sub_id in &find_class.fields {
42 let id = &sub_id.id;
43 ret.push(parse_quote! {
44 static mut #id: jfieldID = ::std::ptr::null_mut();
45 });
46 }
47 }
48 ret
49 }
50}
51
52#[derive(PartialEq, Debug)]
53pub struct JniFindClass {
54 pub id: syn::Ident,
55 pub path: LitStr,
56 pub methods: Vec<JniClassItemWithId>,
57 pub static_methods: Vec<JniClassItemWithId>,
58 pub static_fields: Vec<JniClassItemWithId>,
59 pub fields: Vec<JniClassItemWithId>,
60}
61impl Parse for JniFindClass {
62 fn parse(input: ParseStream) -> syn::Result<Self> {
63 let id = input.parse()?;
64 input.parse::<Token![,]>()?;
65 let path = input.parse()?;
66 Ok(Self {
67 id,
68 path,
69 methods: Vec::new(),
70 static_methods: Vec::new(),
71 static_fields: Vec::new(),
72 fields: Vec::new(),
73 })
74 }
75}
76
77#[derive(PartialEq, Debug)]
78pub struct JniClassItemWithId {
79 pub id: syn::Ident,
80 pub class_id: syn::Ident,
81 pub name: LitStr,
82 pub sig: LitStr,
83}
84impl Parse for JniClassItemWithId {
85 fn parse(input: ParseStream) -> syn::Result<Self> {
86 let id = input.parse()?;
87 input.parse::<Token![,]>()?;
88 let class_id = input.parse()?;
89 input.parse::<Token![,]>()?;
90 let name = input.parse()?;
91 input.parse::<Token![,]>()?;
92 let sig = input.parse()?;
93 if input.peek(Token![,]) {
94 input.parse::<Token![,]>()?;
95 }
96 Ok(Self {
97 id,
98 class_id,
99 name,
100 sig,
101 })
102 }
103}
104
105macro_rules! add_jni_method_id {
106 ($mac:ident, $calls: ident, $sub_calls: ident, $name: expr) => {
107 let get_method_id: JniClassItemWithId =
108 syn::parse2($mac.tokens.clone()).unwrap_or_else(|err| {
109 panic!(
110 "Can not parse '{}' call: {}, code: {}",
111 $name, err, $mac.tokens
112 )
113 });
114 let class_id = get_method_id.class_id.to_string();
115 let find_class = $calls.get_mut(&class_id).unwrap_or_else(|| {
116 panic!(
117 "Can not find class_id for {}({}, {}, ...) call",
118 $name, get_method_id.id, class_id
119 );
120 });
121 if let Some(wrong_usage_pos) = find_class.$sub_calls.iter().position(|elem| {
122 elem.name == get_method_id.name
123 && elem.sig == get_method_id.sig
124 && get_method_id.id != elem.id
125 }) {
126 let prev_get_method_id = &find_class.$sub_calls[wrong_usage_pos];
127 panic!(
128 "{} called twice with different id, {} vs {} for class {}",
129 $name, prev_get_method_id.id, get_method_id.id, class_id
130 );
131 }
132 if !find_class.$sub_calls.iter().any(|x| *x == get_method_id) {
133 find_class.$sub_calls.push(get_method_id);
134 }
135 };
136}
137
138pub struct JniCacheMacroCallsVisitor<'a> {
139 pub inner: &'a mut JniCacheMacroCalls,
140 pub errors: Vec<syn::Error>,
141}
142
143impl<'ast> Visit<'ast> for JniCacheMacroCallsVisitor<'ast> {
144 fn visit_macro(&mut self, mac: &'ast Macro) {
145 static SWIG_JNI_GET_METHOD_ID: &str = "swig_jni_get_method_id";
146 static SWIG_JNI_GET_STATIC_METHOD_ID: &str = "swig_jni_get_static_method_id";
147 static SWIG_JNI_GET_STATIC_FIELD_ID: &str = "swig_jni_get_static_field_id";
148 static SWIG_JNI_GET_FIELD_ID: &str = "swig_jni_get_field_id";
149
150 if mac.path.is_ident("swig_jni_find_class") {
151 let find_class: JniFindClass =
152 syn::parse2(mac.tokens.clone()).expect("Can not parse swig_jni_find_class call");
153 let id = find_class.id.to_string();
154 if let Some(call) = self.inner.calls.get(&id) {
155 if *call != find_class {
156 println!(
157 "waring=You use the same id '{}' for different classes '{}' vs '{}'",
158 id,
159 call.path.value(),
160 find_class.path.value()
161 );
162 self.errors.push(syn::Error::new(
163 mac.span(),
164 format!(
165 "You use the same id '{}' for different classes '{}' vs '{}'",
166 id,
167 call.path.value(),
168 find_class.path.value()
169 ),
170 ));
171 return;
172 }
173 }
174 self.inner.calls.insert(id, find_class);
175 } else if mac.path.is_ident(SWIG_JNI_GET_METHOD_ID) {
176 let calls = &mut self.inner.calls;
177 add_jni_method_id!(mac, calls, methods, SWIG_JNI_GET_METHOD_ID);
178 } else if mac.path.is_ident(SWIG_JNI_GET_STATIC_METHOD_ID) {
179 let calls = &mut self.inner.calls;
180 add_jni_method_id!(mac, calls, static_methods, SWIG_JNI_GET_STATIC_METHOD_ID);
181 } else if mac.path.is_ident(SWIG_JNI_GET_STATIC_FIELD_ID) {
182 let calls = &mut self.inner.calls;
183 add_jni_method_id!(mac, calls, static_fields, SWIG_JNI_GET_STATIC_FIELD_ID);
184 } else if mac.path.is_ident(SWIG_JNI_GET_FIELD_ID) {
185 let calls = &mut self.inner.calls;
186 add_jni_method_id!(mac, calls, fields, SWIG_JNI_GET_FIELD_ID);
187 }
188 syn::visit::visit_macro(self, mac)
189 }
190}