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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
use jni_sys::{jboolean, JNI_TRUE};
use std::{borrow::Cow, os::raw::c_char};

use log::warn;

use crate::{errors::*, objects::JString, strings::JNIStr, JNIEnv};

/// Reference to a string in the JVM. Holds a pointer to the array
/// returned by `GetStringUTFChars`. Calls `ReleaseStringUTFChars` on Drop.
/// Can be converted to a `&JNIStr` with the same cost as the `&CStr.from_ptr`
/// conversion.
pub struct JavaStr<'local, 'other_local: 'obj_ref, 'obj_ref> {
    internal: *const c_char,
    obj: &'obj_ref JString<'other_local>,
    env: JNIEnv<'local>,
}

impl<'local, 'other_local: 'obj_ref, 'obj_ref> JavaStr<'local, 'other_local, 'obj_ref> {
    /// Get a pointer to the character array beneath a [JString]
    ///
    /// The string will be `NULL` terminated and encoded as
    /// [Modified UTF-8](https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8) /
    /// [CESU-8](https://en.wikipedia.org/wiki/CESU-8).
    ///
    /// The implementation may either create a copy of the character array for
    /// the given `String` or it may pin it to avoid it being collected by the
    /// garbage collector.
    ///
    /// Returns a tuple with the pointer and the status of whether the implementation
    /// created a copy of the underlying character array.
    ///
    /// # Warning
    ///
    /// The caller must release the array when they are done with it via
    /// [Self::release_string_utf_chars]
    ///
    /// # Safety
    ///
    /// The caller must guarantee that the Object passed in is an instance of `java.lang.String`,
    /// passing in anything else will lead to undefined behaviour (The JNI implementation
    /// is likely to crash or abort the process).
    unsafe fn get_string_utf_chars(
        env: &JNIEnv<'_>,
        obj: &JString<'_>,
    ) -> Result<(*const c_char, bool)> {
        non_null!(obj, "get_string_utf_chars obj argument");
        let mut is_copy: jboolean = 0;
        let ptr: *const c_char = jni_non_null_call!(
            env.get_raw(),
            GetStringUTFChars,
            obj.as_raw(),
            &mut is_copy as *mut _
        );

        let is_copy = is_copy == JNI_TRUE;
        Ok((ptr, is_copy))
    }

    /// Release the backing string
    ///
    /// This will either free the copy that was made by `GetStringUTFChars` or unpin it so it
    /// may be released by the garbage collector once there are no further references to the string.
    ///
    /// # Safety
    ///
    /// The caller must guarantee that [Self::internal] was constructed from a valid pointer obtained from [Self::get_string_utf_chars]
    unsafe fn release_string_utf_chars(&mut self) -> Result<()> {
        non_null!(self.obj, "release_string_utf_chars obj argument");
        // This method is safe to call in case of pending exceptions (see the chapter 2 of the spec)
        jni_unchecked!(
            self.env.get_raw(),
            ReleaseStringUTFChars,
            self.obj.as_raw(),
            self.internal
        );

        Ok(())
    }

    /// Get a [JavaStr] from a [JNIEnv] and a [JString].
    /// You probably want [JNIEnv::get_string] instead of this method.
    pub fn from_env(env: &JNIEnv<'local>, obj: &'obj_ref JString<'other_local>) -> Result<Self> {
        Ok(unsafe {
            let (ptr, _) = Self::get_string_utf_chars(env, obj)?;

            Self::from_raw(env, obj, ptr)
        })
    }

    /// Get the raw string pointer from the JavaStr.
    ///
    /// The string will be `NULL` terminated and encoded as
    /// [Modified UTF-8](https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8) /
    /// [CESU-8](https://en.wikipedia.org/wiki/CESU-8).
    pub fn get_raw(&self) -> *const c_char {
        self.internal
    }

    /// Consumes the `JavaStr`, returning the raw string pointer
    ///
    /// The string will be `NULL` terminated and encoded as
    /// [Modified UTF-8](https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8) /
    /// [CESU-8](https://en.wikipedia.org/wiki/CESU-8).
    ///
    /// # Warning
    /// The programmer is responsible for making sure the backing string gets
    /// released when they are done with it, for example by reconstructing a
    /// [JavaStr] with [`Self::from_raw`], which will release the backing string
    /// when it is dropped.
    pub fn into_raw(self) -> *const c_char {
        let mut _dont_call_drop = std::mem::ManuallyDrop::new(self);

        // Drop the `JNIEnv` in place. As of this writing, that's a no-op, but if `JNIEnv`
        // gains any drop code in the future, this will run it.
        //
        // Safety: The `&mut` proves that `self.env` is valid and not aliased. It is not
        // accessed again after this point. Because `self` has been moved into `ManuallyDrop`,
        // the `JNIEnv` will not be dropped twice.
        unsafe {
            std::ptr::drop_in_place(&mut _dont_call_drop.env);
        }

        _dont_call_drop.internal
    }

    /// Get a [JavaStr] from it's raw components
    ///
    /// # Safety
    ///
    /// The caller must guarantee that `ptr` is a valid, non-null pointer returned by [`Self::into_raw`],
    /// and that `obj` is the same `String` object originally used to create the [JavaStr]
    ///
    /// # Example
    /// ```rust,no_run
    /// # use jni::{errors::Result, JNIEnv, strings::JavaStr};
    /// #
    /// # fn example(env: &mut JNIEnv) -> Result<()> {
    /// let jstring = env.new_string("foo")?;
    /// let java_str = env.get_string(&jstring)?;
    ///
    /// let ptr = java_str.into_raw();
    /// // Do whatever you need with the pointer
    /// let java_str = unsafe { JavaStr::from_raw(env, &jstring, ptr) };
    /// # Ok(())
    /// # }
    /// ```
    pub unsafe fn from_raw(
        env: &JNIEnv<'local>,
        obj: &'obj_ref JString<'other_local>,
        ptr: *const c_char,
    ) -> Self {
        Self {
            internal: ptr,
            obj,

            // Safety: The cloned `JNIEnv` will not be used to create any local references, only to
            // release `ptr`.
            env: env.unsafe_clone(),
        }
    }
}

