bindgen_jni/
emit_rust.rs

1use super::*;
2use class_file_visitor::*;
3use gather_java::*;
4
5use std::collections::*;
6use std::error::Error;
7use std::fmt::Write;
8use std::io;
9
10struct KnownDocsUrl {
11    label:  String,
12    url:    String,
13}
14
15impl KnownDocsUrl {
16    pub fn from(java_class: &Class) -> Option<KnownDocsUrl> {
17        let java_name = java_class.this_class().name();
18
19        //let prefix = if java_name.starts_with("android/") {
20        //    "https://developer.android.com/reference/kotlin/"
21        //} else if java_name.starts_with("java/") {
22        //    "https://docs.oracle.com/javase/7/docs/api/index.html?"
23        //} else {
24        //    return None;
25        //};
26
27        let prefix = "https://developer.android.com/reference/kotlin/";
28
29        for ch in java_name.chars() {
30            match ch {
31                'a'..='z' => {},
32                'A'..='Z' => {},
33                '0'..='9' => {},
34                '_' | '$' | '/' => {},
35                _ch => return None,
36            }
37        }
38
39        let last_slash = java_name.rfind(|ch| ch == '/');
40        let no_namespace = if let Some(last_slash) = last_slash {
41            &java_name[(last_slash+1)..]
42        } else {
43            &java_name[..]
44        };
45
46        Some(KnownDocsUrl{
47            label:  no_namespace.to_owned().replace("$","."),
48            url:    format!("{}{}.html", prefix, java_name.replace("$",".")),
49        })
50    }
51}
52
53const DEREF_FN : &'static str = "fn deref(&self) -> &Self::Target { unsafe { &*(self as *const Self as *const Self::Target) } }";
54
55#[derive(Debug, Default)]
56pub struct Struct {
57    pub rust_mod_prefix:    String,
58    pub rust_struct_name:   String,
59    pub java_class:         Class,
60}
61
62impl Struct {
63    pub fn write(&self, context: &Context, indent: &str, out: &mut impl io::Write) -> io::Result<()> {
64        if let Some(url) = KnownDocsUrl::from(&self.java_class) {
65            writeln!(out, "{}/// [{}]({})", indent, url.label, url.url)?;
66        }
67        //self.write_java_doc_comment(context, indent, out)?;
68        //writeln!(out, "{}/// * access_flags: {:?}", indent, self.java_class.access_flags() )?;
69        self.write_rust_struct(context, indent, out)?;
70        Ok(())
71    }
72
73    fn write_java_doc_comment(&self, _context: &Context, indent: &str, out: &mut impl io::Write) -> io::Result<()> {
74        // Emit an initial doc comment in the form of a proto-Java class definition.
75        writeln!(out, "{}/// ```java", indent)?;
76        write!(out, "{}/// ", indent)?;
77        // Ignored access_flags: SUPER, SYNTHETIC, ANNOTATION
78        if self.java_class.access_flags().contains(ClassAccessFlags::PUBLIC  ) { write!(out, "public ")? }
79        if self.java_class.access_flags().contains(ClassAccessFlags::STATIC  ) { write!(out, "static ")? }
80        if self.java_class.access_flags().contains(ClassAccessFlags::ABSTRACT) { write!(out, "abstract ")? }
81        if self.java_class.access_flags().contains(ClassAccessFlags::FINAL   ) { write!(out, "final ")? }
82        let keyword = if self.java_class.access_flags().contains(ClassAccessFlags::INTERFACE) {
83            "interface"
84        } else if self.java_class.access_flags().contains(ClassAccessFlags::ENUM) {
85            "enum"
86        } else {
87            "class"
88        };
89        write!(out, "{} {}", keyword, self.java_class.this_class().name().replace("/", "."))?;
90        if let Some(super_class) = self.java_class.super_class() {
91            if super_class.name() != "java/lang/Object" {
92                write!(out, " extends {}", super_class.name().replace("/", "."))?;
93            }
94        }
95        let mut write_implements = false;
96        for interface in self.java_class.interfaces() {
97            if !write_implements {
98                write!(out, " implements ")?;
99                write_implements = true;
100            } else {
101                write!(out, ", ")?;
102            }
103            write!(out, "{}", interface.name().replace("/", "."))?;
104        }
105        writeln!(out, " {{ ... }}")?;
106        writeln!(out, "{}/// ```", indent)?;
107        Ok(())
108    }
109
110    fn write_rust_struct(&self, context: &Context, indent: &str, out: &mut impl io::Write) -> io::Result<()> {
111        let struct_access = if self.java_class.access_flags().contains(ClassAccessFlags::PUBLIC) {
112            "pub "
113        } else {
114            ""
115        };
116
117        let struct_body = if self.java_class.access_flags().contains(ClassAccessFlags::STATIC) {
118            ""
119        } else {
120            "(::jni_sys::jobject)"
121        };
122
123        writeln!(out, "{}#[repr(transparent)] {}struct {}{};", indent, struct_access, &self.rust_struct_name, struct_body)?;
124        if let Some(super_class) = self.java_class.super_class() {
125            let rust_super_class_name = context.jni_type_to_rust_type.get(super_class.name()).unwrap();
126            //let rust_super_name = "::java::lang::Object"; // XXX
127            writeln!(out, "{}impl ::std::ops::Deref for {} {{ type Target = {}; {} }}", indent, &self.rust_struct_name, rust_super_class_name, DEREF_FN)?;
128        }
129        writeln!(out, "{}impl {} {{", indent, &self.rust_struct_name)?;
130        writeln!(out, "{}    // ...", indent)?;
131        writeln!(out, "{}}}", indent)?;
132        Ok(())
133    }
134}
135
136
137
138#[derive(Debug, Default)]
139pub struct Module {
140    // For consistent diffs / printing order, these should *not* be HashMaps
141    pub structs: BTreeMap<String, Struct>,
142    pub modules: BTreeMap<String, Module>,
143}
144
145impl Module {
146    pub fn write(&self, context: &Context, indent: &str, out: &mut impl io::Write) -> io::Result<()> {
147        let next_indent = format!("{}    ", indent);
148
149        for (name, module) in self.modules.iter() {
150            if indent.is_empty() {
151                writeln!(out, "#[allow(non_camel_case_types)] // We map Java inner classes to Outer_Inner")?;
152                writeln!(out, "#[allow(dead_code)] // We generate structs for private Java types too, just in case.")?;
153            }
154            writeln!(out, "{}pub mod {} {{", indent, name)?;
155            module.write(context, &next_indent[..], out)?;
156            writeln!(out, "{}}}", indent)?;
157            writeln!(out, "")?;
158        }
159
160        for (name, structure) in self.structs.iter() {
161            if indent.is_empty() {
162                if name.contains("_") { writeln!(out, "#[allow(non_camel_case_types)] // We map Java inner classes to Outer_Inner")?; }
163                if !structure.java_class.access_flags().contains(ClassAccessFlags::PUBLIC) { writeln!(out, "#[allow(dead_code)] // We generate structs for private Java types too, just in case.")?; }
164            }
165            structure.write(context, indent, out)?;
166            writeln!(out, "")?;
167        }
168
169        Ok(())
170    }
171}
172
173
174
175#[derive(Debug, Default)]
176pub struct Context {
177    module:                 Module,
178    jni_type_to_rust_type:  HashMap<String, String>,
179}
180
181impl Context {
182    pub fn new() -> Self { Default::default() }
183
184    pub fn add_struct(&mut self, java_class: Class) -> Result<(), Box<dyn Error>> {
185        let mut rust_mod : &mut Module = &mut self.module;
186        let mut rust_mod_prefix     = String::from("crate::");
187        let mut rust_struct_name    = String::new();
188
189        for component in JniPathIter::new(java_class.this_class().name()) {
190            match component {
191                JniIdentifier::Namespace(id) => {
192                    let id = match RustIdentifier::from_str(id) {
193                        RustIdentifier::Identifier(id) => id,
194                        RustIdentifier::KeywordRawSafe(id) => id,
195                        RustIdentifier::KeywordUnderscorePostfix(id) => id,
196                        RustIdentifier::NonIdentifier(id) => Err(format!("Unable to add_struct(): parent java namespace name {:?} has no rust equivalent", id))?,
197                    };
198
199                    write!(&mut rust_mod_prefix, "{}::", id)?;
200                    rust_mod = rust_mod.modules.entry(id.to_owned()).or_insert(Default::default());
201                },
202                JniIdentifier::ContainingClass(id) => {
203                    let id = match RustIdentifier::from_str(id) {
204                        RustIdentifier::Identifier(id) => id,
205                        RustIdentifier::KeywordRawSafe(id) => id,
206                        RustIdentifier::KeywordUnderscorePostfix(id) => id,
207                        RustIdentifier::NonIdentifier(id) => Err(format!("Unable to add_struct(): parent java class name {:?} has no rust equivalent", id))?,
208                    };
209
210                    write!(&mut rust_struct_name, "{}_", id)?;
211                },
212                JniIdentifier::LeafClass(id) => {
213                    let id = match RustIdentifier::from_str(id) {
214                        RustIdentifier::Identifier(id) => id,
215                        RustIdentifier::KeywordRawSafe(id) => id,
216                        RustIdentifier::KeywordUnderscorePostfix(id) => id,
217                        RustIdentifier::NonIdentifier(id) => Err(format!("Unable to add_struct(): java class name {:?} has no rust equivalent", id))?,
218                    };
219
220                    write!(&mut rust_struct_name, "{}", id)?;
221                    let id = &rust_struct_name[..];
222
223                    if rust_mod.structs.contains_key(id) {
224                        Err(format!("Unable to add_struct(): java class name {:?} was already added", id))?
225                    }
226
227                    self.jni_type_to_rust_type.insert(java_class.this_class().name().clone(), format!("{}{}", rust_mod_prefix, &rust_struct_name));
228
229                    rust_mod.structs.insert(rust_struct_name.clone(), Struct {
230                        rust_mod_prefix,
231                        rust_struct_name,
232                        java_class,
233                    });
234
235                    return Ok(());
236                },
237            }
238        }
239
240        Err(format!("Failed to find LeafClass in {:?}", java_class.this_class().name()))?
241    }
242
243    pub fn write(&self, out: &mut impl io::Write) -> io::Result<()> {
244        self.module.write(self, "", out)
245    }
246}