1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Implementation of the `call_method!` series of macros. These macros are meant to be used as
16 //! function-like macros and implement a statically type-safe way to call Java methods while also
17 //! caching the method id. The macro arguments are implemented in [`mod ast`](mod@ast) and the
18 //! generated code is implemented in [`mod codegen`](mod@codegen).
19 
20 use proc_macro2::TokenStream;
21 use syn::parse_quote;
22 
23 use ast::{ConstructorArgs, InstanceArgs, StaticArgs};
24 use codegen::{MethodCall, MethodInfo, Receiver};
25 
26 mod ast;
27 mod codegen;
28 
29 /// See [`crate::call_method!`] for usage.
call_method(args: TokenStream) -> syn::Result<TokenStream>30 pub fn call_method(args: TokenStream) -> syn::Result<TokenStream> {
31     let args = syn::parse2::<InstanceArgs>(args)?;
32 
33     let method_info = MethodInfo::new(args.cls, args.name, args.sig);
34     let receiver = Receiver::Instance(args.this);
35     let method_call = MethodCall::new(
36         args.env,
37         method_info,
38         receiver,
39         args.args.into_iter().collect(),
40     );
41 
42     method_call.generate().map_err(syn::Error::from)
43 }
44 
45 /// See [`crate::call_static_method!`] for usage.
call_static_method(args: TokenStream) -> syn::Result<TokenStream>46 pub fn call_static_method(args: TokenStream) -> syn::Result<TokenStream> {
47     let args = syn::parse2::<StaticArgs>(args)?;
48 
49     let method_info = MethodInfo::new(args.cls, args.name, args.sig);
50     let receiver = Receiver::Static;
51     let method_call = MethodCall::new(
52         args.env,
53         method_info,
54         receiver,
55         args.args.into_iter().collect(),
56     );
57 
58     method_call.generate().map_err(syn::Error::from)
59 }
60 
61 /// See [`crate::call_constructor!`] for usage.
call_constructor(args: TokenStream) -> syn::Result<TokenStream>62 pub fn call_constructor(args: TokenStream) -> syn::Result<TokenStream> {
63     let args = syn::parse2::<ConstructorArgs>(args)?;
64 
65     let name = parse_quote!["<init>"];
66     let method_info = MethodInfo::new(args.cls, name, args.sig);
67     let receiver = Receiver::Constructor;
68     let method_call = MethodCall::new(
69         args.env,
70         method_info,
71         receiver,
72         args.args.into_iter().collect(),
73     );
74 
75     method_call.generate().map_err(syn::Error::from)
76 }
77 
78 #[cfg(test)]
79 mod test {
80     use super::*;
81     use quote::quote;
82 
83     #[test]
call_method_error()84     fn call_method_error() {
85         let out = call_method(quote![&mut env, &CLS, "method", "INVALID", &this_obj]);
86         assert!(out.is_err());
87     }
88 
89     #[test]
call_static_method_error()90     fn call_static_method_error() {
91         let out = call_static_method(quote![&mut env, &CLS, "method", "INVALID"]);
92         assert!(out.is_err());
93     }
94 
95     #[test]
call_constructor_error()96     fn call_constructor_error() {
97         let out = call_constructor(quote![&mut env, &CLS, "INVALID"]);
98         assert!(out.is_err());
99     }
100 }
101