rust_jni/jni/
class.rs

1use java_string::*;
2use jni::string::String;
3use jni::*;
4use jni_sys;
5use std::fmt;
6use std::os::raw::c_char;
7use std::ptr;
8
9include!("call_jni_method.rs");
10include!("generate_class.rs");
11
12/// A type representing a Java
13/// [`Class`](https://docs.oracle.com/javase/10/docs/api/java/lang/Class.html).
14// TODO: examples.
15// TODO: custom debug.
16#[derive(Debug)]
17pub struct Class<'env> {
18    object: Object<'env>,
19}
20
21impl<'env> Class<'env> {
22    /// Find an existing Java class by it's name. The name is a fully qualified class or array
23    /// type name.
24    ///
25    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#findclass)
26    pub fn find<'a>(
27        env: &'a JniEnv<'a>,
28        class_name: &str,
29        token: &NoException<'a>,
30    ) -> JavaResult<'a, Class<'a>> {
31        let class_name = to_java_string(class_name);
32        // Safe because the arguments are correct and because `FindClass` throws an exception
33        // before returning `null`.
34        let raw_class = unsafe {
35            call_nullable_jni_method!(env, FindClass, token, class_name.as_ptr() as *const c_char)?
36        };
37        // Safe because the argument is a valid class reference.
38        Ok(unsafe { Self::from_raw(env, raw_class) })
39    }
40
41    /// Define a new Java class from a `.class` file contents.
42    ///
43    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#defineclass)
44    pub fn define<'a>(
45        env: &'a JniEnv<'a>,
46        bytes: &[u8],
47        token: &NoException<'a>,
48    ) -> JavaResult<'a, Class<'a>> {
49        // Safe because the arguments are correct and because `DefineClass` throws an exception
50        // before returning `null`.
51        let raw_class = unsafe {
52            call_nullable_jni_method!(
53                env,
54                DefineClass,
55                token,
56                ptr::null() as *const c_char,
57                ptr::null_mut() as jni_sys::jobject,
58                bytes.as_ptr() as *const jni_sys::jbyte,
59                bytes.len() as jni_sys::jsize
60            )?
61        };
62        // Safe because the argument is a valid class reference.
63        Ok(unsafe { Self::from_raw(env, raw_class) })
64    }
65
66    /// Get the parent class of this class. Will return
67    /// [`None`](https://doc.rust-lang.org/std/option/enum.Option.html#variant.None) for the
68    /// [`Object`](struct.Object.html) class or any interface.
69    ///
70    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#getsuperclass)
71    pub fn parent(&self, _token: &NoException) -> Option<Class<'env>> {
72        // Safe because the argument is ensured to be correct references by construction.
73        let raw_java_class =
74            unsafe { call_jni_method!(self.env(), GetSuperclass, self.raw_object()) };
75        if raw_java_class == ptr::null_mut() {
76            None
77        } else {
78            // Safe because the argument is ensured to be a correct reference.
79            Some(unsafe { Self::__from_jni(self.env(), raw_java_class) })
80        }
81    }
82
83    /// Check if this class is a subtype of the other class.
84    ///
85    /// In Java a class is a subtype of the other class if that other class is a direct or
86    /// an indirect parent of this class or an interface this class or any it's parent is
87    /// implementing.
88    ///
89    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/functions.html#isassignablefrom)
90    pub fn is_subtype_of(&self, class: &Class, _token: &NoException) -> bool {
91        // Safe because arguments are ensured to be the correct by construction.
92        let assignable = unsafe {
93            call_jni_method!(
94                self.env(),
95                IsAssignableFrom,
96                self.raw_object() as jni_sys::jclass,
97                class.raw_object() as jni_sys::jclass
98            )
99        };
100        // Safe because `bool` conversion is safe internally.
101        unsafe { bool::__from_jni(self.env(), assignable) }
102    }
103
104    /// Unsafe because the argument mught not be a valid class reference.
105    unsafe fn from_raw<'a>(env: &'a JniEnv<'a>, raw_class: jni_sys::jclass) -> Class<'a> {
106        Class {
107            object: Object::__from_jni(env, raw_class as jni_sys::jobject),
108        }
109    }
110}
111
112java_class!(
113    Class,
114    "[`Class`](struct.Class.html)",
115    constructors = (),
116    methods = (),
117    static_methods = (),
118);
119
120#[cfg(test)]
121pub fn test_class<'env>(env: &'env JniEnv<'env>, raw_object: jni_sys::jobject) -> Class<'env> {
122    Class {
123        object: test_object(env, raw_object),
124    }
125}
126
127#[cfg(test)]
128mod class_tests {
129    use super::*;
130    use jni::testing::*;
131    use std::mem;
132    use std::ops::Deref;
133
134    fn test_value<'env>(env: &'env JniEnv<'env>, raw_object: jni_sys::jobject) -> Class<'env> {
135        test_class(env, raw_object)
136    }
137
138    generate_tests!(Class, "Ljava/lang/Class;");
139
140    #[test]
141    fn find() {
142        const RAW_OBJECT: jni_sys::jobject = 0x91011 as jni_sys::jobject;
143        let calls = test_raw_jni_env!(vec![JniCall::FindClass(FindClass {
144            name: "test-class".to_owned(),
145            result: RAW_OBJECT,
146        })]);
147        let vm = test_vm(ptr::null_mut());
148        let env = test_env(&vm, calls.env);
149        let class = Class::find(&env, "test-class", &NoException::test()).unwrap();
150        calls.assert_eq(&class, RAW_OBJECT);
151    }
152
153    #[test]
154    fn find_not_found() {
155        const EXCEPTION: jni_sys::jobject = 0x2835 as jni_sys::jobject;
156        let calls = test_raw_jni_env!(vec![
157            JniCall::FindClass(FindClass {
158                name: "test-class".to_owned(),
159                result: ptr::null_mut(),
160            }),
161            JniCall::ExceptionOccurred(ExceptionOccurred { result: EXCEPTION }),
162            JniCall::ExceptionClear(ExceptionClear {}),
163        ]);
164        let vm = test_vm(ptr::null_mut());
165        let env = test_env(&vm, calls.env);
166        let exception = Class::find(&env, "test-class", &NoException::test()).unwrap_err();
167        calls.assert_eq(&exception, EXCEPTION);
168    }
169
170    #[test]
171    fn define() {
172        const RAW_OBJECT: jni_sys::jobject = 0x91011 as jni_sys::jobject;
173        let calls = test_raw_jni_env!(vec![JniCall::DefineClass(DefineClass {
174            name: ptr::null(),
175            loader: ptr::null_mut(),
176            buffer: vec![17, (230 as u8) as i8],
177            result: RAW_OBJECT,
178        })]);
179        let vm = test_vm(ptr::null_mut());
180        let env = test_env(&vm, calls.env);
181        let class = Class::define(&env, &[17, 230], &NoException::test()).unwrap();
182        calls.assert_eq(&class, RAW_OBJECT);
183    }
184
185    #[test]
186    fn define_not_found() {
187        const EXCEPTION: jni_sys::jobject = 0x2835 as jni_sys::jobject;
188        let calls = test_raw_jni_env!(vec![
189            JniCall::DefineClass(DefineClass {
190                name: ptr::null(),
191                loader: ptr::null_mut(),
192                buffer: vec![17, (230 as u8) as i8],
193                result: ptr::null_mut(),
194            }),
195            JniCall::ExceptionOccurred(ExceptionOccurred { result: EXCEPTION }),
196            JniCall::ExceptionClear(ExceptionClear {}),
197        ]);
198        let vm = test_vm(ptr::null_mut());
199        let env = test_env(&vm, calls.env);
200        let exception = Class::define(&env, &[17, 230], &NoException::test()).unwrap_err();
201        calls.assert_eq(&exception, EXCEPTION);
202    }
203
204    #[test]
205    fn is_subtype_of() {
206        const RAW_CLASS1: jni_sys::jobject = 0x2835 as jni_sys::jobject;
207        const RAW_CLASS2: jni_sys::jobject = 0x294875 as jni_sys::jobject;
208        let calls = test_raw_jni_env!(vec![JniCall::IsAssignableFrom(IsAssignableFrom {
209            class1: RAW_CLASS1,
210            class2: RAW_CLASS2,
211            result: jni_sys::JNI_TRUE,
212        })]);
213        let vm = test_vm(ptr::null_mut());
214        let env = test_env(&vm, calls.env);
215        let class1 = test_class(&env, RAW_CLASS1);
216        let class2 = test_class(&env, RAW_CLASS2);
217        assert!(class1.is_subtype_of(&class2, &NoException::test()));
218    }
219
220    #[test]
221    fn is_not_subtype_of() {
222        const RAW_CLASS1: jni_sys::jobject = 0x2835 as jni_sys::jobject;
223        const RAW_CLASS2: jni_sys::jobject = 0x294875 as jni_sys::jobject;
224        let calls = test_raw_jni_env!(vec![JniCall::IsAssignableFrom(IsAssignableFrom {
225            class1: RAW_CLASS1,
226            class2: RAW_CLASS2,
227            result: jni_sys::JNI_FALSE,
228        })]);
229        let vm = test_vm(ptr::null_mut());
230        let env = test_env(&vm, calls.env);
231        let class1 = test_class(&env, RAW_CLASS1);
232        let class2 = test_class(&env, RAW_CLASS2);
233        assert!(!class1.is_subtype_of(&class2, &NoException::test()));
234    }
235
236    #[test]
237    fn parent() {
238        const RAW_CLASS1: jni_sys::jobject = 0x2835 as jni_sys::jobject;
239        const RAW_CLASS2: jni_sys::jobject = 0x294875 as jni_sys::jobject;
240        let calls = test_raw_jni_env!(vec![JniCall::GetSuperclass(GetSuperclass {
241            class: RAW_CLASS1,
242            result: RAW_CLASS2,
243        })]);
244        let vm = test_vm(ptr::null_mut());
245        let env = test_env(&vm, calls.env);
246        let class = test_class(&env, RAW_CLASS1);
247        let parent = class.parent(&NoException::test()).unwrap();
248        calls.assert_eq(&parent, RAW_CLASS2);
249    }
250
251    #[test]
252    fn no_parent() {
253        const RAW_CLASS: jni_sys::jobject = 0x2835 as jni_sys::jobject;
254        let calls = test_raw_jni_env!(vec![JniCall::GetSuperclass(GetSuperclass {
255            class: RAW_CLASS,
256            result: ptr::null_mut(),
257        })]);
258        let vm = test_vm(ptr::null_mut());
259        let env = test_env(&vm, calls.env);
260        let class = test_class(&env, RAW_CLASS);
261        assert!(class.parent(&NoException::test()).is_none());
262    }
263}