1# Migrating to 0.21 2 3Version 0.21 makes extensive breaking changes in order to improve safety. Most projects that use this library will need to be changed accordingly. 4 5This is a guide for migrating to 0.21. For a full list of changes in this release, please see the [changelog](../CHANGELOG.md). 6 7Most of these changes are needed to ensure that all local references (`JObject` and the like) have the correct lifetime and can't be used after they're deleted, which would cause undefined behavior. See [issue #392](https://github.com/jni-rs/jni-rs/issues/392) for a discussion of the problem these changes solve. 8 9 10## `JNIEnv` parameter of `extern fn`s should now be `mut` 11 12In `extern fn`s that are directly called by the JVM, the `JNIEnv` parameter will usually need to be `mut`. 13 14```rust 15#[no_mangle] 16pub extern "system" fn Java_HelloWorld_hello<'local>(mut env: JNIEnv<'local>, 17 class: JClass<'local>, 18 input: JString<'local>) 19 -> jstring { 20 … 21} 22``` 23 24This is needed because most `JNIEnv` methods now take a `&mut self` parameter. 25 26 27## `JNIEnv`, `JObject`, etc are now `!Copy` and should be borrowed 28 29Functions that are *not* directly called by the JVM should, in most cases, borrow local references (`JObject`, `JString`, and so on) and mutably borrow `JNIEnv`s. 30 31```rust 32pub fn print_string(env: &mut JNIEnv, 33 string: &JString) 34 -> Result<()> { 35 println!("{}", env.get_string(string)?.to_string_lossy()); 36 Ok(()) 37} 38``` 39 40This is needed because these types no longer have the `Copy` or `Clone` traits. 41 42 43## `JNIEnv::with_local_frame` closure now takes a `&mut JNIEnv` parameter 44 45When using `JNIEnv::with_local_frame`, `Executor::with_attached`, or `Executor::with_attached_capacity`, the closure must now take a parameter of type `&mut JNIEnv`. 46 47```rust 48env.with_local_frame(16, |env| { 49 … 50}) 51``` 52 53The closure must only use the `JNIEnv` passed to it in that parameter, and not the `JNIEnv` that `with_local_frame` was called on. 54 55 56## `JNIEnv::with_local_frame` closure can return a generic `Result` 57 58_Note: This also applies to `Executor::with_attached` and `Executor::with_attached_capacity` which are thin wrappers over `with_local_frame`_ 59 60The closure passed to `with_local_frame` is now free to return a generic `Result` as long as the error type implements `From<jni::errors::Error>`. 61 62This can be particularly beneficial when running large amounts of code within a local frame in a crate that defines its own `Result` and `Error` types which need to be propagated to the caller. 63 64There are a few trade offs with this change though: 651. Sometimes the compiler won't be able to infer the generic error type (E.g. for code that simply returns `Ok(())`) and so it has to be explicitly specified 662. Since it's no longer assumed that code always wants to return a single local reference this special case has to be handled differently 67 68Two options for clarifying the error type for the compiler if it's ambiguous would be: 69 701. Specify the type of the return value as part of an assignment: 71```rust 72let result: MyResult<()> = env.with_local_frame(10, |env| { Ok(()) }); 73``` 74 752. Specify the generic `E` error parameter: 76```rust 77env.with_local_frame::<_, _, MyError>(10, |env| { Ok(()) })?; 78``` 79 80Code that returns a local reference to the calling frame can either use `JNIEnv::with_local_frame_returning_local` (which is marginally optimized for that special case) or else return a `GlobalRef` instead. (This approach works reasonably well in Rust, compared to C, because a global reference will be automatically deleted once it is dropped so it doesn't introduce a memory leak hazard like it would in C) 81 82 83## Passing object reference parameters to Java methods 84 85When passing an object reference as a parameter to a Java method or constructor, it needs to be explicitly borrowed, as in `(&obj).into()`, instead of simply `obj.into()`. 86 87```rust 88env.call_static_method( 89 "com/example/SomeClass", 90 "someStaticMethod", 91 "(Ljava/lang/Object;)V", 92 &[ 93 (&obj).into(), 94 ], 95) 96``` 97 98 99## `JList` and `JMap` methods all take a `&mut JNIEnv` parameter 100 101All methods of `JList` and `JMap` now require a parameter of type `&mut JNIEnv`. They no longer store the `JNIEnv` that was used to construct them. 102 103This is needed because most `JNIEnv` methods now take a `&mut self` parameter, and if the `JList` or `JMap` did store a `&mut JNIEnv`, then the caller would be unable to use the `JNIEnv` for anything else without first dropping the `JList` or `JMap`. 104 105 106## `JList` and `JMap` iterators no longer implement `Iterator` 107 108Just like the methods of `JList` and `JMap`, their iterator now also requires a `&mut JNIEnv` in order to get the next element. 109 110For this reason, the iterator returned by `JList::iter` and `JMap::iter` no longer implements `std::iter::Iterator` and can no longer be used with a `for` loop. Instead, it has a `next` method that takes a `&mut JNIEnv` parameter, and can be used with a `while let` loop. 111 112```rust 113let mut iterator = list.iter(env)?; 114 115while let Some(obj) = iterator.next(env)? { 116 let obj: AutoLocal<JObject> = env.auto_local(obj); 117 // Do something with `obj` here. 118} 119``` 120 121 122## Local references no longer leak 123 124`JNIEnv` methods that look up Java classes no longer leak local references ([#109](https://github.com/jni-rs/jni-rs/issues/109)), so it is no longer necessary to wrap calls to these methods inside `JNIEnv::with_local_frame`. 125 126 127## `cannot borrow as mutable` 128 129If your project has a function that takes two or more parameters, one of them is `JNIEnv`, and another is something returned by a `JNIEnv` method (like `JObject`), then calls to that function may not compile. 130 131```rust 132fn example_function( 133 env: &mut JNIEnv, 134 obj: &JObject, 135) { 136 // … 137} 138 139example_function( 140 env, 141 // ERROR: cannot borrow `*env` as mutable more than once at a time 142 &env.new_object( 143 "com/example/SomeClass", 144 "()V", 145 &[], 146 )?, 147) 148``` 149 150To fix this, the `JNIEnv` parameter needs to come *last*. 151 152```rust 153fn example_function( 154 obj: &JObject, 155 env: &mut JNIEnv, 156) { 157 // … 158} 159 160example_function( 161 &env.new_object( 162 "com/example/SomeClass", 163 "()V", 164 &[], 165 )?, 166 env, 167); 168``` 169 170 171## `invocation` feature now finds and loads the JVM dynamically; `JavaVM::new` returns different error 172 173The `invocation` feature has been changed to locate and load the Java Virtual Machine at run time, using the [java-locator](https://crates.io/crates/java-locator) and [libloading](https://crates.io/crates/libloading) libraries. 174 175`JavaVM::new` now returns a different error type, `StartJvmError`, when it fails. This new error type covers failures to locate and load the JVM library as well as failures to initialize the JVM. 176 177If you need to load a specific JVM library instead of automatically discovering one, use `JavaVM::with_libjvm` instead. 178 179On non-Windows platforms, it is no longer necessary for the Java runtime's `lib` folder to be on the shared library search path (`LD_LIBRARY_PATH` or equivalent). Unfortunately, this is *not* true on Windows, where the Java runtime's DLLs must still be on the `PATH` (or added to the DLL search path with [`SetDllDirectory`](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setdlldirectoryw)). 180 181 182## `Desc` has been redesigned 183 184The `Desc` trait has been redesigned. If your project contains any implementations of `Desc` and/or calls to `Desc::lookup`, they will need to be updated. 185 186If your project uses `Desc` directly like this, please post a comment on [issue #403](https://github.com/jni-rs/jni-rs/issues/403) explaining your situation. We view `Desc` as an implementation detail, and are considering sealing it and hiding its contents in a future release. 187 188Changes to `Desc` in this release are as follows: 189 190- `Desc` is now an `unsafe trait`. The safety requirements haven't actually changed, but they were undocumented until now. 191 192- `Desc` now has an associated type, `Output`, which is now the type returned by the `lookup` method. 193 194Please see the documentation for the `Desc` trait for more information. 195 196 197## `JNIEnv::call_*method_unchecked` is now `unsafe` 198 199The `JNIEnv::call_*method_unchecked` methods are now `unsafe`. Their safety requirements haven't changed, but they were undocumented until now. 200 201 202## `AutoLocal` type and lifetime parameters changed 203 204`AutoLocal` now has one lifetime parameter, representing the local reference frame it belongs to, and one type parameter, representing the type of object reference it contains (`JObject`, `JClass`, and so on). 205 206This means that the object reference stored in an `AutoLocal` no longer has its type erased. If it was a `JClass` before wrapping it in `AutoLocal`, it will still be a `JClass` after. 207 208```rust 209// `AutoLocal` type parameters now include the type of object reference… 210let auto_local: AutoLocal<'local, JClass<'local>>; 211 212// …so the type of the object reference is now kept instead of being erased. 213let local: &JClass<'local> = &*auto_local; 214``` 215 216 217## `JObject` no longer implements `From<&AutoLocal> + From<&GlobalRef>` 218 219It is no longer possible to directly convert an `&AutoLocal` or `&GlobalRef` into a `JObject`. Instead, a `JObject` can be *borrowed* from `AutoLocal` or `GlobalRef` through their implementations of `Deref` and/or `AsRef<JObject>`. 220 221```rust 222let global_ref: GlobalRef; 223 224// You can get a `JObject` from a `GlobalRef` two ways: 225let object_ref: &JObject<'static> = &*global_ref; 226let object_ref: &JObject<'static> = global_ref.as_ref(); 227``` 228 229 230## `JNIEnv::get_superclass` now returns `Option` 231 232The `JNIEnv::get_superclass` method previously returned a `JClass`, which would be null if the class in question doesn't have a superclass. It now returns `Option<JClass>` instead, and when it's `Some`, the `JClass` inside is never null. 233 234 235## `JNIEnv::{get,release}_string_utf_chars` removed 236 237The methods `JNIEnv::get_string_utf_chars` and `JNIEnv::release_string_utf_chars` have been removed. These methods have been replaced with `JavaStr::into_raw` and `JavaStr::from_raw`. To get a `JavaStr`, use `JNIEnv::get_string` or `JNIEnv::get_string_unchecked`. See [issue #372](https://github.com/jni-rs/jni-rs/pull/372) for discussion. 238 239 240## `JPrimitiveArray<T>` and `JObjectArray` provide safe reference wrappers (like `JObject`) for `jarray` 241 242This affects all `JNIEnv` array APIs including: 243- `new_<type>_array` 244- `get_array_length` 245- `get_object_array_element` 246- `set_object_array_element` 247- `get_<type>_array_region` 248- `set_<type>_array_region` 249- `get_array_elements` 250- `get_<type>_array_elements` (all removed) 251- `get_primitive_array_elements` (also renamed to `get_array_elements_critical`) 252 253With the exception of `get_array_length` these APIs now take or return a 254reference to a `JPrimitiveArray` or a `JObjectArray` instead of a `sys` type 255like `jarray` or `jbyteArray`. This improves safety since the sys types can be 256copied and easily read as invalid pointers after a reference has been deleted. 257 258`get_array_length` will accept a reference to a `JPrimitiveArray` or a `JObjectArray` 259since these both implement the `AsJArrayRaw` trait. You can also query the `.len()` 260of an array via the `AutoElements`/`AutoElementsCritical` APIs if you use those. 261 262There are the following type-specific aliases for `JPrimitiveArray<T>`: 263- `JBooleanArray` 264- `JByteArray` 265- `JCharArray` 266- `JShortArray` 267- `JIntArray` 268- `JLongArray` 269- `JFloatArray` 270- `JDoubleArray` 271 272 273## `AutoArray` and `AutoPrimitiveArray` renamed to `AutoElements` and `AutoElementsCritical` respectively 274 275This rename was done to: 2761. Clarify the connection between these APIs (they both provide temporary access to the elements of an array) 2772. Clarify their differences (`AutoElementsCritical` is an alternative that defines a restricted "critical" section that helps JNI avoid the need to copy the data for the array) 2783. Differentiate these from the new `JPrimitiveArray`/`JObjectArray` types and aliases like `JByteArray` that represent the array itself (not the elements) 279 280 281## `AutoElements<T>` and `AutoElementsCritical<T>` now implement `Deref<Target=[T]>` and `DerefMut` 282 283Previously accessing array elements required the use of `unsafe` code to access array elements via `AutoArray::as_ptr()` after acquiring an `AutoArray` guard. 284 285It's now possible to read and write elements via the `Deref` and `DerefMut` traits that will deref into a `[T]` slice like: 286 287```rust 288let byte_array = env.new_byte_array(100)?; 289 290{ 291 let mut elements = unsafe { env.get_array_elements(&byte_array, ReleaseMode::CopyBack) }?; 292 293 elements[0] = 0xff; 294 assert_eq!(elements[0], 0xff); 295 296 // elements released (copied back to Java) here on Drop 297} 298``` 299 300If you are accessing a `JPrimitiveArray<jbyte>`/`JByteArray` you may find it helpful 301to utilize the [bytemuck](https://crates.io/crates/bytemuck) crate in case you 302need to access the elements as `u8` unsigned bytes instead of `i8`. 303 304Although this hasn't changed, it seems worth highlighting that if you are 305accessing a `JPrimitiveArray<jboolean>`/`JBooleanArray` you are responsible for 306only storing values of `0` or `1`, since other values could lead to undefined 307behaviour for the JVM. 308 309 310## `AutoArray/AutoPrimitiveArray::size()` replace by `AutoElements/AutoElementsCritical::len()` 311 312Previously `AutoArray::size()` was a wrapper for calling `JNIEnv::get_array_length()` which could 313fail, where as `AutoElements` and `AutoElementsCritical` now need to know the array length to 314be constructed, and this constant is accessible via the `.len()` method. 315 316This change was required to support being able to `Deref` to a `[T]` slice. 317 318 319## `get_array_elements[_critical]` are `unsafe` 320 321Previously `get_array_elements` and `get_primitive_array_critical` could be called by safe code 322but there were multiple ways in which these APIs could quite easily lead to undefined behaviour. 323 324Since it is only possible to acquire an `AutoElements` and `AutoElementsCritical` instance via 325`get_array_elements` and `get_primitive_array_critical`, that's where we now document 326the safety rules that need to be followed to avoid any undefined behaviour. 327