Skip to main content

java_jni_extras_proc/
lib.rs

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
16// output structs
17struct 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// parse helper structs
78#[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
149// state
150impl 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
210// Primary parsers
211impl 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
395// Primary output
396impl 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}