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 {
2021}
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| {
4950})
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