use crate::{JNI_OK, JNIEnv, JNIInvPtr, JavaVM, JavaVMInitArgs, JavaVMOption, jint};
use alloc::ffi::CString;
use alloc::vec::Vec;
use core::ffi::{c_char, c_void};
use core::ptr::null_mut;
use sync_ptr::SyncMutPtr;
#[cfg(feature = "loadjvm")]
use alloc::boxed::Box;
#[cfg(feature = "loadjvm")]
use alloc::string::String;
#[cfg(all(feature = "loadjvm", not(feature = "dynlink")))]
use alloc::string::ToString;
#[cfg(feature = "loadjvm")]
use core::error::Error;
#[cfg(feature = "loadjvm")]
use core::fmt::{Display, Formatter};
#[cfg(not(feature = "dynlink"))]
use crate::jsize;
#[cfg(not(feature = "dynlink"))]
use sync_ptr::{SyncFnPtr, sync_fn_ptr_from_addr};
#[cfg(feature = "dynlink")]
mod dynlink {
use crate::{JNIEnv, JNIInvPtr, JavaVMInitArgs, jint, jsize};
unsafe extern "system" {
pub fn JNI_CreateJavaVM(invoker: *mut JNIInvPtr, env: *mut JNIEnv, initargs: *mut JavaVMInitArgs) -> jint;
pub fn JNI_GetCreatedJavaVMs(array: *mut JNIInvPtr, len: jsize, out: *mut jsize) -> jint;
}
}
#[cfg(not(feature = "dynlink"))]
type JNI_CreateJavaVM = unsafe extern "C" fn(*mut JNIInvPtr, *mut JNIEnv, *mut JavaVMInitArgs) -> jint;
#[cfg(not(feature = "dynlink"))]
type JNI_GetCreatedJavaVMs = unsafe extern "C" fn(*mut JNIInvPtr, jsize, *mut jsize) -> jint;
#[cfg(not(feature = "dynlink"))]
#[derive(Debug, Copy, Clone)]
pub struct JNIDynamicLink {
JNI_CreateJavaVM: SyncFnPtr<JNI_CreateJavaVM>,
JNI_GetCreatedJavaVMs: SyncFnPtr<JNI_GetCreatedJavaVMs>,
}
#[cfg(not(feature = "dynlink"))]
impl JNIDynamicLink {
#[must_use]
pub fn new(JNI_CreateJavaVM: *const c_void, JNI_GetCreatedJavaVMs: *const c_void) -> Self {
assert!(!JNI_GetCreatedJavaVMs.is_null(), "JNI_GetCreatedJavaVMs is null");
assert!(!JNI_CreateJavaVM.is_null(), "JNI_CreateJavaVM is null");
unsafe {
Self {
JNI_CreateJavaVM: sync_fn_ptr_from_addr!(JNI_CreateJavaVM, JNI_CreateJavaVM),
JNI_GetCreatedJavaVMs: sync_fn_ptr_from_addr!(JNI_GetCreatedJavaVMs, JNI_GetCreatedJavaVMs),
}
}
}
#[must_use]
pub fn JNI_CreateJavaVM(&self) -> JNI_CreateJavaVM {
self.JNI_CreateJavaVM.unwrap()
}
#[must_use]
pub fn JNI_GetCreatedJavaVMs(&self) -> JNI_GetCreatedJavaVMs {
self.JNI_GetCreatedJavaVMs.unwrap()
}
}
#[cfg(feature = "std")]
#[cfg(not(feature = "dynlink"))]
mod std_link {
use crate::linking::JNIDynamicLink;
static LINK: std::sync::RwLock<Option<JNIDynamicLink>> = std::sync::RwLock::new(None);
pub fn link_write() -> std::sync::RwLockWriteGuard<'static, Option<JNIDynamicLink>> {
LINK.write().unwrap_or_else(|e| {
LINK.clear_poison();
e.into_inner()
})
}
pub fn link_read() -> std::sync::RwLockReadGuard<'static, Option<JNIDynamicLink>> {
LINK.read().unwrap_or_else(|e| {
LINK.clear_poison();
e.into_inner()
})
}
}
#[cfg(feature = "std")]
#[cfg(not(feature = "dynlink"))]
pub use std_link::{link_read, link_write};
#[cfg(not(feature = "std"))]
#[cfg(not(feature = "dynlink"))]
mod spin_link {
use crate::linking::JNIDynamicLink;
use core::cell::UnsafeCell;
use core::ops::{Deref, DerefMut};
use core::sync::atomic::AtomicUsize;
use core::sync::atomic::Ordering::SeqCst;
struct UCellWrapper(UnsafeCell<Option<JNIDynamicLink>>);
unsafe impl Send for UCellWrapper {}
unsafe impl Sync for UCellWrapper {}
static LOCK: AtomicUsize = AtomicUsize::new(0);
static LINK: UCellWrapper = UCellWrapper(UnsafeCell::new(None));
const USIZE_HALF: usize = usize::MAX / 2;
pub struct SpinLockGuard;
impl Deref for SpinLockGuard {
type Target = Option<JNIDynamicLink>;
fn deref(&self) -> &Self::Target {
unsafe { &*LINK.0.get() }
}
}
impl Drop for SpinLockGuard {
fn drop(&mut self) {
let r = LOCK.fetch_sub(1, SeqCst);
debug_assert!(r != 0);
}
}
pub struct SpinLockGuardMut;
impl Deref for SpinLockGuardMut {
type Target = Option<JNIDynamicLink>;
fn deref(&self) -> &Self::Target {
unsafe { &*LINK.0.get() }
}
}
impl DerefMut for SpinLockGuardMut {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *LINK.0.get() }
}
}
impl Drop for SpinLockGuardMut {
fn drop(&mut self) {
let r = LOCK.fetch_sub(USIZE_HALF, SeqCst);
debug_assert!(r >= USIZE_HALF);
}
}
pub fn link_write() -> SpinLockGuardMut {
loop {
if LOCK.compare_exchange(0, USIZE_HALF, SeqCst, SeqCst).is_ok() {
return SpinLockGuardMut;
}
core::hint::spin_loop();
}
}
pub fn link_read() -> SpinLockGuard {
loop {
if LOCK.fetch_add(1, SeqCst) < USIZE_HALF - 1 {
return SpinLockGuard;
}
LOCK.fetch_sub(1, SeqCst);
core::hint::spin_loop();
}
}
}
#[cfg(not(feature = "std"))]
#[cfg(not(feature = "dynlink"))]
pub use spin_link::{link_read, link_write};
#[cfg(not(feature = "dynlink"))]
#[must_use]
pub fn init_dynamic_link(JNI_CreateJavaVM: *const c_void, JNI_GetCreatedJavaVMs: *const c_void) -> bool {
let mut guard = link_write();
if guard.is_some() {
return false;
}
*guard = Some(JNIDynamicLink::new(JNI_CreateJavaVM, JNI_GetCreatedJavaVMs));
true
}
#[cfg(feature = "dynlink")]
#[allow(clippy::missing_const_for_fn)]
#[must_use]
pub fn init_dynamic_link(_: *const c_void, _: *const c_void) -> bool {
false
}
#[cfg(not(feature = "dynlink"))]
#[must_use]
pub fn is_jvm_loaded() -> bool {
link_read().is_some()
}
#[cfg(not(feature = "dynlink"))]
fn get_link() -> JNIDynamicLink {
link_read().expect("jni_simple::init_dynamic_link not called")
}
#[cfg(feature = "dynlink")]
#[must_use]
#[allow(clippy::missing_const_for_fn)]
pub fn is_jvm_loaded() -> bool {
true
}
#[cfg(feature = "loadjvm")]
#[non_exhaustive]
#[derive(Debug)]
pub enum LoadFromLibraryError {
AlreadyLoaded,
LoadingSharedObjectFailed {
path: String,
error: Box<dyn Error>,
},
JNICreateJavaVmNotFound {
path: String,
error: Box<dyn Error>,
},
JNIGetCreatedJavaVMsNotFound {
path: String,
error: Box<dyn Error>,
},
}
#[cfg(feature = "loadjvm")]
impl Display for LoadFromLibraryError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::AlreadyLoaded => f.write_str("The dynamic linker has already loaded the jvm."),
Self::LoadingSharedObjectFailed { .. } => f.write_str("The dynamic linker failed to load the jvm shared object."),
Self::JNICreateJavaVmNotFound { .. } => f.write_str("The dynamic linker could not find the JNI_CreateJavaVM symbol in the shared object."),
Self::JNIGetCreatedJavaVMsNotFound { .. } => f.write_str("The dynamic linker could not find the JNI_GetCreatedJavaVMs symbol in the shared object."),
}
}
}
#[cfg(feature = "loadjvm")]
impl Error for LoadFromLibraryError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::AlreadyLoaded => None,
Self::LoadingSharedObjectFailed { error, .. } | Self::JNICreateJavaVmNotFound { error, .. } | Self::JNIGetCreatedJavaVMsNotFound { error, .. } => Some(&**error),
}
}
}
#[cfg(feature = "loadjvm")]
impl From<LoadFromLibraryError> for String {
fn from(value: LoadFromLibraryError) -> Self {
alloc::format!("{value}")
}
}
#[cfg(feature = "loadjvm")]
#[cfg(not(feature = "dynlink"))]
pub unsafe fn load_jvm_from_library(path: &str) -> Result<(), LoadFromLibraryError> {
let mut guard = link_write();
if guard.is_some() {
drop(guard);
return Err(LoadFromLibraryError::AlreadyLoaded);
}
unsafe {
let lib = libloading::Library::new(path).map_err(|e| LoadFromLibraryError::LoadingSharedObjectFailed {
path: path.to_string(),
error: Box::new(e),
})?;
#[cfg(any(target_os = "netbsd", target_vendor = "apple"))]
let lib = core::mem::ManuallyDrop::new(lib);
let JNI_CreateJavaVM_ptr = lib
.get::<JNI_CreateJavaVM>(b"JNI_CreateJavaVM\0")
.map_err(|e| LoadFromLibraryError::JNICreateJavaVmNotFound {
path: path.to_string(),
error: Box::new(e),
})?
.try_as_raw_ptr()
.ok_or_else(|| LoadFromLibraryError::JNICreateJavaVmNotFound {
path: path.to_string(),
error: Box::new(libloading::Error::DlSymUnknown),
})?;
if JNI_CreateJavaVM_ptr.is_null() {
return Err(LoadFromLibraryError::JNICreateJavaVmNotFound {
path: path.to_string(),
error: Box::new(libloading::Error::DlSymUnknown),
});
}
let JNI_GetCreatedJavaVMs_ptr = lib
.get::<JNI_GetCreatedJavaVMs>(b"JNI_GetCreatedJavaVMs\0")
.map_err(|e| LoadFromLibraryError::JNIGetCreatedJavaVMsNotFound {
path: path.to_string(),
error: Box::new(e),
})?
.try_as_raw_ptr()
.ok_or_else(|| LoadFromLibraryError::JNIGetCreatedJavaVMsNotFound {
path: path.to_string(),
error: Box::new(libloading::Error::DlSymUnknown),
})?;
if JNI_GetCreatedJavaVMs_ptr.is_null() {
return Err(LoadFromLibraryError::JNIGetCreatedJavaVMsNotFound {
path: path.to_string(),
error: Box::new(libloading::Error::DlSymUnknown),
});
}
#[cfg(not(any(target_os = "netbsd", target_vendor = "apple")))] core::mem::forget(lib);
*guard = Some(JNIDynamicLink::new(JNI_CreateJavaVM_ptr, JNI_GetCreatedJavaVMs_ptr));
drop(guard);
}
Ok(())
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "dynlink")]
pub unsafe fn load_jvm_from_library(_: &str) -> Result<(), LoadFromLibraryError> {
Err(LoadFromLibraryError::AlreadyLoaded)
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "std")]
#[non_exhaustive]
#[derive(Debug)]
pub enum LoadFromJavaHomeError {
AlreadyLoaded,
LoadingSharedObjectFailed {
path: String,
error: Box<dyn Error>,
},
JNICreateJavaVmNotFound {
path: String,
error: Box<dyn Error>,
},
JNIGetCreatedJavaVMsNotFound {
path: String,
error: Box<dyn Error>,
},
UnknownJavaHomeLayout,
IOError(std::io::Error),
EnvironmentVariableError(std::env::VarError),
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "std")]
impl From<LoadFromJavaHomeFolderError> for LoadFromJavaHomeError {
fn from(value: LoadFromJavaHomeFolderError) -> Self {
match value {
LoadFromJavaHomeFolderError::AlreadyLoaded => Self::AlreadyLoaded,
LoadFromJavaHomeFolderError::LoadingSharedObjectFailed { path, error } => Self::LoadingSharedObjectFailed { path, error },
LoadFromJavaHomeFolderError::JNICreateJavaVmNotFound { path, error } => Self::JNICreateJavaVmNotFound { path, error },
LoadFromJavaHomeFolderError::JNIGetCreatedJavaVMsNotFound { path, error } => Self::JNIGetCreatedJavaVMsNotFound { path, error },
LoadFromJavaHomeFolderError::UnknownJavaHomeLayout => Self::UnknownJavaHomeLayout,
LoadFromJavaHomeFolderError::IOError(e) => Self::IOError(e),
}
}
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "std")]
impl From<LoadFromLibraryError> for LoadFromJavaHomeError {
fn from(value: LoadFromLibraryError) -> Self {
match value {
LoadFromLibraryError::AlreadyLoaded => Self::AlreadyLoaded,
LoadFromLibraryError::LoadingSharedObjectFailed { path, error } => Self::LoadingSharedObjectFailed { path, error },
LoadFromLibraryError::JNICreateJavaVmNotFound { path, error } => Self::JNICreateJavaVmNotFound { path, error },
LoadFromLibraryError::JNIGetCreatedJavaVMsNotFound { path, error } => Self::JNIGetCreatedJavaVMsNotFound { path, error },
}
}
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "std")]
impl Display for LoadFromJavaHomeError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::AlreadyLoaded => f.write_str("The dynamic linker has already loaded the jvm."),
Self::LoadingSharedObjectFailed { .. } => f.write_str("The dynamic linker failed to load the jvm shared object."),
Self::JNICreateJavaVmNotFound { .. } => f.write_str("The dynamic linker could not find the JNI_CreateJavaVM symbol in the shared object."),
Self::JNIGetCreatedJavaVMsNotFound { .. } => f.write_str("The dynamic linker could not find the JNI_GetCreatedJavaVMs symbol in the shared object."),
Self::UnknownJavaHomeLayout => f.write_str("The layout of the java installation was not recognized."),
Self::IOError(_) => f.write_str("I/O Error while determining the layout of the java installation."),
Self::EnvironmentVariableError(_) => f.write_str("The environment variable JAVA_HOME is invalid"),
}
}
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "std")]
impl Error for LoadFromJavaHomeError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::AlreadyLoaded | Self::UnknownJavaHomeLayout => None,
Self::LoadingSharedObjectFailed { error, .. } | Self::JNICreateJavaVmNotFound { error, .. } | Self::JNIGetCreatedJavaVMsNotFound { error, .. } => Some(&**error),
Self::IOError(e) => Some(e),
Self::EnvironmentVariableError(e) => Some(e),
}
}
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "std")]
impl From<LoadFromJavaHomeError> for String {
fn from(value: LoadFromJavaHomeError) -> Self {
alloc::format!("{value}")
}
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "std")]
pub unsafe fn load_jvm_from_java_home() -> Result<(), LoadFromJavaHomeError> {
let java_home = std::env::var("JAVA_HOME").map_err(LoadFromJavaHomeError::EnvironmentVariableError)?;
unsafe {
load_jvm_from_java_home_folder(&java_home)?;
}
Ok(())
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "std")]
#[non_exhaustive]
#[derive(Debug)]
pub enum LoadFromJavaHomeFolderError {
AlreadyLoaded,
LoadingSharedObjectFailed {
path: String,
error: Box<dyn Error>,
},
JNICreateJavaVmNotFound {
path: String,
error: Box<dyn Error>,
},
JNIGetCreatedJavaVMsNotFound {
path: String,
error: Box<dyn Error>,
},
UnknownJavaHomeLayout,
IOError(std::io::Error),
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "std")]
impl Display for LoadFromJavaHomeFolderError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
Self::AlreadyLoaded => f.write_str("The dynamic linker has already loaded the jvm."),
Self::LoadingSharedObjectFailed { .. } => f.write_str("The dynamic linker failed to load the jvm shared object."),
Self::JNICreateJavaVmNotFound { .. } => f.write_str("The dynamic linker could not find the JNI_CreateJavaVM symbol in the shared object."),
Self::JNIGetCreatedJavaVMsNotFound { .. } => f.write_str("The dynamic linker could not find the JNI_GetCreatedJavaVMs symbol in the shared object."),
Self::UnknownJavaHomeLayout => f.write_str("The layout of the java installation was not recognized."),
Self::IOError(_) => f.write_str("I/O Error while determining the layout of the java installation."),
}
}
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "std")]
impl From<LoadFromLibraryError> for LoadFromJavaHomeFolderError {
fn from(value: LoadFromLibraryError) -> Self {
match value {
LoadFromLibraryError::AlreadyLoaded => Self::AlreadyLoaded,
LoadFromLibraryError::LoadingSharedObjectFailed { path, error } => Self::LoadingSharedObjectFailed { path, error },
LoadFromLibraryError::JNICreateJavaVmNotFound { path, error } => Self::JNICreateJavaVmNotFound { path, error },
LoadFromLibraryError::JNIGetCreatedJavaVMsNotFound { path, error } => Self::JNIGetCreatedJavaVMsNotFound { path, error },
}
}
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "std")]
impl From<LoadFromJavaHomeFolderError> for String {
fn from(value: LoadFromJavaHomeFolderError) -> Self {
alloc::format!("{value}")
}
}
#[cfg(feature = "loadjvm")]
#[cfg(feature = "std")]
pub unsafe fn load_jvm_from_java_home_folder(java_home: &str) -> Result<(), LoadFromJavaHomeFolderError> {
static COMMON_LIBJVM_PATHS: &[&[&str]] = &[
#[cfg(all(unix, not(target_vendor = "apple")))]
&["lib", "server", "libjvm.so"], #[cfg(all(unix, not(target_vendor = "apple")))]
&["jre", "lib", "amd64", "server", "libjvm.so"], #[cfg(all(unix, not(target_vendor = "apple")))]
&["lib", "amd64", "server", "libjvm.so"], #[cfg(all(unix, not(target_vendor = "apple")))]
&["jre", "lib", "aarch32", "server", "libjvm.so"], #[cfg(all(unix, not(target_vendor = "apple")))]
&["lib", "aarch32", "server", "libjvm.so"], #[cfg(all(unix, not(target_vendor = "apple")))]
&["jre", "lib", "aarch64", "server", "libjvm.so"], #[cfg(all(unix, not(target_vendor = "apple")))]
&["lib", "aarch64", "server", "libjvm.so"], #[cfg(windows)]
&["jre", "bin", "server", "jvm.dll"], #[cfg(windows)]
&["bin", "server", "jvm.dll"], #[cfg(target_vendor = "apple")]
&["jre", "lib", "server", "libjvm.dylib"], #[cfg(target_vendor = "apple")]
&["Contents", "Home", "jre", "lib", "server", "libjvm.dylib"], #[cfg(target_vendor = "apple")]
&["lib", "server", "libjvm.dylib"], #[cfg(target_vendor = "apple")]
&["Contents", "Home", "lib", "server", "libjvm.dylib"], ];
for parts in COMMON_LIBJVM_PATHS {
let mut buf = std::path::PathBuf::from(java_home);
for part in *parts {
buf.push(part);
}
if buf.try_exists().map_err(LoadFromJavaHomeFolderError::IOError)? {
let full_path = buf
.to_str()
.ok_or_else(|| LoadFromJavaHomeFolderError::IOError(std::io::Error::other("Failed to concatenate JAVA_HOME library path")))?;
unsafe {
load_jvm_from_library(full_path)?;
}
return Ok(());
}
}
Err(LoadFromJavaHomeFolderError::UnknownJavaHomeLayout)
}
pub unsafe fn JNI_GetCreatedJavaVMs(vms: &mut [Option<JavaVM>]) -> Result<usize, jint> {
#[cfg(not(feature = "dynlink"))]
let link = get_link().JNI_GetCreatedJavaVMs();
#[cfg(feature = "dynlink")]
let link = dynlink::JNI_GetCreatedJavaVMs;
let mut buf: [JNIInvPtr; 64] = [SyncMutPtr::null(); 64];
let mut count: jint = 0;
let res = unsafe { link(buf.as_mut_ptr(), 64, &raw mut count) };
if res != JNI_OK {
return Err(res);
}
let count = usize::try_from(count).expect("JNI_GetCreatedJavaVMs did set count to < 0");
for (i, env) in buf.into_iter().enumerate().take(count) {
assert!(!env.is_null(), "JNI_GetCreatedJavaVMs VM #{i} is null! count is {count}");
}
for (i, target) in vms.iter_mut().enumerate() {
if i >= count {
*target = None;
continue;
}
*target = Some(JavaVM { vtable: buf[i] });
}
Ok(count)
}
pub unsafe fn JNI_GetCreatedJavaVMs_first() -> Result<Option<JavaVM>, jint> {
unsafe {
let mut vm = [None];
_ = JNI_GetCreatedJavaVMs(vm.as_mut())?;
Ok(vm[0])
}
}
pub unsafe fn JNI_CreateJavaVM(arguments: *mut JavaVMInitArgs) -> Result<(JavaVM, JNIEnv), jint> {
#[cfg(feature = "asserts")]
{
assert!(!arguments.is_null(), "JNI_CreateJavaVM arguments must not be null");
}
#[cfg(not(feature = "dynlink"))]
let link = get_link().JNI_CreateJavaVM();
#[cfg(feature = "dynlink")]
let link = dynlink::JNI_CreateJavaVM;
let mut jvm: JNIInvPtr = SyncMutPtr::null();
let mut env: JNIEnv = JNIEnv { vtable: null_mut() };
let res = unsafe { link(&raw mut jvm, &raw mut env, arguments) };
if res != JNI_OK {
return Err(res);
}
assert!(!jvm.is_null(), "JNI_CreateJavaVM returned JNI_OK but the JavaVM pointer is null");
assert!(!env.vtable.is_null(), "JNI_CreateJavaVM returned JNI_OK but the JNIEnv pointer is null");
Ok((JavaVM { vtable: jvm }, env))
}
pub unsafe fn JNI_CreateJavaVM_with_string_args<T: AsRef<str>>(version: jint, arguments: &[T], ignore_unrecognized_options: bool) -> Result<(JavaVM, JNIEnv), jint> {
unsafe {
struct DropGuard(*mut c_char);
impl Drop for DropGuard {
fn drop(&mut self) {
unsafe {
_ = CString::from_raw(self.0);
}
}
}
let mut vm_args: Vec<JavaVMOption> = Vec::with_capacity(arguments.len());
let mut dealloc_list = Vec::with_capacity(arguments.len());
for arg in arguments {
let jvm_arg = CString::new(arg.as_ref()).expect("Argument contains 0 byte").into_raw();
dealloc_list.push(DropGuard(jvm_arg));
vm_args.push(JavaVMOption {
optionString: jvm_arg,
extraInfo: null_mut(),
});
}
let mut args = JavaVMInitArgs {
version,
nOptions: i32::try_from(vm_args.len()).expect("Too many arguments"),
options: vm_args.as_mut_ptr(),
ignoreUnrecognized: u8::from(ignore_unrecognized_options),
};
let result = JNI_CreateJavaVM(&raw mut args);
drop(dealloc_list);
result
}
}