# Migrating to 0.21

Version 0.21 makes extensive breaking changes in order to improve safety. Most projects that use this library will need to be changed accordingly.

This is a guide for migrating to 0.21. For a full list of changes in this release, please see the [changelog](../CHANGELOG.md).

Most 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.


## `JNIEnv` parameter of `extern fn`s should now be `mut`

In `extern fn`s that are directly called by the JVM, the `JNIEnv` parameter will usually need to be `mut`.

```rust
#[no_mangle]
pub extern "system" fn Java_HelloWorld_hello<'local>(mut env: JNIEnv<'local>,
                                                     class: JClass<'local>,
                                                     input: JString<'local>)
                                                     -> jstring {
    …
}
```

This is needed because most `JNIEnv` methods now take a `&mut self` parameter.


## `JNIEnv`, `JObject`, etc are now `!Copy` and should be borrowed

Functions 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.

```rust
pub fn print_string(env: &mut JNIEnv,
                    string: &JString)
                    -> Result<()> {
    println!("{}", env.get_string(string)?.to_string_lossy());
    Ok(())
}
```

This is needed because these types no longer have the `Copy` or `Clone` traits.


## `JNIEnv::with_local_frame` closure now takes a `&mut JNIEnv` parameter

When using `JNIEnv::with_local_frame`, `Executor::with_attached`, or `Executor::with_attached_capacity`, the closure must now take a parameter of type `&mut JNIEnv`.

```rust
env.with_local_frame(16, |env| {
    …
})
```

The closure must only use the `JNIEnv` passed to it in that parameter, and not the `JNIEnv` that `with_local_frame` was called on.


## `JNIEnv::with_local_frame` closure can return a generic `Result`

_Note: This also applies to `Executor::with_attached` and `Executor::with_attached_capacity` which are thin wrappers over `with_local_frame`_

The 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>`.

This 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.

There are a few trade offs with this change though:
1. 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
2. Since it's no longer assumed that code always wants to return a single local reference this special case has to be handled differently

Two options for clarifying the error type for the compiler if it's ambiguous would be:

1. Specify the type of the return value as part of an assignment:
```rust
let result: MyResult<()> = env.with_local_frame(10, |env| { Ok(()) });
```

2. Specify the generic `E` error parameter:
```rust
env.with_local_frame::<_, _, MyError>(10, |env| { Ok(()) })?;
```

Code 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)


## Passing object reference parameters to Java methods

When 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()`.

```rust
env.call_static_method(
    "com/example/SomeClass",
    "someStaticMethod",
    "(Ljava/lang/Object;)V",
    &[
        (&obj).into(),
    ],
)
```


## `JList` and `JMap` methods all take a `&mut JNIEnv` parameter

All 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.

This 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`.


## `JList` and `JMap` iterators no longer implement `Iterator`

Just like the methods of `JList` and `JMap`, their iterator now also requires a `&mut JNIEnv` in order to get the next element.

For 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.

```rust
let mut iterator = list.iter(env)?;

while let Some(obj) = iterator.next(env)? {
    let obj: AutoLocal<JObject> = env.auto_local(obj);
    // Do something with `obj` here.
}
```


## Local references no longer leak

`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`.


## `cannot borrow as mutable`

If 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.

```rust
fn example_function(
    env: &mut JNIEnv,
    obj: &JObject,
) {
    // …
}

example_function(
    env,
    // ERROR: cannot borrow `*env` as mutable more than once at a time
    &env.new_object(
        "com/example/SomeClass",
        "()V",
        &[],
    )?,
)
```

To fix this, the `JNIEnv` parameter needs to come *last*.

```rust
fn example_function(
    obj: &JObject,
    env: &mut JNIEnv,
) {
    // …
}

example_function(
    &env.new_object(
        "com/example/SomeClass",
        "()V",
        &[],
    )?,
    env,
);
```


## `invocation` feature now finds and loads the JVM dynamically; `JavaVM::new` returns different error

The `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.

`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.

If you need to load a specific JVM library instead of automatically discovering one, use `JavaVM::with_libjvm` instead.

On 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)).


## `Desc` has been redesigned

The `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.

If 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.

Changes to `Desc` in this release are as follows:

- `Desc` is now an `unsafe trait`. The safety requirements haven't actually changed, but they were undocumented until now.

- `Desc` now has an associated type, `Output`, which is now the type returned by the `lookup` method.

Please see the documentation for the `Desc` trait for more information.


## `JNIEnv::call_*method_unchecked` is now `unsafe`

The `JNIEnv::call_*method_unchecked` methods are now `unsafe`. Their safety requirements haven't changed, but they were undocumented until now.


