1use jni::ToJni;
2use jni_sys;
3use raw::*;
4use std::ffi::{CStr, CString};
5use std::marker::PhantomData;
6use std::os::raw::c_void;
7use std::ptr;
8use std::slice;
9use version::{self, JniVersion};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum JvmVerboseOption {
16 Class,
20 Gc,
24 Jni,
28}
29
30fn verbose_option_to_string(option: &JvmVerboseOption) -> &'static str {
31 match option {
32 JvmVerboseOption::Class => "class",
33 JvmVerboseOption::Gc => "gc",
34 JvmVerboseOption::Jni => "jni",
35 }
36}
37
38#[cfg(test)]
39mod verbose_option_to_string_tests {
40 use super::*;
41
42 #[test]
43 fn test() {
44 assert_eq!(verbose_option_to_string(&JvmVerboseOption::Class), "class");
45 assert_eq!(verbose_option_to_string(&JvmVerboseOption::Gc), "gc");
46 assert_eq!(verbose_option_to_string(&JvmVerboseOption::Jni), "jni");
47 }
48}
49
50#[derive(Debug, Clone, PartialEq, Eq)]
55pub enum JvmOption {
56 Verbose(JvmVerboseOption),
60 SystemProperty(String, String),
64 CheckedJni,
68 Unknown(String),
72}
73
74impl JvmOption {
75 unsafe fn from_raw(option: &jni_sys::JavaVMOption) -> Self {
77 let option_string = CStr::from_ptr((*option).optionString).to_str().unwrap();
79 let system_property_prefix = "-D";
80 match option_string {
81 "-verbose:gc" => JvmOption::Verbose(JvmVerboseOption::Gc),
82 "-verbose:jni" => JvmOption::Verbose(JvmVerboseOption::Jni),
83 "-verbose:class" => JvmOption::Verbose(JvmVerboseOption::Class),
84 "-Xcheck:jni" => JvmOption::CheckedJni,
85 option if option.starts_with(system_property_prefix) => {
86 let parts: Vec<&str> = option
87 .split_at(system_property_prefix.len())
88 .1
89 .splitn(2, "=")
90 .collect();
91 if parts.len() != 2 {
92 JvmOption::Unknown(option.to_owned())
93 } else {
94 JvmOption::SystemProperty(parts[0].to_owned(), parts[1].to_owned())
95 }
96 }
97 option => JvmOption::Unknown(option.to_owned()),
98 }
99 }
100}
101
102#[cfg(test)]
103mod jvm_option_tests {
104 use super::*;
105
106 #[test]
107 fn from_raw_checked_jni() {
108 let option_string = CStr::from_bytes_with_nul(b"-Xcheck:jni\0").unwrap();
109 let option = &jni_sys::JavaVMOption {
110 optionString: option_string.as_ptr() as *mut i8,
111 extraInfo: ptr::null_mut(),
112 };
113 assert_eq!(
114 unsafe { JvmOption::from_raw(option) },
115 JvmOption::CheckedJni
116 );
117 }
118
119 #[test]
120 fn from_raw_verbose() {
121 let option_string = CStr::from_bytes_with_nul(b"-verbose:jni\0").unwrap();
122 let option = &jni_sys::JavaVMOption {
123 optionString: option_string.as_ptr() as *mut i8,
124 extraInfo: ptr::null_mut(),
125 };
126 assert_eq!(
127 unsafe { JvmOption::from_raw(option) },
128 JvmOption::Verbose(JvmVerboseOption::Jni)
129 );
130
131 let option_string = CStr::from_bytes_with_nul(b"-verbose:gc\0").unwrap();
132 let option = &jni_sys::JavaVMOption {
133 optionString: option_string.as_ptr() as *mut i8,
134 extraInfo: ptr::null_mut(),
135 };
136 assert_eq!(
137 unsafe { JvmOption::from_raw(option) },
138 JvmOption::Verbose(JvmVerboseOption::Gc)
139 );
140
141 let option_string = CStr::from_bytes_with_nul(b"-verbose:class\0").unwrap();
142 let option = &jni_sys::JavaVMOption {
143 optionString: option_string.as_ptr() as *mut i8,
144 extraInfo: ptr::null_mut(),
145 };
146 assert_eq!(
147 unsafe { JvmOption::from_raw(option) },
148 JvmOption::Verbose(JvmVerboseOption::Class)
149 );
150 }
151
152 #[test]
153 fn from_raw_system_property() {
154 let option_string = CStr::from_bytes_with_nul(b"-Dkey=value\0").unwrap();
155 let option = &jni_sys::JavaVMOption {
156 optionString: option_string.as_ptr() as *mut i8,
157 extraInfo: ptr::null_mut(),
158 };
159 assert_eq!(
160 unsafe { JvmOption::from_raw(option) },
161 JvmOption::SystemProperty("key".to_owned(), "value".to_owned())
162 );
163 }
164
165 #[test]
166 fn from_raw_unknown() {
167 let option_string = CStr::from_bytes_with_nul(b"tyhb\0").unwrap();
168 let option = &jni_sys::JavaVMOption {
169 optionString: option_string.as_ptr() as *mut i8,
170 extraInfo: ptr::null_mut(),
171 };
172 assert_eq!(
173 unsafe { JvmOption::from_raw(option) },
174 JvmOption::Unknown("tyhb".to_owned())
175 );
176
177 let option_string = CStr::from_bytes_with_nul(b"-Dkey~value\0").unwrap();
178 let option = &jni_sys::JavaVMOption {
179 optionString: option_string.as_ptr() as *mut i8,
180 extraInfo: ptr::null_mut(),
181 };
182 assert_eq!(
183 unsafe { JvmOption::from_raw(option) },
184 JvmOption::Unknown("-Dkey~value".to_owned())
185 );
186 }
187
188 #[test]
189 fn to_string() {
190 assert_eq!(option_to_string(&JvmOption::CheckedJni), "-Xcheck:jni");
191 assert_eq!(
192 option_to_string(&JvmOption::Verbose(JvmVerboseOption::Gc)),
193 "-verbose:gc"
194 );
195 assert_eq!(
196 option_to_string(&JvmOption::Verbose(JvmVerboseOption::Jni)),
197 "-verbose:jni"
198 );
199 assert_eq!(
200 option_to_string(&JvmOption::Verbose(JvmVerboseOption::Class)),
201 "-verbose:class"
202 );
203 assert_eq!(
204 option_to_string(&JvmOption::SystemProperty(
205 "key".to_owned(),
206 "value".to_owned()
207 )),
208 "-Dkey=value"
209 );
210 assert_eq!(
211 option_to_string(&JvmOption::Unknown("qwer".to_owned())),
212 "qwer"
213 );
214 }
215}
216
217fn option_to_string(option: &JvmOption) -> String {
218 match option {
219 JvmOption::CheckedJni => "-Xcheck:jni".to_owned(),
220 JvmOption::Verbose(option) => format!("-verbose:{}", verbose_option_to_string(option)),
221 JvmOption::SystemProperty(key, value) => format!("-D{}={}", key, value),
222 JvmOption::Unknown(value) => value.clone(),
223 }
224}
225
226#[cfg(test)]
227mod option_to_string_tests {
228 use super::*;
229
230 #[test]
231 fn test() {
232 assert_eq!(option_to_string(&JvmOption::CheckedJni), "-Xcheck:jni");
233 assert_eq!(
234 option_to_string(&JvmOption::Verbose(JvmVerboseOption::Gc)),
235 "-verbose:gc"
236 );
237 assert_eq!(
238 option_to_string(&JvmOption::Verbose(JvmVerboseOption::Jni)),
239 "-verbose:jni"
240 );
241 assert_eq!(
242 option_to_string(&JvmOption::Verbose(JvmVerboseOption::Class)),
243 "-verbose:class"
244 );
245 assert_eq!(
246 option_to_string(&JvmOption::SystemProperty(
247 "key".to_owned(),
248 "value".to_owned()
249 )),
250 "-Dkey=value"
251 );
252 assert_eq!(
253 option_to_string(&JvmOption::Unknown("qwer".to_owned())),
254 "qwer"
255 );
256 }
257}
258
259#[derive(Debug, Clone, PartialEq, Eq)]
274pub struct InitArguments {
275 version: JniVersion,
276 options: Vec<JvmOption>,
277 ignore_unrecognized: bool,
278}
279
280impl InitArguments {
281 pub fn get_default(version: JniVersion) -> Option<Self> {
287 let arguments = Self::get_default_or_closest_supported(version);
288 if arguments.version == version {
289 Some(arguments)
290 } else {
291 None
292 }
293 }
294
295 pub fn get_default_or_closest_supported(version: JniVersion) -> Self {
302 let mut raw_arguments = jni_sys::JavaVMInitArgs {
303 version: version::to_raw(version),
304 nOptions: 0,
305 options: ptr::null_mut(),
306 ignoreUnrecognized: jni_sys::JNI_FALSE,
307 };
308 unsafe {
310 JNI_GetDefaultJavaVMInitArgs(
313 &mut raw_arguments as *mut jni_sys::JavaVMInitArgs as *mut c_void,
314 );
315 }
316 unsafe { Self::from_raw(&raw_arguments).with_option(JvmOption::CheckedJni) }
319 }
320
321 unsafe fn from_raw(raw_arguments: &jni_sys::JavaVMInitArgs) -> InitArguments {
323 let options = slice::from_raw_parts(raw_arguments.options, raw_arguments.nOptions as usize)
324 .iter()
325 .map(|value| JvmOption::from_raw(value))
326 .collect();
327 InitArguments {
328 version: version::from_raw(raw_arguments.version),
329 ignore_unrecognized: to_bool(raw_arguments.ignoreUnrecognized),
330 options,
331 }
332 }
333
334 pub fn get_latest_default() -> Self {
338 Self::get_default_or_closest_supported(JniVersion::V8)
339 }
340
341 pub fn with_options(mut self, options: &[JvmOption]) -> Self {
345 self.options.extend_from_slice(options);
346 self
347 }
348
349 pub fn with_option(self, option: JvmOption) -> Self {
353 self.with_options(&[option])
354 }
355
356 pub fn unchecked(self) -> Self {
358 InitArguments {
359 version: self.version,
360 ignore_unrecognized: self.ignore_unrecognized,
361 options: self.options
362 .iter()
363 .filter(|&option| *option != JvmOption::CheckedJni)
364 .cloned()
365 .collect(),
366 }
367 }
368
369 pub fn checked(self) -> Self {
373 self.with_option(JvmOption::CheckedJni)
374 }
375
376 pub fn ignore_unrecognized_options(mut self) -> Self {
380 self.ignore_unrecognized = true;
381 self
382 }
383
384 pub fn fail_on_unrecognized_options(mut self) -> Self {
388 self.ignore_unrecognized = false;
389 self
390 }
391
392 pub fn version(&self) -> JniVersion {
396 self.version
397 }
398}
399
400pub struct RawInitArguments<'a> {
403 pub raw_arguments: jni_sys::JavaVMInitArgs,
404 _buffer: PhantomData<&'a Vec<CString>>,
405}
406
407pub fn to_raw<'a, 'b, 'c: 'a + 'b>(
408 arguments: &InitArguments,
409 strings_buffer: &'a mut Vec<CString>,
410 options_buffer: &'b mut Vec<jni_sys::JavaVMOption>,
411) -> RawInitArguments<'c> {
412 *strings_buffer = arguments
413 .options
414 .iter()
415 .map(|_| "")
416 .map(CString::new)
417 .map(Result::unwrap)
418 .collect();
419 *options_buffer = arguments
420 .options
421 .iter()
422 .zip(strings_buffer.iter_mut())
423 .map(|(option, ref mut buffer)| {
424 let buffer: &mut CString = buffer;
426 *buffer = CString::new(option_to_string(option)).unwrap();
427 jni_sys::JavaVMOption {
428 optionString: buffer.as_ptr() as *mut i8,
429 extraInfo: ptr::null_mut(),
430 }
431 })
432 .collect();
433 RawInitArguments {
434 raw_arguments: jni_sys::JavaVMInitArgs {
435 version: version::to_raw(arguments.version),
436 nOptions: options_buffer.len() as i32,
437 options: options_buffer.as_mut_ptr(),
438 ignoreUnrecognized: unsafe { bool::__to_jni(&arguments.ignore_unrecognized) },
440 },
441 _buffer: PhantomData::<&'c Vec<CString>>,
442 }
443}
444
445#[cfg(test)]
446pub unsafe fn from_raw(raw_arguments: &jni_sys::JavaVMInitArgs) -> InitArguments {
447 InitArguments::from_raw(raw_arguments)
448}
449
450#[cfg(test)]
451pub fn test(version: JniVersion) -> InitArguments {
452 InitArguments {
453 version: version,
454 options: vec![],
455 ignore_unrecognized: true,
456 }
457}
458
459#[cfg(test)]
460pub mod tests {
461 use super::*;
462
463 fn default_options() -> Vec<JvmOption> {
464 vec![
465 JvmOption::SystemProperty("key".to_owned(), "value".to_owned()),
466 JvmOption::Unknown("qwer".to_owned()),
467 ]
468 }
469
470 fn resulting_options() -> Vec<JvmOption> {
471 vec![
472 JvmOption::SystemProperty("key".to_owned(), "value".to_owned()),
473 JvmOption::Unknown("qwer".to_owned()),
474 JvmOption::CheckedJni,
475 ]
476 }
477
478 pub fn default_args() -> InitArguments {
479 InitArguments {
480 version: JniVersion::V4,
481 options: default_options(),
482 ignore_unrecognized: false,
483 }
484 }
485
486 fn check_arguments(version: JniVersion) {
487 let actual_arguments = get_default_java_vm_init_args_call_input();
488 assert_eq!(actual_arguments.version, version::to_raw(version));
489 assert_eq!(actual_arguments.nOptions, 0);
490 assert_eq!(actual_arguments.options, ptr::null_mut());
491 assert_eq!(actual_arguments.ignoreUnrecognized, jni_sys::JNI_FALSE);
492 }
493
494 #[test]
495 fn to_raw_test() {
496 let arguments = InitArguments {
497 version: JniVersion::V4,
498 options: default_options(),
499 ignore_unrecognized: false,
500 };
501 let mut strings_buffer = vec![];
502 let mut options_buffer = vec![];
503 let raw_arguments = to_raw(&arguments, &mut strings_buffer, &mut options_buffer);
504 assert_eq!(
505 raw_arguments.raw_arguments.version,
506 version::to_raw(JniVersion::V4)
507 );
508 assert_eq!(raw_arguments.raw_arguments.nOptions, 2);
509 assert_eq!(
510 raw_arguments.raw_arguments.ignoreUnrecognized,
511 jni_sys::JNI_FALSE
512 );
513 let raw_options = unsafe {
514 slice::from_raw_parts(
515 raw_arguments.raw_arguments.options,
516 raw_arguments.raw_arguments.nOptions as usize,
517 )
518 };
519 for (raw_option, option) in raw_options.iter().zip(default_options().into_iter()) {
520 assert_eq!(option, unsafe { JvmOption::from_raw(raw_option) });
521 }
522 }
523
524 #[test]
525 fn get_default_supported() {
526 let mut strings_buffer = vec![];
527 let mut options_buffer = vec![];
528 let mut raw_arguments = to_raw(&default_args(), &mut strings_buffer, &mut options_buffer);
529 let _locked = setup_get_default_java_vm_init_args_call(GetDefaultJavaVMInitArgsCall::new(
530 &mut raw_arguments.raw_arguments as *mut jni_sys::JavaVMInitArgs as *mut c_void,
531 ));
532 assert_eq!(
533 InitArguments::get_default(JniVersion::V4),
534 Some(InitArguments {
535 version: JniVersion::V4,
536 options: resulting_options(),
537 ignore_unrecognized: false
538 })
539 );
540 check_arguments(JniVersion::V4);
541 }
542
543 #[test]
544 fn get_default_unsupported() {
545 let mut strings_buffer = vec![];
546 let mut options_buffer = vec![];
547 let mut raw_arguments = to_raw(&default_args(), &mut strings_buffer, &mut options_buffer);
548 let _locked = setup_get_default_java_vm_init_args_call(GetDefaultJavaVMInitArgsCall::new(
549 &mut raw_arguments.raw_arguments as *mut jni_sys::JavaVMInitArgs as *mut c_void,
550 ));
551 assert_eq!(InitArguments::get_default(JniVersion::V1), None);
552 check_arguments(JniVersion::V1);
553 }
554
555 #[test]
556 fn get_default_or_closest_supported() {
557 let mut strings_buffer = vec![];
558 let mut options_buffer = vec![];
559 let mut raw_arguments = to_raw(&default_args(), &mut strings_buffer, &mut options_buffer);
560 let _locked = setup_get_default_java_vm_init_args_call(GetDefaultJavaVMInitArgsCall::new(
561 &mut raw_arguments.raw_arguments as *mut jni_sys::JavaVMInitArgs as *mut c_void,
562 ));
563 assert_eq!(
564 InitArguments::get_default_or_closest_supported(JniVersion::V1),
565 InitArguments {
566 version: JniVersion::V4,
567 options: resulting_options(),
568 ignore_unrecognized: false
569 }
570 );
571 check_arguments(JniVersion::V1);
572 }
573
574 #[test]
575 fn get_latest_default() {
576 let mut strings_buffer = vec![];
577 let mut options_buffer = vec![];
578 let mut raw_arguments = to_raw(&default_args(), &mut strings_buffer, &mut options_buffer);
579 let _locked = setup_get_default_java_vm_init_args_call(GetDefaultJavaVMInitArgsCall::new(
580 &mut raw_arguments.raw_arguments as *mut jni_sys::JavaVMInitArgs as *mut c_void,
581 ));
582 assert_eq!(
583 InitArguments::get_latest_default(),
584 InitArguments {
585 version: JniVersion::V4,
586 options: resulting_options(),
587 ignore_unrecognized: false
588 }
589 );
590 check_arguments(JniVersion::V8);
591 }
592
593 #[test]
594 fn with_options() {
595 let arguments = InitArguments {
596 version: JniVersion::V4,
597 options: vec![JvmOption::CheckedJni],
598 ignore_unrecognized: false,
599 };
600 assert_eq!(
601 arguments.with_options(&[
602 JvmOption::Verbose(JvmVerboseOption::Gc),
603 JvmOption::Unknown("test".to_owned()),
604 ]),
605 InitArguments {
606 version: JniVersion::V4,
607 options: vec![
608 JvmOption::CheckedJni,
609 JvmOption::Verbose(JvmVerboseOption::Gc),
610 JvmOption::Unknown("test".to_owned()),
611 ],
612 ignore_unrecognized: false,
613 }
614 );
615 }
616
617 #[test]
618 fn with_option() {
619 let arguments = InitArguments {
620 version: JniVersion::V4,
621 options: vec![JvmOption::CheckedJni],
622 ignore_unrecognized: false,
623 };
624 assert_eq!(
625 arguments.with_option(JvmOption::Verbose(JvmVerboseOption::Gc)),
626 InitArguments {
627 version: JniVersion::V4,
628 options: vec![
629 JvmOption::CheckedJni,
630 JvmOption::Verbose(JvmVerboseOption::Gc),
631 ],
632 ignore_unrecognized: false,
633 }
634 );
635 }
636
637 #[test]
638 fn unchecked() {
639 let arguments = InitArguments {
640 version: JniVersion::V4,
641 options: vec![
642 JvmOption::Verbose(JvmVerboseOption::Gc),
643 JvmOption::CheckedJni,
644 ],
645 ignore_unrecognized: false,
646 };
647 assert_eq!(
648 arguments.unchecked(),
649 InitArguments {
650 version: JniVersion::V4,
651 options: vec![JvmOption::Verbose(JvmVerboseOption::Gc)],
652 ignore_unrecognized: false,
653 }
654 );
655 }
656
657 #[test]
658 fn checked() {
659 let arguments = InitArguments {
660 version: JniVersion::V4,
661 options: vec![JvmOption::Verbose(JvmVerboseOption::Gc)],
662 ignore_unrecognized: false,
663 };
664 assert_eq!(
665 arguments.checked(),
666 InitArguments {
667 version: JniVersion::V4,
668 options: vec![
669 JvmOption::Verbose(JvmVerboseOption::Gc),
670 JvmOption::CheckedJni,
671 ],
672 ignore_unrecognized: false,
673 }
674 );
675 }
676
677 #[test]
678 fn ignore_unrecognized_options() {
679 let arguments = InitArguments {
680 version: JniVersion::V4,
681 options: vec![],
682 ignore_unrecognized: false,
683 };
684 assert_eq!(
685 arguments.ignore_unrecognized_options(),
686 InitArguments {
687 version: JniVersion::V4,
688 options: vec![],
689 ignore_unrecognized: true,
690 }
691 );
692 }
693
694 #[test]
695 fn fail_on_unrecognized_options() {
696 let arguments = InitArguments {
697 version: JniVersion::V4,
698 options: vec![],
699 ignore_unrecognized: true,
700 };
701 assert_eq!(
702 arguments.fail_on_unrecognized_options(),
703 InitArguments {
704 version: JniVersion::V4,
705 options: vec![],
706 ignore_unrecognized: false,
707 }
708 );
709 }
710
711 #[test]
712 fn version() {
713 let arguments = InitArguments {
714 version: JniVersion::V4,
715 options: vec![],
716 ignore_unrecognized: true,
717 };
718 assert_eq!(arguments.version(), JniVersion::V4);
719 }
720}
721
722fn to_bool(value: jni_sys::jboolean) -> bool {
723 match value {
724 jni_sys::JNI_TRUE => true,
725 jni_sys::JNI_FALSE => false,
726 value => panic!("Unexpected jboolean value {}", value),
727 }
728}