1#[macro_export]
2macro_rules! call_static {
3 ($path:tt, $method:tt, $sig:tt, $args:expr, $ret:expr) => {
4 {
5 use auto_jni::once_cell::sync::OnceCell;
6 use auto_jni::jni::objects::{JClass, JStaticMethodID};
7 use crate::java;
8 static FNPTR: OnceCell<JStaticMethodID> = OnceCell::new();
9 static CLASS: OnceCell<JClass> = OnceCell::new();
10 let mut java = java();
11 let fnptr = FNPTR.get_or_init(|| {
12 java.get_static_method_id($path, $method, $sig).unwrap()
13 });
14 let class = CLASS.get_or_init(|| {
15 java.find_class($path).unwrap()
16 });
17
18 unsafe {
19 java.call_static_method_unchecked(class, fnptr, $ret, $args).unwrap()
20 }
21 }
22 };
23}
24
25
26#[macro_export]
27macro_rules! call {
28 ($obj:expr, $path:tt, $method:tt, $sig:tt, $args:expr, $ret:expr) => {
29 {
30 use once_cell::sync::OnceCell;
31 use jni::objects::{JClass, JMethodID};
32 use crate::java;
33 static FNPTR: OnceCell<JMethodID> = OnceCell::new();
34 let mut java = java();
35 let fnptr = FNPTR.get_or_init(|| {
36 let class = java.find_class($path).unwrap();
37 java.get_method_id(class, $method, $sig).unwrap()
38 });
39
40 unsafe {
41 java.call_method_unchecked($obj, fnptr, $ret, $args).unwrap()
42 }
43 }
44 };
45}
46
47#[macro_export]
50macro_rules! create {
51 ($path:tt, $sig:tt, $args:expr) => {
52 {
53 use once_cell::sync::OnceCell;
54 use jni::objects::{JClass, JMethodID};
55 use crate::java;
56 static FNPTR: OnceCell<JMethodID> = OnceCell::new();
57 static CLASS: OnceCell<JClass> = OnceCell::new();
58 let mut java = java();
59 let class = CLASS.get_or_init(|| {
60 java.find_class($path).unwrap()
61 });
62 let fnptr = FNPTR.get_or_init(|| {
63 java.get_method_id(class, "<init>", $sig).unwrap()
64 });
65
66 let obj = unsafe {
67 java.new_object_unchecked(class, *fnptr, $args).unwrap()
68 };
69 java.new_global_ref(obj).unwrap()
70 }
71 };
72}
73
74#[macro_export]
75macro_rules! once {
76 ($code:expr) => {
77 {
78 static ONCE: OnceCell<JObject> = OnceCell::new();
79
80 ONCE.get_or_init(|| {$code})
81 }
82
83 };
84}
85
86use std::collections::HashMap;
87use std::io::Write;
88use std::fs::File;
89use std::path::Path;
90use jni::signature::{Primitive, ReturnType};
91use crate::parse_javap_output;
92
93pub fn generate_bindings_file(class_name: Vec<&str>, class_path: Option<String>, output_path: &Path, jvm_options: Option<Vec<String>>) -> std::io::Result<()> {
94 let mut file = File::create(output_path)?;
95
96 writeln!(file, "use auto_jni::jni::objects::{{JObject, GlobalRef}};")?;
98 writeln!(file, "use auto_jni::errors::JNIError;")?;
99 writeln!(file, "use auto_jni::{{call, call_static, create}};")?;
100 writeln!(file, "use auto_jni::jni::objects::JValue;")?;
101 writeln!(file, "use auto_jni::jni::signature::{{Primitive, ReturnType}};")?;
102 writeln!(file, "use auto_jni::jni;")?;
103 writeln!(file, "use auto_jni::once_cell;")?;
104 writeln!(file, "use auto_jni::lazy_static::lazy_static;")?;
105 writeln!(file, "use auto_jni::jni::{{InitArgsBuilder, JNIEnv, JNIVersion, JavaVM}};")?;
106 writeln!(file, "use auto_jni::jni::objects::JObjectArray;")?;
107 writeln!(file)?;
108
109 writeln!(file, "lazy_static! {{ static ref JAVA: JavaVM = create_jvm(); }}")?;
111 writeln!(file)?;
112 writeln!(file, "fn create_jvm() -> JavaVM {{")?;
113 writeln!(file, " let jvm_args = InitArgsBuilder::new()")?;
114 writeln!(file, " .version(JNIVersion::V8)")?;
115 if let Some(jvm_options) = jvm_options {
116 for option in jvm_options {
117 writeln!(file, " .option(\"{}\")", option.replace("\\", "\\\\"))?;
118 }
119 }
120
121 writeln!(file, " .build().unwrap();")?;
122 writeln!(file, " JavaVM::new(jvm_args).unwrap()")?;
123 writeln!(file, "}}")?;
124 writeln!(file)?;
125
126 writeln!(file, "pub fn java() -> JNIEnv<'static> {{")?;
127 writeln!(file, " JAVA.attach_current_thread_permanently().unwrap()")?;
128 writeln!(file, "}}")?;
129 writeln!(file)?;
130
131 for class in class_name {
134 let bindings = parse_javap_output(class, class_path.clone());
135 let struct_name = class.replace('.', "_");
136
137 writeln!(file, "#[allow(non_snake_case)]")?;
139 writeln!(file, "#[allow(non_camel_case_types)]")?;
140 writeln!(file, "pub struct {} {{", struct_name)?;
141 writeln!(file, " inner: GlobalRef,")?;
142 writeln!(file, "}}")?;
143 writeln!(file)?;
144
145 writeln!(file, "#[allow(non_snake_case)]")?;
147 writeln!(file, "#[allow(non_camel_case_types)]")?;
148 writeln!(file, "impl<'a> {} {{", struct_name)?;
149
150 println!("Length: {}", bindings.len());
151
152 let mut methods: HashMap<String, i32> = HashMap::new();
153 let mut enums: Vec<String> = Vec::new();
154
155 for mut binding in bindings {
157 println!("Creating binding for: {}", binding.name);
158
159 let mut enums_to_add: Vec<String> = binding.args.iter()
160 .filter(|arg| arg.contains('$'))
161 .map(|arg| arg.to_string())
162 .collect::<Vec<String>>();
163
164 for mut enum_name in enums_to_add {
165 enum_name.remove(0);
166 if !enums.iter().any(|e| e == &enum_name) {
167 enums.push(enum_name.clone());
168 writeln!(file, " #[allow(non_snake_case)]")?;
169 writeln!(file, " pub fn {}_from_str(s: &str) -> JObject {{", enum_name.replace("/", "_").replace("$", "_"))?;
170 writeln!(file, " call_static!(")?;
171 writeln!(file, " \"{}\",", enum_name)?;
172 writeln!(file, " \"valueOf\",")?;
173 writeln!(file, " \"(Ljava/lang/String;)L{};\",", enum_name)?;
174 writeln!(file, " &[JValue::Object(&java().new_string(s).unwrap()).as_jni()],")?;
175 writeln!(file, " ReturnType::Object")?;
176 writeln!(file, " ).l().unwrap()")?;
177 writeln!(file, " }}")?;
178 }
179 }
180
181 if binding.name.contains('$') {
183 let mut split = binding.name.split('$');
184 split.next();
185 binding.name = split.next().unwrap().to_string();
186 }
187
188 let args: Vec<(String, String)> = binding.args.iter().enumerate()
190 .map(|(i, arg_type)| {
191 (format!("arg_{}", i), arg_type.to_string())
192 })
193 .collect();
194
195 let return_type = match binding.return_type.as_str() {
197 "I" => "i32",
198 "J" => "i64",
199 "D" => "f64",
200 "F" => "f32",
201 "Z" => "bool",
202 "B" => "i8",
203 "C" => "u16",
204 "S" => "i16",
205 "V" => "()",
206 _ => "JObject<'static>"
207 };
208
209 let mut method_name = if binding.name.to_ascii_lowercase() == "x" {
210 "new".to_string()
211 } else {
212 binding.name.clone()
213 };
214
215 if methods.contains_key(&method_name) {
216 methods.insert(method_name.clone(), methods.get(&method_name.clone()).unwrap() + 1);
217 method_name.push_str(format!("_{}", &methods.get(&method_name.clone()).unwrap().to_string()).as_str());
218 } else {
219 methods.insert(method_name.clone(), 1);
220 }
221
222 writeln!(file, " #[allow(non_snake_case)]")?;
224 write!(file, " pub fn {}(", method_name)?;
225
226 if binding.is_constructor {
228 for (i, (arg_name, arg_type)) in args.iter().enumerate() {
230 write!(file, "{}: {}", arg_name, java_type_to_rust(arg_type))?;
231 if i < args.len() - 1 {
232 write!(file, ", ")?;
233 }
234 }
235 writeln!(file, ") -> Result<Self, JNIError> {{")?;
236
237 writeln!(file, " Ok(Self {{")?;
238 write!(file, " inner: create!(\"{}\", \"{}\", &[",
239 binding.path,
240 binding.signature)?;
241 for (i, (arg_name, arg_type)) in args.iter().enumerate() {
242 write!(file, "{}", get_input_type(arg_name, arg_type))?;
243 if i < args.len() - 1 {
244 write!(file, ", ")?;
245 }
246 }
247 writeln!(file, "])")?;
248 writeln!(file, " }})")?;
249 } else if binding.is_static {
250 for (i, (arg_name, arg_type)) in args.iter().enumerate() {
251 write!(file, "{}: {}", arg_name, java_type_to_rust(arg_type))?;
252 if i < args.len() - 1 {
253 write!(file, ", ")?;
254 }
255 }
256 writeln!(file, ") -> Result<{}, JNIError> {{", return_type)?;
257 let return_type = get_return_type(&*binding.return_type);
258 writeln!(file, " {} call_static!(", if return_type == ReturnType::Primitive(Primitive::Void) {
259 "".to_string()
260 } else {
261 "let result =".to_string()
262 })?;
263 writeln!(file, " \"{}\",", binding.path)?;
264 writeln!(file, " \"{}\",", binding.name)?;
265 writeln!(file, " \"{}\",", binding.signature)?;
266 write!(file, " &[")?;
267 for (i, (arg_name, arg_type)) in args.iter().enumerate() {
268 write!(file, "{}", get_input_type(arg_name, arg_type))?;
269 if i < args.len() - 1 {
270 write!(file, ", ")?;
271 }
272 }
273 writeln!(file, "],")?;
274 writeln!(file, " {}", convert_return_type_to_string(return_type.clone()))?;
275 writeln!(file, " );")?;
276 writeln!(file, " Ok({})", return_type_to_function(return_type.clone()))?;
277 } else {
278 write!(file, "instance: &'a GlobalRef, ")?;
279 for (i, (arg_name, arg_type)) in args.iter().enumerate() {
280 write!(file, "{}: {}", arg_name, java_type_to_rust(arg_type))?;
281 if i < args.len() - 1 {
282 write!(file, ", ")?;
283 }
284 }
285 writeln!(file, ") -> Result<{}, JNIError> {{", return_type)?;
286 let return_type = get_return_type(&*binding.return_type);
287 writeln!(file, " {} call!(", if return_type == ReturnType::Primitive(Primitive::Void) {
288 "".to_string()
289 } else {
290 "let result =".to_string()
291 })?;
292 writeln!(file, " instance.as_obj(),")?;
293 writeln!(file, " \"{}\",", binding.path)?;
294 writeln!(file, " \"{}\",", binding.name)?;
295 writeln!(file, " \"{}\",", binding.signature)?;
296 write!(file, " &[")?;
297 for (i, (arg_name, arg_type)) in args.iter().enumerate() {
298 write!(file, "{}", get_input_type(arg_name, arg_type))?;
299 if i < args.len() - 1 {
300 write!(file, ", ")?;
301 }
302 }
303 let return_type = get_return_type(&*binding.return_type);
304 writeln!(file, "],")?;
305 writeln!(file, " {}", convert_return_type_to_string(return_type.clone()))?;
306 writeln!(file, " );")?;
307 writeln!(file, " Ok({})", return_type_to_function(return_type.clone()))?;
308 }
309 writeln!(file, " }}")?;
310 }
311
312 writeln!(file, " pub fn inner(&self) -> GlobalRef {{")?;
314 writeln!(file, " self.inner.clone()")?;
315 writeln!(file, " }}")?;
316
317 writeln!(file, "}}")?;
318 writeln!(file)?;
319 }
320
321 Ok(())
322}
323
324fn java_type_to_rust(java_type: &str) -> &str {
326 match java_type {
327 "I" => "i32",
328 "J" => "i64",
329 "D" => "f64",
330 "F" => "f32",
331 "Z" => "bool",
332 "B" => "i8",
333 "C" => "u16",
334 "S" => "i16",
335 "V" => "()",
336 t if t.starts_with("L") => "&JObject",
337 t if t.starts_with("[") => "&JObjectArray",
338 _ => "JObject"
339 }
340}
341
342fn return_type_to_function(return_type: ReturnType) -> String {
345 match return_type {
346 ReturnType::Primitive(Primitive::Int) => "result.i().unwrap()".to_string(),
347 ReturnType::Primitive(Primitive::Long) => "result.j().unwrap()".to_string(),
348 ReturnType::Primitive(Primitive::Double) => "result.d().unwrap()".to_string(),
349 ReturnType::Primitive(Primitive::Float) => "result.f().unwrap()".to_string(),
350 ReturnType::Primitive(Primitive::Boolean) => "result.z().unwrap()".to_string(),
351 ReturnType::Primitive(Primitive::Byte) => "result.b().unwrap()".to_string(),
352 ReturnType::Primitive(Primitive::Char) => "result.c().unwrap()".to_string(),
353 ReturnType::Primitive(Primitive::Short) => "result.s().unwrap()".to_string(),
354 ReturnType::Primitive(Primitive::Void) => "()".to_string(),
355 ReturnType::Object => "result.l().unwrap()".to_string(),
356 _ => "".to_string()
357 }
358}
359
360fn get_input_type(arg_name: &str, arg_type: &str) -> String {
362 match arg_type {
363 "I" => format!("JValue::Int({}).as_jni()", arg_name),
364 "J" => format!("JValue::Long({}).as_jni()", arg_name),
365 "D" => format!("JValue::Double({}).as_jni()", arg_name),
366 "F" => format!("JValue::Float({}).as_jni()", arg_name),
367 "Z" => format!("JValue::Bool({} as u8).as_jni()", arg_name),
368 "B" => format!("JValue::Byte({}).as_jni()", arg_name),
369 "C" => format!("JValue::Char({}).as_jni()", arg_name),
370 "S" => format!("JValue::Short({}).as_jni()", arg_name),
371 t => format!("JValue::Object({}).as_jni()", arg_name),
372 _ => arg_type.to_string()
373 }
374}
375
376fn get_return_type(return_type: &str) -> ReturnType {
378 match return_type {
379 "I" => ReturnType::Primitive(Primitive::Int),
380 "J" => ReturnType::Primitive(Primitive::Long),
381 "D" => ReturnType::Primitive(Primitive::Double),
382 "F" => ReturnType::Primitive(Primitive::Float),
383 "Z" => ReturnType::Primitive(Primitive::Boolean),
384 "B" => ReturnType::Primitive(Primitive::Byte),
385 "C" => ReturnType::Primitive(Primitive::Char),
386 "S" => ReturnType::Primitive(Primitive::Short),
387 "V" => ReturnType::Primitive(Primitive::Void),
388 t if t.starts_with("L") => ReturnType::Object,
389 t if t.starts_with("[") => ReturnType::Object,
390 _ => ReturnType::Object
391 }
392}
393
394fn convert_return_type_to_string(return_type: ReturnType) -> String {
396 match return_type {
397 ReturnType::Primitive(Primitive::Int) => "ReturnType::Primitive(Primitive::Int)".to_string(),
398 ReturnType::Primitive(Primitive::Long) => "ReturnType::Primitive(Primitive::Long)".to_string(),
399 ReturnType::Primitive(Primitive::Double) => "ReturnType::Primitive(Primitive::Double)".to_string(),
400 ReturnType::Primitive(Primitive::Float) => "ReturnType::Primitive(Primitive::Float)".to_string(),
401 ReturnType::Primitive(Primitive::Boolean) => "ReturnType::Primitive(Primitive::Boolean)".to_string(),
402 ReturnType::Primitive(Primitive::Byte) => "ReturnType::Primitive(Primitive::Byte)".to_string(),
403 ReturnType::Primitive(Primitive::Char) => "ReturnType::Primitive(Primitive::Char)".to_string(),
404 ReturnType::Primitive(Primitive::Short) => "ReturnType::Primitive(Primitive::Short)".to_string(),
405 ReturnType::Primitive(Primitive::Void) => "ReturnType::Primitive(Primitive::Void)".to_string(),
406 ReturnType::Object => "ReturnType::Object".to_string(),
407 _ => "".to_string()
408 }
409}
410
411pub use {call, create, call_static, once};