droid_wrap_derive/
lib.rs

1/*
2 * Copyright (c) 2024. The RigelA open source project team and
3 * its contributors reserve all rights.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and limitations under the License.
12 */
13
14mod utils;
15
16use proc_macro::TokenStream;
17
18use heck::ToLowerCamelCase;
19use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
20use quote::{quote, ToTokens};
21use syn::{
22    parse2, parse_macro_input, punctuated::Punctuated, Field, FieldMutability, Fields, FieldsNamed,
23    ImplItem, ItemFn, ItemImpl, ItemStruct, ItemTrait, LitInt, Token, TraitItem, Type,
24    TypeParamBound, Visibility,
25};
26
27use crate::utils::{get_object_return_value_token, get_result_token, get_return_value_token, parse_function_signature, ClassMetadata, FieldMetadata, InterfaceMetadata, MethodMetadata};
28
29//noinspection SpellCheckingInspection
30/// 定义java class,将此属性标记在struct上,可以自动实现操作java对象的必要功能。
31///
32/// # Arguments
33///
34/// * `attrs`: 属性输入。
35/// * `input`: struct输入。
36///
37/// returns: TokenStream
38///
39/// # Examples
40///
41/// ```
42/// use droid_wrap_derive::java_class;
43/// #[java_class(name = "java/lang/System")]
44/// struct System;
45/// ```
46#[proc_macro_attribute]
47pub fn java_class(attrs: TokenStream, input: TokenStream) -> TokenStream {
48    let attrs: ClassMetadata = parse2(Into::<TokenStream2>::into(attrs)).unwrap();
49    let cls = attrs.class_name;
50    let based = attrs.base_class;
51    let mut item = parse_macro_input!(input as ItemStruct);
52    let name = item.ident.clone();
53    let generics = item.generics.clone();
54    let mut item2 = item.clone();
55
56    let mut add_field_this = Field {
57        attrs: vec![],
58        vis: Visibility::Inherited,
59        mutability: FieldMutability::None,
60        ident: Some(Ident::new("_this", Span::call_site())),
61        colon_token: None,
62        ty: Type::Verbatim(quote! {droid_wrap_utils::GlobalRef}),
63    };
64    let mut add_field_super = Field {
65        attrs: vec![],
66        vis: Visibility::Inherited,
67        mutability: FieldMutability::None,
68        ident: Some(Ident::new("_based", Span::call_site())),
69        colon_token: None,
70        ty: Type::Verbatim(based.to_token_stream()),
71    };
72
73    let (fields, added_this, added_super, added_default) = match item.fields {
74        Fields::Named(mut f) => {
75            let mut added_default = TokenStream2::new();
76            for i in f.named.iter() {
77                let i = i.ident.clone();
78                added_default.extend(quote! {#i: fields.#i,});
79            }
80            f.named.push(add_field_this);
81            let added_super = if based.is_some() {
82                f.named.push(add_field_super);
83                quote! {_based}
84            } else {
85                quote!()
86            };
87            (Fields::Named(f), quote! {_this}, added_super, added_default)
88        }
89        Fields::Unnamed(mut f) => {
90            let mut added_default = TokenStream2::new();
91            for i in 0..f.unnamed.len() {
92                let i = LitInt::new(&i.to_string(), Span::call_site());
93                added_default.extend(quote! {#i: fields.#i,});
94            }
95            add_field_this.ident = None;
96            add_field_super.ident = None;
97            let len = LitInt::new(&f.unnamed.len().to_string(), Span::call_site());
98            let added_this = quote! {#len};
99            f.unnamed.push(add_field_this);
100            let added_super = if based.is_some() {
101                let len = LitInt::new(&f.unnamed.len().to_string(), Span::call_site());
102                f.unnamed.push(add_field_super);
103                quote! {#len}
104            } else {
105                quote!()
106            };
107            (Fields::Unnamed(f), added_this, added_super, added_default)
108        }
109        Fields::Unit => {
110            let mut fields = Punctuated::<Field, Token![,]>::new();
111            fields.push(add_field_this);
112            let added_super = if based.is_some() {
113                fields.push(add_field_super);
114                quote! {_based}
115            } else {
116                quote!()
117            };
118            (
119                Fields::Named(FieldsNamed {
120                    brace_token: Default::default(),
121                    named: fields,
122                }),
123                quote! {_this},
124                added_super,
125                quote!(),
126            )
127        }
128    };
129    item.fields = fields;
130
131    let build_self = if based.is_some() {
132        quote! {
133            Self {#added_this: this.clone(), #added_super: this.into(), #added_default }
134        }
135    } else {
136        quote! {
137            Self {#added_this:this.clone(), #added_default }
138        }
139    };
140
141    let (item2_token, name2) = if added_default.is_empty() {
142        (quote!(), quote! {()})
143    } else {
144        let name2 = Ident::new((name.to_string() + "Default").as_str(), Span::call_site());
145        item2.ident = name2.clone();
146        (quote! {#item2}, quote! {#name2})
147    };
148
149    let impl_based_deref = if based.is_some() {
150        quote! {
151            impl #generics std::ops::Deref for #name #generics {
152                type Target = #based;
153
154                fn deref(&self) -> &Self::Target {
155                    &self.#added_super
156                }
157            }
158
159            impl #generics std::ops::DerefMut for #name #generics {
160                fn deref_mut(&mut self) -> &mut Self::Target {
161                    &mut self.#added_super
162                }
163            }
164        }
165    } else {
166        quote! {
167            impl #generics std::ops::Deref for #name #generics {
168                type Target = droid_wrap_utils::GlobalRef;
169
170                fn deref(&self) -> &Self::Target {
171                    &self.#added_this
172                }
173            }
174        }
175    };
176
177    let stream = quote! {
178        #item
179        #item2_token
180
181        impl #generics JObjNew for #name #generics {
182            type Fields = #name2;
183
184            fn _new(this: &droid_wrap_utils::GlobalRef, fields: Self::Fields) -> Self {
185                #build_self
186            }
187        }
188
189        impl #generics JType for #name #generics {
190            type Error = droid_wrap_utils::Error;
191            const CLASS: &'static str = #cls;
192            const OBJECT_SIG: &'static str = concat!("L", #cls, ";");
193        }
194
195        impl #generics JObjRef for #name #generics {
196            fn java_ref(&self) -> droid_wrap_utils::GlobalRef {
197                self.#added_this.clone()
198            }
199        }
200
201        impl #generics PartialEq for #name #generics {
202            fn eq(&self, other: &Self) -> bool {
203                let r = self.java_ref();
204                let t = other.java_ref();
205                if r.is_null() && t.is_null() {
206                    return true;
207                }
208                if r.is_null() {
209                    return false;
210                }
211                droid_wrap_utils::vm_attach!(mut env);
212                env.call_method(
213                    r.clone(),
214                    "equals",
215                    "(Ljava/lang/Object;)Z",
216                    &[t.as_obj().into()]
217                ).unwrap().z().unwrap()
218            }
219    }
220
221        impl #generics ToString for #name #generics {
222            fn to_string(&self) -> String {
223                let r = self.java_ref();
224                if r.is_null() {
225                    return "null".to_string();
226                }
227                droid_wrap_utils::vm_attach!(mut env);
228                let s = match env.call_method(r.clone(), "toString", format!("()L{};", String::CLASS).as_str(), &[]) {
229                    Ok(s) => match s.l() {
230                        Ok(s) => s,
231                        Err(e) => return e.to_string()
232                    },
233                    Err(e) => return e.to_string()
234                };
235                let s = match env.get_string((&s).into()) {
236                    Ok(s) => s,
237                    Err(e) => return e.to_string()
238                };
239                s.to_str().unwrap().to_string()
240            }
241        }
242
243        impl #generics std::fmt::Debug for #name #generics {
244            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245                write!(f, "{}", self.to_string())
246            }
247        }
248
249        impl #generics From<&droid_wrap_utils::GlobalRef> for #name #generics {
250            fn from(obj: &droid_wrap_utils::GlobalRef) -> Self {
251                Self::_new(&obj, <Self as JObjNew>::Fields::default())
252            }
253        }
254
255        impl #generics Into<droid_wrap_utils::GlobalRef> for &#name #generics {
256            fn into(self) -> droid_wrap_utils::GlobalRef {
257                self.java_ref()
258            }
259        }
260
261        #impl_based_deref
262    };
263    stream.into()
264}
265
266/// 实现java类的方法,将此属性标记在fn函数上,可以自动实现调用java方法,可以自动识别静态方法(如果参数中没有“self”)。
267///
268/// # Arguments
269///
270/// * `attrs`: 属性输入。
271/// * `input`: 函数输入。
272///
273/// returns: TokenStream
274///
275/// # Examples
276///
277/// ```
278/// use droid_wrap_derive::java_method;
279/// struct System;
280/// impl System {
281/// #[java_method]
282/// fn current_time_millis() -> i64 {}
283/// }
284/// ```
285#[proc_macro_attribute]
286pub fn java_method(attrs: TokenStream, input: TokenStream) -> TokenStream {
287    let attrs: MethodMetadata = parse2(Into::<TokenStream2>::into(attrs)).unwrap();
288    let type_bounds = attrs.type_bounds;
289    let overload = attrs.overload;
290    let item = parse_macro_input!(input as ItemFn);
291    let attrs = item.attrs.clone();
292    let stmts = item.block.stmts.clone();
293    let name = if overload.is_none() {
294        item.sig.ident.to_string()
295    } else {
296        overload.unwrap().to_token_stream().to_string()
297    }
298    .to_lower_camel_case();
299    let vis = item.vis.clone();
300    let sig = item.sig.clone();
301
302    let (self_, _, arg_types_sig, fmt, arg_values, ret_type) =
303        parse_function_signature(&sig, &type_bounds);
304    let (ret_value, ret_type_sig, is_result_type) =
305        get_return_value_token(&ret_type, &sig.generics, &type_bounds, &None);
306
307    let ret_value = get_result_token(is_result_type, &ret_value);
308
309    let class_token = if let Some(it) = type_bounds.iter().find(|i| i.0.to_string() == "Self") {
310        let tt = it.1.clone();
311        quote! {<Self as #tt>::CLASS}
312    } else {
313        quote! {Self::CLASS}
314    };
315
316    if self_.is_none() {
317        quote! {
318            #(#attrs)*
319            #vis #sig {
320                #(#stmts)*
321                droid_wrap_utils::vm_attach!(mut env);
322                let ret = env.call_static_method(
323                    #class_token,
324                    #name,
325                    format!(#fmt, #arg_types_sig #ret_type_sig).as_str(),
326                    &[#arg_values],
327                );
328                #ret_value
329            }
330        }
331    } else {
332        quote! {
333            #(#attrs)*
334            #vis #sig {
335                #(#stmts)*
336                droid_wrap_utils::vm_attach!(mut env);
337                let ret = env.call_method(
338                    #self_.java_ref(),
339                    #name,
340                    format!(#fmt, #arg_types_sig #ret_type_sig).as_str(),
341                    &[#arg_values],
342                );
343                #ret_value
344            }
345        }
346    }
347    .into()
348}
349
350/// 实现java类的构造器,将此属性标记在fn函数上,可以自动实现调用java类的构造器。
351///
352/// # Arguments
353///
354/// * `_`: 未使用。
355/// * `input`: 函数输入。
356///
357/// returns: TokenStream
358///
359/// # Examples
360///
361/// ```
362/// use droid_wrap_derive::java_constructor;
363/// struct Integer;
364/// impl Integer {
365/// #[java_constructor]
366/// fn new(value: i32) -> Self {}
367/// }
368/// ```
369#[proc_macro_attribute]
370pub fn java_constructor(_: TokenStream, input: TokenStream) -> TokenStream {
371    let item = parse_macro_input!(input as ItemFn);
372    let attrs = item.attrs.clone();
373    let vis = item.vis.clone();
374    let sig = item.sig.clone();
375    let stmts = item.block.stmts.clone();
376    let (self_, _, arg_types, fmt, arg_values, ret_type) = parse_function_signature(&sig, &vec![]);
377
378    if !self_.is_none() {
379        panic!(
380            "Incorrect constructor, please remove the '{}' in the arguments!",
381            self_.to_token_stream()
382        );
383    }
384
385    if !ret_type.to_string().contains("Self") {
386        panic!(
387            "Incorrect constructor, please modify the '{}' to 'Self', 'Option<Self>' or 'Result<Self, Self::Error>' in the return value!",
388            ret_type
389        );
390    }
391
392    let (ret_value, _) = get_object_return_value_token(&ret_type);
393
394    let stream = quote! {
395        #(#attrs)*
396        #vis #sig {
397            #(#stmts)*
398            droid_wrap_utils::vm_attach!(mut env);
399            let obj = env.new_object(
400                <Self as JType>::CLASS,
401                format!(#fmt, #arg_types "V").as_str(),
402                &[#arg_values],
403            )
404            .unwrap();
405            #ret_value
406        }
407    };
408
409    stream.into()
410}
411
412/// 定义java interface,将此属性标记在trait上,可以自动实现提供java对象与rust对象的互操作的功能。
413///
414/// # Arguments
415///
416/// * `attrs`: 属性。
417/// * `input`: 特征输入。
418///
419/// returns: TokenStream
420///
421/// # Examples
422///
423/// ```
424/// use droid_wrap_derive::java_interface;
425/// trait Runnable {
426/// fn run(&self);
427/// }
428/// ```
429#[proc_macro_attribute]
430pub fn java_interface(attrs: TokenStream, input: TokenStream) -> TokenStream {
431    let attrs: InterfaceMetadata = parse2(Into::<TokenStream2>::into(attrs)).unwrap();
432    let cls = attrs.interface_name;
433    let mut item = parse_macro_input!(input as ItemTrait);
434    item.supertraits
435        .push(TypeParamBound::Verbatim(quote! {JObjRef}));
436    item.supertraits
437        .push(TypeParamBound::Verbatim(quote! {JObjNew}));
438    item.supertraits
439        .push(TypeParamBound::Verbatim(quote! {PartialEq}));
440    item.supertraits
441        .push(TypeParamBound::Verbatim(quote! {std::fmt::Debug}));
442
443    item.items.push(TraitItem::Verbatim(quote! {
444        #[doc = #cls]
445        const CLASS: &'static str = #cls;
446    }));
447    item.items.push(TraitItem::Verbatim(quote! {
448        #[doc = concat!("L", #cls, ";")]
449        const OBJECT_SIG: &'static str = concat!("L", #cls, ";");
450    }));
451    item.items.push(TraitItem::Verbatim(quote! {
452        /// 数组维度
453        const DIM: u8 = 0;
454    }));
455    let stream = quote! {
456        #item
457    };
458    stream.into()
459}
460
461//noinspection SpellCheckingInspection
462/// 实现java interface,将此属性标记在impl上,可以自动实现java接口的动态代理,从而实现java层回调rust层。
463/// 其中在接口中定义的每一个方法将自动实现并暴露给java层,但以下划线“_”开头的函数除外。
464///
465/// # Arguments
466///
467/// * `attrs`: 属性。
468/// * `input`: impl输入。
469///
470/// returns: TokenStream
471///
472/// # Examples
473///
474/// ```
475/// use std::fmt::{Debug, Formatter};
476/// use droid_wrap_derive::{java_interface,java_implement};
477/// #[java_interface(name = "java/lang/Runnable")]
478/// trait Runnable {
479/// fn run(&self);
480/// }
481/// struct RunnableImpl;
482/// impl PartialEq for RunnableImpl {
483///     fn eq(&self, other: &Self) -> bool {
484///         todo!()
485///     }
486/// }
487/// impl Debug for RunnableImpl {
488///     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
489///         todo!()
490///     }
491/// }
492/// #[java_implement]
493/// impl Runnable for RunnableImpl {
494/// fn run(&self) {
495/// println!("Called from java.");
496/// }
497/// }
498/// ```
499#[proc_macro_attribute]
500pub fn java_implement(attrs: TokenStream, input: TokenStream) -> TokenStream {
501    let item: TokenStream2 = input.into();
502    let attrs: TokenStream2 = attrs.into();
503
504    let item = parse2::<ItemImpl>(item.clone()).unwrap();
505
506    let mut methods = TokenStream2::new();
507    for item in item.items.iter() {
508        match item {
509            ImplItem::Fn(f) => {
510                let name = f.sig.ident.clone();
511                if name.to_string().starts_with("_") {
512                    // 跳过下划线开头的函数
513                    continue;
514                }
515                let name_camel = f.sig.ident.to_string().to_lower_camel_case();
516                let (self_, arg_types, _, _, _, ret_type) =
517                    parse_function_signature(&f.sig, &vec![]);
518                if self_.is_none() {
519                    continue;
520                }
521
522                let ret_token = match ret_type.to_string().as_str() {
523                    "()" => quote! {droid_wrap_utils::null_value(env)},
524                    "bool" => quote! {droid_wrap_utils::wrapper_bool_value(ret, env)},
525                    "i32" => quote! {droid_wrap_utils::wrapper_integer_value(ret, env)},
526                    "u32" => quote! {droid_wrap_utils::wrapper_integer_value(ret as u32, env)},
527                    "i64" => quote! {droid_wrap_utils::wrapper_long_value(ret, env)},
528                    "u64" => quote! {droid_wrap_utils::wrapper_long_value(ret as u64, env)},
529                    _ => quote! {ret.java_ref()},
530                };
531
532                let mut arg_tokens = TokenStream2::new();
533                for i in 0..arg_types.len() {
534                    let (unwrapped_ty, origin_ty) = &arg_types[i];
535                    let ty_str = unwrapped_ty.to_string();
536                    let ar = if [
537                        "bool", "char", "i16", "u16", "i32", "u32", "i64", "u64", "f32", "f64",
538                    ]
539                    .contains(&ty_str.as_str())
540                    {
541                        quote! {
542                            droid_wrap_utils::ParseJObjectType::<#unwrapped_ty>::parse(&args2[#i], env),
543                        }
544                    } else if origin_ty.to_string().starts_with("Option") {
545                        quote! {{
546                            if args2[#i].is_null() {
547                                None
548                            } else {
549                                let r = env.new_global_ref(&args2[#i]).unwrap();
550                                Some(#unwrapped_ty::_new(&r, Default::default()))
551                            }
552                        },}
553                    } else {
554                        quote! {{
555                            let r = env.new_global_ref(&args2[#i]).unwrap();
556                            #unwrapped_ty::_new(&r, Default::default())
557                        },}
558                    };
559                    arg_tokens.extend(ar);
560                }
561                methods.extend(quote! {
562                    Ok(#name_camel) => {
563                        let args2 = droid_wrap_utils::to_vec(env, &args);
564                        let ret = self_.#name(#arg_tokens);
565                        #ret_token
566                    },
567                })
568            }
569            _ => {}
570        }
571    }
572    // println!("b{}", methods);
573
574    let name = item.self_ty.clone();
575    let class_token = match item.trait_ {
576        None => quote! {Self::CLASS},
577        Some((_, ref p, _)) => quote! {<Self as #p>::CLASS},
578    };
579
580    let impl_new = quote! {
581        fn new(fields: Self::Fields) -> std::sync::Arc<Self> {
582            use std::sync::Arc;
583            let interface = #class_token.replace("/", ".");
584            let proxy = droid_wrap_utils::new_proxy(&[&interface]);
585            let ret = Arc::new(Self::_new(&proxy, fields));
586            let self_ = ret.clone();
587            droid_wrap_utils::bind_proxy_handler(&proxy, move |env, method, args| {
588                let name = env.call_method(&method, "getName", "()Ljava/lang/String;", &[]).unwrap().l().unwrap();
589                let name = env.get_string((&name).into()).unwrap();
590                match name.to_str() {
591                    #methods
592                    _ => droid_wrap_utils::null_value(env)
593                }
594            });
595            ret
596        }
597    };
598
599    let stream = quote! {
600        #attrs
601        #item
602
603        impl JProxy for #name {
604            #impl_new
605        }
606
607        impl Drop for #name {
608            fn drop(&mut self) {
609                self.release();
610            }
611        }
612    };
613
614    stream.into()
615}
616
617/// 实现java类的字段,将此属性标记在带有get或set的fn函数上,可以自动实现访问java字段的能力,可以自动识别静态字段(如果参数中没有“self”)。
618///
619/// # Arguments
620///
621/// * `_`: 未使用。
622/// * `input`: 函数输入。
623///
624/// returns: TokenStream
625///
626/// # Examples
627///
628/// ```
629/// use droid_wrap_derive::java_field;
630///
631/// pub struct LayoutParams;
632///
633/// impl LayoutParams {
634///     #[java_field]
635///     pub fn get_width(&self) -> i32 {}
636///
637///     #[java_field]
638///     pub fn set_width(&self, value: i32) {}
639/// }
640/// ```
641#[proc_macro_attribute]
642pub fn java_field(attrs: TokenStream, input: TokenStream) -> TokenStream {
643    let attrs: FieldMetadata = parse2(Into::<TokenStream2>::into(attrs)).unwrap();
644    let default_value = attrs.default_value.clone();
645    let item = parse_macro_input!(input as ItemFn);
646    let attrs = item.attrs.clone();
647    let stmts = item.block.stmts.clone();
648    let name = item.sig.ident.to_string().to_lower_camel_case();
649    let vis = item.vis.clone();
650    let sig = item.sig.clone();
651
652    let (is_set, name) = if name.starts_with("get") {
653        (false, name.trim_start_matches("get").to_lower_camel_case())
654    } else if name.starts_with("set") {
655        (true, name.trim_start_matches("set").to_lower_camel_case())
656    } else {
657        panic!("Field name `{}` must start with get or set.", name);
658    };
659
660    let (self_, arg_types, arg_types_sig, _, arg_values, ret_type) =
661        parse_function_signature(&sig, &vec![]);
662    if is_set {
663        if arg_types.len() != 1 {
664            panic!(
665                "The number of setter arguments for the field `{}` must be one.",
666                name
667            )
668        }
669    } else {
670        if !arg_types.is_empty() {
671            panic!("The getter field `{}` cannot provide any arguments.", name)
672        }
673    }
674
675    let (ret_value, ret_type_sig, is_result_type) =
676        get_return_value_token(&ret_type, &sig.generics, &vec![], &default_value);
677
678    let ret_value = get_result_token(is_result_type, &ret_value);
679
680    let opt = if is_set {
681        if self_.is_none() {
682            quote! {
683                let ret = env.set_static_field(Self::CLASS, #name, #arg_types_sig #arg_values);
684            }
685        } else {
686            quote! {
687                let ret = env.set_field(#self_.java_ref(), #name, #arg_types_sig #arg_values);
688            }
689        }
690    } else {
691        if self_.is_none() {
692            quote! {
693                let ret = env.get_static_field(Self::CLASS, #name, #ret_type_sig);
694            }
695        } else {
696            quote! {
697                let ret = env.get_field(#self_.java_ref(), #name, #ret_type_sig);
698            }
699        }
700    };
701    let stream = quote! {
702        #(#attrs)*
703        #vis #sig {
704            #(#stmts)*
705            droid_wrap_utils::vm_attach!(mut env);
706            #opt
707            #ret_value
708        }
709    };
710    stream.into()
711}