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 fn is_custom_object(&self) -> bool {
447 if let Self::Object(o) = self {
448 let n = o.to_string();
449 n != "java.lang.Object"
450 } else {
451 false
452 }
453 }
454
455 fn to_rust_return_type(&self, imports: &JavaImports) -> Type {
456 match self {
457 JavaType::Void => parse_quote!(()),
458 JavaType::Boolean => parse_quote!(bool),
459 JavaType::Byte => parse_quote!(i8),
460 JavaType::Char => parse_quote!(u16),
461 JavaType::Short => parse_quote!(i16),
462 JavaType::Int => parse_quote!(i32),
463 JavaType::Long => parse_quote!(i64),
464 JavaType::Float => parse_quote!(f32),
465 JavaType::Double => parse_quote!(f64),
466 JavaType::String => parse_quote!(jni::objects::JString<'c>),
467 JavaType::Object(o) => {
468 imports.resolve_rust_type(o)
469 }
470 JavaType::Array(a) => {
471 let at = a.to_rust_concrete_type(imports);
472 if a.is_primitive() {
473 parse_quote!(jni::objects::JPrimitiveArray<'c, #at>)
474 } else {
475 parse_quote!(jni::objects::JObjectArray<'c, #at>)
476 }
477 }
478 }
479 }
480 fn to_rust_type(&self, imports: &JavaImports) -> Type {
481 match self {
482 JavaType::Void => parse_quote!(()),
483 JavaType::Boolean => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jboolean>),
484 JavaType::Byte => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jbyte>),
485 JavaType::Char => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jchar>),
486 JavaType::Short => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jshort>),
487 JavaType::Int => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jint>),
488 JavaType::Long => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jlong>),
489 JavaType::Float => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jfloat>),
490 JavaType::Double => parse_quote!(impl JavaTyped<'c, JType=jni::sys::jdouble>),
491 JavaType::String => parse_quote!(impl JavaTyped<'c, JType=jni::objects::JString<'c>>),
492 JavaType::Object(o) => {
493 imports.resolve_rust_type(o)
494 }
495 JavaType::Array(a) => {
496 let at = a.to_rust_concrete_type(imports);
497 if a.is_primitive() {
498 parse_quote!(impl JavaTyped<'c, JType=JPrimitiveArray<'c, #at>>)
499 } else {
500 parse_quote!(impl JavaTyped<'c, JType=JObjectArray<'c, #at>>)
501 }
502 }
503 }
504 }
505 fn to_rust_concrete_type(&self, imports: &JavaImports) -> Type {
506 match self {
507 JavaType::Void => parse_quote!(()),
508 JavaType::Boolean => parse_quote!(jni::sys::jboolean),
509 JavaType::Byte => parse_quote!(jni::sys::jbyte),
510 JavaType::Char => parse_quote!(jni::sys::jchar),
511 JavaType::Short => parse_quote!(jni::sys::jshort),
512 JavaType::Int => parse_quote!(jni::sys::jint),
513 JavaType::Long => parse_quote!(jni::sys::jlong),
514 JavaType::Float => parse_quote!(jni::sys::jfloat),
515 JavaType::Double => parse_quote!(jni::sys::jdouble),
516 JavaType::String => parse_quote!(jni::objects::JString<'c>),
517 JavaType::Object(o) => {
518 imports.resolve_rust_type(o)
519 }
520 JavaType::Array(a) => {
521 let at = a.to_rust_concrete_type(imports);
522 if a.is_primitive() {
523 parse_quote!(jni::objects::JPrimitiveArray<'c, #at>)
524 } else {
525 parse_quote!(jni::objects::JObjectArray<'c, #at>)
526 }
527 }
528 }
529 }
530 fn to_rust_simplified_type(&self) -> Type {
531 match self {
532 JavaType::Void => parse_quote!(()),
533 JavaType::Boolean => parse_quote!(jni::sys::jboolean),
534 JavaType::Byte => parse_quote!(jni::sys::jbyte),
535 JavaType::Char => parse_quote!(jni::sys::jchar),
536 JavaType::Short => parse_quote!(jni::sys::jshort),
537 JavaType::Int => parse_quote!(jni::sys::jint),
538 JavaType::Long => parse_quote!(jni::sys::jlong),
539 JavaType::Float => parse_quote!(jni::sys::jfloat),
540 JavaType::Double => parse_quote!(jni::sys::jdouble),
541 JavaType::String => parse_quote!(jni::objects::JString<'c>),
542 JavaType::Object(_) => {
543 parse_quote!(jni::objects::JObject<'c>)
544 }
545 JavaType::Array(a) => {
546 let at = a.to_rust_simplified_type();
547 if a.is_primitive() {
548 parse_quote!(jni::objects::JPrimitiveArray<'c, #at>)
549 } else {
550 parse_quote!(jni::objects::JObjectArray<'c, #at>)
551 }
552 }
553 }
554 }
555}
556impl JavaArg {
557 fn convert_rust_to_java(&self, _imports: &JavaImports) -> proc_macro2::TokenStream {
558 let name = &self.name;
559 quote! {
560 let #name = JavaTyped::into_java(#name, env)?;
561 }
562 }
563 fn to_rust_arg(&self, imports: &JavaImports) -> RustArg {
564 let typ: Type = self.typ.to_rust_type(imports);
565
566 RustArg {
567 name: self.name.clone(),
568 typ,
569 }
570 }
571}
572struct RustArg {
573 name: Ident,
574 typ: Type,
575}
576
577impl ToTokens for JavaMod {
578 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
579 let name = &self.class.name;
580 let package_class = format!("{}.{}", self.package, name);
581 let class_path = format!("L{}/{};", self.package.partial_class_path(), name);
582
583 let native_registrations = if self.class.body.natives.is_empty() {
584 quote!()
585 } else {
586 let mut nats = proc_macro2::TokenStream::default();
587 for nat in self.class.body.natives.iter() {
588 let name = &nat.signature.name;
589 let body = &nat.body;
590 let body = quote!({#body});
591 let args = nat.signature.args
592 .iter()
593 .map(|a| {
594 let JavaArg { name, typ } = a;
595 let typ = typ.to_rust_concrete_type(&self.import_paths);
596 quote! {
597 #name: #typ
598 }
599 });
600
601 let ret_typ = nat.signature.returns.to_rust_simplified_type();
602
603 let body = if nat.signature.returns == JavaType::Void {
604 quote! {
605 { #body; Ok(()) }
606 }
607 } else if nat.signature.returns.is_primitive() {
608 quote! {
609 {
610 let x = #body;
611 Ok(x)
612 }
613 }
614 } else if nat.signature.returns.is_custom_object() {
615 quote! {
616 {
617 let v = #body;
618 Ok(v.into_java(env)?.into())
619 }
620 }
621 } else {
622 quote! {
623 {
624 let v = #body;
625 v.into_java(env)
626 }
627 }
628 };
629
630 let func = if nat.is_static {
631 quote! {
632 fn #name<'c>(
633 env: &mut jni::Env<'c>,
634 class: jni::objects::JClass<'c>,
635 #(#args),*
636 ) -> jni::errors::Result<#ret_typ> #body
637 }
638 } else {
639 quote! {
640 fn #name<'c>(
641 env: &mut jni::Env<'c>,
642 this: jni::objects::JObject<'c>,
643 #(#args),*
644 ) -> jni::errors::Result<#ret_typ> #body
645 }
646 };
647
648 let stat = if nat.is_static {
649 quote!(static)
650 } else {
651 quote!()
652 };
653 let arg_types = nat.signature.args
654 .iter()
655 .map(|t|
656 parse_str::<proc_macro2::TokenStream>(&t.typ.jtype().unwrap())
657 .map_err(|e| e.to_compile_error())
658 .unwrap_or_else(|t| t))
659 .collect::<Vec<_>>();
660 let ret = nat.signature.returns.jtype()
661 .map(|t| {
662 let t = parse_str::<proc_macro2::TokenStream>(&t)
663 .map_err(|e| e.to_compile_error())
664 .unwrap_or_else(|t| t);
665 quote!(-> #t)
666 }
667 ).unwrap_or_default();
668 nats.append_all(quote! {
669 const _: jni::NativeMethod = jni::native_method!{
670 java_type = #package_class, #stat extern fn #name(#(#arg_types),*)#ret,
671 };
672 #func
673 });
674 }
675 quote! {
676 const _: () = const {
677 #nats
678 };
679 }
680 };
681
682 let (validation_checks, methods) = if self.class.body.externs.is_empty() {
683 (quote!(), quote!())
684 } else {
685 let mut exts = proc_macro2::TokenStream::default();
686 let mut methods = proc_macro2::TokenStream::default();
687
688 for ext in self.class.body.externs.iter() {
689 let name = ext.signature.name.to_string();
690 let sig = ext.signature.jni_sig();
691 if ext.is_static {
692 exts.append_all(quote! {
693 env.get_static_method_id(
694 jni::jni_str!(#package_class),
695 jni::jni_str!(#name),
696 jni::jni_sig!(#sig),
697 )?;
698 });
699 } else {
700 exts.append_all(quote! {
701 env.get_method_id(
702 jni::jni_str!(#package_class),
703 jni::jni_str!(#name),
704 jni::jni_sig!(#sig),
705 )?;
706 });
707 }
708 let method = {
709 let name = &ext.signature.name;
710 let sig = ext.signature.jni_sig();
711 let display_name = name.to_string();
712 let mut conversions = Vec::new();
713 let mut params = Vec::new();
714 let args = ext.signature.args
715 .iter()
716 .map(|a| {
717 conversions.push(a.convert_rust_to_java(&self.import_paths));
718 let name = &a.name;
719 params.push(if a.typ.is_primitive() {
720 quote! { #name.into() }
721 } else {
722 quote! { (&#name).into() }
723 });
724 a.to_rust_arg(&self.import_paths)
725 });
726
727 let ret = ext.signature.returns.to_rust_return_type(&self.import_paths);
728
729 let capt = if ext.signature.returns == JavaType::Void {
730 quote!()
731 } else {
732 quote!(let ret = )
733 };
734
735 let ret_conv = match &ext.signature.returns {
736 JavaType::Void => quote!(Ok(())),
737 JavaType::Boolean => quote! { ret.z() },
738 JavaType::Byte => quote! { ret.b() },
739 JavaType::Char => quote! { ret.c() },
740 JavaType::Short => quote! { ret.s() },
741 JavaType::Int => quote! { ret.i() },
742 JavaType::Long => quote! { ret.j() },
743 JavaType::Float => quote! { ret.f() },
744 JavaType::Double => quote! { ret.d() },
745 JavaType::String => quote! { jni::objects::JString::cast_local(env, ret.l()?) },
746 JavaType::Object(o) => {
747 if o.to_string() == "java.lang.Object" {
748 quote!(ret.l())
749 } else {
750 let n = o.last();
751 let nn = n.to_string();
752 if let Some(rt) = self.import_paths.rust_types.get(&nn) {
753 let mut rt = rt.clone();
754 *rt.last_mut() = n.clone();
755 quote! { Ok(#rt(ret.l()?)) }
756 } else {
757 quote! { ret.l() }
758 }
759 }
760 },
761 array @ JavaType::Array(_) => {
762 let t = array.to_rust_concrete_type(&self.import_paths);
763 quote! {
764 env.cast_local::<#t>(ret.l()?)
765 }
766 },
767 };
768
769 if ext.is_static {
770 quote! {
771 pub fn #name<'c, 'r>(env: &'r mut jni::Env<'c>, #(#args),*) -> Result<#ret, jni::errors::Error> {
772 #(#conversions)*
773 #capt env.call_static_method(
774 jni::jni_str!(#package_class),
775 jni::jni_str!(#display_name),
776 jni::jni_sig!(#sig),
777 &[#(#params),*]
778 )?;
779 #ret_conv
780 }
781 }
782 } else {
783 quote! {
784
785 }
786 }
787 };
788 methods.append_all(method);
789 }
790
791 (exts, methods)
792 };
793
794 tokens.append_all(quote! {
795 #native_registrations
796 #[derive(Default)]
797 pub struct #name<'local>(jni::objects::JObject<'local>);
798 impl<'local> JavaTyped<'local> for #name<'local> {
799 type JType = jni::objects::JObject<'local>;
800 fn into_java<'r>(self, env: &'r mut jni::Env<'local>) -> jni::errors::Result<Self::JType> {
801 Ok(self.0)
802 }
803 }
804 impl<'local> std::ops::Deref for #name<'local> {
805 type Target = jni::objects::JObject<'local>;
806 fn deref(&self) -> &Self::Target {
807 &self.0
808 }
809 }
810 unsafe impl jni::refs::Reference for #name<'_> {
811 type Kind<'env> = #name<'env>;
812 type GlobalKind = #name<'static>;
813 fn as_raw(&self) -> jni::sys::jobject { self.0.as_raw() }
814 fn class_name() -> std::borrow::Cow<'static, jni::strings::JNIStr> {
815 std::borrow::Cow::Borrowed(jni::jni_str!(#package_class))
816 }
817 fn lookup_class<'caller>(
818 env: &jni::Env<'_>,
819 loader_context: &jni::refs::LoaderContext
820 ) -> jni::errors::Result<impl std::ops::Deref<Target = jni::refs::Global<jni::objects::JClass<'static>>> + 'caller> {
821 jni::objects::JObject::lookup_class(env, loader_context)
822 }
823 }
824 impl<'l> AsRef<jni::objects::JObject<'l>> for #name<'l> {
825 fn as_ref(&self) -> &jni::objects::JObject<'l> { &self.0 }
826 }
827 impl<'l> From<#name<'l>> for jni::objects::JObject<'l> {
828 fn from(value: #name<'l>) -> Self {
829 value.0
830 }
831 }
832 impl<'l> From<jni::objects::JObject<'l>> for #name<'l> {
833 fn from(value: jni::objects::JObject<'l>) -> Self {
834 Self(value)
835 }
836 }
837 impl<'local> #name<'local> {
838 pub const JAVA_SIGNATURE: jni::signature::FieldSignature<'static> = jni::jni_sig!(#class_path);
839 pub fn _validate_interface(env: &mut jni::Env<'_>) -> jni::errors::Result<()> {
840 #validation_checks
841 Ok(())
842 }
843
844 #methods
845 }
846 });
847 }
848}
849
850impl ToTokens for RustArg {
851 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
852 let Self { name, typ } = &self;
853 tokens.append_all(quote! {
854 #name: #typ
855 })
856 }
857}
858
859impl ToTokens for ScopedName {
860 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
861 let names = &self.1;
862 let prefix = match self.0 {
863 Prefix::Crate => quote!(crate::),
864 Prefix::This => quote!(self::),
865 Prefix::Super => quote!(super::),
866 Prefix::None => quote!()
867 };
868 tokens.append_all(quote! {
869 #prefix #(#names)::*
870 })
871 }
872}
873
874impl JavaType {
875 fn type_name(&self) -> Cow<'static, str> {
876 Cow::Borrowed(match self {
877 JavaType::Void => "void",
878 JavaType::Boolean => "boolean",
879 JavaType::Byte => "byte",
880 JavaType::Char => "char",
881 JavaType::Short => "short",
882 JavaType::Int => "int",
883 JavaType::Long => "long",
884 JavaType::Float => "float",
885 JavaType::Double => "double",
886 JavaType::String => "java.lang.String",
887 JavaType::Object(o) => return Cow::Owned(o.to_string()),
888 JavaType::Array(a) => return Cow::Owned(format!("{}[]", a.type_name()))
889 })
890 }
891 fn jtype(&self) -> Option<Cow<'static, str>> {
892 Some(Cow::Borrowed(match self {
893 JavaType::Void => return None,
894 JavaType::Boolean => "boolean",
895 JavaType::Byte => "byte",
896 JavaType::Char => "char",
897 JavaType::Short => "short",
898 JavaType::Int => "int",
899 JavaType::Long => "jlong",
900 JavaType::Float => "float",
901 JavaType::Double => "double",
902 JavaType::String => "java.lang.String",
903 JavaType::Object(o) => return Some(Cow::Owned(o.to_string())),
904 JavaType::Array(a) => return Some(Cow::Owned(format!("{}[]", a.type_name())))
905 }))
906 }
907 fn signature(&self) -> Cow<'static, str> {
908 Cow::Borrowed(match self {
909 JavaType::Void => "V",
910 JavaType::Boolean => "Z",
911 JavaType::Byte => "B",
912 JavaType::Char => "C",
913 JavaType::Short => "S",
914 JavaType::Int => "I",
915 JavaType::Long => "J",
916 JavaType::Float => "F",
917 JavaType::Double => "D",
918 JavaType::String => "Ljava/lang/String;",
919 JavaType::Object(o) => return Cow::Owned(o.class_path()),
920 JavaType::Array(a) => return Cow::Owned(format!("[{}", a.signature()))
921 })
922 }
923}