Skip to main content

duchess_reflect/
reflect.rs

1use std::{cell::RefCell, collections::BTreeMap, env, path::PathBuf, process::Command, sync::Arc};
2
3use proc_macro2::Span;
4
5use crate::{
6    argument::{DuchessDeclaration, Ident, JavaPackage, MethodSelector},
7    class_info::{
8        ClassDecl, ClassInfo, DotId, Generic, Id, Method, RootMap, SpannedPackageInfo, Type,
9    },
10    upcasts::Upcasts,
11};
12
13impl DuchessDeclaration {
14    pub fn to_root_map(&self, reflector: &mut Reflector) -> syn::Result<RootMap> {
15        let mut subpackages = BTreeMap::new();
16        let mut classes = BTreeMap::new();
17        for package in &self.packages {
18            package.to_spanned_packages(
19                &package.package_name.ids,
20                reflector,
21                &mut subpackages,
22                &mut classes,
23            )?;
24        }
25
26        let upcasts: Upcasts = Upcasts::from_iter(classes.values().map(|v| &**v));
27
28        Ok(RootMap {
29            subpackages,
30            classes,
31            upcasts,
32        })
33    }
34}
35
36impl JavaPackage {
37    fn to_spanned_packages(
38        &self,
39        name: &[Ident],
40        reflector: &mut Reflector,
41        map: &mut BTreeMap<Id, SpannedPackageInfo>,
42        classes: &mut BTreeMap<DotId, Arc<ClassInfo>>,
43    ) -> syn::Result<()> {
44        let (first, rest) = name.split_first().unwrap();
45
46        let package_info = || SpannedPackageInfo {
47            name: first.to_id(),
48            span: first.span,
49            subpackages: Default::default(),
50            classes: Default::default(),
51        };
52
53        let first_id = first.to_id();
54
55        // As written, this allows the same package more than once. I don't see any reason to forbid it,
56        // but maybe we want to?
57        let parent = map.entry(first_id).or_insert_with(package_info);
58
59        if rest.is_empty() {
60            self.insert_classes_into_root_map(reflector, parent, classes)
61        } else {
62            self.to_spanned_packages(rest, reflector, &mut parent.subpackages, classes)
63        }
64    }
65
66    fn insert_classes_into_root_map(
67        &self,
68        reflector: &mut Reflector,
69        package: &mut SpannedPackageInfo,
70        classes: &mut BTreeMap<DotId, Arc<ClassInfo>>,
71    ) -> syn::Result<()> {
72        for c in &self.classes {
73            let (dot_id, info) = match c {
74                ClassDecl::Reflected(c) => {
75                    let dot_id = self.make_absolute_dot_id(c.span, &c.name)?;
76                    let info = reflector.reflect(&dot_id, c.span)?;
77
78                    // We copy over the span and kind for proper error specification and error checking
79                    (
80                        dot_id,
81                        Arc::new(ClassInfo {
82                            span: c.span,
83                            kind: c.kind,
84                            ..(*info).clone()
85                        }),
86                    )
87                }
88                ClassDecl::Specified(c) => {
89                    let dot_id = self.make_absolute_dot_id(c.span, &c.name)?;
90                    (
91                        dot_id.clone(),
92                        Arc::new(ClassInfo {
93                            name: dot_id,
94                            ..c.clone()
95                        }),
96                    )
97                }
98            };
99
100            package.classes.push(dot_id.clone());
101            classes.insert(dot_id, info);
102        }
103        Ok(())
104    }
105
106    /// The users give classnames that may not include java package information.
107    fn make_absolute_dot_id(&self, span: Span, class_dot_id: &DotId) -> syn::Result<DotId> {
108        let package_ids: Vec<Id> = self.package_name.ids.iter().map(|n| n.to_id()).collect();
109
110        let (package, class) = class_dot_id.split();
111
112        // If the user just wrote (e.g.) `String`, add the `java.lang` ourselves.
113        if package.is_empty() {
114            return Ok(DotId::new(&package_ids, &class));
115        }
116
117        // Otherwise, check that the package the user wrote matches our name.
118        if &package_ids[..] != package {
119            return Err(syn::Error::new(
120                span,
121                format!("expected package `{}`", self.package_name),
122            ));
123        }
124
125        Ok(class_dot_id.clone())
126    }
127}
128
129/// Reflection cache. Given fully qualified java class names,
130/// look up info about their interfaces.
131#[derive(Default)]
132pub struct Reflector {
133    classes: RefCell<BTreeMap<DotId, Arc<ClassInfo>>>,
134}
135
136impl Reflector {
137    /// Returns the (potentially cached) info about `class_name`;
138    pub fn reflect(&self, class_name: &DotId, span: Span) -> syn::Result<Arc<ClassInfo>> {
139        // yields an error if we cannot reflect on that class.
140        if let Some(class) = self.classes.borrow().get(class_name).map(Arc::clone) {
141            return Ok(class);
142        }
143
144        let mut javap_path = PathBuf::new();
145        if let Ok(java_home) = env::var("JAVA_HOME") {
146            javap_path.extend([java_home.as_str(), "bin"]);
147        }
148        javap_path.push("javap");
149
150        let mut command = Command::new(javap_path);
151
152        // If the CLASSPATH variable is set we will use it, otherwise allow javap to use its default classpath
153        if let Some(classpath) = env::var("CLASSPATH").ok() {
154            command.arg("-cp").arg(classpath);
155        }
156
157        command.arg("-p").arg(format!("{}", class_name));
158
159        let output_or_err = command.output();
160
161        let output = match output_or_err {
162            Ok(o) => o,
163            Err(err) => {
164                return Err(syn::Error::new(
165                    span,
166                    format!("failed to execute `{command:?}`: {err}"),
167                ));
168            }
169        };
170
171        if !output.status.success() {
172            return Err(syn::Error::new(
173                span,
174                format!(
175                    "unsuccessful execution of `{command:?}` (exit status: {}): {}",
176                    output.status,
177                    String::from_utf8(output.stderr).unwrap_or(String::from("error"))
178                ),
179            ));
180        }
181
182        let s = match String::from_utf8(output.stdout) {
183            Ok(o) => o,
184            Err(err) => {
185                return Err(syn::Error::new(
186                    span,
187                    format!("failed to parse output of `{command:?}` as utf-8: {err}"),
188                ));
189            }
190        };
191
192        let mut ci = ClassInfo::parse(&s, span)?;
193
194        // reset the span for the cached data to the call site so that when others look it up,
195        // they get the same span.
196        ci.span = Span::call_site();
197        Ok(self
198            .classes
199            .borrow_mut()
200            .entry(class_name.clone())
201            .or_insert(Arc::new(ci))
202            .clone())
203    }
204
205    ///
206    pub fn reflect_method(&self, method_selector: &MethodSelector) -> syn::Result<ReflectedMethod> {
207        match method_selector {
208            MethodSelector::ClassName(cn) => {
209                let dot_id = cn.to_dot_id();
210                let class_info = self.reflect(&dot_id, cn.span)?;
211                match class_info.constructors.len() {
212                    1 => Ok(ReflectedMethod::Constructor(class_info, 0)),
213                    0 => Err(syn::Error::new(cn.span, "no constructors found".to_string())),
214                    n => Err(syn::Error::new(cn.span, format!("{n} constructors found, use an explicit class declaration to disambiguate")))
215                }
216            }
217            MethodSelector::MethodName(cn, mn) => {
218                let dot_id = cn.to_dot_id();
219                let class_info = self.reflect(&dot_id, cn.span)?;
220                let methods: Vec<(MethodIndex, &Method)> = class_info
221                    .methods
222                    .iter()
223                    .enumerate()
224                    .filter(|(_i, m)| &m.name[..] == &mn.text[..])
225                    .collect();
226                match methods.len() {
227                    1 => {
228                        let (id, _method) = methods[0];
229                        Ok(ReflectedMethod::Method(class_info, id))
230                    },
231                    0 => Err(syn::Error::new(cn.span,  format!("no methods named `{mn}` found"))),
232                    n => Err(syn::Error::new(cn.span, format!("{n} methods named `{mn}` found, use an explicit class declaration to disambiguate") )),
233                }
234            }
235            MethodSelector::ClassInfo(_) => todo!(),
236        }
237    }
238}
239
240pub type ConstructorIndex = usize;
241pub type MethodIndex = usize;
242
243/// Reflection on something callable.
244#[derive(Clone, Debug)]
245pub enum ReflectedMethod {
246    Constructor(Arc<ClassInfo>, ConstructorIndex),
247    Method(Arc<ClassInfo>, MethodIndex),
248}
249
250impl ReflectedMethod {
251    /// The name of this callable thing in Rust
252    pub fn name(&self) -> Id {
253        match self {
254            ReflectedMethod::Constructor(..) => Id::from("new"),
255            ReflectedMethod::Method(c, m) => c.methods[*m].name.clone(),
256        }
257    }
258
259    pub fn class(&self) -> &ClassInfo {
260        match self {
261            ReflectedMethod::Constructor(c, _) => c,
262            ReflectedMethod::Method(c, _) => c,
263        }
264    }
265
266    /// Is this something that is called on a *type*?
267    pub fn is_static(&self) -> bool {
268        match self {
269            ReflectedMethod::Constructor(..) => true,
270            ReflectedMethod::Method(c, m) => c.methods[*m].flags.is_static,
271        }
272    }
273
274    pub fn generics(&self) -> &Vec<Generic> {
275        match self {
276            ReflectedMethod::Constructor(c, t) => &c.constructors[*t].generics,
277            ReflectedMethod::Method(c, m) => &c.methods[*m].generics,
278        }
279    }
280
281    pub fn argument_tys(&self) -> &Vec<Type> {
282        match self {
283            ReflectedMethod::Constructor(c, t) => &c.constructors[*t].argument_tys,
284            ReflectedMethod::Method(c, m) => &c.methods[*m].argument_tys,
285        }
286    }
287}