impl<'local, 'other_local: 'obj_ref, 'obj_ref> ::std::ops::Deref
    for JavaStr<'local, 'other_local, 'obj_ref>
{
    type Target = JNIStr;
    fn deref(&self) -> &Self::Target {
        self.into()
    }
}

impl<'local, 'other_local: 'obj_ref, 'obj_ref: 'java_str, 'java_str>
    From<&'java_str JavaStr<'local, 'other_local, 'obj_ref>> for &'java_str JNIStr
{
    fn from(other: &'java_str JavaStr) -> &'java_str JNIStr {
        unsafe { JNIStr::from_ptr(other.internal) }
    }
}

impl<'local, 'other_local: 'obj_ref, 'obj_ref: 'java_str, 'java_str>
    From<&'java_str JavaStr<'local, 'other_local, 'obj_ref>> for Cow<'java_str, str>
{
    fn from(other: &'java_str JavaStr) -> Cow<'java_str, str> {
        let jni_str: &JNIStr = other;
        jni_str.into()
    }
}

impl<'local, 'other_local: 'obj_ref, 'obj_ref> From<JavaStr<'local, 'other_local, 'obj_ref>>
    for String
{
    fn from(other: JavaStr) -> String {
        let cow: Cow<str> = (&other).into();
        cow.into_owned()
    }
}

impl<'local, 'other_local: 'obj_ref, 'obj_ref> Drop for JavaStr<'local, 'other_local, 'obj_ref> {
    fn drop(&mut self) {
        match unsafe { self.release_string_utf_chars() } {
            Ok(()) => {}
            Err(e) => warn!("error dropping java str: {}", e),
        }
    }
}