use std::{
marker::PhantomData,
os::raw::{c_char, c_void},
ptr, slice, str,
str::FromStr,
sync::{Mutex, MutexGuard},
};
use log::warn;
use crate::{
descriptors::Desc,
errors::*,
objects::{
AutoLocal, GlobalRef, JByteBuffer, JClass, JFieldID, JList, JMap, JMethodID, JObject,
JStaticFieldID, JStaticMethodID, JString, JThrowable, JValue,
},
signature::{JavaType, Primitive, TypeSignature},
strings::{JNIString, JavaStr},
sys::{
self, jarray, jboolean, jbooleanArray, jbyte, jbyteArray, jchar, jcharArray, jdouble,
jdoubleArray, jfloat, jfloatArray, jint, jintArray, jlong, jlongArray, jobjectArray,
jshort, jshortArray, jsize, jvalue, JNINativeMethod,
},
JNIVersion, JavaVM,
};
#[derive(Clone)]
#[repr(transparent)]
pub struct JNIEnv<'a> {
internal: *mut sys::JNIEnv,
lifetime: PhantomData<&'a ()>,
}
impl<'a> JNIEnv<'a> {
pub unsafe fn from_raw(ptr: *mut sys::JNIEnv) -> Result<Self> {
non_null!(ptr, "from_raw ptr argument");
Ok(JNIEnv {
internal: ptr,
lifetime: PhantomData,
})
}
pub fn get_version(&self) -> Result<JNIVersion> {
Ok(jni_unchecked!(self.internal, GetVersion).into())
}
pub fn define_class<S>(&self, name: S, loader: JObject<'a>, buf: &[u8]) -> Result<JClass<'a>>
where
S: Into<JNIString>,
{
let name = name.into();
let class = jni_non_null_call!(
self.internal,
DefineClass,
name.as_ptr(),
loader.into_inner(),
buf.as_ptr() as *const jbyte,
buf.len() as jsize
);
Ok(class)
}
pub fn find_class<S>(&self, name: S) -> Result<JClass<'a>>
where
S: Into<JNIString>,
{
let name = name.into();
let class = jni_non_null_call!(self.internal, FindClass, name.as_ptr());
Ok(class)
}
pub fn get_superclass<'c, T>(&self, class: T) -> Result<JClass<'a>>
where
T: Desc<'a, JClass<'c>>,
{
let class = class.lookup(self)?;
Ok(jni_non_void_call!(self.internal, GetSuperclass, class.into_inner()).into())
}
pub fn is_assignable_from<'t, 'u, T, U>(&self, class1: T, class2: U) -> Result<bool>
where
T: Desc<'a, JClass<'t>>,
U: Desc<'a, JClass<'u>>,
{
let class1 = class1.lookup(self)?;
let class2 = class2.lookup(self)?;
Ok(jni_unchecked!(
self.internal,
IsAssignableFrom,
class1.into_inner(),
class2.into_inner()
) == sys::JNI_TRUE)
}
pub fn is_instance_of<'c, O, T>(&self, object: O, class: T) -> Result<bool>
where
O: Into<JObject<'a>>,
T: Desc<'a, JClass<'c>>,
{
let class = class.lookup(self)?;
Ok(jni_unchecked!(
self.internal,
IsInstanceOf,
object.into().into_inner(),
class.into_inner()
) == sys::JNI_TRUE)
}
pub fn is_same_object<'b, 'c, O, T>(&self, ref1: O, ref2: T) -> Result<bool>
where
O: Into<JObject<'b>>,
T: Into<JObject<'c>>,
{
Ok(jni_unchecked!(
self.internal,
IsSameObject,
ref1.into().into_inner(),
ref2.into().into_inner()
) == sys::JNI_TRUE)
}
pub fn throw<'e, E>(&self, obj: E) -> Result<()>
where
E: Desc<'a, JThrowable<'e>>,
{
let throwable = obj.lookup(self)?;
let res: i32 = jni_unchecked!(self.internal, Throw, throwable.into_inner());
if res == 0 {
Ok(())
} else {
Err(format!("throw failed with code {}", res).into())
}
}
pub fn throw_new<'c, S, T>(&self, class: T, msg: S) -> Result<()>
where
S: Into<JNIString>,
T: Desc<'a, JClass<'c>>,
{
let class = class.lookup(self)?;
let msg = msg.into();
let res: i32 = jni_unchecked!(self.internal, ThrowNew, class.into_inner(), msg.as_ptr());
if res == 0 {
Ok(())
} else {
Err(format!("throw_new failed with code {}", res).into())
}
}
pub fn exception_occurred(&self) -> Result<JThrowable<'a>> {
let throwable = jni_unchecked!(self.internal, ExceptionOccurred);
Ok(JThrowable::from(throwable))
}
pub fn exception_describe(&self) -> Result<()> {
jni_unchecked!(self.internal, ExceptionDescribe);
Ok(())
}
pub fn exception_clear(&self) -> Result<()> {
jni_unchecked!(self.internal, ExceptionClear);
Ok(())
}
#[allow(unused_variables, unreachable_code)]
pub fn fatal_error<S: Into<JNIString>>(&self, msg: S) -> ! {
let msg = msg.into();
let res: Result<()> = catch!({
jni_unchecked!(self.internal, FatalError, msg.as_ptr());
unreachable!()
});
panic!(res.unwrap_err());
}
pub fn exception_check(&self) -> Result<bool> {
let check = jni_unchecked!(self.internal, ExceptionCheck) == sys::JNI_TRUE;
Ok(check)
}
pub fn new_direct_byte_buffer(&self, data: &mut [u8]) -> Result<JByteBuffer<'a>> {
let obj: JObject = jni_non_null_call!(
self.internal,
NewDirectByteBuffer,
data.as_mut_ptr() as *mut c_void,
data.len() as jlong
);
Ok(JByteBuffer::from(obj))
}
pub fn get_direct_buffer_address(&self, buf: JByteBuffer) -> Result<&mut [u8]> {
non_null!(buf, "get_direct_buffer_address argument");
let ptr: *mut c_void =
jni_unchecked!(self.internal, GetDirectBufferAddress, buf.into_inner());
non_null!(ptr, "get_direct_buffer_address return value");
let capacity = self.get_direct_buffer_capacity(buf)?;
unsafe { Ok(slice::from_raw_parts_mut(ptr as *mut u8, capacity as usize)) }
}
pub fn get_direct_buffer_capacity(&self, buf: JByteBuffer) -> Result<jlong> {
let capacity = jni_unchecked!(self.internal, GetDirectBufferCapacity, buf.into_inner());
match capacity {
-1 => Err(Error::from(ErrorKind::Other(sys::JNI_ERR))),
_ => Ok(capacity),
}
}
pub fn new_global_ref<O>(&self, obj: O) -> Result<GlobalRef>
where
O: Into<JObject<'a>>,
{
let new_ref: JObject =
jni_unchecked!(self.internal, NewGlobalRef, obj.into().into_inner()).into();
let global = unsafe { GlobalRef::from_raw(self.get_java_vm()?, new_ref.into_inner()) };
Ok(global)
}
pub fn new_local_ref<T>(&self, obj: JObject<'a>) -> Result<JObject<'a>> {
let local: JObject = jni_unchecked!(self.internal, NewLocalRef, obj.into_inner()).into();
Ok(local)
}
pub fn auto_local<'b, O>(&'b self, obj: O) -> AutoLocal<'a, 'b>
where
O: Into<JObject<'a>>,
{
AutoLocal::new(self, obj.into())
}
pub fn delete_local_ref(&self, obj: JObject) -> Result<()> {
jni_unchecked!(self.internal, DeleteLocalRef, obj.into_inner());
Ok(())
}
pub fn push_local_frame(&self, capacity: i32) -> Result<()> {
let res = jni_unchecked!(self.internal, PushLocalFrame, capacity);
jni_error_code_to_result(res)
}
pub fn pop_local_frame(&self, result: JObject<'a>) -> Result<JObject<'a>> {
Ok(jni_unchecked!(self.internal, PopLocalFrame, result.into_inner()).into())
}
pub fn with_local_frame<F>(&self, capacity: i32, f: F) -> Result<JObject<'a>>
where
F: FnOnce() -> Result<JObject<'a>>,
{
self.push_local_frame(capacity)?;
let res = f();
match res {
Ok(obj) => self.pop_local_frame(obj),
Err(e) => {
self.pop_local_frame(JObject::null())?;
Err(e)
}
}
}
pub fn alloc_object<'c, T>(&self, class: T) -> Result<JObject<'a>>
where
T: Desc<'a, JClass<'c>>,
{
let class = class.lookup(self)?;
Ok(jni_non_null_call!(
self.internal,
AllocObject,
class.into_inner()
))
}
#[allow(clippy::redundant_closure_call)]
fn get_method_id_base<'c, T, U, V, C, R>(
&self,
class: T,
name: U,
sig: V,
get_method: C,
) -> Result<R>
where
T: Desc<'a, JClass<'c>>,
U: Into<JNIString>,
V: Into<JNIString>,
C: for<'d> Fn(&JClass<'d>, &JNIString, &JNIString) -> Result<R>,
{
let class = class.lookup(self)?;
let ffi_name = name.into();
let sig = sig.into();
let res: Result<R> = catch!({ get_method(&class, &ffi_name, &sig) });
match res {
Ok(m) => Ok(m),
Err(e) => match *e.kind() {
ErrorKind::NullPtr(_) => {
let name: String = ffi_name.into();
let sig: String = sig.into();
Err(ErrorKind::MethodNotFound(name, sig).into())
}
_ => Err(e),
},
}
}
pub fn get_method_id<'c, T, U, V>(&self, class: T, name: U, sig: V) -> Result<JMethodID<'a>>
where
T: Desc<'a, JClass<'c>>,
U: Into<JNIString>,
V: Into<JNIString>,
{
self.get_method_id_base(class, name, sig, |class, name, sig| {
Ok(jni_non_null_call!(
self.internal,
GetMethodID,
class.into_inner(),
name.as_ptr(),
sig.as_ptr()
))
})
}
pub fn get_static_method_id<'c, T, U, V>(
&self,
class: T,
name: U,
sig: V,
) -> Result<JStaticMethodID<'a>>
where
T: Desc<'a, JClass<'c>>,
U: Into<JNIString>,
V: Into<JNIString>,
{
self.get_method_id_base(class, name, sig, |class, name, sig| {
Ok(jni_non_null_call!(
self.internal,
GetStaticMethodID,
class.into_inner(),
name.as_ptr(),
sig.as_ptr()
))
})
}
pub fn get_field_id<'c, T, U, V>(&self, class: T, name: U, sig: V) -> Result<JFieldID<'a>>
where
T: Desc<'a, JClass<'c>>,
U: Into<JNIString>,
V: Into<JNIString>,
{
let class = class.lookup(self)?;
let ffi_name = name.into();
let ffi_sig = sig.into();
let res: Result<JFieldID> = catch!({
Ok(jni_non_null_call!(
self.internal,
GetFieldID,
class.into_inner(),
ffi_name.as_ptr(),
ffi_sig.as_ptr()
))
});
match res {
Ok(m) => Ok(m),
Err(e) => match *e.kind() {
ErrorKind::NullPtr(_) => {
let name: String = ffi_name.into();
let sig: String = ffi_sig.into();
Err(ErrorKind::FieldNotFound(name, sig).into())
}
_ => Err(e),
},
}
}
pub fn get_static_field_id<'c, T, U, V>(
&self,
class: T,
name: U,
sig: V,
) -> Result<JStaticFieldID<'a>>
where
T: Desc<'a, JClass<'c>>,
U: Into<JNIString>,
V: Into<JNIString>,
{
let class = class.lookup(self)?;
let ffi_name = name.into();
let ffi_sig = sig.into();
let res: Result<JStaticFieldID> = catch!({
Ok(jni_non_null_call!(
self.internal,
GetStaticFieldID,
class.into_inner(),
ffi_name.as_ptr(),
ffi_sig.as_ptr()
))
});
match res {
Ok(m) => Ok(m),
Err(e) => match *e.kind() {
ErrorKind::NullPtr(_) => {
let name: String = ffi_name.into();
let sig: String = ffi_sig.into();
Err(ErrorKind::FieldNotFound(name, sig).into())
}
_ => Err(e),
},
}
}
pub fn get_object_class<'b, O>(&self, obj: O) -> Result<JClass<'a>>
where
O: Into<JObject<'b>>,
{
let obj = obj.into();
non_null!(obj, "get_object_class");
Ok(jni_unchecked!(self.internal, GetObjectClass, obj.into_inner()).into())
}
pub fn call_static_method_unchecked<'c, 'm, T, U>(
&self,
class: T,
method_id: U,
ret: JavaType,
args: &[JValue],
) -> Result<JValue<'a>>
where
T: Desc<'a, JClass<'c>>,
U: Desc<'a, JStaticMethodID<'m>>,
{
let class = class.lookup(self)?;
let method_id = method_id.lookup(self)?.into_inner();
let class = class.into_inner();
let args: Vec<jvalue> = args.iter().map(|v| v.to_jni()).collect();
let jni_args = args.as_ptr();
Ok(match ret {
JavaType::Object(_) | JavaType::Array(_) => {
let obj: JObject = jni_non_void_call!(
self.internal,
CallStaticObjectMethodA,
class,
method_id,
jni_args
)
.into();
obj.into()
}
JavaType::Method(_) => unimplemented!(),
JavaType::Primitive(p) => match p {
Primitive::Boolean => jni_non_void_call!(
self.internal,
CallStaticBooleanMethodA,
class,
method_id,
jni_args
)
.into(),
Primitive::Char => jni_non_void_call!(
self.internal,
CallStaticCharMethodA,
class,
method_id,
jni_args
)
.into(),
Primitive::Short => jni_non_void_call!(
self.internal,
CallStaticShortMethodA,
class,
method_id,
jni_args
)
.into(),
Primitive::Int => jni_non_void_call!(
self.internal,
CallStaticIntMethodA,
class,
method_id,
jni_args
)
.into(),
Primitive::Long => jni_non_void_call!(
self.internal,
CallStaticLongMethodA,
class,
method_id,
jni_args
)
.into(),
Primitive::Float => jni_non_void_call!(
self.internal,
CallStaticFloatMethodA,
class,
method_id,
jni_args
)
.into(),
Primitive::Double => jni_non_void_call!(
self.internal,
CallStaticDoubleMethodA,
class,
method_id,
jni_args
)
.into(),
Primitive::Byte => jni_non_void_call!(
self.internal,
CallStaticByteMethodA,
class,
method_id,
jni_args
)
.into(),
Primitive::Void => {
jni_void_call!(
self.internal,
CallStaticVoidMethodA,
class,
method_id,
jni_args
);
return Ok(JValue::Void);
}
},
})
}
pub fn call_method_unchecked<'m, O, T>(
&self,
obj: O,
method_id: T,
ret: JavaType,
args: &[JValue],
) -> Result<JValue<'a>>
where
O: Into<JObject<'a>>,
T: Desc<'a, JMethodID<'m>>,
{
let method_id = method_id.lookup(self)?.into_inner();
let obj = obj.into().into_inner();
let args: Vec<jvalue> = args.iter().map(|v| v.to_jni()).collect();
let jni_args = args.as_ptr();
Ok(match ret {
JavaType::Object(_) | JavaType::Array(_) => {
let obj: JObject =
jni_non_void_call!(self.internal, CallObjectMethodA, obj, method_id, jni_args)
.into();
obj.into()
}
JavaType::Method(_) => unimplemented!(),
JavaType::Primitive(p) => match p {
Primitive::Boolean => {
jni_non_void_call!(self.internal, CallBooleanMethodA, obj, method_id, jni_args)
.into()
}
Primitive::Char => {
jni_non_void_call!(self.internal, CallCharMethodA, obj, method_id, jni_args)
.into()
}
Primitive::Short => {
jni_non_void_call!(self.internal, CallShortMethodA, obj, method_id, jni_args)
.into()
}
Primitive::Int => {
jni_non_void_call!(self.internal, CallIntMethodA, obj, method_id, jni_args)
.into()
}
Primitive::Long => {
jni_non_void_call!(self.internal, CallLongMethodA, obj, method_id, jni_args)
.into()
}
Primitive::Float => {
jni_non_void_call!(self.internal, CallFloatMethodA, obj, method_id, jni_args)
.into()
}
Primitive::Double => {
jni_non_void_call!(self.internal, CallDoubleMethodA, obj, method_id, jni_args)
.into()
}
Primitive::Byte => {
jni_non_void_call!(self.internal, CallByteMethodA, obj, method_id, jni_args)
.into()
}
Primitive::Void => {
jni_void_call!(self.internal, CallVoidMethodA, obj, method_id, jni_args);
return Ok(JValue::Void);
}
},
})
}
pub fn call_method<O, S, T>(
&self,
obj: O,
name: S,
sig: T,
args: &[JValue],
) -> Result<JValue<'a>>
where
O: Into<JObject<'a>>,
S: Into<JNIString>,
T: Into<JNIString> + AsRef<str>,
{
let obj = obj.into();
non_null!(obj, "call_method obj argument");
let parsed = TypeSignature::from_str(sig.as_ref())?;
if parsed.args.len() != args.len() {
return Err(ErrorKind::InvalidArgList.into());
}
let class = self.auto_local(self.get_object_class(obj)?);
self.call_method_unchecked(obj, (&class, name, sig), parsed.ret, args)
}
pub fn call_static_method<'c, T, U, V>(
&self,
class: T,
name: U,
sig: V,
args: &[JValue],
) -> Result<JValue<'a>>
where
T: Desc<'a, JClass<'c>>,
U: Into<JNIString>,
V: Into<JNIString> + AsRef<str>,
{
let parsed = TypeSignature::from_str(&sig)?;
if parsed.args.len() != args.len() {
return Err(ErrorKind::InvalidArgList.into());
}
let class = class.lookup(self)?;
self.call_static_method_unchecked(class, (class, name, sig), parsed.ret, args)
}
pub fn new_object<'c, T, U>(
&self,
class: T,
ctor_sig: U,
ctor_args: &[JValue],
) -> Result<JObject<'a>>
where
T: Desc<'a, JClass<'c>>,
U: Into<JNIString> + AsRef<str>,
{
let parsed = TypeSignature::from_str(&ctor_sig)?;
if parsed.args.len() != ctor_args.len() {
return Err(ErrorKind::InvalidArgList.into());
}
if parsed.ret != JavaType::Primitive(Primitive::Void) {
return Err(ErrorKind::InvalidCtorReturn.into());
}
let class = class.lookup(self)?;
let method_id: JMethodID = (class, ctor_sig).lookup(self)?;
self.new_object_unchecked(class, method_id, ctor_args)
}
pub fn new_object_unchecked<'c, T>(
&self,
class: T,
ctor_id: JMethodID,
ctor_args: &[JValue],
) -> Result<JObject<'a>>
where
T: Desc<'a, JClass<'c>>,
{
let class = class.lookup(self)?;
let jni_args: Vec<jvalue> = ctor_args.iter().map(|v| v.to_jni()).collect();
let jni_args = jni_args.as_ptr();
Ok(jni_non_null_call!(
self.internal,
NewObjectA,
class.into_inner(),
ctor_id.into_inner(),
jni_args
))
}
pub fn get_list(&self, obj: JObject<'a>) -> Result<JList<'a, '_>> {
non_null!(obj, "get_list obj argument");
JList::from_env(self, obj)
}
pub fn get_map(&self, obj: JObject<'a>) -> Result<JMap<'a, '_>> {
non_null!(obj, "get_map obj argument");
JMap::from_env(self, obj)
}
pub fn get_string(&self, obj: JString<'a>) -> Result<JavaStr<'a, '_>> {
non_null!(obj, "get_string obj argument");
JavaStr::from_env(self, obj)
}
pub fn get_string_utf_chars(&self, obj: JString) -> Result<*const c_char> {
non_null!(obj, "get_string_utf_chars obj argument");
let ptr: *const c_char = jni_non_null_call!(
self.internal,
GetStringUTFChars,
obj.into_inner(),
::std::ptr::null::<jboolean>() as *mut jboolean
);
Ok(ptr)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn release_string_utf_chars(&self, obj: JString, arr: *const c_char) -> Result<()> {
non_null!(obj, "release_string_utf_chars obj argument");
jni_unchecked!(self.internal, ReleaseStringUTFChars, obj.into_inner(), arr);
Ok(())
}
pub fn new_string<S: Into<JNIString>>(&self, from: S) -> Result<JString<'a>> {
let ffi_str = from.into();
Ok(jni_non_null_call!(
self.internal,
NewStringUTF,
ffi_str.as_ptr()
))
}
pub fn get_array_length(&self, array: jarray) -> Result<jsize> {
non_null!(array, "get_array_length array argument");
let len: jsize = jni_unchecked!(self.internal, GetArrayLength, array);
Ok(len)
}
pub fn new_object_array<'c, T, U>(
&self,
length: jsize,
element_class: T,
initial_element: U,
) -> Result<jobjectArray>
where
T: Desc<'a, JClass<'c>>,
U: Into<JObject<'a>>,
{
let class = element_class.lookup(self)?;
Ok(jni_non_null_call!(
self.internal,
NewObjectArray,
length,
class.into_inner(),
initial_element.into().into_inner()
))
}
pub fn get_object_array_element(
&self,
array: jobjectArray,
index: jsize,
) -> Result<JObject<'a>> {
non_null!(array, "get_object_array_element array argument");
Ok(jni_non_void_call!(self.internal, GetObjectArrayElement, array, index).into())
}
pub fn set_object_array_element<O>(
&self,
array: jobjectArray,
index: jsize,
value: O,
) -> Result<()>
where
O: Into<JObject<'a>>,
{
non_null!(array, "set_object_array_element array argument");
jni_void_call!(
self.internal,
SetObjectArrayElement,
array,
index,
value.into().into_inner()
);
Ok(())
}
pub fn byte_array_from_slice(&self, buf: &[u8]) -> Result<jbyteArray> {
let length = buf.len() as i32;
let bytes: jbyteArray = self.new_byte_array(length)?;
jni_unchecked!(
self.internal,
SetByteArrayRegion,
bytes,
0,
length,
buf.as_ptr() as *const i8
);
Ok(bytes)
}
pub fn convert_byte_array(&self, array: jbyteArray) -> Result<Vec<u8>> {
non_null!(array, "convert_byte_array array argument");
let length = jni_non_void_call!(self.internal, GetArrayLength, array);
let mut vec = vec![0u8; length as usize];
jni_unchecked!(
self.internal,
GetByteArrayRegion,
array,
0,
length,
vec.as_mut_ptr() as *mut i8
);
Ok(vec)
}
pub fn new_boolean_array(&self, length: jsize) -> Result<jbooleanArray> {
let array: jbooleanArray = jni_non_null_call!(self.internal, NewBooleanArray, length);
Ok(array)
}
pub fn new_byte_array(&self, length: jsize) -> Result<jbyteArray> {
let array: jbyteArray = jni_non_null_call!(self.internal, NewByteArray, length);
Ok(array)
}
pub fn new_char_array(&self, length: jsize) -> Result<jcharArray> {
let array: jcharArray = jni_non_null_call!(self.internal, NewCharArray, length);
Ok(array)
}
pub fn new_short_array(&self, length: jsize) -> Result<jshortArray> {
let array: jshortArray = jni_non_null_call!(self.internal, NewShortArray, length);
Ok(array)
}
pub fn new_int_array(&self, length: jsize) -> Result<jintArray> {
let array: jintArray = jni_non_null_call!(self.internal, NewIntArray, length);
Ok(array)
}
pub fn new_long_array(&self, length: jsize) -> Result<jlongArray> {
let array: jlongArray = jni_non_null_call!(self.internal, NewLongArray, length);
Ok(array)
}
pub fn new_float_array(&self, length: jsize) -> Result<jfloatArray> {
let array: jfloatArray = jni_non_null_call!(self.internal, NewFloatArray, length);
Ok(array)
}
pub fn new_double_array(&self, length: jsize) -> Result<jdoubleArray> {
let array: jdoubleArray = jni_non_null_call!(self.internal, NewDoubleArray, length);
Ok(array)
}
pub fn get_boolean_array_region(
&self,
array: jbooleanArray,
start: jsize,
buf: &mut [jboolean],
) -> Result<()> {
non_null!(array, "get_boolean_array_region array argument");
jni_void_call!(
self.internal,
GetBooleanArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_mut_ptr()
);
Ok(())
}
pub fn get_byte_array_region(
&self,
array: jbyteArray,
start: jsize,
buf: &mut [jbyte],
) -> Result<()> {
non_null!(array, "get_byte_array_region array argument");
jni_void_call!(
self.internal,
GetByteArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_mut_ptr()
);
Ok(())
}
pub fn get_char_array_region(
&self,
array: jcharArray,
start: jsize,
buf: &mut [jchar],
) -> Result<()> {
non_null!(array, "get_char_array_region array argument");
jni_void_call!(
self.internal,
GetCharArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_mut_ptr()
);
Ok(())
}
pub fn get_short_array_region(
&self,
array: jshortArray,
start: jsize,
buf: &mut [jshort],
) -> Result<()> {
non_null!(array, "get_short_array_region array argument");
jni_void_call!(
self.internal,
GetShortArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_mut_ptr()
);
Ok(())
}
pub fn get_int_array_region(
&self,
array: jintArray,
start: jsize,
buf: &mut [jint],
) -> Result<()> {
non_null!(array, "get_int_array_region array argument");
jni_void_call!(
self.internal,
GetIntArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_mut_ptr()
);
Ok(())
}
pub fn get_long_array_region(
&self,
array: jlongArray,
start: jsize,
buf: &mut [jlong],
) -> Result<()> {
non_null!(array, "get_long_array_region array argument");
jni_void_call!(
self.internal,
GetLongArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_mut_ptr()
);
Ok(())
}
pub fn get_float_array_region(
&self,
array: jfloatArray,
start: jsize,
buf: &mut [jfloat],
) -> Result<()> {
non_null!(array, "get_float_array_region array argument");
jni_void_call!(
self.internal,
GetFloatArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_mut_ptr()
);
Ok(())
}
pub fn get_double_array_region(
&self,
array: jdoubleArray,
start: jsize,
buf: &mut [jdouble],
) -> Result<()> {
non_null!(array, "get_double_array_region array argument");
jni_void_call!(
self.internal,
GetDoubleArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_mut_ptr()
);
Ok(())
}
pub fn set_boolean_array_region(
&self,
array: jbooleanArray,
start: jsize,
buf: &[jboolean],
) -> Result<()> {
non_null!(array, "set_boolean_array_region array argument");
jni_void_call!(
self.internal,
SetBooleanArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_ptr()
);
Ok(())
}
pub fn set_byte_array_region(
&self,
array: jbyteArray,
start: jsize,
buf: &[jbyte],
) -> Result<()> {
non_null!(array, "set_byte_array_region array argument");
jni_void_call!(
self.internal,
SetByteArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_ptr()
);
Ok(())
}
pub fn set_char_array_region(
&self,
array: jcharArray,
start: jsize,
buf: &[jchar],
) -> Result<()> {
non_null!(array, "set_char_array_region array argument");
jni_void_call!(
self.internal,
SetCharArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_ptr()
);
Ok(())
}
pub fn set_short_array_region(
&self,
array: jshortArray,
start: jsize,
buf: &[jshort],
) -> Result<()> {
non_null!(array, "set_short_array_region array argument");
jni_void_call!(
self.internal,
SetShortArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_ptr()
);
Ok(())
}
pub fn set_int_array_region(&self, array: jintArray, start: jsize, buf: &[jint]) -> Result<()> {
non_null!(array, "set_int_array_region array argument");
jni_void_call!(
self.internal,
SetIntArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_ptr()
);
Ok(())
}
pub fn set_long_array_region(
&self,
array: jlongArray,
start: jsize,
buf: &[jlong],
) -> Result<()> {
non_null!(array, "set_long_array_region array argument");
jni_void_call!(
self.internal,
SetLongArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_ptr()
);
Ok(())
}
pub fn set_float_array_region(
&self,
array: jfloatArray,
start: jsize,
buf: &[jfloat],
) -> Result<()> {
non_null!(array, "set_float_array_region array argument");
jni_void_call!(
self.internal,
SetFloatArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_ptr()
);
Ok(())
}
pub fn set_double_array_region(
&self,
array: jdoubleArray,
start: jsize,
buf: &[jdouble],
) -> Result<()> {
non_null!(array, "set_double_array_region array argument");
jni_void_call!(
self.internal,
SetDoubleArrayRegion,
array,
start,
buf.len() as jsize,
buf.as_ptr()
);
Ok(())
}
pub fn get_field_unchecked<'f, O, T>(
&self,
obj: O,
field: T,
ty: JavaType,
) -> Result<JValue<'a>>
where
O: Into<JObject<'a>>,
T: Desc<'a, JFieldID<'f>>,
{
let obj = obj.into();
non_null!(obj, "get_field_typed obj argument");
let field = field.lookup(self)?.into_inner();
let obj = obj.into_inner();
Ok(match ty {
JavaType::Object(_) | JavaType::Array(_) => {
let obj: JObject =
jni_non_void_call!(self.internal, GetObjectField, obj, field).into();
obj.into()
}
JavaType::Method(_) => unimplemented!(),
JavaType::Primitive(p) => match p {
Primitive::Boolean => {
jni_unchecked!(self.internal, GetBooleanField, obj, field).into()
}
Primitive::Char => jni_unchecked!(self.internal, GetCharField, obj, field).into(),
Primitive::Short => jni_unchecked!(self.internal, GetShortField, obj, field).into(),
Primitive::Int => jni_unchecked!(self.internal, GetIntField, obj, field).into(),
Primitive::Long => jni_unchecked!(self.internal, GetLongField, obj, field).into(),
Primitive::Float => jni_unchecked!(self.internal, GetFloatField, obj, field).into(),
Primitive::Double => {
jni_unchecked!(self.internal, GetDoubleField, obj, field).into()
}
Primitive::Byte => jni_unchecked!(self.internal, GetByteField, obj, field).into(),
Primitive::Void => {
return Err(ErrorKind::WrongJValueType("void", "see java field").into());
}
},
})
}
pub fn set_field_unchecked<'f, O, T>(&self, obj: O, field: T, val: JValue) -> Result<()>
where
O: Into<JObject<'a>>,
T: Desc<'a, JFieldID<'f>>,
{
let obj = obj.into();
non_null!(obj, "set_field_typed obj argument");
let field = field.lookup(self)?.into_inner();
let obj = obj.into_inner();
match val {
JValue::Object(o) => {
jni_unchecked!(self.internal, SetObjectField, obj, field, o.into_inner());
}
JValue::Bool(b) => {
jni_unchecked!(self.internal, SetBooleanField, obj, field, b);
}
JValue::Char(c) => {
jni_unchecked!(self.internal, SetCharField, obj, field, c);
}
JValue::Short(s) => {
jni_unchecked!(self.internal, SetShortField, obj, field, s);
}
JValue::Int(i) => {
jni_unchecked!(self.internal, SetIntField, obj, field, i);
}
JValue::Long(l) => {
jni_unchecked!(self.internal, SetLongField, obj, field, l);
}
JValue::Float(f) => {
jni_unchecked!(self.internal, SetFloatField, obj, field, f);
}
JValue::Double(d) => {
jni_unchecked!(self.internal, SetDoubleField, obj, field, d);
}
JValue::Byte(b) => {
jni_unchecked!(self.internal, SetByteField, obj, field, b);
}
JValue::Void => {
return Err(ErrorKind::WrongJValueType("void", "see java field").into());
}
};
Ok(())
}
pub fn get_field<O, S, T>(&self, obj: O, name: S, ty: T) -> Result<JValue<'a>>
where
O: Into<JObject<'a>>,
S: Into<JNIString>,
T: Into<JNIString> + AsRef<str>,
{
let obj = obj.into();
let class = self.auto_local(self.get_object_class(obj)?);
let parsed = JavaType::from_str(ty.as_ref())?;
let field_id: JFieldID = (&class, name, ty).lookup(self)?;
self.get_field_unchecked(obj, field_id, parsed)
}
pub fn set_field<O, S, T>(&self, obj: O, name: S, ty: T, val: JValue) -> Result<()>
where
O: Into<JObject<'a>>,
S: Into<JNIString>,
T: Into<JNIString> + AsRef<str>,
{
let obj = obj.into();
let parsed = JavaType::from_str(ty.as_ref())?;
let in_type = val.primitive_type();
match parsed {
JavaType::Object(_) | JavaType::Array(_) => {
if in_type.is_some() {
return Err(
ErrorKind::WrongJValueType(val.type_name(), "see java field").into(),
);
}
}
JavaType::Primitive(p) => {
if let Some(in_p) = in_type {
if in_p == p {
} else {
return Err(
ErrorKind::WrongJValueType(val.type_name(), "see java field").into(),
);
}
} else {
return Err(
ErrorKind::WrongJValueType(val.type_name(), "see java field").into(),
);
}
}
JavaType::Method(_) => unimplemented!(),
}
let class = self.auto_local(self.get_object_class(obj)?);
self.set_field_unchecked(obj, (&class, name, ty), val)
}
pub fn get_static_field_unchecked<'c, 'f, T, U>(
&self,
class: T,
field: U,
ty: JavaType,
) -> Result<JValue<'a>>
where
T: Desc<'a, JClass<'c>>,
U: Desc<'a, JStaticFieldID<'f>>,
{
let class = class.lookup(self)?.into_inner();
let field_id = field.lookup(self)?.into_inner();
Ok(match ty {
JavaType::Object(_) | JavaType::Array(_) => {
let obj: JObject =
jni_non_void_call!(self.internal, GetStaticObjectField, class, field_id).into();
obj.into()
}
JavaType::Method(_) => {
return Err(ErrorKind::WrongJValueType("Method", "see java field").into())
}
JavaType::Primitive(p) => match p {
Primitive::Boolean => {
jni_unchecked!(self.internal, GetStaticBooleanField, class, field_id).into()
}
Primitive::Char => {
jni_unchecked!(self.internal, GetStaticCharField, class, field_id).into()
}
Primitive::Short => {
jni_unchecked!(self.internal, GetStaticShortField, class, field_id).into()
}
Primitive::Int => {
jni_unchecked!(self.internal, GetStaticIntField, class, field_id).into()
}
Primitive::Long => {
jni_unchecked!(self.internal, GetStaticLongField, class, field_id).into()
}
Primitive::Float => {
jni_unchecked!(self.internal, GetStaticFloatField, class, field_id).into()
}
Primitive::Double => {
jni_unchecked!(self.internal, GetStaticDoubleField, class, field_id).into()
}
Primitive::Byte => {
jni_unchecked!(self.internal, GetStaticByteField, class, field_id).into()
}
Primitive::Void => {
return Err(ErrorKind::WrongJValueType("void", "see java field").into());
}
},
})
}
pub fn get_static_field<'c, T, U, V>(&self, class: T, field: U, sig: V) -> Result<JValue<'a>>
where
T: Desc<'a, JClass<'c>>,
U: Into<JNIString>,
V: Into<JNIString> + AsRef<str>,
{
let ty = JavaType::from_str(sig.as_ref())?;
let class = class.lookup(self)?;
self.get_static_field_unchecked(class, (class, field, sig), ty)
}
#[allow(unused_variables)]
pub fn set_rust_field<O, S, T>(&self, obj: O, field: S, rust_object: T) -> Result<()>
where
O: Into<JObject<'a>>,
S: AsRef<str>,
T: Send + 'static,
{
let obj = obj.into();
let class = self.auto_local(self.get_object_class(obj)?);
let field_id: JFieldID = (&class, &field, "J").lookup(self)?;
let guard = self.lock_obj(obj)?;
let field_ptr = self
.get_field_unchecked(obj, field_id, JavaType::Primitive(Primitive::Long))?
.j()? as *mut Mutex<T>;
if !field_ptr.is_null() {
return Err(format!("field already set: {}", field.as_ref()).into());
}
let mbox = Box::new(::std::sync::Mutex::new(rust_object));
let ptr: *mut Mutex<T> = Box::into_raw(mbox);
self.set_field_unchecked(obj, field_id, (ptr as crate::sys::jlong).into())
}
#[allow(unused_variables)]
pub fn get_rust_field<O, S, T>(&self, obj: O, field: S) -> Result<MutexGuard<T>>
where
O: Into<JObject<'a>>,
S: Into<JNIString>,
T: Send + 'static,
{
let obj = obj.into();
let guard = self.lock_obj(obj)?;
let ptr = self.get_field(obj, field, "J")?.j()? as *mut Mutex<T>;
non_null!(ptr, "rust value from Java");
unsafe {
Ok((*ptr).lock().unwrap())
}
}
#[allow(unused_variables)]
pub fn take_rust_field<O, S, T>(&self, obj: O, field: S) -> Result<T>
where
O: Into<JObject<'a>>,
S: AsRef<str>,
T: Send + 'static,
{
let obj = obj.into();
let class = self.auto_local(self.get_object_class(obj)?);
let field_id: JFieldID = (&class, &field, "J").lookup(self)?;
let mbox = {
let guard = self.lock_obj(obj)?;
let ptr = self
.get_field_unchecked(obj, field_id, JavaType::Primitive(Primitive::Long))?
.j()? as *mut Mutex<T>;
non_null!(ptr, "rust value from Java");
let mbox = unsafe { Box::from_raw(ptr) };
drop(mbox.try_lock()?);
self.set_field_unchecked(
obj,
field_id,
(::std::ptr::null_mut::<()>() as sys::jlong).into(),
)?;
mbox
};
Ok(mbox.into_inner().unwrap())
}
pub fn lock_obj<O>(&self, obj: O) -> Result<MonitorGuard<'a>>
where
O: Into<JObject<'a>>,
{
let inner = obj.into().into_inner();
let _ = jni_unchecked!(self.internal, MonitorEnter, inner);
Ok(MonitorGuard {
obj: inner,
env: self.internal,
life: Default::default(),
})
}
pub fn get_native_interface(&self) -> *mut sys::JNIEnv {
self.internal
}
pub fn get_java_vm(&self) -> Result<JavaVM> {
let mut raw = ptr::null_mut();
let res = jni_unchecked!(self.internal, GetJavaVM, &mut raw);
jni_error_code_to_result(res)?;
unsafe { JavaVM::from_raw(raw) }
}
pub fn ensure_local_capacity(&self, capacity: jint) -> Result<()> {
jni_void_call!(self.internal, EnsureLocalCapacity, capacity);
Ok(())
}
pub fn register_native_methods<'c, T>(&self, class: T, methods: &[NativeMethod]) -> Result<()>
where
T: Desc<'a, JClass<'c>>,
{
let class = class.lookup(self)?;
let jni_native_methods: Vec<JNINativeMethod> = methods
.iter()
.map(|nm| JNINativeMethod {
name: nm.name.as_ptr() as *mut c_char,
signature: nm.sig.as_ptr() as *mut c_char,
fnPtr: nm.fn_ptr,
})
.collect();
let res = jni_non_void_call!(
self.internal,
RegisterNatives,
class.into_inner(),
jni_native_methods.as_ptr(),
jni_native_methods.len() as jint
);
jni_error_code_to_result(res)
}
pub fn unregister_native_methods<'c, T>(&self, class: T) -> Result<()>
where
T: Desc<'a, JClass<'c>>,
{
let class = class.lookup(self)?;
let res = jni_non_void_call!(self.internal, UnregisterNatives, class.into_inner());
jni_error_code_to_result(res)
}
}
pub struct NativeMethod {
pub name: JNIString,
pub sig: JNIString,
pub fn_ptr: *mut c_void,
}
pub struct MonitorGuard<'a> {
obj: sys::jobject,
env: *mut sys::JNIEnv,
life: PhantomData<&'a ()>,
}
impl<'a> Drop for MonitorGuard<'a> {
fn drop(&mut self) {
let res: Result<()> = catch!({
jni_unchecked!(self.env, MonitorExit, self.obj);
Ok(())
});
if let Err(e) = res {
warn!("error releasing java monitor: {}", e)
}
}
}