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),
}
}
}