• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use jni_sys::{jboolean, JNI_TRUE};
2 use std::{borrow::Cow, os::raw::c_char};
3 
4 use log::warn;
5 
6 use crate::{errors::*, objects::JString, strings::JNIStr, JNIEnv};
7 
8 /// Reference to a string in the JVM. Holds a pointer to the array
9 /// returned by `GetStringUTFChars`. Calls `ReleaseStringUTFChars` on Drop.
10 /// Can be converted to a `&JNIStr` with the same cost as the `&CStr.from_ptr`
11 /// conversion.
12 pub struct JavaStr<'local, 'other_local: 'obj_ref, 'obj_ref> {
13     internal: *const c_char,
14     obj: &'obj_ref JString<'other_local>,
15     env: JNIEnv<'local>,
16 }
17 
18 impl<'local, 'other_local: 'obj_ref, 'obj_ref> JavaStr<'local, 'other_local, 'obj_ref> {
19     /// Get a pointer to the character array beneath a [JString]
20     ///
21     /// The string will be `NULL` terminated and encoded as
22     /// [Modified UTF-8](https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8) /
23     /// [CESU-8](https://en.wikipedia.org/wiki/CESU-8).
24     ///
25     /// The implementation may either create a copy of the character array for
26     /// the given `String` or it may pin it to avoid it being collected by the
27     /// garbage collector.
28     ///
29     /// Returns a tuple with the pointer and the status of whether the implementation
30     /// created a copy of the underlying character array.
31     ///
32     /// # Warning
33     ///
34     /// The caller must release the array when they are done with it via
35     /// [Self::release_string_utf_chars]
36     ///
37     /// # Safety
38     ///
39     /// The caller must guarantee that the Object passed in is an instance of `java.lang.String`,
40     /// passing in anything else will lead to undefined behaviour (The JNI implementation
41     /// is likely to crash or abort the process).
get_string_utf_chars( env: &JNIEnv<'_>, obj: &JString<'_>, ) -> Result<(*const c_char, bool)>42     unsafe fn get_string_utf_chars(
43         env: &JNIEnv<'_>,
44         obj: &JString<'_>,
45     ) -> Result<(*const c_char, bool)> {
46         non_null!(obj, "get_string_utf_chars obj argument");
47         let mut is_copy: jboolean = 0;
48         let ptr: *const c_char = jni_non_null_call!(
49             env.get_raw(),
50             GetStringUTFChars,
51             obj.as_raw(),
52             &mut is_copy as *mut _
53         );
54 
55         let is_copy = is_copy == JNI_TRUE;
56         Ok((ptr, is_copy))
57     }
58 
59     /// Release the backing string
60     ///
61     /// This will either free the copy that was made by `GetStringUTFChars` or unpin it so it
62     /// may be released by the garbage collector once there are no further references to the string.
63     ///
64     /// # Safety
65     ///
66     /// The caller must guarantee that [Self::internal] was constructed from a valid pointer obtained from [Self::get_string_utf_chars]
release_string_utf_chars(&mut self) -> Result<()>67     unsafe fn release_string_utf_chars(&mut self) -> Result<()> {
68         non_null!(self.obj, "release_string_utf_chars obj argument");
69         // This method is safe to call in case of pending exceptions (see the chapter 2 of the spec)
70         jni_unchecked!(
71             self.env.get_raw(),
72             ReleaseStringUTFChars,
73             self.obj.as_raw(),
74             self.internal
75         );
76 
77         Ok(())
78     }
79 
80     /// Get a [JavaStr] from a [JNIEnv] and a [JString].
81     /// You probably want [JNIEnv::get_string] instead of this method.
from_env(env: &JNIEnv<'local>, obj: &'obj_ref JString<'other_local>) -> Result<Self>82     pub fn from_env(env: &JNIEnv<'local>, obj: &'obj_ref JString<'other_local>) -> Result<Self> {
83         Ok(unsafe {
84             let (ptr, _) = Self::get_string_utf_chars(env, obj)?;
85 
86             Self::from_raw(env, obj, ptr)
87         })
88     }
89 
90     /// Get the raw string pointer from the JavaStr.
91     ///
92     /// The string will be `NULL` terminated and encoded as
93     /// [Modified UTF-8](https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8) /
94     /// [CESU-8](https://en.wikipedia.org/wiki/CESU-8).
get_raw(&self) -> *const c_char95     pub fn get_raw(&self) -> *const c_char {
96         self.internal
97     }
98 
99     /// Consumes the `JavaStr`, returning the raw string pointer
100     ///
101     /// The string will be `NULL` terminated and encoded as
102     /// [Modified UTF-8](https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8) /
103     /// [CESU-8](https://en.wikipedia.org/wiki/CESU-8).
104     ///
105     /// # Warning
106     /// The programmer is responsible for making sure the backing string gets
107     /// released when they are done with it, for example by reconstructing a
108     /// [JavaStr] with [`Self::from_raw`], which will release the backing string
109     /// when it is dropped.
into_raw(self) -> *const c_char110     pub fn into_raw(self) -> *const c_char {
111         let mut _dont_call_drop = std::mem::ManuallyDrop::new(self);
112 
113         // Drop the `JNIEnv` in place. As of this writing, that's a no-op, but if `JNIEnv`
114         // gains any drop code in the future, this will run it.
115         //
116         // Safety: The `&mut` proves that `self.env` is valid and not aliased. It is not
117         // accessed again after this point. Because `self` has been moved into `ManuallyDrop`,
118         // the `JNIEnv` will not be dropped twice.
119         unsafe {
120             std::ptr::drop_in_place(&mut _dont_call_drop.env);
121         }
122 
123         _dont_call_drop.internal
124     }
125 
126     /// Get a [JavaStr] from it's raw components
127     ///
128     /// # Safety
129     ///
130     /// The caller must guarantee that `ptr` is a valid, non-null pointer returned by [`Self::into_raw`],
131     /// and that `obj` is the same `String` object originally used to create the [JavaStr]
132     ///
133     /// # Example
134     /// ```rust,no_run
135     /// # use jni::{errors::Result, JNIEnv, strings::JavaStr};
136     /// #
137     /// # fn example(env: &mut JNIEnv) -> Result<()> {
138     /// let jstring = env.new_string("foo")?;
139     /// let java_str = env.get_string(&jstring)?;
140     ///
141     /// let ptr = java_str.into_raw();
142     /// // Do whatever you need with the pointer
143     /// let java_str = unsafe { JavaStr::from_raw(env, &jstring, ptr) };
144     /// # Ok(())
145     /// # }
146     /// ```
from_raw( env: &JNIEnv<'local>, obj: &'obj_ref JString<'other_local>, ptr: *const c_char, ) -> Self147     pub unsafe fn from_raw(
148         env: &JNIEnv<'local>,
149         obj: &'obj_ref JString<'other_local>,
150         ptr: *const c_char,
151     ) -> Self {
152         Self {
153             internal: ptr,
154             obj,
155 
156             // Safety: The cloned `JNIEnv` will not be used to create any local references, only to
157             // release `ptr`.
158             env: env.unsafe_clone(),
159         }
160     }
161 }
162 
163 impl<'local, 'other_local: 'obj_ref, 'obj_ref> ::std::ops::Deref
164     for JavaStr<'local, 'other_local, 'obj_ref>
165 {
166     type Target = JNIStr;
deref(&self) -> &Self::Target167     fn deref(&self) -> &Self::Target {
168         self.into()
169     }
170 }
171 
172 impl<'local, 'other_local: 'obj_ref, 'obj_ref: 'java_str, 'java_str>
173     From<&'java_str JavaStr<'local, 'other_local, 'obj_ref>> for &'java_str JNIStr
174 {
from(other: &'java_str JavaStr) -> &'java_str JNIStr175     fn from(other: &'java_str JavaStr) -> &'java_str JNIStr {
176         unsafe { JNIStr::from_ptr(other.internal) }
177     }
178 }
179 
180 impl<'local, 'other_local: 'obj_ref, 'obj_ref: 'java_str, 'java_str>
181     From<&'java_str JavaStr<'local, 'other_local, 'obj_ref>> for Cow<'java_str, str>
182 {
from(other: &'java_str JavaStr) -> Cow<'java_str, str>183     fn from(other: &'java_str JavaStr) -> Cow<'java_str, str> {
184         let jni_str: &JNIStr = other;
185         jni_str.into()
186     }
187 }
188 
189 impl<'local, 'other_local: 'obj_ref, 'obj_ref> From<JavaStr<'local, 'other_local, 'obj_ref>>
190     for String
191 {
from(other: JavaStr) -> String192     fn from(other: JavaStr) -> String {
193         let cow: Cow<str> = (&other).into();
194         cow.into_owned()
195     }
196 }
197 
198 impl<'local, 'other_local: 'obj_ref, 'obj_ref> Drop for JavaStr<'local, 'other_local, 'obj_ref> {
drop(&mut self)199     fn drop(&mut self) {
200         match unsafe { self.release_string_utf_chars() } {
201             Ok(()) => {}
202             Err(e) => warn!("error dropping java str: {}", e),
203         }
204     }
205 }
206