rust_jni/
generate.rs

1// TODO: improve these macros to exclude some repetition at call-site.
2// For example, investigate https://docs.rs/macro-attr.
3
4// TODO: use public rustdoc links for `rust-jni` types.
5
6// TODO(https://github.com/rust-lang/rust/issues/29599): use concat_idents!($class, Interface)
7// to not make the user specify the interface name.
8
9// TODO(https://github.com/rust-lang/rust/issues/29661): remove $super_super_class once default
10// associated types are stabilized.
11
12// TODO: invent some way to not repeat the interface declaration in class declarations.
13
14/// Generate Java interface wrapper.
15#[macro_export]
16macro_rules! java_interface {
17    (
18        interface = $interface:ident,
19        link = $link:expr,
20        extends = ($($($extended_interface:ident)::+),*),
21        methods = ($(
22            doc = $method_documentation:expr,
23            link = $method_link:expr,
24            java_name = $java_method_name:expr,
25            $method_name:ident
26                ($($method_argument_name:ident: $method_argument_type:ty),*)
27                -> $method_result:ty,
28        )*),
29    ) => {
30        /// Rust wrapper type for the
31        #[doc = $link]
32        /// Java interface.
33        pub trait $interface<'env>: $($($extended_interface)::* <'env>),* {
34            $(
35                #[doc = $method_documentation]
36                ///
37                #[doc = $method_link]
38                fn $method_name(
39                    &self,
40                    $($method_argument_name: $method_argument_type,)*
41                    token: &::rust_jni::NoException<'env>,
42                ) -> ::rust_jni::JavaResult<'env, $method_result>;
43            )*
44        }
45    };
46}
47
48/// Helper macro.
49///
50/// DO NOT USE MANUALLY!
51#[doc(hidden)]
52#[macro_export]
53macro_rules! __generate_super_interface {
54    (
55        class = $class:ident,
56        class_link = $class_link:expr,
57        interface_link = $implemented_interface_link:expr,
58        extends = $super_class:path,
59        name = $($implemented_interface:ident)::+,
60        name_path = $implemented_interface_path:path,
61        methods = ($(
62            $interface_method_name:ident
63                ($(
64                    $interface_method_argument_name:ident:
65                    $interface_method_argument_type:ty
66                ),*) -> $interface_method_result:ty,
67        )*),
68    ) => {
69        /// Implement
70        #[doc = $implemented_interface_link]
71        /// interface for
72        #[doc = $class_link]
73        ///.
74        impl<'env> $($implemented_interface)::* <'env> for $class<'env> {
75            $(
76                fn $interface_method_name(
77                    &self,
78                    $($interface_method_argument_name: $interface_method_argument_type),*,
79                    token: &::rust_jni::NoException<'env>,
80                ) -> ::rust_jni::JavaResult<'env, $interface_method_result> {
81                    <$super_class as $implemented_interface>
82                        ::$interface_method_name(
83                            self, $($interface_method_argument_name),*, token
84                        )
85                }
86            )*
87        }
88    };
89}
90
91/// Helper macro.
92///
93/// DO NOT USE MANUALLY!
94#[macro_export]
95#[doc(hidden)]
96macro_rules! __generate_super_interfaces {
97    (
98        class = $class:ident,
99        class_link = $class_link:expr,
100        extends = $super_class:path,
101        interfaces = ($(
102            link = $implemented_interface_link:expr,
103            name = $($implemented_interface:ident)::+,
104            methods = ($(
105                $interface_method_name:ident
106                    ($(
107                        $interface_method_argument_name:ident:
108                        $interface_method_argument_type:ty
109                    ),*) -> $interface_method_result:ty,
110            )*),
111        )*),
112    ) => {
113        $(
114            __generate_super_interface!(
115                class = $class,
116                class_link = $class_link,
117                interface_link = $implemented_interface_link,
118                extends = $super_class,
119                name = $($implemented_interface)::+,
120                name_path = $($implemented_interface)::+,
121                methods = ($(
122                    $interface_method_name
123                        ($(
124                            $interface_method_argument_name:
125                            $interface_method_argument_type
126                        ),*) -> $interface_method_result,
127                )*),
128            );
129        )*
130    };
131}
132
133/// Generate Java class wrapper.
134#[macro_export]
135macro_rules! java_class {
136    (
137        package = $package:expr,
138        class = $class:ident,
139        link = $java_link:expr,
140        rust_link = $rust_link:expr,
141        extends = $($super_class:ident)::+,
142        super_link = $rust_super_link:expr,
143        implements = ($(
144            name = $($implemented_interface:ident)::+,
145            link = $implemented_interface_link:expr,
146            methods = ($(
147                $interface_method_name:ident
148                    ($(
149                        $interface_method_argument_name:ident:
150                        $interface_method_argument_type:ty
151                    ),*) -> $interface_method_result:ty,
152            )*),
153        )*),
154        constructors = ($(
155            doc = $constructor_documentation:expr,
156            link = $constructor_link:expr,
157            $constructor_name:ident
158                ($($constructor_argument_name:ident: $constructor_argument_type:ty),*),
159        )*),
160        methods = ($(
161            doc = $method_documentation:expr,
162            link = $method_link:expr,
163            java_name = $java_method_name:expr,
164            $method_name:ident
165                ($($method_argument_name:ident: $method_argument_type:ty),*)
166                -> $method_result:ty,
167        )*),
168        static_methods = ($(
169            doc = $static_method_documentation:expr,
170            link = $static_method_link:expr,
171            java_name = $java_static_method_name:expr,
172            $static_method_name:ident
173                ($($static_method_argument_name:ident: $static_method_argument_type:ty),*)
174                -> $static_method_result:ty,
175        )*),
176        native_methods = ($(
177            function_name = $native_function_name:ident,
178            $native_method_name:ident
179                ($($native_method_argument_name:ident: $native_method_argument_type:ty),*)
180                -> $native_method_result:ty,
181        )*),
182        static_native_methods = ($(
183            function_name = $static_native_function_name:ident,
184            $static_native_method_name:ident
185                ($($static_native_method_argument_name:ident: $static_native_method_argument_type:ty),*)
186                -> $static_native_method_result:ty,
187        )*),
188        super_classes = ($(
189            $($super_super_class:ident)::+,
190            link = $super_super_class_link:expr
191        ),*),
192        super_interfaces = ($(
193            name = $($implemented_super_interface:ident)::+,
194            link = $implemented_super_interface_link:expr,
195            methods = ($(
196                $super_interface_method_name:ident
197                    ($(
198                        $super_interface_method_argument_name:ident:
199                        $super_interface_method_argument_type:ty
200                    ),*) -> $super_interface_method_result:ty,
201            )*),
202        )*),
203    ) => {
204        /// Rust wrapper type for the
205        #[doc = $java_link]
206        /// Java class.
207        #[derive(Debug)]
208        pub struct $class<'env> {
209            object: $($super_class)::* <'env>,
210        }
211
212        // Tools for mapping Rust and JNI types.
213
214        /// Make
215        #[doc = $rust_link]
216        /// mappable to [`jobject`](https://docs.rs/jni-sys/0.3.0/jni_sys/type.jobject.html).
217        impl<'a> ::rust_jni::JavaType for $class<'a> {
218            #[doc(hidden)]
219            type __JniType = <::rust_jni::java::lang::Object<'a> as ::rust_jni::JavaType>::__JniType;
220
221            #[doc(hidden)]
222            fn __signature() -> &'static str {
223                concat!("L", $package, "/", stringify!($class), ";")
224            }
225        }
226
227        /// Make
228        #[doc = $rust_link]
229        /// convertible to [`jobject`](https://docs.rs/jni-sys/0.3.0/jni_sys/type.jobject.html).
230        #[doc(hidden)]
231        impl<'a> ::rust_jni::__generator::ToJni for $class<'a> {
232            unsafe fn __to_jni(&self) -> Self::__JniType {
233                self.raw_object()
234            }
235        }
236
237        /// Make
238        #[doc = $rust_link]
239        /// convertible from [`jobject`](https://docs.rs/jni-sys/0.3.0/jni_sys/type.jobject.html).
240        #[doc(hidden)]
241        impl<'env> ::rust_jni::__generator::FromJni<'env> for $class<'env> {
242            unsafe fn __from_jni(env: &'env ::rust_jni::JniEnv<'env>, value: Self::__JniType) -> Self {
243                Self {
244                    object: <$($super_class)::* as ::rust_jni::__generator::FromJni<'env>>::__from_jni(env, value),
245                }
246            }
247        }
248
249        // Tools for the object hierarchy.
250
251        /// Make
252        #[doc = $rust_link]
253        /// castable to itself.
254        impl<'env> ::rust_jni::Cast<'env, $class<'env>> for $class<'env> {
255            #[doc(hidden)]
256            fn cast<'a>(&'a self) -> &'a $class<'env> {
257                self
258            }
259        }
260
261        /// Make
262        #[doc = $rust_link]
263        /// castable to it's superclass
264        #[doc = $rust_super_link]
265        ///.
266        impl<'env> ::rust_jni::Cast<'env, $($super_class)::*<'env>> for $class<'env> {
267            #[doc(hidden)]
268            fn cast<'a>(&'a self) -> &'a $($super_class)::*<'env> {
269                self
270            }
271        }
272
273        $(
274            /// Make
275            #[doc = $rust_link]
276            /// castable to it's superclass
277            #[doc = $super_super_class_link]
278            ///.
279            impl<'env> ::rust_jni::Cast<'env, $($super_super_class)::* <'env>> for $class<'env> {
280                #[doc(hidden)]
281                fn cast<'a>(&'a self) -> &'a $($super_super_class)::* <'env> {
282                    self
283                }
284            }
285        )*
286
287        /// Allow
288        #[doc = $rust_link]
289        /// to be used in place of it's superclass
290        #[doc = $rust_super_link]
291        ///.
292        ///
293        /// Since all Java class wrappers implement
294        /// [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html), to superclass,
295        /// this works transitively for all parent classes.
296        impl<'env> ::std::ops::Deref for $class<'env> {
297            type Target = $($super_class)::* <'env>;
298
299            fn deref(&self) -> &Self::Target {
300                &self.object
301            }
302        }
303
304        impl<'env> $class<'env> {
305            // Non-Java class methods.
306
307            /// Get the Java class object for
308            #[doc = $rust_link]
309            ///.
310            ///
311            /// [`Object::getClass` javadoc](https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#getClass())
312            pub fn get_class(env: &'env ::rust_jni::JniEnv<'env>, token: &::rust_jni::NoException<'env>)
313                -> ::rust_jni::JavaResult<'env, ::rust_jni::java::lang::Class<'env>> {
314                ::rust_jni::java::lang::Class::find(env, concat!($package, "/", stringify!($class)), token)
315            }
316
317            /// Clone the
318            #[doc = $rust_link]
319            ///. This is not a deep clone of the Java object,
320            /// but a Rust-like clone of the value. Since Java objects are reference counted, this
321            /// will increment the reference count.
322            ///
323            /// This method has a different signature from the one in the
324            /// [`Clone`](https://doc.rust-lang.org/nightly/core/clone/trait.Clone.html) trait
325            /// because cloning a Java object is only safe when there is no pending exception and
326            /// because cloning a java object cat throw an exception.
327            ///
328            /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#newlocalref)
329            pub fn clone(&self, token: &::rust_jni::NoException<'env>) -> ::rust_jni::JavaResult<'env, Self>
330            where
331                Self: Sized,
332            {
333                <Self as ::rust_jni::Cast<$($super_class)::*>>::cast(self)
334                    .clone(token)
335                    .map(|object| Self { object })
336            }
337
338            /// Convert the object to a string.
339            ///
340            /// [`Object::toString` javadoc](https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#toString())
341            //
342            // This function is needed because Java classes implement
343            // [`ToString`](https://doc.rust-lang.org/std/string/trait.ToString.html) trait through
344            // the [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html) trait, which
345            // has a
346            // [`to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string)
347            // method which takes precedence over methods inherited from
348            // [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html)-s.
349            pub fn to_string(&self, token: &::rust_jni::NoException<'env>)
350                -> ::rust_jni::JavaResult<'env, ::rust_jni::java::lang::String<'env>> {
351                <$class as ::rust_jni::Cast<::rust_jni::java::lang::Object>>::cast(self).to_string(token)
352            }
353
354            // Declared methods.
355
356            $(
357                #[doc = $constructor_documentation]
358                ///
359                #[doc = $constructor_link]
360                pub fn $constructor_name(
361                    env: &'env ::rust_jni::JniEnv<'env>,
362                    $($constructor_argument_name: $constructor_argument_type,)*
363                    token: &::rust_jni::NoException<'env>,
364                ) -> ::rust_jni::JavaResult<'env, Self> {
365                    // Safe because method arguments are correct.
366                    unsafe {
367                        ::rust_jni::__generator::call_constructor::<Self, _, fn($($constructor_argument_type,)*)>
368                        (
369                            env,
370                            ($($constructor_argument_name,)*),
371                            token,
372                        )
373                    }
374                }
375            )*
376
377            $(
378                #[doc = $method_documentation]
379                ///
380                #[doc = $method_link]
381                pub fn $method_name(
382                    &self,
383                    $($method_argument_name: $method_argument_type,)*
384                    token: &::rust_jni::NoException<'env>,
385                ) -> ::rust_jni::JavaResult<'env, $method_result> {
386                    // Safe because the method name and arguments are correct.
387                    unsafe {
388                        ::rust_jni::__generator::call_method::<_, _, _,
389                            fn($($method_argument_type,)*) -> $method_result
390                        >
391                        (
392                            self,
393                            $java_method_name,
394                            ($($method_argument_name,)*),
395                            token,
396                        )
397                    }
398                }
399            )*
400
401            $(
402                #[doc = $static_method_documentation]
403                ///
404                #[doc = $static_method_link]
405                pub fn $static_method_name(
406                    env: &'env ::rust_jni::JniEnv<'env>,
407                    $($static_method_argument_name: $static_method_argument_type,)*
408                    token: &::rust_jni::NoException<'env>,
409                ) -> ::rust_jni::JavaResult<'env, $static_method_result> {
410                    // Safe because the method name and arguments are correct.
411                    unsafe {
412                        ::rust_jni::__generator::call_static_method::<Self, _, _,
413                            fn($($static_method_argument_type,)*) -> $static_method_result
414                        >
415                        (
416                            env,
417                            $java_static_method_name,
418                            ($($static_method_argument_name,)*),
419                            token,
420                        )
421                    }
422                }
423            )*
424        }
425
426        $(
427            /// Implement
428            #[doc = $implemented_interface_link]
429            /// interface for
430            #[doc = $rust_link]
431            ///.
432            impl<'env> $($implemented_interface)::* <'env> for $class<'env> {
433                $(
434                    fn $interface_method_name(
435                        &self,
436                        $($interface_method_argument_name: $interface_method_argument_type),*,
437                        token: &::rust_jni::NoException<'env>,
438                    ) -> ::rust_jni::JavaResult<'env, $interface_method_result> {
439                            Self::$interface_method_name(
440                                self, $($interface_method_argument_name),*, token
441                            )
442                    }
443                )*
444            }
445        )*
446
447        __generate_super_interfaces!(
448            class = $class,
449            class_link = $rust_link,
450            extends = $($super_class)::+,
451            interfaces = ($(
452                link = $implemented_super_interface_link,
453                name = $($implemented_super_interface)::+,
454                methods = ($(
455                    $super_interface_method_name
456                        ($(
457                            $super_interface_method_argument_name:
458                            $super_interface_method_argument_type
459                        ),*) -> $super_interface_method_result,
460                )*),
461            )*),
462        );
463
464        // Native method stubs.
465
466        $(
467            #[no_mangle]
468            #[doc(hidden)]
469            pub unsafe extern "C" fn $native_function_name(
470                raw_env: *mut ::jni_sys::JNIEnv,
471                object: ::jni_sys::jobject,
472                $($native_method_argument_name: <$native_method_argument_type as ::rust_jni::JavaType>::__JniType,)*
473            ) -> <$native_method_result as ::rust_jni::JavaType>::__JniType {
474                // TODO: make sure `$native_method_result: ::rust_jni::__generator::FromJni`.
475                // Compile-time check that declared arguments implement the `JniArgumentType`
476                // trait.
477                $(::rust_jni::__generator::test_jni_argument_type($native_method_argument_name);)*
478                ::rust_jni::__generator::native_method_wrapper(raw_env, |env, token| {
479                    // Compile-time check that declared arguments implement the `FromJni` trait.
480                    $(
481                        {
482                            let value =
483                                <$native_method_argument_type as ::rust_jni::__generator::FromJni>
484                                    ::__from_jni(env, $native_method_argument_name);
485                            ::rust_jni::__generator::test_from_jni_type(&value);
486                            ::std::mem::forget(value);
487                        }
488                    )*
489
490                    let object = <$class as ::rust_jni::__generator::FromJni>::__from_jni(env, object);
491                    object
492                        .$native_method_name(
493                            $(::rust_jni::__generator::FromJni::__from_jni(env, $native_method_argument_name),)*
494                            &token,
495                        )
496                        .map(|value| {
497                            let result = ::rust_jni::__generator::ToJni::__to_jni(&value);
498                            // We don't want to delete the reference to result for object results.
499                            ::std::mem::forget(value);
500                            result
501                        })
502                })
503            }
504        )*
505
506        $(
507            #[no_mangle]
508            #[doc(hidden)]
509            pub unsafe extern "C" fn $static_native_function_name(
510                raw_env: *mut ::jni_sys::JNIEnv,
511                raw_class: ::jni_sys::jclass,
512                $($static_native_method_argument_name: <$static_native_method_argument_type as ::rust_jni::JavaType>::__JniType,)*
513            ) -> <$static_native_method_result as ::rust_jni::JavaType>::__JniType {
514                // TODO: make sure `$native_method_result: ::rust_jni::__generator::FromJni`.
515                // Compile-time check that declared arguments implement the `JniArgumentType`
516                // trait.
517                $(::rust_jni::__generator::test_jni_argument_type($static_native_method_argument_name);)*
518                ::rust_jni::__generator::native_method_wrapper(raw_env, |env, token| {
519                    // Compile-time check that declared arguments implement the `FromJni` trait.
520                    $(
521                        {
522                            let value =
523                                <$static_native_method_argument_type as ::rust_jni::__generator::FromJni>
524                                    ::__from_jni(env, $static_native_method_argument_name);
525                            ::rust_jni::__generator::test_from_jni_type(&value);
526                            ::std::mem::forget(value);
527                        }
528                    )*
529
530                    let class = $class::get_class(env, &token)?;
531                    let raw_class = <::rust_jni::java::lang::Class as ::rust_jni::__generator::FromJni>::__from_jni(env, raw_class);
532                    if !class.is_same_as(&raw_class, &token) {
533                        // This should never happen, as native method's link name has the class,
534                        // so it must be bound to a correct clas by the JVM.
535                        // Still, this is a good test to ensure that the system
536                        // is in a consistent state.
537                        panic!(concat!(
538                            "Native method ",
539                            stringify!($static_native_function_name),
540                            " does not belong to class ",
541                            $package, "/", stringify!($class),
542                        ));
543                    }
544
545                    $class::$static_native_method_name(
546                        env,
547                        $(::rust_jni::__generator::FromJni::__from_jni(env, $static_native_method_argument_name),)*
548                        &token,
549                    )
550                    .map(|value| {
551                        let result = ::rust_jni::__generator::ToJni::__to_jni(&value);
552                        // We don't want to delete the reference to result for object results.
553                        ::std::mem::forget(value);
554                        result
555                    })
556                })
557            }
558        )*
559
560        // Common traits for convenience.
561
562        /// Allow displaying
563        #[doc = $rust_link]
564        ///.
565        ///
566        /// [`Object::toString` javadoc](https://docs.oracle.com/javase/10/docs/api/java/lang/Object.html#toString())
567        ///
568        /// This is mostly a convenience for debugging. Always prefer using
569        /// [`to_string`](struct.Object.html#methods.to_string) to printing the object as is, because
570        /// the former checks for a pending exception in compile-time rather than the run-time.
571        impl<'env> ::std::fmt::Display for $class<'env> {
572            fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
573                <$class as ::rust_jni::Cast<::rust_jni::java::lang::Object>>::cast(self)
574                    .fmt(formatter)
575            }
576        }
577
578        /// Allow comparing
579        #[doc = $rust_link]
580        /// to Java objects. Java objects are compared by-reference to preserve
581        /// original Java semantics. To compare objects by value, call the
582        /// [`equals`](struct.Object.html#method.equals) method.
583        ///
584        /// Will panic if there is a pending exception in the current thread.
585        ///
586        /// This is mostly a convenience for using `assert_eq!()` in tests. Always prefer using
587        /// [`is_same_as`](struct.Object.html#methods.is_same_as) to comparing with `==`, because
588        /// the former checks for a pending exception in compile-time rather than the run-time.
589        impl<'env, T> PartialEq<T> for $class<'env> where T: ::rust_jni::Cast<'env, ::rust_jni::java::lang::Object<'env>> {
590            fn eq(&self, other: &T) -> bool {
591                <$class as ::rust_jni::Cast<::rust_jni::java::lang::Object>>::cast(self)
592                    .eq(other)
593            }
594        }
595
596        /// Allow comparing
597        #[doc = $rust_link]
598        /// to Java objects. Java objects are compared by-reference to preserve
599        /// original Java semantics. To compare objects by value, call the
600        /// [`equals`](struct.Object.html#method.equals) method.
601        ///
602        /// Will panic if there is a pending exception in the current thread.
603        ///
604        /// This is mostly a convenience for using `assert_eq!()` in tests. Always prefer using
605        /// [`is_same_as`](struct.Object.html#methods.is_same_as) to comparing with `==`, because
606        /// the former checks for a pending exception in compile-time rather than the run-time.
607        impl<'env> Eq for $class<'env> {}
608    };
609}