use std::str;
use std::marker::PhantomData;
use std::iter::IntoIterator;
use std::slice;
use std::sync::{Mutex, MutexGuard};
use std::str::FromStr;
use errors::*;
use std::os::raw::{
c_char,
c_void,
};
use std::ptr;
use sys::{
self,
jarray,
jboolean,
jbyte,
jchar,
jdouble,
jfloat,
jint,
jlong,
jshort,
jsize,
jvalue,
jbooleanArray,
jbyteArray,
jcharArray,
jdoubleArray,
jfloatArray,
jintArray,
jlongArray,
jobjectArray,
jshortArray,
};
use strings::JNIString;
use strings::JavaStr;
use objects::AutoLocal;
use objects::GlobalRef;
use objects::JByteBuffer;
use objects::JClass;
use objects::JFieldID;
use objects::JList;
use objects::JMap;
use objects::JMethodID;
use objects::JObject;
use objects::JStaticFieldID;
use objects::JStaticMethodID;
use objects::JString;
use objects::JThrowable;
use objects::JValue;
use descriptors::Desc;
use signature::JavaType;
use signature::Primitive;
use signature::TypeSignature;
use JNIVersion;
use 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>,
{
non_null!(loader, "define_class loader argument");
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<T>(&self, class: T) -> Result<JClass<'a>>
where
T: Desc<'a, JClass<'a>>,
{
let class = class.lookup(self)?;
Ok(jni_non_void_call!(self.internal, GetSuperclass, class.into_inner()).into())
}
pub fn is_assignable_from<T, U>(&self, class1: T, class2: U) -> Result<bool>
where
T: Desc<'a, JClass<'a>>,
U: Desc<'a, JClass<'a>>,
{
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<T>(&self, object: JObject<'a>, class: T) -> Result<bool>
where
T: Desc<'a, JClass<'a>>,
{
let class = class.lookup(self)?;
Ok(jni_unchecked!(
self.internal,
IsInstanceOf,
object.into_inner(),
class.into_inner()
) == sys::JNI_TRUE,
)
}
pub fn throw<E>(&self, obj: E) -> Result<()>
where
E: Desc<'a, JThrowable<'a>>,
{
let throwable = obj.lookup(self)?;
let res: i32 = jni_unchecked!(self.internal, Throw, throwable.into_inner());
if res < 0 {
Err(format!("throw failed with code {}", res).into())
} else {
Ok(())
}
}
pub fn throw_new<S, T>(&self, class: T, msg: S) -> Result<()>
where
S: Into<JNIString>,
T: Desc<'a, JClass<'a>>,
{
self.throw((class, msg))
}
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(&self, obj: JObject) -> Result<GlobalRef> {
let new_ref: JObject = jni_unchecked!(self.internal, NewGlobalRef, obj.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(&'a self, obj: JObject<'a>) -> AutoLocal<'a> {
AutoLocal::new(self, obj)
}
pub fn delete_local_ref(&self, obj: JObject) -> Result<()> {
Ok(jni_unchecked!(self.internal, DeleteLocalRef, obj.into_inner()))
}
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<T>(&self, class: T) -> Result<JObject<'a>>
where
T: Desc<'a, JClass<'a>>,
{
let class = class.lookup(self)?;
Ok(jni_non_null_call!(self.internal, AllocObject, class.into_inner()))
}
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(&self, obj: JObject) -> Result<JClass<'a>> {
non_null!(obj, "get_object_class");
Ok(jni_unchecked!(self.internal, GetObjectClass, obj.into_inner()).into())
}
pub fn call_static_method_unchecked<T, U>(
&self,
class: T,
method_id: U,
ret: JavaType,
args: &[JValue],
) -> Result<JValue<'a>>
where
T: Desc<'a, JClass<'a>>,
U: Desc<'a, JStaticMethodID<'a>>,
{
let class = class.lookup(self)?;
let method_id = method_id.lookup(self)?.into_inner();
let class = class.into_inner();
let args: Vec<jvalue> = args.into_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) => {
let v: JValue = 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_non_void_call!(
self.internal,
CallStaticVoidMethodA,
class,
method_id,
jni_args
).into(),
};
v.into()
}
})
}
pub fn call_method_unchecked<T>(
&self,
obj: JObject,
method_id: T,
ret: JavaType,
args: &[JValue],
) -> Result<JValue<'a>>
where
T: Desc<'a, JMethodID<'a>>,
{
let method_id = method_id.lookup(self)?.into_inner();
let obj = obj.into_inner();
let args: Vec<jvalue> = args.into_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) => {
let v: JValue = 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);
}
};
v.into()
}
})
}
pub fn call_method<S, T>(
&self,
obj: JObject,
name: S,
sig: T,
args: &[JValue],
) -> Result<JValue<'a>>
where
S: Into<JNIString>,
T: Into<JNIString> + AsRef<str>,
{
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)?.into());
self.call_method_unchecked(obj, (&class, name, sig), parsed.ret, args)
}
pub fn call_static_method<T, U, V>(
&self,
class: T,
name: U,
sig: V,
args: &[JValue],
) -> Result<JValue<'a>>
where
T: Desc<'a, JClass<'a>>,
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.into_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)
}
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<T>(
&self,
length: jsize,
element_class: T,
initial_element: JObject,
) -> Result<jobjectArray>
where
T: Desc<'a, JClass<'a>>,
{
let class = element_class.lookup(self)?;
Ok(jni_non_null_call!(
self.internal,
NewObjectArray,
length,
class.into_inner(),
initial_element.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(
&self,
array: jobjectArray,
index: jsize,
value: JObject,
) -> Result<()> {
non_null!(array, "set_object_array_element array argument");
Ok(jni_void_call!(
self.internal,
SetObjectArrayElement,
array,
index,
value.into_inner()
))
}
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<T>(&self, obj: JObject, field: T, ty: JavaType) -> Result<JValue<'a>>
where
T: Desc<'a, JFieldID<'a>>,
{
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) => {
let v: JValue = 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());
}
};
v.into()
}
})
}
pub fn set_field_unchecked<T>(&self, obj: JObject, field: T, val: JValue) -> Result<()>
where
T: Desc<'a, JFieldID<'a>>,
{
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<S, T>(&self, obj: JObject, name: S, ty: T) -> Result<JValue<'a>>
where
S: Into<JNIString>,
T: Into<JNIString> + AsRef<str>,
{
let class = self.auto_local(self.get_object_class(obj)?.into());
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<S, T>(&self, obj: JObject, name: S, ty: T, val: JValue) -> Result<()>
where
S: Into<JNIString>,
T: Into<JNIString> + AsRef<str>,
{
let parsed = JavaType::from_str(ty.as_ref())?;
let in_type = val.primitive_type();
match parsed {
JavaType::Object(_) | JavaType::Array(_) => {
if let None = in_type {
} else {
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)?.into());
self.set_field_unchecked(obj, (&class, name, ty), val)
}
pub fn get_static_field_unchecked<T, U>(
&self,
class: T,
field: U,
ty: JavaType,
) -> Result<JValue<'a>>
where
T: Desc<'a, JClass<'a>>,
U: Desc<'a, JStaticFieldID<'a>>,
{
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) => {
let v: JValue = 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());
}
};
v.into()
}
})
}
pub fn get_static_field<T, U, V>(&self, class: T, field: U, sig: V) -> Result<JValue>
where
T: Desc<'a, JClass<'a>>,
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<S, T>(&self, obj: JObject, field: S, rust_object: T) -> Result<()>
where
S: AsRef<str>,
T: Send + 'static,
{
let class = self.auto_local(self.get_object_class(obj)?.into());
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 ::sys::jlong).into())
}
#[allow(unused_variables)]
pub fn get_rust_field<S, T>(&self, obj: JObject, field: S) -> Result<MutexGuard<T>>
where
S: Into<JNIString>,
T: Send + 'static,
{
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<S, T>(&self, obj: JObject, field: S) -> Result<T>
where
S: AsRef<str>,
T: Send + 'static,
{
let class = self.auto_local(self.get_object_class(obj)?.into());
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)
};
let _ = 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(&self, obj: JObject) -> Result<MonitorGuard<'a>> {
let _ = jni_unchecked!(self.internal, MonitorEnter, obj.into_inner());
Ok(MonitorGuard {
obj: obj.into_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<()> {
Ok(jni_void_call!(self.internal, EnsureLocalCapacity, capacity))
}
}
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(())
});
match res {
Err(e) => warn!("error releasing java monitor: {}", e),
_ => {}
}
}
}