1use proc_macro::{TokenStream};
2use std::borrow::Cow;
3use std::collections::HashMap;
4use std::fmt::{Display, Formatter};
5use quote::{quote, ToTokens, TokenStreamExt};
6use syn::{parse_macro_input, Ident, Token, braced, parenthesized, bracketed, parse_str, Type, parse_quote};
7use syn::parse::{Parse, ParseStream};
8
9#[proc_macro]
10pub fn java_class(input: TokenStream) -> TokenStream {
11 let mut java_mod = parse_macro_input!(input as JavaMod);
12 java_mod.resolve();
13 quote!{ #java_mod }.into()
14}
15
16struct JavaMod {
18 package: DottedName,
19 import_paths: JavaImports,
20 class: JavaClass,
21}
22
23#[derive(Default)]
24struct JavaImports {
25 java_types: HashMap<String, DottedName>,
26 rust_types: HashMap<String, ScopedName>,
27}
28
29struct JavaClass {
30 name: Ident,
31 body: JavaClassBody,
32}
33
34struct JavaClassBody {
35 natives: Vec<NativeFunction>,
36 externs: Vec<ExternFunction>,
37}
38
39struct NativeFunction {
40 is_static: bool,
41 signature: JavaSignature,
42 body: proc_macro2::TokenStream,
43}
44
45struct ExternFunction {
46 is_static: bool,
47 signature: JavaSignature,
48}
49
50struct JavaSignature {
51 name: Ident,
52 args: Vec<JavaArg>,
53 returns: JavaType,
54}
55
56struct JavaArg {
57 name: Ident,
58 typ: JavaType,
59}
60
61#[derive(PartialEq)]
62enum JavaType {
63 Void,
64 Boolean,
65 Byte,
66 Char,
67 Short,
68 Int,
69 Long,
70 Float,
71 Double,
72 String,
73 Object(DottedName),
74 Array(Box<Self>),
75}
76
77#[derive(Clone, Debug, PartialEq)]
79struct DottedName(Vec<Ident>);
80impl Parse for DottedName {
81 fn parse(input: ParseStream) -> syn::Result<Self> {
82 let mut names = Vec::new();
83 loop {
84 let name: Ident = input.parse()?;
85 names.push(name);
86 if input.peek(Token![.]) {
87 let _ = input.parse::<Token![.]>()?;
88 } else {
89 break Ok(Self(names))
90 }
91 }
92 }
93}
94#[derive(Clone, Debug, PartialEq, Eq)]
95enum Prefix {
96 Crate,
97 This,
98 Super,
99 None,
100}
101#[derive(Clone, Debug)]
102struct ScopedName(Prefix, Vec<Ident>);
103impl Parse for ScopedName {
104 fn parse(input: ParseStream) -> syn::Result<Self> {
105 let mut names = Vec::new();
106 let prefix = if input.peek(Token![self]) {
107 let _ = input.parse::<Token![self]>()?;
108 let _ = input.parse::<Token![::]>()?;
109 Prefix::This
110 } else if input.peek(Token![super]) {
111 let _ = input.parse::<Token![super]>()?;
112 let _ = input.parse::<Token![::]>()?;
113 Prefix::Super
114 } else if input.peek(Token![crate]) {
115 let _ = input.parse::<Token![crate]>()?;
116 let _ = input.parse::<Token![::]>()?;
117 Prefix::Crate
118 } else {
119 Prefix::None
120 };
121 loop {
122 if input.peek(Ident) && input.peek2(Token![::]) {
123 let name: Ident = input.parse()?;
124 let _ = input.parse::<Token![::]>()?;
125 names.push(name);
126 } else {
127 break Ok(Self(prefix, names))
128 }
129 }
130 }
131}
132
133mod kw {
134 syn::custom_keyword!(class);
135 syn::custom_keyword!(package);
136 syn::custom_keyword!(native);
137 syn::custom_keyword!(import);
138 syn::custom_keyword!(void);
139 syn::custom_keyword!(boolean);
140 syn::custom_keyword!(byte);
141 syn::custom_keyword!(char);
142 syn::custom_keyword!(short);
143 syn::custom_keyword!(int);
144 syn::custom_keyword!(long);
145 syn::custom_keyword!(float);
146 syn::custom_keyword!(double);
147}
148
149impl DottedName {
151 fn first(&self) -> &Ident {
152 self.0.first().unwrap()
153 }
154 fn last(&self) -> &Ident {
155 self.0.last().unwrap()
156 }
157 fn last_mut(&mut self) -> &mut Ident {
158 self.0.last_mut().unwrap()
159 }
160 fn is_dotted(&self) -> bool {
161 self.0.len() > 1
162 }
163 fn class_path(&self) -> String {
164 format!("L{};", self.partial_class_path())
165 }
166 fn partial_class_path(&self) -> String {
167 self.0.iter().map(ToString::to_string).collect::<Vec<String>>().join("/")
168 }
169}
170impl Display for DottedName {
171 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
172 write!(f, "{}", self.0.iter().map(ToString::to_string).collect::<Vec<String>>().join("."))
173 }
174}
175impl ScopedName {
176 fn push(&mut self, name: Ident) {
177 self.1.push(name);
178 }
179 fn last_mut(&mut self) -> &mut Ident {
180 self.1.last_mut().unwrap()
181 }
182}
183impl JavaImports {
184 fn add(&mut self, import: DottedName) {
185 let name = import.last().to_string();
186 self.java_types.insert(name, import);
187 }
188 fn add_use(&mut self, scoped: ScopedName, import: DottedName) {
189 let name = import.last().to_string();
190 self.java_types.insert(name.clone(), import);
191 self.rust_types.insert(name, scoped);
192 }
193 fn resolve_rust_type(&self, o: &DottedName) -> Type {
194 if o.to_string() == "java.lang.Object" {
195 parse_quote!(jni::objects::JObject<'c>)
196 } else {
197 let n = o.last();
198 let nn = n.to_string();
199 if let Some(rt) = self.rust_types.get(&nn) {
200 let mut rt = rt.clone();
201 *rt.last_mut() = n.clone();
202 parse_quote!(#rt<'c>)
203 } else {
204 parse_quote!(jni::objects::JObject<'c>)
205 }
206 }
207 }
208}
209
210impl Parse for JavaMod {
212 fn parse(input: ParseStream) -> syn::Result<Self> {
213 let _ = input.parse::<kw::package>()?;
214 let package: DottedName = input.parse()?;
215 let _ = input.parse::<Token![;]>()?;
216
217 let mut import_paths = JavaImports::default();
218 loop {
219 if input.peek(kw::import) {
220 let _ = input.parse::<kw::import>()?;
221 let io: DottedName = input.parse()?;
222 let _ = input.parse::<Token![;]>()?;
223 import_paths.add(io);
224 } else if input.peek(Token![use]) {
225 let _ = input.parse::<Token![use]>()?;
226 let mut scoped: ScopedName = input.parse()?;
227 let path: DottedName = input.parse()?;
228 let _ = input.parse::<Token![;]>()?;
229 scoped.push(path.last().clone());
230 import_paths.add_use(scoped, path);
231 } else {
232 break
233 }
234 }
235
236 let class: JavaClass = input.parse()?;
237
238 Ok(Self {
239 package,
240 import_paths,
241 class,
242 })
243 }
244}
245
246impl Parse for JavaClass {
247 fn parse(input: ParseStream) -> syn::Result<Self> {
248 let _ = input.parse::<kw::class>()?;
249 let name: Ident = input.parse()?;
250 let body;
251 braced!(body in input);
252 let body: JavaClassBody = body.parse()?;
253 Ok(Self {
254 name,
255 body,
256 })
257 }
258}
259
260impl Parse for JavaClassBody {
261 fn parse(input: ParseStream) -> syn::Result<Self> {
262 let mut natives = Vec::new();
263 let mut externs = Vec::new();
264 while !input.is_empty() {
265 if input.peek(kw::native) || input.peek2(kw::native) {
266 let native: NativeFunction = input.parse()?;
267 natives.push(native);
268 } else {
269 let ext: ExternFunction = input.parse()?;
270 externs.push(ext);
271 }
272 }
273
274 Ok(Self {
275 natives,
276 externs,
277 })
278 }
279}
280
281impl Parse for NativeFunction {
282 fn parse(input: ParseStream) -> syn::Result<Self> {
283 let is_static = if input.peek(Token![static]) {
284 let _ = input.parse::<Token![static]>()?;
285 let _ = input.parse::<kw::native>()?;
286 true
287 } else {
288 let _ = input.parse::<kw::native>()?;
289 false
290 };
291
292 let signature: JavaSignature = input.parse()?;
293
294 let body;
295 braced!(body in input);
296 let body: proc_macro2::TokenStream = body.parse()?;
297
298 Ok(Self {
299 is_static,
300 signature,
301 body,
302 })
303 }
304}
305impl Parse for ExternFunction {
306 fn parse(input: ParseStream) -> syn::Result<Self> {
307 let is_static = if input.peek(Token![static]) {
308 let _ = input.parse::<Token![static]>()?;
309 true
310 } else {
311 false
312 };
313
314 let signature: JavaSignature = input.parse()?;
315
316 let _ = input.parse::<Token![;]>()?;
317
318 Ok(Self {
319 is_static,
320 signature,
321 })
322 }
323}
324impl Parse for JavaSignature {
325 fn parse(input: ParseStream) -> syn::Result<Self> {
326 let _ = input.parse::<Token![fn]>()?;
327 let name: Ident = input.parse()?;
328 let raw_args;
329 parenthesized!(raw_args in input);
330 let mut args = Vec::new();
331
332 while !raw_args.is_empty() {
333 let name: Ident = raw_args.parse()?;
334 let _ = raw_args.parse::<Token![:]>()?;
335 let typ: JavaType = raw_args.parse()?;
336 args.push(JavaArg { name, typ });
337 if raw_args.peek(Token![,]) {
338 let _ = raw_args.parse::<Token![,]>()?;
339 }
340 }
341
342 let returns = if input.peek(Token![->]) {
343 let _ = input.parse::<Token![->]>()?;
344 input.parse()?
345 } else {
346 JavaType::Void
347 };
348
349 Ok(Self {
350 name,
351 args,
352 returns,
353 })
354 }
355}
356
357impl Parse for JavaType {
358 fn parse(input: ParseStream) -> syn::Result<Self> {
359 #[allow(unused_labels)]
360 let mut base = 'base: {
361 macro_rules! check {
362 ($kw:tt, $typ:tt) => {
363 if input.peek(kw::$kw) { let _ = input.parse::<kw::$kw>()?; break 'base Self::$typ }
364 };
365 }
366 check!(boolean, Boolean);
367 check!(byte, Byte);
368 check!(char, Char);
369 check!(short, Short);
370 check!(int, Int);
371 check!(long, Long);
372 check!(float, Float);
373 check!(double, Double);
374
375 let name: DottedName = input.parse()?;
376
377 let n = name.to_string();
378 if n == "String" || n == "java.lang.String" {
379 JavaType::String
380 } else {
381 JavaType::Object(name)
382 }
383 };
384
385 while input.peek(syn::token::Bracket) {
386 let i;
387 bracketed!(i in input);
388 base = JavaType::Array(Box::new(base));
389 }
390
391 Ok(base)
392 }
393}
394
395impl JavaMod {
397 fn resolve(&mut self) {
398 self.import_paths.add(parse_str::<DottedName>(&format!("{}.{}", self.package, self.class.name)).unwrap());
399 self.import_paths.add(parse_str::<DottedName>("java.lang.Object").unwrap());
400 self.import_paths.add(parse_str::<DottedName>("java.lang.String").unwrap());
401
402 self.class.body.resolve(&self.import_paths);
403 }
404}
405impl JavaClassBody {
406 fn resolve(&mut self, imports: &JavaImports) {
407 for ext in self.externs.iter_mut() {
408 ext.signature.resolve(imports);
409 }
410 for nat in self.natives.iter_mut() {
411 nat.signature.resolve(imports);
412 }
413 }
414}
415impl JavaSignature {
416 fn resolve(&mut self, imports: &JavaImports) {
417 for arg in self.args.iter_mut() {
418 arg.typ.resolve(imports);
419 }
420 self.returns.resolve(imports);
421 }
422 fn jni_sig(&self) -> String {
423 let args = self.args.iter().map(|t| t.typ.signature()).collect::<Vec<_>>().join("");
424 let ret = self.returns.signature();
425 format!("({args}){ret}")
426 }
427}
428impl JavaType {
429 fn resolve(&mut self, imports: &JavaImports) {
430 if let Self::Object(name) = self {
431 if !name.is_dotted() {
432 let n = name.first().to_string();
433 if let Some(path) = imports.java_types.get(&n) {
434 let l = name.0.pop().unwrap();
435 *name = (*path).clone();
436 *name.last_mut() = l;
437 }
438 }
439 } else if let Self::Array(a) = self {
440 a.resolve(imports);
441 }
442 }
443 fn is_primitive(&self) -> bool {
444 !matches!(self, Self::String | Self::Object(_) | Self::Array(_))
445 }
446
447 fn to_rust_return_type(&self, imports: &JavaImports) -> Type {
448 match self {
449 JavaType::Void => parse_quote!(()),
450 JavaType::Boolean => parse_quote!(bool),
451 JavaType::Byte => parse_quote!(i8),
452 JavaType::Char => parse_quote!(u16),
453 JavaType::Short => parse_quote!(i16),
454 JavaType::Int => parse_quote!(i32),
455 JavaType::Long => parse_quote!(i64),
456 JavaType::Float => parse_quote!(f32),
457 JavaType::Double => parse_quote!(f64),
458 JavaType::String => parse_quote!(jni::objects::JString<'c>),
459 JavaType::Object(o) => {
460 imports.resolve_rust_type(o)
461 }
462 JavaType::Array(a) => {
463 let at = a.to_rust_concrete_type(imports);
464 if a.is_primitive() {
465 parse_quote!(jni::objects::JPrimitiveArray<'c, #at>)
466 } else {
467 parse_quote!(jni::objects::JObjectArray<'c, #at>)
468 }
469 }
470 }
471 }
472 fn to_rust_type(&self, imports: &JavaImports) -> Type {
473 match self {
474 JavaType::Void => parse_quote!(()),
475 JavaType::Boolean => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jboolean>),
476 JavaType::Byte => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jbyte>),
477 JavaType::Char => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jchar>),
478 JavaType::Short => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jshort>),
479 JavaType::Int => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jint>),
480 JavaType::Long => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jlong>),
481 JavaType::Float => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jfloat>),
482 JavaType::Double => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jdouble>),
483 JavaType::String => parse_quote!(impl JavaTyped<'c, JType=jni::objects::JString<'c>>),
484 JavaType::Object(o) => {
485 imports.resolve_rust_type(o)
486 }
487 JavaType::Array(a) => {
488 let at = a.to_rust_concrete_type(imports);
489 if a.is_primitive() {
490 parse_quote!(impl JavaTyped<'c, JType=JPrimitiveArray<'c, #at>>)
491 } else {
492 parse_quote!(impl JavaTyped<'c, JType=JObjectArray<'c, #at>>)
493 }
494 }
495 }
496 }
497 fn to_rust_concrete_type(&self, imports: &JavaImports) -> Type {
498 match self {
499 JavaType::Void => parse_quote!(()),
500 JavaType::Boolean => parse_quote!(jni::sys::jboolean),
501 JavaType::Byte => parse_quote!(jni::sys::jbyte),
502 JavaType::Char => parse_quote!(jni::sys::jchar),
503 JavaType::Short => parse_quote!(jni::sys::jshort),
504 JavaType::Int => parse_quote!(jni::sys::jint),
505 JavaType::Long => parse_quote!(jni::sys::jlong),
506 JavaType::Float => parse_quote!(jni::sys::jfloat),
507 JavaType::Double => parse_quote!(jni::sys::jdouble),
508 JavaType::String => parse_quote!(jni::objects::JString<'c>),
509 JavaType::Object(o) => {
510 imports.resolve_rust_type(o)
511 }
512 JavaType::Array(a) => {
513 let at = a.to_rust_concrete_type(imports);
514 if a.is_primitive() {
515 parse_quote!(jni::objects::JPrimitiveArray<'c, #at>)
516 } else {
517 parse_quote!(jni::objects::JObjectArray<'c, #at>)
518 }
519 }
520 }
521 }
522}
523impl JavaArg {
524 fn convert_rust_to_java(&self, _imports: &JavaImports) -> proc_macro2::TokenStream {
525 let name = &self.name;
526 quote! {
527 let #name = JavaTyped::into_java(#name, env)?;
528 }
529 }
530 fn to_rust_arg(&self, imports: &JavaImports) -> RustArg {
531 let typ: Type = self.typ.to_rust_type(imports);
532
533 RustArg {
534 name: self.name.clone(),
535 typ,
536 }
537 }
538}
539struct RustArg {
540 name: Ident,
541 typ: Type,
542}
543
544impl ToTokens for JavaMod {
545 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
546 let name = &self.class.name;
547 let package_class = format!("{}.{}", self.package, name);
548 let class_path = format!("L{}/{};", self.package.partial_class_path(), name);
549
550 let native_registrations = if self.class.body.natives.is_empty() {
551 quote!()
552 } else {
553 let mut nats = proc_macro2::TokenStream::default();
554 for nat in self.class.body.natives.iter() {
555 let name = &nat.signature.name;
556 let body = &nat.body;
557 let body = quote!({#body});
558 let args = nat.signature.args
559 .iter()
560 .map(|a| {
561 let JavaArg { name, typ } = a;
562 let typ = typ.to_rust_concrete_type(&self.import_paths);
563 quote! {
564 #name: #typ
565 }
566 });
567
568 let ret_typ = nat.signature.returns.to_rust_concrete_type(&self.import_paths);
569
570 let body = if nat.signature.returns == JavaType::Void {
571 quote! {
572 { #body; Ok(()) }
573 }
574 } else if nat.signature.returns.is_primitive() {
575 quote! {
576 {
577 let x = #body;
578 Ok(x)
579 }
580 }
581 } else {
582 quote! {
583 {
584 let v = #body;
585 v.into_java(env)
586 }
587 }
588 };
589
590 let func = if nat.is_static {
591 quote! {
592 fn #name<'c>(
593 env: &mut jni::Env<'c>,
594 class: jni::objects::JClass<'c>,
595 #(#args),*
596 ) -> jni::errors::Result<#ret_typ> #body
597 }
598 } else {
599 quote! {
600 fn #name<'c>(
601 env: &mut jni::Env<'c>,
602 this: jni::objects::JObject<'c>,
603 #(#args),*
604 ) -> jni::errors::Result<#ret_typ> #body
605 }
606 };
607
608 let stat = if nat.is_static {
609 quote!(static)
610 } else {
611 quote!()
612 };
613 let arg_types = nat.signature.args
614 .iter()
615 .map(|t|
616 parse_str::<proc_macro2::TokenStream>(&t.typ.jtype().unwrap())
617 .map_err(|e| e.to_compile_error())
618 .unwrap_or_else(|t| t))
619 .collect::<Vec<_>>();
620 let ret = nat.signature.returns.jtype()
621 .map(|t| {
622 let t = parse_str::<proc_macro2::TokenStream>(&t)
623 .map_err(|e| e.to_compile_error())
624 .unwrap_or_else(|t| t);
625 quote!(-> #t)
626 }
627 ).unwrap_or_default();
628 nats.append_all(quote! {
629 const _: jni::NativeMethod = jni::native_method!{
630 java_type = #package_class, #stat extern fn #name(#(#arg_types),*)#ret,
631 };
632 #func
633 });
634 }
635 quote! {
636 const _: () = const {
637 #nats
638 };
639 }
640 };
641
642 let (validation_checks, methods) = if self.class.body.externs.is_empty() {
643 (quote!(), quote!())
644 } else {
645 let mut exts = proc_macro2::TokenStream::default();
646 let mut methods = proc_macro2::TokenStream::default();
647
648 for ext in self.class.body.externs.iter() {
649 let name = ext.signature.name.to_string();
650 let sig = ext.signature.jni_sig();
651 if ext.is_static {
652 exts.append_all(quote! {
653 env.get_static_method_id(
654 jni::jni_str!(#package_class),
655 jni::jni_str!(#name),
656 jni::jni_sig!(#sig),
657 )?;
658 });
659 } else {
660 exts.append_all(quote! {
661 env.get_method_id(
662 jni::jni_str!(#package_class),
663 jni::jni_str!(#name),
664 jni::jni_sig!(#sig),
665 )?;
666 });
667 }
668 let method = {
669 let name = &ext.signature.name;
670 let sig = ext.signature.jni_sig();
671 let display_name = name.to_string();
672 let mut conversions = Vec::new();
673 let mut params = Vec::new();
674 let args = ext.signature.args
675 .iter()
676 .map(|a| {
677 conversions.push(a.convert_rust_to_java(&self.import_paths));
678 let name = &a.name;
679 params.push(if a.typ.is_primitive() {
680 quote! { #name.into() }
681 } else {
682 quote! { (&#name).into() }
683 });
684 a.to_rust_arg(&self.import_paths)
685 });
686
687 let ret = ext.signature.returns.to_rust_return_type(&self.import_paths);
688
689 let capt = if ext.signature.returns == JavaType::Void {
690 quote!()
691 } else {
692 quote!(let ret = )
693 };
694
695 let ret_conv = match &ext.signature.returns {
696 JavaType::Void => quote!(Ok(())),
697 JavaType::Boolean => quote! { ret.z() },
698 JavaType::Byte => quote! { ret.b() },
699 JavaType::Char => quote! { ret.c() },
700 JavaType::Short => quote! { ret.s() },
701 JavaType::Int => quote! { ret.i() },
702 JavaType::Long => quote! { ret.j() },
703 JavaType::Float => quote! { ret.f() },
704 JavaType::Double => quote! { ret.d() },
705 JavaType::String => quote! { jni::objects::JString::cast_local(env, ret.l()?) },
706 JavaType::Object(o) => {
707 if o.to_string() == "java.lang.Object" {
708 quote!(ret.l())
709 } else {
710 let n = o.last();
711 let nn = n.to_string();
712 if let Some(rt) = self.import_paths.rust_types.get(&nn) {
713 let mut rt = rt.clone();
714 *rt.last_mut() = n.clone();
715 quote! { Ok(#rt(ret.l()?)) }
716 } else {
717 quote! { ret.l() }
718 }
719 }
720 },
721 array @ JavaType::Array(_) => {
722 let t = array.to_rust_concrete_type(&self.import_paths);
723 quote! {
724 env.cast_local::<#t>(ret.l()?)
725 }
726 },
727 };
728
729 if ext.is_static {
730 quote! {
731 pub fn #name<'c, 'r>(env: &'r mut jni::Env<'c>, #(#args),*) -> Result<#ret, jni::errors::Error> {
732 #(#conversions)*
733 #capt env.call_static_method(
734 jni::jni_str!(#package_class),
735 jni::jni_str!(#display_name),
736 jni::jni_sig!(#sig),
737 &[#(#params),*]
738 )?;
739 #ret_conv
740 }
741 }
742 } else {
743 quote! {
744
745 }
746 }
747 };
748 methods.append_all(method);
749 }
750
751 (exts, methods)
752 };
753
754 tokens.append_all(quote! {
755 #native_registrations
756 #[derive(Default)]
757 pub struct #name<'local>(jni::objects::JObject<'local>);
758 impl<'local> JavaTyped<'local> for #name<'local> {
759 type JType = jni::objects::JObject<'local>;
760 fn into_java<'r>(self, env: &'r mut jni::Env<'local>) -> jni::errors::Result<Self::JType> {
761 Ok(self.0)
762 }
763 }
764 impl<'local> std::ops::Deref for #name<'local> {
765 type Target = jni::objects::JObject<'local>;
766 fn deref(&self) -> &Self::Target {
767 &self.0
768 }
769 }
770 unsafe impl jni::refs::Reference for #name<'_> {
771 type Kind<'env> = #name<'env>;
772 type GlobalKind = #name<'static>;
773 fn as_raw(&self) -> jni::sys::jobject { self.0.as_raw() }
774 fn class_name() -> std::borrow::Cow<'static, jni::strings::JNIStr> {
775 std::borrow::Cow::Borrowed(jni::jni_str!(#package_class))
776 }
777 fn lookup_class<'caller>(
778 env: &jni::Env<'_>,
779 loader_context: &jni::refs::LoaderContext
780 ) -> jni::errors::Result<impl std::ops::Deref<Target = jni::refs::Global<jni::objects::JClass<'static>>> + 'caller> {
781 jni::objects::JObject::lookup_class(env, loader_context)
782 }
783 }
784 impl<'l> AsRef<jni::objects::JObject<'l>> for #name<'l> {
785 fn as_ref(&self) -> &jni::objects::JObject<'l> { &self.0 }
786 }
787 impl<'l> From<#name<'l>> for jni::objects::JObject<'l> {
788 fn from(value: #name<'l>) -> jni::objects::JObject<'l> {
789 value.0
790 }
791 }
792 impl<'local> #name<'local> {
793 pub const JAVA_SIGNATURE: jni::signature::FieldSignature<'static> = jni::jni_sig!(#class_path);
794 pub fn _validate_interface(env: &mut jni::Env<'_>) -> jni::errors::Result<()> {
795 #validation_checks
796 Ok(())
797 }
798
799 #methods
800 }
801 });
802 }
803}
804
805impl ToTokens for RustArg {
806 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
807 let Self { name, typ } = &self;
808 tokens.append_all(quote! {
809 #name: #typ
810 })
811 }
812}
813
814impl ToTokens for ScopedName {
815 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
816 let names = &self.1;
817 let prefix = match self.0 {
818 Prefix::Crate => quote!(crate::),
819 Prefix::This => quote!(self::),
820 Prefix::Super => quote!(super::),
821 Prefix::None => quote!()
822 };
823 tokens.append_all(quote! {
824 #prefix #(#names)::*
825 })
826 }
827}
828
829impl JavaType {
830 fn type_name(&self) -> Cow<'static, str> {
831 Cow::Borrowed(match self {
832 JavaType::Void => "void",
833 JavaType::Boolean => "boolean",
834 JavaType::Byte => "byte",
835 JavaType::Char => "char",
836 JavaType::Short => "short",
837 JavaType::Int => "int",
838 JavaType::Long => "long",
839 JavaType::Float => "float",
840 JavaType::Double => "double",
841 JavaType::String => "java.lang.String",
842 JavaType::Object(o) => return Cow::Owned(o.to_string()),
843 JavaType::Array(a) => return Cow::Owned(format!("{}[]", a.type_name()))
844 })
845 }
846 fn jtype(&self) -> Option<Cow<'static, str>> {
847 Some(Cow::Borrowed(match self {
848 JavaType::Void => return None,
849 JavaType::Boolean => "boolean",
850 JavaType::Byte => "byte",
851 JavaType::Char => "char",
852 JavaType::Short => "short",
853 JavaType::Int => "int",
854 JavaType::Long => "jlong",
855 JavaType::Float => "float",
856 JavaType::Double => "double",
857 JavaType::String => "java.lang.String",
858 JavaType::Object(o) => return Some(Cow::Owned(o.to_string())),
859 JavaType::Array(a) => return Some(Cow::Owned(format!("{}[]", a.type_name())))
860 }))
861 }
862 fn signature(&self) -> Cow<'static, str> {
863 Cow::Borrowed(match self {
864 JavaType::Void => "V",
865 JavaType::Boolean => "Z",
866 JavaType::Byte => "B",
867 JavaType::Char => "C",
868 JavaType::Short => "S",
869 JavaType::Int => "I",
870 JavaType::Long => "J",
871 JavaType::Float => "F",
872 JavaType::Double => "D",
873 JavaType::String => "Ljava/lang/String;",
874 JavaType::Object(o) => return Cow::Owned(o.class_path()),
875 JavaType::Array(a) => return Cow::Owned(format!("[{}", a.signature()))
876 })
877 }
878}