1 use std::{ffi::CString, os::raw::c_void};
2 
3 use thiserror::Error;
4 
5 use crate::{
6     sys::{JavaVMInitArgs, JavaVMOption},
7     JNIVersion,
8 };
9 
10 /// Errors that can occur when invoking a [`JavaVM`](super::vm::JavaVM) with the
11 /// [Invocation API](https://docs.oracle.com/en/java/javase/12/docs/specs/jni/invocation.html).
12 #[derive(Debug, Error)]
13 pub enum JvmError {
14     /// An internal `0` byte was found when constructing a string.
15     #[error("internal null in option: {0}")]
16     NullOptString(String),
17 }
18 
19 /// Builder for JavaVM InitArgs.
20 ///
21 /// *This API requires "invocation" feature to be enabled,
22 /// see ["Launching JVM from Rust"](struct.JavaVM.html#launching-jvm-from-rust).*
23 #[derive(Debug)]
24 pub struct InitArgsBuilder {
25     opts: Vec<String>,
26     ignore_unrecognized: bool,
27     version: JNIVersion,
28 }
29 
30 impl Default for InitArgsBuilder {
default() -> Self31     fn default() -> Self {
32         InitArgsBuilder {
33             opts: vec![],
34             ignore_unrecognized: false,
35             version: JNIVersion::V8,
36         }
37     }
38 }
39 
40 impl InitArgsBuilder {
41     /// Create a new default InitArgsBuilder
new() -> Self42     pub fn new() -> Self {
43         Default::default()
44     }
45 
46     /// Add an option to the init args
47     ///
48     /// The `vfprintf`, `abort`, and `exit` options are unsupported at this time.
option(self, opt_string: &str) -> Self49     pub fn option(self, opt_string: &str) -> Self {
50         let mut s = self;
51 
52         match opt_string {
53             "vfprintf" | "abort" | "exit" => return s,
54             _ => {}
55         }
56 
57         s.opts.push(opt_string.into());
58 
59         s
60     }
61 
62     /// Set JNI version for the init args
63     ///
64     /// Default: V8
version(self, version: JNIVersion) -> Self65     pub fn version(self, version: JNIVersion) -> Self {
66         let mut s = self;
67         s.version = version;
68         s
69     }
70 
71     /// Set the `ignoreUnrecognized` init arg flag
72     ///
73     /// If ignoreUnrecognized is true, JavaVM::new ignores all unrecognized option strings that
74     /// begin with "-X" or "_". If ignoreUnrecognized is false, JavaVM::new returns Err as soon as
75     /// it encounters any unrecognized option strings.
76     ///
77     /// Default: `false`
ignore_unrecognized(self, ignore: bool) -> Self78     pub fn ignore_unrecognized(self, ignore: bool) -> Self {
79         let mut s = self;
80         s.ignore_unrecognized = ignore;
81         s
82     }
83 
84     /// Build the `InitArgs`
85     ///
86     /// This will check for internal nulls in the option strings and will return
87     /// an error if one is found.
build(self) -> Result<InitArgs, JvmError>88     pub fn build(self) -> Result<InitArgs, JvmError> {
89         let mut opts = Vec::with_capacity(self.opts.len());
90         for opt in self.opts {
91             let option_string =
92                 CString::new(opt.as_str()).map_err(|_| JvmError::NullOptString(opt))?;
93             let jvm_opt = JavaVMOption {
94                 optionString: option_string.into_raw(),
95                 extraInfo: ::std::ptr::null_mut(),
96             };
97             opts.push(jvm_opt);
98         }
99 
100         Ok(InitArgs {
101             inner: JavaVMInitArgs {
102                 version: self.version.into(),
103                 ignoreUnrecognized: self.ignore_unrecognized as _,
104                 options: opts.as_ptr() as _,
105                 nOptions: opts.len() as _,
106             },
107             opts,
108         })
109     }
110 
111     /// Returns collected options
options(&self) -> Vec<String>112     pub fn options(&self) -> Vec<String> {
113         self.opts.clone()
114     }
115 }
116 
117 /// JavaVM InitArgs.
118 ///
119 /// *This API requires "invocation" feature to be enabled,
120 /// see ["Launching JVM from Rust"](struct.JavaVM.html#launching-jvm-from-rust).*
121 pub struct InitArgs {
122     inner: JavaVMInitArgs,
123     opts: Vec<JavaVMOption>,
124 }
125 
126 impl InitArgs {
inner_ptr(&self) -> *mut c_void127     pub(crate) fn inner_ptr(&self) -> *mut c_void {
128         &self.inner as *const _ as _
129     }
130 }
131 
132 impl Drop for InitArgs {
drop(&mut self)133     fn drop(&mut self) {
134         for opt in self.opts.iter() {
135             unsafe { CString::from_raw(opt.optionString) };
136         }
137     }
138 }
139