rust_jni/
attach_arguments.rs

1use init_arguments::InitArguments;
2use java_string::*;
3use jni_sys;
4use std::marker::PhantomData;
5use std::os::raw::c_char;
6use std::ptr;
7use version::{self, JniVersion};
8
9/// Arguments for attaching a thread to the JVM.
10///
11/// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#attachcurrentthread)
12///
13/// # Example
14/// ```
15/// use rust_jni::{AttachArguments, InitArguments, JniVersion};
16///
17/// let options = InitArguments::get_default(JniVersion::V8).unwrap();
18/// let attach_arguments = AttachArguments::new(&options);
19///
20/// assert_eq!(attach_arguments.version(), JniVersion::V8);
21/// ```
22#[derive(Debug, PartialEq, Eq)]
23pub struct AttachArguments<'a> {
24    version: JniVersion,
25    thread_name: Option<&'a str>,
26    // TODO(#7): support thread groups.
27}
28
29impl<'a> AttachArguments<'a> {
30    /// Create attach arguments with the default thread name.
31    ///
32    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#attachcurrentthread)
33    pub fn new(init_arguments: &InitArguments) -> Self {
34        AttachArguments {
35            thread_name: None,
36            version: init_arguments.version(),
37        }
38    }
39
40    /// Create attach arguments with a specified thread name.
41    ///
42    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#attachcurrentthread)
43    pub fn named(init_arguments: &InitArguments, thread_name: &'a str) -> Self {
44        AttachArguments {
45            thread_name: Some(thread_name),
46            version: init_arguments.version(),
47        }
48    }
49
50    /// Return the JNI version these arguments will request when attaching a thread to a Java VM.
51    ///
52    /// [JNI documentation](https://docs.oracle.com/javase/10/docs/specs/jni/invocation.html#attachcurrentthread)
53    pub fn version(&self) -> JniVersion {
54        self.version
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61    use init_arguments;
62
63    #[test]
64    fn new() {
65        let init_arguments = init_arguments::test(JniVersion::V4);
66        assert_eq!(
67            AttachArguments::new(&init_arguments),
68            AttachArguments {
69                thread_name: None,
70                version: JniVersion::V4
71            }
72        );
73    }
74
75    #[test]
76    fn named() {
77        let init_arguments = init_arguments::test(JniVersion::V4);
78        assert_eq!(
79            AttachArguments::named(&init_arguments, "test-name"),
80            AttachArguments {
81                thread_name: Some("test-name"),
82                version: JniVersion::V4,
83            }
84        );
85    }
86
87    #[test]
88    fn version() {
89        let arguments = AttachArguments {
90            version: JniVersion::V4,
91            thread_name: None,
92        };
93        assert_eq!(arguments.version(), JniVersion::V4);
94    }
95}
96
97/// A wrapper around `jni_sys::JavaVMAttachArgs` with a lifetime to ensure
98/// there's no access to freed memory.
99pub struct RawAttachArguments<'a> {
100    pub raw_arguments: jni_sys::JavaVMAttachArgs,
101    #[allow(dead_code)]
102    buffer_len: usize,
103    _buffer: PhantomData<&'a Vec<u8>>,
104}
105
106/// Convert `AttachArguments` to `jni_sys::JavaVMAttachArgs`. Uses a buffer for storing
107/// the Java string with the thread name.
108pub fn to_raw<'a>(arguments: &AttachArguments, buffer: &'a mut Vec<u8>) -> RawAttachArguments<'a> {
109    let version = version::to_raw(arguments.version);
110    let group = ptr::null_mut();
111    let raw_arguments = jni_sys::JavaVMAttachArgs {
112        name: match arguments.thread_name {
113            None => ptr::null_mut(),
114            Some(ref thread_name) => {
115                *buffer = to_java_string(thread_name);
116                buffer.as_ptr() as *mut c_char
117            }
118        },
119        version,
120        group,
121    };
122    RawAttachArguments {
123        raw_arguments,
124        buffer_len: buffer.len(),
125        _buffer: PhantomData::<&'a Vec<u8>>,
126    }
127}
128
129#[cfg(test)]
130mod to_raw_tests {
131    use super::*;
132    use init_arguments;
133    use std::slice;
134
135    #[test]
136    fn to_raw() {
137        let init_arguments = init_arguments::test(JniVersion::V8);
138        let arguments = AttachArguments::new(&init_arguments);
139        let mut buffer: Vec<u8> = vec![];
140        let raw_arguments = super::to_raw(&arguments, &mut buffer);
141        assert_eq!(raw_arguments.raw_arguments.group, ptr::null_mut());
142        assert_eq!(raw_arguments.raw_arguments.name, ptr::null_mut());
143        assert_eq!(
144            raw_arguments.raw_arguments.version,
145            version::to_raw(JniVersion::V8)
146        );
147    }
148
149    #[test]
150    fn to_raw_named() {
151        let init_arguments = init_arguments::test(JniVersion::V8);
152        let test_name = "test-name";
153        let arguments = AttachArguments::named(&init_arguments, test_name);
154        let mut buffer: Vec<u8> = vec![];
155        let raw_arguments = super::to_raw(&arguments, &mut buffer);
156        assert_eq!(raw_arguments.raw_arguments.group, ptr::null_mut());
157        assert_eq!(
158            raw_arguments.raw_arguments.version,
159            version::to_raw(JniVersion::V8)
160        );
161        assert_eq!(
162            from_java_string(unsafe {
163                slice::from_raw_parts(
164                    raw_arguments.raw_arguments.name as *const u8,
165                    raw_arguments.buffer_len,
166                )
167            }).unwrap(),
168            test_name
169        );
170    }
171}