## `AutoLocal` type and lifetime parameters changed

`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).

This 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.

```rust
// `AutoLocal` type parameters now include the type of object reference…
let auto_local: AutoLocal<'local, JClass<'local>>;

// …so the type of the object reference is now kept instead of being erased.
let local: &JClass<'local> = &*auto_local;
```


## `JObject` no longer implements `From<&AutoLocal> + From<&GlobalRef>`

It 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>`.

```rust
let global_ref: GlobalRef;

// You can get a `JObject` from a `GlobalRef` two ways:
let object_ref: &JObject<'static> = &*global_ref;
let object_ref: &JObject<'static> = global_ref.as_ref();
```


## `JNIEnv::get_superclass` now returns `Option`

The `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.


## `JNIEnv::{get,release}_string_utf_chars` removed

The 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.


## `JPrimitiveArray<T>` and `JObjectArray` provide safe reference wrappers (like `JObject`) for `jarray`

This affects all `JNIEnv` array APIs including:
- `new_<type>_array`
- `get_array_length`
- `get_object_array_element`
- `set_object_array_element`
- `get_<type>_array_region`
- `set_<type>_array_region`
- `get_array_elements`
- `get_<type>_array_elements` (all removed)
- `get_primitive_array_elements` (also renamed to `get_array_elements_critical`)

With the exception of `get_array_length` these APIs now take or return a
reference to a `JPrimitiveArray` or a `JObjectArray` instead of a `sys` type
like `jarray` or `jbyteArray`. This improves safety since the sys types can be
copied and easily read as invalid pointers after a reference has been deleted.

`get_array_length` will accept a reference to a `JPrimitiveArray` or a `JObjectArray`
since these both implement the `AsJArrayRaw` trait. You can also query the `.len()`
of an array via the `AutoElements`/`AutoElementsCritical` APIs if you use those.

There are the following type-specific aliases for `JPrimitiveArray<T>`:
- `JBooleanArray`
- `JByteArray`
- `JCharArray`
- `JShortArray`
- `JIntArray`
- `JLongArray`
- `JFloatArray`
- `JDoubleArray`


## `AutoArray` and `AutoPrimitiveArray` renamed to `AutoElements` and `AutoElementsCritical` respectively

This rename was done to:
1. Clarify the connection between these APIs (they both provide temporary access to the elements of an array)
2. 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)
3. Differentiate these from the new `JPrimitiveArray`/`JObjectArray` types and aliases like `JByteArray` that represent the array itself (not the elements)


## `AutoElements<T>` and `AutoElementsCritical<T>` now implement `Deref<Target=[T]>` and `DerefMut`

Previously accessing array elements required the use of `unsafe` code to access array elements via `AutoArray::as_ptr()` after acquiring an `AutoArray` guard.

It's now possible to read and write elements via the `Deref` and `DerefMut` traits that will deref into a `[T]` slice like:

```rust
let byte_array = env.new_byte_array(100)?;

{
    let mut elements = unsafe { env.get_array_elements(&byte_array, ReleaseMode::CopyBack) }?;

    elements[0] = 0xff;
    assert_eq!(elements[0], 0xff);

    // elements released (copied back to Java) here on Drop
}
```

If you are accessing a `JPrimitiveArray<jbyte>`/`JByteArray` you may find it helpful
to utilize the [bytemuck](https://crates.io/crates/bytemuck) crate in case you
need to access the elements as `u8` unsigned bytes instead of `i8`.

Although this hasn't changed, it seems worth highlighting that if you are
accessing a `JPrimitiveArray<jboolean>`/`JBooleanArray` you are responsible for
only storing values of `0` or `1`, since other values could lead to undefined
behaviour for the JVM.


## `AutoArray/AutoPrimitiveArray::size()` replace by `AutoElements/AutoElementsCritical::len()`

Previously `AutoArray::size()` was a wrapper for calling `JNIEnv::get_array_length()` which could
fail, where as `AutoElements` and `AutoElementsCritical` now need to know the array length to
be constructed, and this constant is accessible via the `.len()` method.

This change was required to support being able to `Deref` to a `[T]` slice.


## `get_array_elements[_critical]` are `unsafe`

Previously `get_array_elements` and `get_primitive_array_critical` could be called by safe code
but there were multiple ways in which these APIs could quite easily lead to undefined behaviour.

Since it is only possible to acquire an `AutoElements` and `AutoElementsCritical` instance via
`get_array_elements` and `get_primitive_array_critical`, that's where we now document
the safety rules that need to be followed to avoid any undefined behaviour.
