1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#[derive(Copy, Clone)]
#[doc(hidden)]
pub struct JMethodIDHack {
	pub jmethodid: jni::sys::jmethodID,
}

unsafe impl Send for JMethodIDHack {
}
unsafe impl Sync for JMethodIDHack {
}

/// At the point of instantiaction the macros creates global cache to
/// access java class by name.
///
/// ```
/// let class: Result<jni::objects::JClass<'static>, uni_jnihelpers::JniError>;
/// class = get_java_class!(jni_env, "my/class/full/Name");
/// ```
#[macro_export]
macro_rules! get_java_class {
	($env:expr, $class_name:expr) => {{
		use $crate::{
			reimport::{
				jni::objects::JClass,
				parking_lot::{
					const_rwlock,
					RwLock,
				},
			},
			JniError,
			UniGlobalRef as UGR,
			};

		static CACHE: RwLock<Option<UGR<JClass<'static>>>> = const_rwlock(None);

		let read_lock = CACHE.read();
		let result = match read_lock.as_ref() {
			Some(bc) => Ok(bc.clone()),
			None => {
				std::mem::drop(read_lock);
				match $env.find_class($class_name) {
					Err(e) => Err(JniError::JniError(e)),
					Ok(class) => {
						let class: JClass<'static> = class.into_inner().into();
						match UGR::try_new($env, class) {
							Err(e) => Err(e),
							Ok(class) => {
								*CACHE.write() = Some(class.clone());
								Ok(class)
							},
						}
					},
				}
			},
			};

		result
		}};
}


/// At the point of instantiaction the macros creates global cache to
/// access java method by name.
///
/// ```
/// let method: jni::sys::jmethodID = get_java_method!(
///     jni_env,
///     class_ref,  // jni::objects::JClass
///     "methodName",
///     "(Lmethod;Lsignature;)V"
/// )?;
/// ```
#[macro_export]
macro_rules! get_java_method {
	($env:expr, $class:expr, $method_name:expr, $method_signature:expr) => {{
		use $crate::{
			macroses::JMethodIDHack,
			reimport::{
				jni::objects::JMethodID,
				parking_lot::{
					const_rwlock,
					RwLock,
				},
			},
			};

		static CACHE: RwLock<Option<JMethodIDHack>> = const_rwlock(None);

		let method = CACHE.read();
		let jmethodid = match method.as_ref() {
			Some(method) => method.jmethodid,
			None => {
				std::mem::drop(method);
				let maybe_method =
					$env.get_method_id($class, $method_name, $method_signature);
				let method = match maybe_method {
					Ok(method) => method,
					Err(e) => {
						log::error!("get_method err:{:?}", e);
						// We call it one more time to allow Java to print stack trace for
						// potential exception
						let _ =
							$env.get_method_id($class, $method_name, $method_signature);
						panic!("get_method failed");
					},
				};

				let method = JMethodIDHack {
					jmethodid: method.into_inner(),
				};

				*CACHE.write() = Some(method);
				method.jmethodid
			},
			};

		JMethodID::from(jmethodid)
		}};
}