duchess_reflect/
codegen.rs

1use crate::{
2    argument::DuchessDeclaration,
3    class_info::{
4        ClassInfo, Constructor, DotId, Field, Id, Method, NonRepeatingType, RootMap,
5        SpannedPackageInfo, Type,
6    },
7    reflect::Reflector,
8    signature::Signature,
9    upcasts::Upcasts,
10};
11use inflector::Inflector;
12use proc_macro2::{Ident, Literal, Span, TokenStream};
13use quote::quote_spanned;
14
15impl DuchessDeclaration {
16    pub fn to_tokens(&self) -> syn::Result<TokenStream> {
17        let reflector = &mut Reflector::default();
18        let root_map = self.to_root_map(reflector)?;
19        let () = root_map.check(reflector)?;
20        root_map.to_tokens(reflector)
21    }
22}
23
24impl RootMap {
25    fn to_tokens(self, reflector: &mut Reflector) -> syn::Result<TokenStream> {
26        self.to_packages()
27            .map(|p| p.to_tokens(&[], &self, reflector))
28            .collect()
29    }
30}
31
32impl SpannedPackageInfo {
33    fn to_tokens(
34        &self,
35        parents: &[Id],
36        root_map: &RootMap,
37        reflector: &mut Reflector,
38    ) -> syn::Result<TokenStream> {
39        let package_id = DotId::new(parents, &self.name);
40        let name = self.name.to_ident(self.span);
41
42        let subpackage_tokens: TokenStream = self
43            .subpackages
44            .values()
45            .map(|p| p.to_tokens(&package_id, root_map, reflector))
46            .collect::<Result<_, _>>()?;
47
48        let class_tokens: TokenStream = self
49            .classes
50            .iter()
51            .map(|class_id| root_map.classes[class_id].to_tokens(&root_map.upcasts))
52            .collect::<Result<_, _>>()?;
53
54        let supers: Vec<TokenStream> = package_id
55            .iter()
56            .map(|_| quote_spanned!(self.span => super))
57            .collect();
58
59        Ok(quote_spanned!(self.span =>
60            #[allow(unused_imports)]
61            pub mod #name {
62                // Import the contents of the parent module that we are created inside
63                use #(#supers ::)* *;
64
65                // Import the java package provided by duchess
66                use duchess::java;
67
68                #subpackage_tokens
69                #class_tokens
70            }
71        ))
72    }
73}
74
75impl ClassInfo {
76    pub fn to_tokens(&self, upcasts: &Upcasts) -> syn::Result<TokenStream> {
77        let struct_name = self.struct_name();
78        let cached_class = self.cached_class();
79        let this_ty = self.this_type();
80        let java_class_generics_with_defaults = self.class_generic_names_with_defaults();
81        let java_class_generics = self.class_generic_names();
82
83        // Convert constructors
84        let constructors: Vec<_> = self
85            .constructors
86            .iter()
87            .map(|c| self.constructor(c))
88            .collect::<Result<_, _>>()?;
89
90        // Convert static methods (not instance methods, those are different)
91        let static_methods: Vec<_> = self
92            .methods
93            .iter()
94            .filter(|m| self.should_mirror_in_rust(m.flags.privacy))
95            .filter(|m| m.flags.is_static)
96            .map(|m| self.static_method(m))
97            .collect::<Result<_, _>>()?;
98
99        // Convert instance methods (not static methods, those are different)
100        let op_methods: Vec<_> = self
101            .methods
102            .iter()
103            .filter(|m| self.should_mirror_in_rust(m.flags.privacy))
104            .filter(|m| !m.flags.is_static)
105            .map(|m| self.op_struct_method(m))
106            .collect::<Result<_, _>>()?;
107
108        // Convert instance methods (not static methods, those are different)
109        let obj_methods: Vec<_> = self
110            .methods
111            .iter()
112            .filter(|m| self.should_mirror_in_rust(m.flags.privacy))
113            .filter(|m| !m.flags.is_static)
114            .map(|m| self.obj_struct_method(m))
115            .collect::<Result<_, _>>()?;
116
117        let assoc_struct_declarations = self.assoc_structs(upcasts, op_methods, obj_methods)?;
118
119        // Convert instance methods of the form `Foo::method`
120        let inherent_object_methods: Vec<_> = self
121            .methods
122            .iter()
123            .filter(|m| self.should_mirror_in_rust(m.flags.privacy))
124            .filter(|m| !m.flags.is_static)
125            .map(|m| self.inherent_object_method(m))
126            .collect::<Result<_, _>>()?;
127
128        // Generate static field getters
129        let static_field_getters: Vec<_> = self
130            .fields
131            .iter()
132            .filter(|f: &&Field| self.should_mirror_in_rust(f.flags.privacy))
133            .filter(|f| f.flags.is_static)
134            .map(|f| self.static_field_getter(f))
135            .collect::<Result<_, _>>()?;
136
137        let upcast_impls = self.upcast_impls(upcasts)?;
138
139        let output = quote_spanned! {
140            self.span =>
141
142            #[allow(non_camel_case_types)]
143            pub struct #struct_name<#(#java_class_generics_with_defaults,)*> {
144                _empty: std::convert::Infallible,
145                _dummy: ::core::marker::PhantomData<(#(#java_class_generics,)*)>
146            }
147
148            // Hide other generated items
149            #[allow(unused_imports)]
150            #[allow(nonstandard_style)]
151            const _: () = {
152                #assoc_struct_declarations
153
154                unsafe impl<#(#java_class_generics,)*> duchess::JavaObject for #struct_name<#(#java_class_generics,)*>
155                where
156                    #(#java_class_generics: duchess::JavaObject,)*
157                {
158                    #cached_class
159                }
160
161                impl<#(#java_class_generics,)*> ::core::convert::AsRef<#struct_name<#(#java_class_generics,)*>> for #struct_name<#(#java_class_generics,)*>
162                where
163                    #(#java_class_generics: duchess::JavaObject,)*
164                {
165                    fn as_ref(&self) -> &#struct_name<#(#java_class_generics,)*> {
166                        self
167                    }
168                }
169
170                impl<#(#java_class_generics,)*> ::core::ops::Deref for #struct_name<#(#java_class_generics,)*>
171                where
172                    #(#java_class_generics: duchess::JavaObject,)*
173                {
174                    type Target = <Self as duchess::plumbing::JavaView>::OfObj<Self>;
175
176                    fn deref(&self) -> &Self::Target {
177                        duchess::plumbing::FromRef::from_ref(self)
178                    }
179                }
180
181                impl<#(#java_class_generics,)*> duchess::prelude::JDeref for #struct_name<#(#java_class_generics,)*>
182                where
183                    #(#java_class_generics: duchess::JavaObject,)*
184                {
185                    fn jderef(&self) -> &Self {
186                        self
187                    }
188                }
189
190                impl<#(#java_class_generics,)*> duchess::prelude::TryJDeref for #struct_name<#(#java_class_generics,)*>
191                where
192                    #(#java_class_generics: duchess::JavaObject,)*
193                {
194                    type Java = Self;
195
196                    fn try_jderef(&self) -> duchess::Nullable<&Self> {
197                        Ok(self)
198                    }
199                }
200
201                // Reflexive upcast impl
202                unsafe impl<#(#java_class_generics,)*> duchess::plumbing::Upcast<#struct_name<#(#java_class_generics,)*>> for #struct_name<#(#java_class_generics,)*>
203                where
204                    #(#java_class_generics: duchess::JavaObject,)*
205                {}
206
207                // Other upcast impls
208                #upcast_impls
209
210                impl< #(#java_class_generics,)* > #this_ty
211                where
212                    #(#java_class_generics: duchess::JavaObject,)*
213                {
214                    #(#constructors)*
215
216                    #(#static_methods)*
217
218                    #(#static_field_getters)*
219
220                    #(#inherent_object_methods)*
221                }
222            };
223        };
224
225        crate::debug_tokens(&self.name, &output);
226
227        Ok(output)
228    }
229
230    /// Construct the various declarations related to the op struct,
231    /// with the exception of any methods that must be invoked.
232    fn assoc_structs(
233        &self,
234        upcasts: &Upcasts,
235        op_struct_methods: Vec<TokenStream>,
236        obj_struct_methods: Vec<TokenStream>,
237    ) -> syn::Result<TokenStream> {
238        let name = self.struct_name();
239        let java_class_generics = self.class_generic_names();
240
241        // Names for the `J` and `N` parameters.
242        // There is no particular reason to create `Ident` for these
243        // except that I decided to pretend we had hygienic procedural
244        // macros in this particular piece of code for some reason.
245        let j = Ident::new("J", self.span);
246        let n = Ident::new("N", self.span);
247
248        // Subtle: the struct itself does not require
249        // that `J: IntoJava<Self>` or `N: FromRef<J>`.
250        // Those requirements are placed only on the `Deref` impls.
251
252        let struct_definition = |struct_name: &Ident| {
253            quote_spanned!(self.span =>
254                #[repr(transparent)]
255                pub struct #struct_name<#(#java_class_generics,)* #j, #n> {
256                    this: #j,
257                    phantom: ::core::marker::PhantomData<(#name<#(#java_class_generics),*>, #n)>,
258                }
259            )
260        };
261
262        let deref_impl = |struct_name: &Ident| {
263            quote_spanned!(self.span =>
264                impl<#(#java_class_generics,)* #j, #n> ::core::ops::Deref
265                for #struct_name<#(#java_class_generics,)* #j, #n>
266                where
267                    #n: duchess::plumbing::FromRef<#j>,
268                {
269                    type Target = #n;
270
271                    fn deref(&self) -> &#n {
272                        duchess::plumbing::FromRef::from_ref(&self.this)
273                    }
274                }
275            )
276        };
277
278        let from_ref_impl = |struct_name: &Ident| {
279            quote_spanned!(self.span =>
280                impl<#(#java_class_generics,)* #j, #n> duchess::plumbing::FromRef<#j> for #struct_name<#(#java_class_generics,)* #j, #n> {
281                    fn from_ref(j: &J) -> &Self {
282                        // This is safe because of the `#[repr(transparent)]`
283                        // on the struct declaration.
284                        unsafe {
285                            ::core::mem::transmute::<&J, &Self>(j)
286                        }
287                    }
288                }
289            )
290        };
291
292        // Construct the default value for the "next" (#n) parameter.
293        let mro = self.mro(upcasts)?;
294
295        let op_name = Id::from(format!("ViewAs{}Op", self.name.class_name())).to_ident(self.span);
296        let op_mro_tokens = self.mro_tokens(&j, "OfOpWith", &mro);
297
298        let obj_name = Id::from(format!("ViewAs{}Obj", self.name.class_name())).to_ident(self.span);
299        let obj_mro_tokens = self.mro_tokens(&j, "OfObjWith", &mro);
300
301        let all_names = &[&op_name, &obj_name];
302
303        let this_ty = self.this_type();
304
305        let other_impls = quote_spanned!(self.span =>
306            impl<#(#java_class_generics,)*> duchess::plumbing::JavaView for #name<#(#java_class_generics,)*>
307            {
308                type OfOp<#j> = #op_name<#(#java_class_generics,)* #j, #op_mro_tokens>;
309
310                type OfOpWith<#j, #n> = #op_name<#(#java_class_generics,)* #j, #n>
311                where
312                    N: duchess::plumbing::FromRef<J>;
313
314                type OfObj<#j> = #obj_name<#(#java_class_generics,)* #j, #obj_mro_tokens>;
315
316                type OfObjWith<#j, #n> = #obj_name<#(#java_class_generics,)* #j, #n>
317                where
318                    N: duchess::plumbing::FromRef<J>;
319            }
320
321            impl<#(#java_class_generics,)* #j, #n> #op_name<#(#java_class_generics,)* #j, #n>
322            where
323                #(#java_class_generics: duchess::JavaObject,)*
324                #j: duchess::plumbing::JvmRefOp<#name<#(#java_class_generics,)*>>,
325                #n: duchess::plumbing::FromRef<#j>,
326            {
327                #(#op_struct_methods)*
328            }
329
330            impl<#(#java_class_generics,)* #j, #n> #obj_name<#(#java_class_generics,)* #j, #n>
331            where
332                #(#java_class_generics: duchess::JavaObject,)*
333                for<'jvm> &'jvm #j: duchess::plumbing::JvmRefOp<#this_ty>,
334            {
335                #(#obj_struct_methods)*
336            }
337        );
338
339        let declarations: TokenStream = all_names
340            .iter()
341            .copied()
342            .flat_map(|n| vec![struct_definition(n), deref_impl(n), from_ref_impl(n)])
343            .chain(Some(other_impls))
344            .collect();
345
346        Ok(declarations)
347    }
348
349    /// Constructs the default "next" type for our [op struct].
350    /// This is based on the method resolution order (mro) for the
351    /// current type. For example, if `Foo` extends `Bar`, then the
352    /// result for `Foo` would be
353    /// `Bar::OfOpWith<J, java::lang::Object::OfOpWith<J, ()>>`.
354    ///
355    /// [op struct]: https://duchess-rs.github.io/duchess/methods.html#op-structs
356    fn mro_tokens(&self, j: &Ident, assoc_name: &str, mro: &[TokenStream]) -> TokenStream {
357        let Some((head, tail)) = mro.split_first() else {
358            return quote_spanned!(self.span => ());
359        };
360
361        let tail_tokens = self.mro_tokens(j, assoc_name, tail);
362
363        let assoc_ident = Ident::new(assoc_name, self.span);
364        quote_spanned!(self.span =>
365            <#head as duchess::plumbing::JavaView>::#assoc_ident<#j, #tail_tokens>
366        )
367    }
368
369    /// Returns the ["method resolution order"][mro] for self. This is a series of
370    /// supertypes (classes or interfaces) ordered such that the more specific types
371    /// appear first. The returned list only includes "proper" supertypes, it does not
372    /// include the current class.
373    ///
374    /// FIXME: The returned list contains the right items, but is in an arbitary order,
375    /// and is not following the documented order. The result is that calls may wind up
376    /// calling methods from supertypes instead of subtypes. This only matters if subtypes
377    /// refine the return type.
378    ///
379    /// [mro]: https://duchess-rs.github.io/duchess/methods.html#method-resolution-order
380    fn mro(&self, upcasts: &Upcasts) -> syn::Result<Vec<TokenStream>> {
381        let class_refs = upcasts.upcasts_for_generated_class(&self.name);
382        class_refs
383            .iter()
384            .map(|r| {
385                let mut sig = Signature::new(&Id::from("supertrait"), self.span, &[])
386                    .with_internal_generics(&self.generics)?;
387                Ok(sig.forbid_capture(|sig| sig.class_ref_ty(r)).unwrap())
388            })
389            .collect()
390    }
391
392    fn upcast_impls(&self, upcasts: &Upcasts) -> syn::Result<TokenStream> {
393        let struct_name = self.struct_name();
394        let java_class_generics = self.class_generic_names();
395        Ok(self.mro(upcasts)?
396            .into_iter()
397            .map(|tokens| {
398                quote_spanned!(self.span =>
399                    unsafe impl<#(#java_class_generics,)*> duchess::plumbing::Upcast<#tokens> for #struct_name<#(#java_class_generics,)*>
400                    where
401                        #(#java_class_generics: duchess::JavaObject,)*
402                    {}
403                )
404            })
405            .collect())
406    }
407
408    fn cached_class(&self) -> TokenStream {
409        let jni_class_name = self.jni_class_name();
410
411        quote_spanned! {
412            self.span =>
413            fn class<'jvm>(jvm: &mut duchess::Jvm<'jvm>) -> duchess::LocalResult<'jvm, duchess::Local<'jvm, java::lang::Class>> {
414                static CLASS: duchess::plumbing::once_cell::sync::OnceCell<duchess::Java<java::lang::Class>> = duchess::plumbing::once_cell::sync::OnceCell::new();
415                let global = CLASS.get_or_try_init::<_, duchess::Error<duchess::Local<java::lang::Throwable>>>(|| {
416                    let class = duchess::plumbing::find_class(jvm, #jni_class_name)?;
417                    Ok(jvm.global(&class))
418                })?;
419                Ok(jvm.local(global))
420            }
421        }
422    }
423
424    fn constructor(&self, constructor: &Constructor) -> syn::Result<TokenStream> {
425        let mut sig = Signature::new(self.name.class_name(), self.span, &self.generics);
426
427        let (input_traits, jvm_op_traits): (Vec<_>, Vec<_>) = constructor
428            .argument_tys
429            .iter()
430            .map(|ty| sig.input_and_jvm_op_traits(ty))
431            .collect::<Result<Vec<_>, _>>()?
432            .into_iter()
433            .unzip();
434
435        let input_names: Vec<_> = (0..input_traits.len())
436            .map(|i| Ident::new(&format!("a{i}"), self.span))
437            .collect();
438
439        let ty = self.this_type();
440        let output_trait = quote_spanned!(self.span => duchess::prelude::JavaConstructor<#ty>);
441
442        let java_class_generics = self.class_generic_names();
443
444        let jni_descriptor = jni_c_str(constructor.descriptor(&self.generics_scope()), self.span);
445
446        // Code to convert each input appropriately
447        let prepare_inputs = self.prepare_inputs(&input_names, &constructor.argument_tys);
448
449        // for debugging JVM invocation failures
450        let name = Literal::string(&self.name.to_string());
451        let descriptor = Literal::string(&constructor.descriptor(&self.generics_scope()));
452
453        let output = quote_spanned!(self.span =>
454            pub fn new(
455                #(#input_names : impl #input_traits,)*
456            ) -> impl #output_trait {
457                struct Impl<
458                    #(#java_class_generics,)*
459                    #(#input_names),*
460                > {
461                    #(#input_names: #input_names,)*
462                    phantom: ::core::marker::PhantomData<(
463                        #(#java_class_generics,)*
464                    )>,
465                }
466
467                impl<
468                    #(#java_class_generics,)*
469                    #(#input_names,)*
470                > ::core::clone::Clone for Impl<
471                    #(#java_class_generics,)*
472                    #(#input_names,)*
473                >
474                where
475                    #(#java_class_generics: duchess::JavaObject,)*
476                    #(#input_names : #jvm_op_traits,)*
477                {
478                    fn clone(&self) -> Self {
479                        Impl {
480                            #(#input_names: Clone::clone(&self.#input_names),)*
481                            phantom: self.phantom,
482                        }
483                    }
484                }
485
486                impl<
487                    #(#java_class_generics,)*
488                    #(#input_names,)*
489                > duchess::prelude::JvmOp for Impl<
490                    #(#java_class_generics,)*
491                    #(#input_names,)*
492                >
493                where
494                    #(#java_class_generics: duchess::JavaObject,)*
495                    #(#input_names : #jvm_op_traits,)*
496                {
497                    type Output<'jvm> = duchess::Local<'jvm, #ty>;
498
499                    fn do_jni<'jvm>(
500                        self,
501                        jvm: &mut duchess::Jvm<'jvm>,
502                    ) -> duchess::LocalResult<'jvm, Self::Output<'jvm>> {
503                        #(#prepare_inputs)*
504
505                        let class = <#ty as duchess::JavaObject>::class(jvm)?;
506
507                        // Cache the method id for the constructor -- note that we only have one cache
508                        // no matter how many generic monomorphizations there are. This makes sense
509                        // given Java's erased-based generics system.
510                        static CONSTRUCTOR: duchess::plumbing::once_cell::sync::OnceCell<duchess::plumbing::MethodPtr> = duchess::plumbing::once_cell::sync::OnceCell::new();
511                        let constructor = CONSTRUCTOR.get_or_try_init(|| {
512                            duchess::plumbing::find_constructor(jvm, &class, #jni_descriptor)
513                        })?;
514
515                        let env = jvm.env();
516                        let obj: ::core::option::Option<duchess::Local<#ty>> = unsafe {
517                            env.invoke(|env| env.NewObjectA, |env, f| f(
518                                env,
519                                duchess::plumbing::JavaObjectExt::as_raw(&*class).as_ptr(),
520                                constructor.as_ptr(),
521                                [
522                                    #(duchess::plumbing::IntoJniValue::into_jni_value(#input_names),)*
523                                ].as_ptr(),
524                            ))
525                        }?;
526                        obj.ok_or_else(|| {
527                            // NewObjectA should only return a null pointer when an exception occurred in the
528                            // constructor, so reaching here is a strange JVM state
529                            duchess::Error::JvmInternal(format!(
530                                "failed to create new `{}` via constructor `{}`",
531                                #name, #descriptor,
532                            ))
533                        })
534                    }
535                }
536
537                impl<
538                    #(#java_class_generics,)*
539                    #(#input_names,)*
540                > ::core::ops::Deref for Impl<
541                    #(#java_class_generics,)*
542                    #(#input_names,)*
543                > {
544                    type Target = <#ty as duchess::plumbing::JavaView>::OfOp<Self>;
545
546                    fn deref(&self) -> &Self::Target {
547                        <Self::Target as duchess::plumbing::FromRef<_>>::from_ref(self)
548                    }
549                }
550
551                Impl {
552                    #(#input_names: #input_names.into_op(),)*
553                    phantom: ::core::default::Default::default()
554                }
555            }
556        );
557
558        // useful for debugging
559        // eprintln!("{output}");
560
561        Ok(output)
562    }
563
564    /// Generates code for the methods that goes on the `ops` object.
565    ///
566    ///
567    /// NB. This function (particularly the JvmOp impl) has significant overlap with `static_method`
568    /// and `static_field_getter`, so if you make changes here, you may well need changes there.
569    fn op_struct_method(&self, method: &Method) -> syn::Result<TokenStream> {
570        let mut sig = Signature::new(&method.name, self.span, &self.generics)
571            .with_internal_generics(&method.generics)?;
572
573        let (input_traits, _jvm_op_traits): (Vec<_>, Vec<_>) = method
574            .argument_tys
575            .iter()
576            .map(|ty| sig.input_and_jvm_op_traits(ty))
577            .collect::<Result<Vec<_>, _>>()?
578            .into_iter()
579            .unzip();
580
581        let input_names: Vec<_> = (0..input_traits.len())
582            .map(|i| Ident::new(&format!("a{i}"), self.span))
583            .collect();
584
585        // The "output trait" is the trait bounds we declare for the user,
586        // e.g., for a method like `Foo method()`, we will declare a
587        // Rust method `-> impl JavaMethod<Foo>`, and this variable
588        // would be `JavaMethod<Foo>`.
589        let output_trait = sig.method_trait(&method.return_ty)?;
590
591        let rust_method_name = Id::from(method.name.to_snake_case()).to_ident(self.span);
592
593        // The generic parameters we need on the Rust method, these include:
594        //
595        // * a type parameter for each java generic
596        // * any fresh generics we created to capture wildcards
597        let rust_method_generics = &sig.rust_generics;
598
599        // The final where clauses we need on the method.
600        // This includes the bounds declared in Java but also
601        // other bounds we added as we converted input types
602        // to account for captures.
603        let sig_where_clauses = &sig.where_clauses;
604
605        let this_ty = self.this_type();
606
607        let inherent_method = quote_spanned!(self.span =>
608            pub fn #rust_method_name<#(#rust_method_generics),*>(
609                &self,
610                #(#input_names: impl #input_traits),*
611            ) -> impl #output_trait
612            where
613                #(#sig_where_clauses,)*
614            {
615                <#this_ty>::#rust_method_name(
616                    Clone::clone(&self.this),
617                    #(#input_names,)*
618                )
619            }
620        );
621
622        Ok(inherent_method)
623    }
624
625    fn obj_struct_method(&self, method: &Method) -> syn::Result<TokenStream> {
626        let mut sig = Signature::new(&method.name, self.span, &self.generics)
627            .with_internal_generics(&method.generics)?;
628
629        let (input_traits, _jvm_op_traits): (Vec<_>, Vec<_>) = method
630            .argument_tys
631            .iter()
632            .map(|ty| sig.input_and_jvm_op_traits(ty))
633            .collect::<Result<Vec<_>, _>>()?
634            .into_iter()
635            .unzip();
636
637        let input_names: Vec<_> = (0..input_traits.len())
638            .map(|i| Ident::new(&format!("a{i}"), self.span))
639            .collect();
640
641        // The "output trait" is the trait bounds we declare for the user,
642        // e.g., for a method like `Foo method()`, we will declare a
643        // Rust method `-> impl JavaMethod<Foo>`, and this variable
644        // would be `JavaMethod<Foo>`.
645        let output_trait = sig.method_trait(&method.return_ty)?;
646
647        let rust_method_name = Id::from(method.name.to_snake_case()).to_ident(self.span);
648
649        // The generic parameters we need on the Rust method, these include:
650        //
651        // * a type parameter for each java generic
652        // * any fresh generics we created to capture wildcards
653        let rust_method_generics = &sig.rust_generics;
654
655        // The final where clauses we need on the method.
656        // This includes the bounds declared in Java but also
657        // other bounds we added as we converted input types
658        // to account for captures.
659        let sig_where_clauses = &sig.where_clauses;
660
661        let this_ty = self.this_type();
662
663        let inherent_method = quote_spanned!(self.span =>
664            pub fn #rust_method_name<'a, #(#rust_method_generics),*>(
665                &'a self,
666                #(#input_names: impl #input_traits + 'a),*
667            ) -> impl #output_trait + 'a
668            where
669                #(#sig_where_clauses,)*
670            {
671                <#this_ty>::#rust_method_name(
672                    &self.this,
673                    #(#input_names,)*
674                )
675            }
676        );
677
678        Ok(inherent_method)
679    }
680
681    fn inherent_object_method(&self, method: &Method) -> syn::Result<TokenStream> {
682        let mut sig = Signature::new(&method.name, self.span, &self.generics)
683            .with_internal_generics(&method.generics)?;
684
685        let (input_traits, jvm_op_traits): (Vec<_>, Vec<_>) = method
686            .argument_tys
687            .iter()
688            .map(|ty| sig.input_and_jvm_op_traits(ty))
689            .collect::<Result<Vec<_>, _>>()?
690            .into_iter()
691            .unzip();
692
693        let input_names: Vec<_> = (0..input_traits.len())
694            .map(|i| Ident::new(&format!("a{i}"), self.span))
695            .collect();
696
697        // The "output type" is the actual type returned by this method,
698        // e.g., `Option<Local<Foo>>`.
699        let output_ty = sig.output_type(&method.return_ty)?;
700
701        // The "output trait" is the trait bounds we declare for the user,
702        // e.g., for a method like `Foo method()`, we will declare a
703        // Rust method `-> impl JavaMethod<Foo>`, and this variable
704        // would be `JavaMethod<Foo>`.
705        let output_trait = sig.method_trait(&method.return_ty)?;
706
707        // The appropriate JNI function to call this method.
708        let jni_call_fn = sig.jni_call_fn(&method.return_ty)?;
709
710        // If this method returns a java object, then this is the
711        // Rust type representing the java class/interface that is returned
712        // (e.g., `Some(java::lang::Object)`).
713        let java_ref_output_ty = match &method.return_ty {
714            Some(java_return_type) => {
715                sig.forbid_capture(|sig| sig.java_ty_if_ref(java_return_type))?
716            }
717            None => None,
718        };
719
720        let jni_descriptor = jni_c_str(&method.descriptor(&self.generics_scope()), self.span);
721
722        // Code to convert each input appropriately
723        let prepare_inputs = self.prepare_inputs(&input_names, &method.argument_tys);
724
725        let jni_method = jni_c_str(&*method.name, self.span);
726
727        let rust_method_name = Id::from(method.name.to_snake_case()).to_ident(self.span);
728        let rust_method_type_name = Id::from(method.name.to_camel_case()).to_ident(self.span);
729
730        // The generic parameters declared on the Java method.
731        let java_class_generics: Vec<_> = self.class_generic_names();
732
733        // The generic parameters we need on the Rust method, these include:
734        //
735        // * a type parameter for each java generic
736        // * any fresh generics we created to capture wildcards
737        let rust_method_generics = &sig.rust_generics;
738
739        // The generic parameters we need on the *method struct* (which will implement the `JvmOp`).
740        // These include the class generics plus all the generics from the method,
741        // plus a type parameter `a0` for each input
742        let this = Ident::new("this", self.span);
743        let method_struct_generics: Vec<_> = java_class_generics
744            .iter()
745            .chain(rust_method_generics)
746            .chain(Some(&this))
747            .chain(&input_names)
748            .collect();
749
750        // For each method `m` in the Java type, we create a struct (named `m`)
751        // that will implement the `JvmOp`.
752        let method_struct = quote_spanned!(self.span =>
753            pub struct #rust_method_type_name<
754                #(#method_struct_generics,)*
755            > {
756                #this: #this,
757                #(#input_names : #input_names,)*
758                phantom: ::core::marker::PhantomData<(
759                    #(#method_struct_generics,)*
760                )>,
761            }
762        );
763
764        // The final where clauses we need on the method.
765        // This includes the bounds declared in Java but also
766        // other bounds we added as we converted input types
767        // to account for captures.
768        let sig_where_clauses = &sig.where_clauses;
769
770        // The Rust type of the class defining this method.
771        let this_ty = self.this_type();
772
773        // Implementation of `JvmOp` for `m` -- when executed, call the method
774        // via JNI, after converting its arguments appropriately.
775        let jvmop_impl = quote_spanned!(self.span =>
776            impl<#(#method_struct_generics),*> ::core::clone::Clone
777            for #rust_method_type_name<#(#method_struct_generics),*>
778            where
779                #this: duchess::plumbing::JvmRefOp<#this_ty>,
780                #(#input_names: #jvm_op_traits,)*
781                #(#java_class_generics: duchess::JavaObject,)*
782                #(#sig_where_clauses,)*
783            {
784                fn clone(&self) -> Self {
785                    #rust_method_type_name {
786                        #this: Clone::clone(&self.#this),
787                        #(#input_names: Clone::clone(&self.#input_names),)*
788                        phantom: self.phantom,
789                    }
790                }
791            }
792
793            impl<#(#method_struct_generics),*> duchess::prelude::JvmOp
794            for #rust_method_type_name<#(#method_struct_generics),*>
795            where
796                #this: duchess::plumbing::JvmRefOp<#this_ty>,
797                #(#input_names: #jvm_op_traits,)*
798                #(#java_class_generics: duchess::JavaObject,)*
799                #(#sig_where_clauses,)*
800            {
801                type Output<'jvm> = #output_ty;
802
803                fn do_jni<'jvm>(
804                    self,
805                    jvm: &mut duchess::Jvm<'jvm>,
806                ) -> duchess::LocalResult<'jvm, Self::Output<'jvm>> {
807                    let this = self.#this.into_as_jref(jvm)?;
808                    let this: & #this_ty = duchess::prelude::AsJRef::as_jref(&this)?;
809                    let this = duchess::plumbing::JavaObjectExt::as_raw(this);
810
811                    #(#prepare_inputs)*
812
813                    // Cache the method id for this method -- note that we only have one cache
814                    // no matter how many generic monomorphizations there are. This makes sense
815                    // given Java's erased-based generics system.
816                    static METHOD: duchess::plumbing::once_cell::sync::OnceCell<duchess::plumbing::MethodPtr> = duchess::plumbing::once_cell::sync::OnceCell::new();
817                    let method = METHOD.get_or_try_init(|| {
818                        let class = <#this_ty as duchess::JavaObject>::class(jvm)?;
819                        duchess::plumbing::find_method(jvm, &class, #jni_method, #jni_descriptor, false)
820                    })?;
821
822                    unsafe {
823                        jvm.env().invoke(|env| env.#jni_call_fn, |env, f| f(
824                            env,
825                            this.as_ptr(),
826                            method.as_ptr(),
827                            [
828                                #(duchess::plumbing::IntoJniValue::into_jni_value(#input_names),)*
829                            ].as_ptr(),
830                        ))
831                    }
832                }
833            }
834        );
835
836        // If we return a Java object, then deref to its op struct.
837        // See [method docs] for more details.
838        // [method docs]:https://duchess-rs.github.io/duchess/methods.html
839        let deref_impl = java_ref_output_ty.map(|java_ref_output_ty| {
840            quote_spanned!(self.span =>
841                impl<#(#method_struct_generics),*> ::core::ops::Deref
842                for #rust_method_type_name<#(#method_struct_generics),*>
843                where
844                    #(#java_class_generics: duchess::JavaObject,)*
845                    #(#sig_where_clauses,)*
846                {
847                    type Target = <#java_ref_output_ty as duchess::plumbing::JavaView>::OfOp<Self>;
848
849                    fn deref(&self) -> &Self::Target {
850                        <Self::Target as duchess::plumbing::FromRef<_>>::from_ref(self)
851                    }
852                }
853            )
854        });
855
856        let inherent_method = quote_spanned!(self.span =>
857            pub fn #rust_method_name<#(#rust_method_generics),*>(
858                #this: impl duchess::prelude::IntoJava<#this_ty>,
859                #(#input_names: impl #input_traits),*
860            ) -> impl #output_trait
861            where
862                #(#sig_where_clauses,)*
863            {
864                #method_struct
865
866                #jvmop_impl
867
868                #deref_impl
869
870                #rust_method_type_name {
871                    #this: #this.into_op(),
872                    #(#input_names: #input_names.into_op(),)*
873                    phantom: ::core::default::Default::default(),
874                }
875            }
876        );
877
878        Ok(inherent_method)
879    }
880
881    /// Generates a static method declaration that should be part of the inherent methods
882    /// for the struct. Unlike instance methods, static methods can be totally self-contained.
883    ///
884    /// NB. This function (particularly the JvmOp impl) has significant overlap with `object_method`
885    /// and `static_field_getter`, so if you make changes here, you may well need changes there.
886    fn static_method(&self, method: &Method) -> syn::Result<TokenStream> {
887        assert!(method.flags.is_static);
888
889        let mut sig = Signature::new(&method.name, self.span, &self.generics)
890            .with_internal_generics(&method.generics)?;
891
892        let (input_traits, jvm_op_traits): (Vec<_>, Vec<_>) = method
893            .argument_tys
894            .iter()
895            .map(|ty| sig.input_and_jvm_op_traits(ty))
896            .collect::<Result<Vec<_>, _>>()?
897            .into_iter()
898            .unzip();
899
900        let input_names: Vec<_> = (0..input_traits.len())
901            .map(|i| Ident::new(&format!("a{i}"), self.span))
902            .collect();
903
904        let output_ty = sig.output_type(&method.return_ty)?;
905        let output_trait = sig.method_trait(&method.return_ty)?;
906        let jni_call_fn = sig.jni_static_call_fn(&method.return_ty)?;
907
908        // If this method returns a java object, then this is the
909        // Rust type representing the java class/interface that is returned
910        // (e.g., `Some(java::lang::Object)`).
911        let java_ref_output_ty = match &method.return_ty {
912            Some(java_return_type) => {
913                sig.forbid_capture(|sig| sig.java_ty_if_ref(java_return_type))?
914            }
915            None => None,
916        };
917
918        let jni_descriptor = jni_c_str(&method.descriptor(&self.generics_scope()), self.span);
919
920        // Code to convert each input appropriately
921        let prepare_inputs = self.prepare_inputs(&input_names, &method.argument_tys);
922
923        let jni_method = jni_c_str(&*method.name, self.span);
924
925        let rust_method_name = Id::from(method.name.to_snake_case()).to_ident(self.span);
926        let rust_method_type_name = Id::from(method.name.to_camel_case()).to_ident(self.span);
927
928        // The generic parameters declared on the Java method.
929        let java_class_generics: Vec<_> = self.class_generic_names();
930
931        // The generic parameters we need on the Rust method, these include:
932        //
933        // * a type parameter for each java generic
934        // * any fresh generics we created to capture wildcards
935        let rust_method_generics = &sig.rust_generics;
936
937        // The generic parameters we need on the *method struct* (which will implement the `JvmOp`).
938        // These include the class generics plus all the generics from the method,
939        // plus a type parameter `a0` for each input.
940        let method_struct_generics: Vec<_> = java_class_generics
941            .iter()
942            .chain(rust_method_generics)
943            .chain(&input_names)
944            .collect();
945
946        // For each method `m` in the Java type, we create a struct (named `m`)
947        // that will implement the `JvmOp`.
948        let method_struct = quote_spanned!(self.span =>
949            pub struct #rust_method_type_name<
950                #(#method_struct_generics,)*
951            > {
952                #(#input_names : #input_names,)*
953                phantom: ::core::marker::PhantomData<(
954                    #(#method_struct_generics,)*
955                )>,
956            }
957        );
958
959        let sig_where_clauses = &sig.where_clauses;
960
961        // Implementation of `JvmOp` for `m` -- when executed, call the method
962        // via JNI, after converting its arguments appropriately.
963        let this_ty = self.this_type();
964        let jvmop_impl = quote_spanned!(self.span =>
965            impl<#(#method_struct_generics),*> ::core::clone::Clone
966            for #rust_method_type_name<#(#method_struct_generics),*>
967            where
968                #(#input_names: #jvm_op_traits,)*
969                #(#java_class_generics: duchess::JavaObject,)*
970                #(#sig_where_clauses,)*
971            {
972                fn clone(&self) -> Self {
973                    #rust_method_type_name {
974                        #(#input_names: Clone::clone(&self.#input_names),)*
975                        phantom: self.phantom,
976                    }
977                }
978            }
979
980            impl<#(#method_struct_generics),*> duchess::prelude::JvmOp
981            for #rust_method_type_name<#(#method_struct_generics),*>
982            where
983                #(#input_names: #jvm_op_traits,)*
984                #(#java_class_generics: duchess::JavaObject,)*
985                #(#sig_where_clauses,)*
986            {
987                type Output<'jvm> = #output_ty;
988
989                fn do_jni<'jvm>(
990                    self,
991                    jvm: &mut duchess::Jvm<'jvm>,
992                ) -> duchess::LocalResult<'jvm, Self::Output<'jvm>> {
993                    #(#prepare_inputs)*
994
995                    // Cache the method id for this method -- note that we only have one cache
996                    // no matter how many generic monomorphizations there are. This makes sense
997                    // given Java's erased-based generics system.
998                    static METHOD: duchess::plumbing::once_cell::sync::OnceCell<duchess::plumbing::MethodPtr> = duchess::plumbing::once_cell::sync::OnceCell::new();
999                    let method = METHOD.get_or_try_init(|| {
1000                        let class = <#this_ty as duchess::JavaObject>::class(jvm)?;
1001                        duchess::plumbing::find_method(jvm, &class, #jni_method, #jni_descriptor, true)
1002                    })?;
1003
1004                    let class = <#this_ty as duchess::JavaObject>::class(jvm)?;
1005                    unsafe {
1006                        jvm.env().invoke(|env| env.#jni_call_fn, |env, f| f(
1007                            env,
1008                            duchess::plumbing::JavaObjectExt::as_raw(&*class).as_ptr(),
1009                            method.as_ptr(),
1010                            [
1011                                #(duchess::plumbing::IntoJniValue::into_jni_value(#input_names),)*
1012                            ].as_ptr(),
1013                        ))
1014                    }
1015                }
1016            }
1017        );
1018
1019        // If we return a Java object, then deref to its op struct.
1020        // See [method docs] for more details.
1021        // [method docs]:https://duchess-rs.github.io/duchess/methods.html
1022        let deref_impl = java_ref_output_ty.map(|java_ref_output_ty| {
1023            quote_spanned!(self.span =>
1024                impl<#(#method_struct_generics),*> ::core::ops::Deref
1025                for #rust_method_type_name<#(#method_struct_generics),*>
1026                where
1027                    #(#java_class_generics: duchess::JavaObject,)*
1028                    #(#sig_where_clauses,)*
1029                {
1030                    type Target = <#java_ref_output_ty as duchess::plumbing::JavaView>::OfOp<Self>;
1031
1032                    fn deref(&self) -> &Self::Target {
1033                        <Self::Target as duchess::plumbing::FromRef<_>>::from_ref(self)
1034                    }
1035                }
1036            )
1037        });
1038
1039        let inherent_method = quote_spanned!(self.span =>
1040            pub fn #rust_method_name<#(#rust_method_generics),*>(
1041                #(#input_names: impl #input_traits),*
1042            ) -> impl #output_trait
1043            where
1044                #(#sig_where_clauses,)*
1045            {
1046                #method_struct
1047
1048                #jvmop_impl
1049
1050                #deref_impl
1051
1052                #rust_method_type_name {
1053                    #(#input_names: #input_names.into_op(),)*
1054                    phantom: ::core::default::Default::default(),
1055                }
1056            }
1057        );
1058
1059        Ok(inherent_method)
1060    }
1061
1062    /// Generates a static field getter that should be part of the inherent methods
1063    /// for the struct.
1064    ///
1065    /// NB. This function (particularly the JvmOp impl) has significant overlap with `object_method`
1066    /// and `static_method`, so if you make changes here, you may well need changes there.
1067    fn static_field_getter(&self, field: &Field) -> syn::Result<TokenStream> {
1068        assert!(field.flags.is_static);
1069
1070        let mut sig = Signature::new(&field.name, self.span, &self.generics);
1071
1072        let output_ty = sig.non_void_output_type(&field.ty)?;
1073        let output_trait = sig.field_trait(&field.ty)?;
1074        let jni_field_fn = sig.jni_static_field_get_fn(&field.ty)?;
1075
1076        let jni_field = jni_c_str(&*field.name, self.span);
1077        let jni_descriptor = jni_c_str(&field.ty.descriptor(&self.generics_scope()), self.span);
1078
1079        let rust_field_name =
1080            Id::from(format!("get_{}", field.name.to_snake_case())).to_ident(self.span);
1081        let rust_field_type_name =
1082            Id::from(format!("{}Getter", field.name.to_camel_case())).to_ident(self.span);
1083
1084        // The generic parameters declared on the Java method.
1085        let java_class_generics: Vec<_> = self.class_generic_names();
1086
1087        // The generic parameters we need on the *method struct* (which will implement the `JvmOp`).
1088        // These include the class generics plus all the generics from the method.
1089        let field_struct_generics: Vec<_> = java_class_generics.clone(); // XX: Unnecessary clone
1090
1091        // For each field `f` in the Java type, we create a struct (named `<f>Getter`)
1092        // that will implement the `JvmOp`.
1093        let field_struct = quote_spanned!(self.span =>
1094            pub struct #rust_field_type_name<
1095                #(#field_struct_generics,)*
1096            > {
1097                phantom: ::core::marker::PhantomData<(
1098                    #(#field_struct_generics,)*
1099                )>,
1100            }
1101        );
1102
1103        let sig_where_clauses = &sig.where_clauses;
1104
1105        // Implementation of `JvmOp` for `f` -- when executed, call the method
1106        // via JNI, after converting its arguments appropriately.
1107        let this_ty = self.this_type();
1108        let jvmop_impl = quote_spanned!(self.span =>
1109            impl<#(#field_struct_generics),*> duchess::prelude::JvmOp
1110            for #rust_field_type_name<#(#field_struct_generics),*>
1111            where
1112                #(#java_class_generics: duchess::JavaObject,)*
1113                #(#sig_where_clauses,)*
1114            {
1115                type Output<'jvm> = #output_ty;
1116
1117                fn do_jni<'jvm>(
1118                    self,
1119                    jvm: &mut duchess::Jvm<'jvm>,
1120                ) -> duchess::LocalResult<'jvm, Self::Output<'jvm>> {
1121
1122                    // Cache the field id for this field -- note that we only have one cache
1123                    // no matter how many generic monomorphizations there are. This makes sense
1124                    // given Java's erased-based generics system.
1125                    static FIELD: duchess::plumbing::once_cell::sync::OnceCell<duchess::plumbing::FieldPtr> = duchess::plumbing::once_cell::sync::OnceCell::new();
1126                    let field = FIELD.get_or_try_init(|| {
1127                        let class = <#this_ty as duchess::JavaObject>::class(jvm)?;
1128                        duchess::plumbing::find_field(jvm, &class, #jni_field, #jni_descriptor, true)
1129                    })?;
1130
1131                    let class = <#this_ty as duchess::JavaObject>::class(jvm)?;
1132                    unsafe {
1133                        jvm.env().invoke(|env| env.#jni_field_fn, |env, f| f(
1134                            env,
1135                            duchess::plumbing::JavaObjectExt::as_raw(&*class).as_ptr(),
1136                            field.as_ptr(),
1137                        ))
1138                    }
1139                }
1140            }
1141
1142            impl<#(#field_struct_generics),*> ::core::clone::Clone for #rust_field_type_name<#(#field_struct_generics),*>
1143            where
1144                #(#java_class_generics: duchess::JavaObject,)*
1145                #(#sig_where_clauses,)*
1146            {
1147                fn clone(&self) -> Self {
1148                    #rust_field_type_name {
1149                        phantom: self.phantom,
1150                    }
1151                }
1152            }
1153        );
1154
1155        let inherent_method = quote_spanned!(self.span =>
1156            pub fn #rust_field_name() -> impl #output_trait
1157            where
1158                #(#sig_where_clauses,)*
1159            {
1160                #field_struct
1161
1162                #jvmop_impl
1163
1164                #rust_field_type_name {
1165                    phantom: ::core::default::Default::default(),
1166                }
1167            }
1168        );
1169
1170        // useful for debugging
1171        // eprintln!("{inherent_method}");
1172
1173        Ok(inherent_method)
1174    }
1175
1176    fn struct_name(&self) -> Ident {
1177        self.name.class_name().to_ident(self.span)
1178    }
1179
1180    fn class_generic_names(&self) -> Vec<Ident> {
1181        self.generics
1182            .iter()
1183            .map(|g| g.to_ident(self.span))
1184            .collect()
1185    }
1186
1187    fn class_generic_names_with_defaults(&self) -> Vec<TokenStream> {
1188        self.class_generic_names()
1189            .into_iter()
1190            .map(|g| quote_spanned!(self.span => #g = java::lang::Object))
1191            .collect()
1192    }
1193
1194    fn this_type(&self) -> TokenStream {
1195        let s = self.struct_name();
1196        if self.generics.is_empty() {
1197            quote_spanned!(self.span => #s)
1198        } else {
1199            let g: Vec<Ident> = self.class_generic_names();
1200            quote_spanned!(self.span => #s < #(#g),* >)
1201        }
1202    }
1203
1204    /// Returns a class name with `/`, like `java/lang/Object` as a &CStr
1205    fn jni_class_name(&self) -> TokenStream {
1206        jni_c_str(self.name.to_jni_name(), self.span)
1207    }
1208
1209    fn prepare_inputs(&self, input_names: &[Ident], input_types: &[Type]) -> Vec<TokenStream> {
1210        input_names
1211            .iter()
1212            .zip(input_types)
1213            .map(|(input_name, input_ty)| match input_ty.to_non_repeating() {
1214                NonRepeatingType::Scalar(_) => quote_spanned!(self.span =>
1215                    let #input_name = self.#input_name.do_jni(jvm)?;
1216                ),
1217                NonRepeatingType::Ref(_) => {
1218                    quote_spanned!(self.span =>
1219                        let #input_name = self.#input_name.into_as_jref(jvm)?;
1220                        let #input_name = match duchess::prelude::AsJRef::as_jref(&#input_name) {
1221                            Ok(v) => Some(v),
1222                            Err(duchess::NullJRef) => None,
1223                        };
1224                    )
1225                }
1226            })
1227            .collect()
1228    }
1229}
1230
1231fn jni_c_str(contents: impl Into<String>, span: Span) -> TokenStream {
1232    let mut contents = contents.into().into_bytes();
1233    // \0 isn't valid UTF-8, so don't need to check that contents doesn't contain interior nul bytes.
1234    contents.push(0);
1235
1236    let byte_string = Literal::byte_string(&contents);
1237    quote_spanned!(span => unsafe { ::core::ffi::CStr::from_bytes_with_nul_unchecked(#byte_string) })
1238}