From 84dfccfe845e79988a515250a18bc053e11519d5 Mon Sep 17 00:00:00 2001 From: Emmanuel Keller Date: Fri, 29 May 2026 21:54:51 +0100 Subject: [PATCH] Upgrade jni crate 0.21.1 -> 0.22.4 Migrate the native layer to the jni crate 0.22.4 (a major API overhaul). The Java public API, JNI ABI / native method symbols, JDK 8 minimum, and the structured ServerException hierarchy are all unchanged. - Native methods now take EnvUnowned and run their body via Env::with_env (the new EnvUnowned/Env split). A with_env_body! macro + resolve_outcome helper keep the existing raw jobject/jstring/jlong return types and the SurrealError::exception sink (resolve() needs T: Default, which raw pointers are not, so the outcome is resolved via into_outcome()). - String method/constructor signatures -> jni_sig!; class and method names -> jni_str! (or JNIString for dynamic names); Env::get_string (deprecated) -> JString::try_to_string. - jni-sys 0.4 makes jboolean = bool: JValue::Bool and the jboolean default closures/params were adjusted accordingly. - JObject -> JThrowable via Env::cast_local; typed JObjectArray; array length/region/element APIs via JObjectArray/JPrimitiveArray. Explicit #[no_mangle] export names are kept (not #[jni_mangle]) so the 189 Java_com_surrealdb_* symbols stay byte-identical. Verified: cargo build/clippy/fmt clean, 189 export symbols unchanged, 288 Java tests pass (2 skipped, OS-specific). --- Cargo.lock | 65 +- Cargo.toml | 2 +- src/main/rust/array.rs | 147 +-- src/main/rust/entry.rs | 53 +- src/main/rust/entryiterator.rs | 27 +- src/main/rust/entrymut.rs | 49 +- src/main/rust/error.rs | 122 ++- src/main/rust/fileref.rs | 55 +- src/main/rust/geometry.rs | 83 +- src/main/rust/id.rs | 291 +++--- src/main/rust/lib.rs | 40 +- src/main/rust/live.rs | 93 +- src/main/rust/macros.rs | 48 +- src/main/rust/object.rs | 131 +-- src/main/rust/recordid.rs | 173 +-- src/main/rust/response.rs | 29 +- src/main/rust/surreal.rs | 1562 +++++++++++++++------------- src/main/rust/syncentryiterator.rs | 31 +- src/main/rust/syncvalueiterator.rs | 31 +- src/main/rust/transaction.rs | 81 +- src/main/rust/value.rs | 473 +++++---- src/main/rust/valueiterator.rs | 27 +- src/main/rust/valuemut.rs | 261 +++-- 23 files changed, 2177 insertions(+), 1697 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9854242c..7f7d301b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1979,19 +1979,68 @@ dependencies = [ "cesu8", "cfg-if", "combine", - "jni-sys", + "jni-sys 0.3.0", "log", "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + [[package]] name = "jni-sys" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -3456,7 +3505,7 @@ checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" dependencies = [ "core-foundation", "core-foundation-sys", - "jni", + "jni 0.21.1", "log", "once_cell", "rustls", @@ -3697,6 +3746,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -3987,7 +4046,7 @@ dependencies = [ "chrono", "dashmap", "futures", - "jni", + "jni 0.22.4", "once_cell", "parking_lot", "rust_decimal", diff --git a/Cargo.toml b/Cargo.toml index 22e910b7..42be8525 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ opt-level = 3 # See: https://github.com/surrealdb/surrealdb/blob//Cargo.toml # Driver-specific dependencies (jni, once_cell, cargo-lock) are independent. [dependencies] -jni = "0.21.1" +jni = "0.22.4" surrealdb = { version = "3.0.5", default-features = false, features = ["rustls", "protocol-http", "protocol-ws"] } serde = "1.0.228" serde_json = "1.0.149" diff --git a/src/main/rust/array.rs b/src/main/rust/array.rs index 7398ab8c..e9b5ff53 100644 --- a/src/main/rust/array.rs +++ b/src/main/rust/array.rs @@ -2,9 +2,10 @@ use std::hash::{DefaultHasher, Hash, Hasher}; use std::ptr::null_mut; use std::sync::Arc; +use crate::with_env_body; use jni::objects::{JClass, JLongArray}; use jni::sys::{jboolean, jint, jlong, jstring}; -use jni::JNIEnv; +use jni::EnvUnowned; use parking_lot::Mutex; use surrealdb::types::{Array, ToSql, Value}; @@ -16,7 +17,7 @@ use crate::{ #[no_mangle] pub extern "system" fn Java_com_surrealdb_Array_deleteInstance<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) { @@ -25,121 +26,137 @@ pub extern "system" fn Java_com_surrealdb_Array_deleteInstance<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_Array_newOf<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptrs: JLongArray<'local>, ) -> jlong { - let ptrs = get_long_array!(&mut env, &ptrs, || 0); - let mut values = Vec::with_capacity(ptrs.len()); - for ptr in ptrs { - let value = take_value_mut_instance!(&mut env, ptr, || 0); - values.push(value); - } - let value = Value::Array(Array::from(values)); - JniTypes::new_value(value.into()) + with_env_body!(env, env, { + let ptrs = get_long_array!(env, &ptrs, || 0); + let mut values = Vec::with_capacity(ptrs.len()); + for ptr in ptrs { + let value = take_value_mut_instance!(env, ptr, || 0); + values.push(value); + } + let value = Value::Array(Array::from(values)); + JniTypes::new_value(value.into()) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Array_len<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jint { - let value = get_value_instance!(&mut env, ptr, || 0 as jint); - if let Value::Array(a) = value.as_ref() { - a.len() as jint - } else { - SurrealError::NullPointerException("Array").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0 as jint); + if let Value::Array(a) = value.as_ref() { + a.len() as jint + } else { + SurrealError::NullPointerException("Array").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Array_get<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, idx: jint, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Array(a) = value.as_ref() { - let val = a.get(idx as usize).cloned().unwrap_or(Value::None); - JniTypes::new_value(val.into()) - } else { - SurrealError::NullPointerException("Array").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Array(a) = value.as_ref() { + let val = a.get(idx as usize).cloned().unwrap_or(Value::None); + JniTypes::new_value(val.into()) + } else { + SurrealError::NullPointerException("Array").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Array_iterator<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Array(a) = value.as_ref() { - let iter = a.clone().into_iter(); - JniTypes::new_array_iter(iter) - } else { - SurrealError::NullPointerException("Array").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Array(a) = value.as_ref() { + let iter = a.clone().into_iter(); + JniTypes::new_array_iter(iter) + } else { + SurrealError::NullPointerException("Array").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Array_synchronizedIterator<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Array(a) = value.as_ref() { - let iter = a.clone().into_iter(); - JniTypes::new_sync_array_iter(Mutex::new(iter).into()) - } else { - SurrealError::NullPointerException("Array").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Array(a) = value.as_ref() { + let iter = a.clone().into_iter(); + JniTypes::new_sync_array_iter(Mutex::new(iter).into()) + } else { + SurrealError::NullPointerException("Array").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Array_toString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if matches!(value.as_ref(), Value::Array(_)) { - new_string!(&mut env, value.to_sql(), null_mut) - } else { - SurrealError::NullPointerException("Array").exception(&mut env, null_mut) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if matches!(value.as_ref(), Value::Array(_)) { + new_string!(env, value.to_sql(), null_mut) + } else { + SurrealError::NullPointerException("Array").exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Array_hashCode<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jint { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Array(a) = value.as_ref() { - let mut hasher = DefaultHasher::new(); - a.hash(&mut hasher); - let hash64 = hasher.finish(); - return (hash64 & 0xFFFFFFFF) as jint; - } - SurrealError::NullPointerException("Array").exception(&mut env, || 0) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Array(a) = value.as_ref() { + let mut hasher = DefaultHasher::new(); + a.hash(&mut hasher); + let hash64 = hasher.finish(); + return (hash64 & 0xFFFFFFFF) as jint; + } + SurrealError::NullPointerException("Array").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Array_equals<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr1: jlong, ptr2: jlong, ) -> jboolean { - let v1 = get_value_instance!(&mut env, ptr1, || false as jboolean); - let v2 = get_value_instance!(&mut env, ptr2, || false as jboolean); - if let (Value::Array(a1), Value::Array(a2)) = (v1.as_ref(), v2.as_ref()) { - return a1.eq(a2) as jboolean; - } - SurrealError::NullPointerException("Array").exception(&mut env, || false as jboolean) + with_env_body!(env, env, { + let v1 = get_value_instance!(env, ptr1, || false as jboolean); + let v2 = get_value_instance!(env, ptr2, || false as jboolean); + if let (Value::Array(a1), Value::Array(a2)) = (v1.as_ref(), v2.as_ref()) { + return a1.eq(a2) as jboolean; + } + SurrealError::NullPointerException("Array").exception(env, || false as jboolean) + }) } diff --git a/src/main/rust/entry.rs b/src/main/rust/entry.rs index 415d60b7..b91148f7 100644 --- a/src/main/rust/entry.rs +++ b/src/main/rust/entry.rs @@ -1,64 +1,75 @@ use std::hash::{DefaultHasher, Hash, Hasher}; use std::ptr::null_mut; +use crate::with_env_body; use crate::{get_entry_instance, new_string, JniTypes}; use jni::objects::JClass; use jni::sys::{jboolean, jint, jlong, jstring}; -use jni::JNIEnv; +use jni::EnvUnowned; use surrealdb::types::ToSql; #[no_mangle] pub extern "system" fn Java_com_surrealdb_Entry_getKey<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let (key, _) = get_entry_instance!(&mut env, ptr, null_mut); - new_string!(&mut env, key, null_mut) + with_env_body!(env, env, { + let (key, _) = get_entry_instance!(env, ptr, null_mut); + new_string!(env, key, null_mut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Entry_getValue<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let (_, value) = get_entry_instance!(&mut env, ptr, || 0); - JniTypes::new_value(value.clone()) + with_env_body!(env, env, { + let (_, value) = get_entry_instance!(env, ptr, || 0); + JniTypes::new_value(value.clone()) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Entry_toString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let (key, value) = get_entry_instance!(&mut env, ptr, null_mut); - new_string!(&mut env, format!("({key},{})", value.to_sql()), null_mut) + with_env_body!(env, env, { + let (key, value) = get_entry_instance!(env, ptr, null_mut); + new_string!(env, format!("({key},{})", value.to_sql()), null_mut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Entry_hashCode<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jint { - let (key, value) = get_entry_instance!(&mut env, ptr, || 0); - let mut hasher = DefaultHasher::new(); - key.hash(&mut hasher); - value.hash(&mut hasher); - let hash64 = hasher.finish(); - (hash64 & 0xFFFFFFFF) as jint + with_env_body!(env, env, { + let (key, value) = get_entry_instance!(env, ptr, || 0); + let mut hasher = DefaultHasher::new(); + key.hash(&mut hasher); + value.hash(&mut hasher); + let hash64 = hasher.finish(); + (hash64 & 0xFFFFFFFF) as jint + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Entry_equals<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr1: jlong, ptr2: jlong, ) -> jboolean { - let (key1, value1) = get_entry_instance!(&mut env, ptr1, || 0); - let (key2, value2) = get_entry_instance!(&mut env, ptr2, || 0); - (key1.eq(key2) && value1.eq(value2)) as jboolean + with_env_body!(env, env, { + let (key1, value1) = get_entry_instance!(env, ptr1, || false); + let (key2, value2) = get_entry_instance!(env, ptr2, || false); + (key1.eq(key2) && value1.eq(value2)) as jboolean + }) } diff --git a/src/main/rust/entryiterator.rs b/src/main/rust/entryiterator.rs index 6e51c367..d3d65bba 100644 --- a/src/main/rust/entryiterator.rs +++ b/src/main/rust/entryiterator.rs @@ -1,30 +1,35 @@ +use crate::with_env_body; use jni::objects::JClass; use jni::sys::{jboolean, jlong}; -use jni::JNIEnv; +use jni::EnvUnowned; use crate::error::SurrealError; use crate::{get_entry_iterator_instance, get_entry_iterator_mut_instance, JniTypes}; #[no_mangle] pub extern "system" fn Java_com_surrealdb_EntryIterator_hasNext<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let iter = get_entry_iterator_instance!(&mut env, ptr, || false as jboolean); - (iter.len() > 0) as jboolean + with_env_body!(env, env, { + let iter = get_entry_iterator_instance!(env, ptr, || false as jboolean); + (iter.len() > 0) as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_EntryIterator_next<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let iter = get_entry_iterator_mut_instance!(&mut env, ptr, || 0); - if let Some((k, v)) = iter.next() { - JniTypes::new_key_value(k, v.into()) - } else { - SurrealError::NoSuchElementException.exception(&mut env, || 0) - } + with_env_body!(env, env, { + let iter = get_entry_iterator_mut_instance!(env, ptr, || 0); + if let Some((k, v)) = iter.next() { + JniTypes::new_key_value(k, v.into()) + } else { + SurrealError::NoSuchElementException.exception(env, || 0) + } + }) } diff --git a/src/main/rust/entrymut.rs b/src/main/rust/entrymut.rs index 1a23b37b..8a81fc53 100644 --- a/src/main/rust/entrymut.rs +++ b/src/main/rust/entrymut.rs @@ -2,18 +2,19 @@ use std::hash::{DefaultHasher, Hash, Hasher}; use std::ptr::null_mut; use crate::error::SurrealError; +use crate::with_env_body; use crate::{ get_entry_mut_instance, get_rust_string, new_string, release_instance, take_value_mut_instance, JniTypes, }; use jni::objects::{JClass, JString}; use jni::sys::{jboolean, jint, jlong, jstring}; -use jni::JNIEnv; +use jni::EnvUnowned; use surrealdb::types::{ToSql, Value}; #[no_mangle] pub extern "system" fn Java_com_surrealdb_EntryMut_deleteInstance<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) { @@ -22,48 +23,56 @@ pub extern "system" fn Java_com_surrealdb_EntryMut_deleteInstance<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_EntryMut_create<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, s: JString<'local>, v: jlong, ) -> jlong { - let s = get_rust_string!(&mut env, s, || 0); - let v = take_value_mut_instance!(&mut env, v, || 0); - JniTypes::new_key_value_mut(s, v) + with_env_body!(env, env, { + let s = get_rust_string!(env, s, || 0); + let v = take_value_mut_instance!(env, v, || 0); + JniTypes::new_key_value_mut(s, v) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_EntryMut_toString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let (key, value) = get_entry_mut_instance!(&mut env, ptr, null_mut); - new_string!(&mut env, format!("({key},{})", value.to_sql()), null_mut) + with_env_body!(env, env, { + let (key, value) = get_entry_mut_instance!(env, ptr, null_mut); + new_string!(env, format!("({key},{})", value.to_sql()), null_mut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_EntryMut_hashCode<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jint { - let (key, value) = get_entry_mut_instance!(&mut env, ptr, || 0); - let mut hasher = DefaultHasher::new(); - key.hash(&mut hasher); - value.hash(&mut hasher); - let hash64 = hasher.finish(); - (hash64 & 0xFFFFFFFF) as jint + with_env_body!(env, env, { + let (key, value) = get_entry_mut_instance!(env, ptr, || 0); + let mut hasher = DefaultHasher::new(); + key.hash(&mut hasher); + value.hash(&mut hasher); + let hash64 = hasher.finish(); + (hash64 & 0xFFFFFFFF) as jint + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_EntryMut_equals<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr1: jlong, ptr2: jlong, ) -> jboolean { - let (key1, value1) = get_entry_mut_instance!(&mut env, ptr1, || 0); - let (key2, value2) = get_entry_mut_instance!(&mut env, ptr2, || 0); - (key1.eq(key2) && value1.eq(value2)) as jboolean + with_env_body!(env, env, { + let (key1, value1) = get_entry_mut_instance!(env, ptr1, || false); + let (key2, value2) = get_entry_mut_instance!(env, ptr2, || false); + (key1.eq(key2) && value1.eq(value2)) as jboolean + }) } diff --git a/src/main/rust/error.rs b/src/main/rust/error.rs index 68d3ab86..b6fe74c6 100644 --- a/src/main/rust/error.rs +++ b/src/main/rust/error.rs @@ -2,7 +2,8 @@ use std::error::Error as StdError; use jni::errors::Error; use jni::objects::{JObject, JThrowable, JValue}; -use jni::JNIEnv; +use jni::strings::JNIString; +use jni::{jni_sig, jni_str, Env}; use surrealdb::types::{ErrorDetails, Number, SurrealValue, Value}; pub(super) enum SurrealError { @@ -42,47 +43,46 @@ fn details_value(error: &surrealdb::Error) -> Value { /// Builds a Java object (Map, String, Number, or null) from a SurrealDB Value via JNI. /// Used for error details only; supports objects, strings, numbers, null, and arrays. -fn value_to_jobject<'a>(env: &mut JNIEnv<'a>, value: &Value) -> Option> { +fn value_to_jobject<'a>(env: &mut Env<'a>, value: &Value) -> Option> { match value { Value::None | Value::Null => Some(JObject::null()), - Value::String(s) => env.new_string(s.to_string()).ok().map(JObject::from), + Value::String(s) => env.new_string(s).ok().map(JObject::from), Value::Number(n) => number_to_jobject(env, n), Value::Bool(b) => { - let class = env.find_class("java/lang/Boolean").ok()?; - let z = if *b { 1u8 } else { 0u8 }; + let class = env.find_class(jni_str!("java/lang/Boolean")).ok()?; env.call_static_method( class, - "valueOf", - "(Z)Ljava/lang/Boolean;", - &[JValue::Bool(z)], + jni_str!("valueOf"), + jni_sig!("(Z)Ljava/lang/Boolean;"), + &[JValue::Bool(*b)], ) .ok() .and_then(|v| v.l().ok()) } Value::Object(map) => { - let class = env.find_class("java/util/LinkedHashMap").ok()?; - let map_obj = env.new_object(class, "()V", &[]).ok()?; + let class = env.find_class(jni_str!("java/util/LinkedHashMap")).ok()?; + let map_obj = env.new_object(class, jni_sig!("()V"), &[]).ok()?; for (k, v) in map.iter() { let key_obj = env.new_string(k).ok().map(JObject::from)?; let val_obj = value_to_jobject(env, v).unwrap_or(JObject::null()); let _ = env.call_method( &map_obj, - "put", - "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", + jni_str!("put"), + jni_sig!("(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"), &[JValue::Object(&key_obj), JValue::Object(&val_obj)], ); } Some(map_obj) } Value::Array(arr) => { - let class = env.find_class("java/util/ArrayList").ok()?; - let list_obj = env.new_object(class, "()V", &[]).ok()?; + let class = env.find_class(jni_str!("java/util/ArrayList")).ok()?; + let list_obj = env.new_object(class, jni_sig!("()V"), &[]).ok()?; for v in arr.iter() { let elem = value_to_jobject(env, v).unwrap_or(JObject::null()); let _ = env.call_method( &list_obj, - "add", - "(Ljava/lang/Object;)Z", + jni_str!("add"), + jni_sig!("(Ljava/lang/Object;)Z"), &[JValue::Object(&elem)], ); } @@ -92,32 +92,37 @@ fn value_to_jobject<'a>(env: &mut JNIEnv<'a>, value: &Value) -> Option(env: &mut JNIEnv<'a>, n: &Number) -> Option> { +fn number_to_jobject<'a>(env: &mut Env<'a>, n: &Number) -> Option> { match n { Number::Int(i) => { - let class = env.find_class("java/lang/Long").ok()?; - env.call_static_method(class, "valueOf", "(J)Ljava/lang/Long;", &[JValue::Long(*i)]) - .ok() - .and_then(|v| v.l().ok()) + let class = env.find_class(jni_str!("java/lang/Long")).ok()?; + env.call_static_method( + class, + jni_str!("valueOf"), + jni_sig!("(J)Ljava/lang/Long;"), + &[JValue::Long(*i)], + ) + .ok() + .and_then(|v| v.l().ok()) } Number::Float(f) => { - let class = env.find_class("java/lang/Double").ok()?; + let class = env.find_class(jni_str!("java/lang/Double")).ok()?; env.call_static_method( class, - "valueOf", - "(D)Ljava/lang/Double;", + jni_str!("valueOf"), + jni_sig!("(D)Ljava/lang/Double;"), &[JValue::Double(*f)], ) .ok() .and_then(|v| v.l().ok()) } Number::Decimal(d) => { - let class = env.find_class("java/lang/Double").ok()?; + let class = env.find_class(jni_str!("java/lang/Double")).ok()?; let f: f64 = d.to_string().parse().unwrap_or(0.0); env.call_static_method( class, - "valueOf", - "(D)Ljava/lang/Double;", + jni_str!("valueOf"), + jni_sig!("(D)Ljava/lang/Double;"), &[JValue::Double(f)], ) .ok() @@ -133,10 +138,7 @@ fn number_to_jobject<'a>(env: &mut JNIEnv<'a>, n: &Number) -> Option /// ErrorKind enum. Base ServerException uses (ErrorKind, String rawKindIfUnknown, message, details, cause); /// subclasses use (String message, Object details, ServerException cause). #[allow(clippy::redundant_closure)] // JObject::null as fn ptr produces 'static, breaking the 'a lifetime -fn build_server_exception<'a>( - env: &mut JNIEnv<'a>, - error: &surrealdb::Error, -) -> Option> { +fn build_server_exception<'a>(env: &mut Env<'a>, error: &surrealdb::Error) -> Option> { // Recursively build the cause first (std::error::Error::source; cause chain when present) let java_cause: JObject = if let Some(source) = error.source() { if let Some(surreal_cause) = source.downcast_ref::() { @@ -176,7 +178,7 @@ fn build_server_exception<'a>( }; let is_base_class = class_name == SERVER_EXCEPTION; - let class = match env.find_class(class_name) { + let class = match env.find_class(JNIString::from(class_name)) { Ok(c) => c, Err(_) => return None, }; @@ -193,9 +195,13 @@ fn build_server_exception<'a>( if is_base_class { // ServerException(ErrorKind kind, String rawKindIfUnknown, String message, Object details, ServerException cause) - let enum_class = env.find_class("com/surrealdb/ErrorKind").ok()?; + let enum_class = env.find_class(jni_str!("com/surrealdb/ErrorKind")).ok()?; let enum_obj = env - .get_static_field(&enum_class, enum_name, "Lcom/surrealdb/ErrorKind;") + .get_static_field( + &enum_class, + JNIString::from(enum_name), + jni_sig!("Lcom/surrealdb/ErrorKind;"), + ) .ok()? .l() .ok()?; @@ -207,7 +213,6 @@ fn build_server_exception<'a>( .unwrap_or(JObject::null()), None => JObject::null(), }; - let sig = "(Lcom/surrealdb/ErrorKind;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;Lcom/surrealdb/ServerException;)V"; let args = [ JValue::Object(&enum_obj), JValue::Object(&raw_kind_jstr), @@ -215,39 +220,48 @@ fn build_server_exception<'a>( JValue::Object(&details_obj), JValue::Object(&java_cause), ]; - env.new_object(class, sig, &args).ok() + env.new_object(class, jni_sig!("(Lcom/surrealdb/ErrorKind;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;Lcom/surrealdb/ServerException;)V"), &args).ok() } else { // Subclass(String message, Object details, ServerException cause) - let sig = "(Ljava/lang/String;Ljava/lang/Object;Lcom/surrealdb/ServerException;)V"; let args = [ JValue::Object(&message_obj), JValue::Object(&details_obj), JValue::Object(&java_cause), ]; - env.new_object(class, sig, &args).ok() + env.new_object( + class, + jni_sig!("(Ljava/lang/String;Ljava/lang/Object;Lcom/surrealdb/ServerException;)V"), + &args, + ) + .ok() } } impl SurrealError { - pub(super) fn exception T>(self, env: &mut JNIEnv, output: F) -> T { - if let Ok(b) = env.exception_check() { - // If there is already an exception thrown we don't add one - if !b { - match &self { - Self::SurrealDB(e) => { - // Build structured server exception via JNI - if let Some(exc) = build_server_exception(env, e) { - let throwable: JThrowable = exc.into(); + pub(super) fn exception T>(self, env: &mut Env, output: F) -> T { + // exception_check() is infallible in jni 0.22 (returns bool, not Result). + // If there is already an exception thrown we don't add one. + if !env.exception_check() { + match &self { + Self::SurrealDB(e) => { + // Build structured server exception via JNI + if let Some(exc) = build_server_exception(env, e) { + // jni 0.22: JObject no longer converts into JThrowable directly; + // use a checked cast (the object is always a Throwable subclass). + if let Ok(throwable) = env.cast_local::(exc) { let _ = env.throw(throwable); - } else { - // Fallback: flat string - let _ = env.throw_new(SURREAL_EXCEPTION, e.to_string()); } + } else { + // Fallback: flat string + let _ = env.throw_new( + JNIString::from(SURREAL_EXCEPTION), + JNIString::from(e.to_string()), + ); } - _ => { - let exc = self.into_exception(); - let _ = env.throw_new(exc.class, exc.msg); - } + } + _ => { + let exc = self.into_exception(); + let _ = env.throw_new(JNIString::from(exc.class), JNIString::from(exc.msg)); } } } diff --git a/src/main/rust/fileref.rs b/src/main/rust/fileref.rs index 84b640c6..1a389a5b 100644 --- a/src/main/rust/fileref.rs +++ b/src/main/rust/fileref.rs @@ -1,8 +1,9 @@ use std::ptr::null_mut; +use crate::with_env_body; use jni::objects::JClass; use jni::sys::{jlong, jstring}; -use jni::JNIEnv; +use jni::EnvUnowned; use surrealdb::types::Value; use crate::error::SurrealError; @@ -10,50 +11,56 @@ use crate::{get_value_instance, new_string, release_instance}; #[no_mangle] pub extern "system" fn Java_com_surrealdb_FileRef_getBucket<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::File(f) = value.as_ref() { - new_string!(&mut env, f.bucket().to_string(), null_mut) - } else { - SurrealError::NullPointerException("FileRef").exception(&mut env, null_mut) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::File(f) = value.as_ref() { + new_string!(env, f.bucket(), null_mut) + } else { + SurrealError::NullPointerException("FileRef").exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_FileRef_getKey<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::File(f) = value.as_ref() { - new_string!(&mut env, f.key().to_string(), null_mut) - } else { - SurrealError::NullPointerException("FileRef").exception(&mut env, null_mut) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::File(f) = value.as_ref() { + new_string!(env, f.key(), null_mut) + } else { + SurrealError::NullPointerException("FileRef").exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_FileRef_toString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::File(f) = value.as_ref() { - let s = format!("f\"{}:{}\"", f.bucket(), f.key()); - new_string!(&mut env, s, null_mut) - } else { - SurrealError::NullPointerException("FileRef").exception(&mut env, null_mut) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::File(f) = value.as_ref() { + let s = format!("f\"{}:{}\"", f.bucket(), f.key()); + new_string!(env, s, null_mut) + } else { + SurrealError::NullPointerException("FileRef").exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_FileRef_deleteInstance<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) { diff --git a/src/main/rust/geometry.rs b/src/main/rust/geometry.rs index 383e1d98..5addda2f 100644 --- a/src/main/rust/geometry.rs +++ b/src/main/rust/geometry.rs @@ -1,9 +1,10 @@ use std::hash::{DefaultHasher, Hash, Hasher}; use std::ptr::null_mut; +use crate::with_env_body; use jni::objects::JClass; use jni::sys::{jboolean, jdoubleArray, jint, jlong, jstring}; -use jni::JNIEnv; +use jni::EnvUnowned; use surrealdb::types::{Geometry, Value}; use crate::error::SurrealError; @@ -11,71 +12,81 @@ use crate::{get_value_instance, new_double_point, new_string}; #[no_mangle] pub extern "system" fn Java_com_surrealdb_Geometry_isPoint<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - if let Value::Geometry(g) = value.as_ref() { - g.is_point() as jboolean - } else { - SurrealError::NullPointerException("Geometry").exception(&mut env, || false as jboolean) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + if let Value::Geometry(g) = value.as_ref() { + g.is_point() as jboolean + } else { + SurrealError::NullPointerException("Geometry").exception(env, || false as jboolean) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Geometry_getPoint<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jdoubleArray { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::Geometry(Geometry::Point(pt)) = value.as_ref() { - return new_double_point!(&mut env, pt, null_mut); - } - SurrealError::NullPointerException("Geometry").exception(&mut env, null_mut) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::Geometry(Geometry::Point(pt)) = value.as_ref() { + return new_double_point!(env, pt, null_mut); + } + SurrealError::NullPointerException("Geometry").exception(env, null_mut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Geometry_equals<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr1: jlong, ptr2: jlong, ) -> jboolean { - let v1 = get_value_instance!(&mut env, ptr1, || false as jboolean); - let v2 = get_value_instance!(&mut env, ptr2, || false as jboolean); - if let (Value::Geometry(g1), Value::Geometry(g2)) = (v1.as_ref(), v2.as_ref()) { - return g1.eq(g2) as jboolean; - } - SurrealError::NullPointerException("Geometry").exception(&mut env, || false as jboolean) + with_env_body!(env, env, { + let v1 = get_value_instance!(env, ptr1, || false as jboolean); + let v2 = get_value_instance!(env, ptr2, || false as jboolean); + if let (Value::Geometry(g1), Value::Geometry(g2)) = (v1.as_ref(), v2.as_ref()) { + return g1.eq(g2) as jboolean; + } + SurrealError::NullPointerException("Geometry").exception(env, || false as jboolean) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Geometry_hashCode<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jint { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Geometry(g) = value.as_ref() { - let mut hasher = DefaultHasher::new(); - g.hash(&mut hasher); - let hash64 = hasher.finish(); - return (hash64 & 0xFFFFFFFF) as jint; - } - SurrealError::NullPointerException("Geometry").exception(&mut env, || 0) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Geometry(g) = value.as_ref() { + let mut hasher = DefaultHasher::new(); + g.hash(&mut hasher); + let hash64 = hasher.finish(); + return (hash64 & 0xFFFFFFFF) as jint; + } + SurrealError::NullPointerException("Geometry").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Geometry_toString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::Geometry(g) = value.as_ref() { - return new_string!(&mut env, g.to_string(), null_mut); - } - SurrealError::NullPointerException("Geometry").exception(&mut env, null_mut) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::Geometry(g) = value.as_ref() { + return new_string!(env, g.to_string(), null_mut); + } + SurrealError::NullPointerException("Geometry").exception(env, null_mut) + }) } diff --git a/src/main/rust/id.rs b/src/main/rust/id.rs index 0ae8ad13..f22810a6 100644 --- a/src/main/rust/id.rs +++ b/src/main/rust/id.rs @@ -2,9 +2,10 @@ use std::hash::{DefaultHasher, Hash, Hasher}; use std::ptr::null_mut; use std::str::FromStr; +use crate::with_env_body; use jni::objects::{JClass, JLongArray, JString}; use jni::sys::{jboolean, jint, jlong, jstring}; -use jni::JNIEnv; +use jni::EnvUnowned; use surrealdb::types::{Array, RecordId, RecordIdKey, ToSql, Uuid, Value}; use crate::error::SurrealError; @@ -15,7 +16,7 @@ use crate::{ #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_newLongId<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, id: jlong, ) -> jlong { @@ -25,255 +26,287 @@ pub extern "system" fn Java_com_surrealdb_Id_newLongId<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_newStringId<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, id: JString<'local>, ) -> jlong { - let id = get_rust_string!(&mut env, id, || 0); - let value = Value::RecordId(RecordId::new("", RecordIdKey::String(id))); - JniTypes::new_value(value.into()) + with_env_body!(env, env, { + let id = get_rust_string!(env, id, || 0); + let value = Value::RecordId(RecordId::new("", RecordIdKey::String(id))); + JniTypes::new_value(value.into()) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_newUuidId<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, id: JString<'local>, ) -> jlong { - let id = get_rust_string!(&mut env, id, || 0); - if let Ok(uuid) = Uuid::from_str(&id) { - let value = Value::RecordId(RecordId::new("", RecordIdKey::Uuid(uuid))); - JniTypes::new_value(value.into()) - } else { - SurrealError::NullPointerException("RecordId").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let id = get_rust_string!(env, id, || 0); + if let Ok(uuid) = Uuid::from_str(&id) { + let value = Value::RecordId(RecordId::new("", RecordIdKey::Uuid(uuid))); + JniTypes::new_value(value.into()) + } else { + SurrealError::NullPointerException("RecordId").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_newArrayId<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptrs: JLongArray<'local>, ) -> jlong { - let ptrs = get_long_array!(&mut env, &ptrs, || 0); - let mut values = Vec::with_capacity(ptrs.len()); - for ptr in ptrs { - let value = take_value_mut_instance!(&mut env, ptr, || 0); - values.push(value); - } - let key = RecordIdKey::Array(Array::from(values)); - let value = Value::RecordId(RecordId::new("", key)); - JniTypes::new_value(value.into()) + with_env_body!(env, env, { + let ptrs = get_long_array!(env, &ptrs, || 0); + let mut values = Vec::with_capacity(ptrs.len()); + for ptr in ptrs { + let value = take_value_mut_instance!(env, ptr, || 0); + values.push(value); + } + let key = RecordIdKey::Array(Array::from(values)); + let value = Value::RecordId(RecordId::new("", key)); + JniTypes::new_value(value.into()) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_isLong<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - if let Value::RecordId(o) = value.as_ref() { - if let RecordIdKey::Number(_) = &o.key { - true as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + if let Value::RecordId(o) = value.as_ref() { + if let RecordIdKey::Number(_) = &o.key { + true as jboolean + } else { + false as jboolean + } } else { - false as jboolean + SurrealError::NullPointerException("Id").exception(env, || false as jboolean) } - } else { - SurrealError::NullPointerException("Id").exception(&mut env, || false as jboolean) - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_getLong<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::RecordId(o) = value.as_ref() { - if let RecordIdKey::Number(i) = &o.key { - return *i as jlong; + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::RecordId(o) = value.as_ref() { + if let RecordIdKey::Number(i) = &o.key { + return *i as jlong; + } } - } - SurrealError::NullPointerException("Id").exception(&mut env, || 0) + SurrealError::NullPointerException("Id").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_isString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - if let Value::RecordId(o) = value.as_ref() { - if let RecordIdKey::String(_) = &o.key { - true as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + if let Value::RecordId(o) = value.as_ref() { + if let RecordIdKey::String(_) = &o.key { + true as jboolean + } else { + false as jboolean + } } else { - false as jboolean + SurrealError::NullPointerException("Id").exception(env, || false as jboolean) } - } else { - SurrealError::NullPointerException("Id").exception(&mut env, || false as jboolean) - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_getString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::RecordId(o) = value.as_ref() { - if let RecordIdKey::String(s) = &o.key { - return new_string!(&mut env, s, null_mut); + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::RecordId(o) = value.as_ref() { + if let RecordIdKey::String(s) = &o.key { + return new_string!(env, s, null_mut); + } } - } - SurrealError::NullPointerException("Id").exception(&mut env, null_mut) + SurrealError::NullPointerException("Id").exception(env, null_mut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_isUuid<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - if let Value::RecordId(o) = value.as_ref() { - if let RecordIdKey::Uuid(_) = &o.key { - true as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + if let Value::RecordId(o) = value.as_ref() { + if let RecordIdKey::Uuid(_) = &o.key { + true as jboolean + } else { + false as jboolean + } } else { - false as jboolean + SurrealError::NullPointerException("Id").exception(env, || false as jboolean) } - } else { - SurrealError::NullPointerException("Id").exception(&mut env, || false as jboolean) - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_getUuid<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::RecordId(o) = value.as_ref() { - if let RecordIdKey::Uuid(uuid) = &o.key { - return new_string!(&mut env, uuid.to_string(), null_mut); + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::RecordId(o) = value.as_ref() { + if let RecordIdKey::Uuid(uuid) = &o.key { + return new_string!(env, uuid.to_string(), null_mut); + } } - } - SurrealError::NullPointerException("Id").exception(&mut env, null_mut) + SurrealError::NullPointerException("Id").exception(env, null_mut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_isObject<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - if let Value::RecordId(o) = value.as_ref() { - if let RecordIdKey::Object(_) = &o.key { - true as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + if let Value::RecordId(o) = value.as_ref() { + if let RecordIdKey::Object(_) = &o.key { + true as jboolean + } else { + false as jboolean + } } else { - false as jboolean + SurrealError::NullPointerException("Id").exception(env, || false as jboolean) } - } else { - SurrealError::NullPointerException("Id").exception(&mut env, || false as jboolean) - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_getObject<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::RecordId(o) = value.as_ref() { - if let RecordIdKey::Object(o) = &o.key { - //TODO avoid cloning? - return JniTypes::new_value(Value::Object(o.clone()).into()); + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::RecordId(o) = value.as_ref() { + if let RecordIdKey::Object(o) = &o.key { + //TODO avoid cloning? + return JniTypes::new_value(Value::Object(o.clone()).into()); + } } - } - SurrealError::NullPointerException("Id").exception(&mut env, || 0) + SurrealError::NullPointerException("Id").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_isArray<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - if let Value::RecordId(o) = value.as_ref() { - if let RecordIdKey::Array(_) = &o.key { - true as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + if let Value::RecordId(o) = value.as_ref() { + if let RecordIdKey::Array(_) = &o.key { + true as jboolean + } else { + false as jboolean + } } else { - false as jboolean + SurrealError::NullPointerException("Id").exception(env, || false as jboolean) } - } else { - SurrealError::NullPointerException("Id").exception(&mut env, || false as jboolean) - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_getArray<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::RecordId(o) = value.as_ref() { - if let RecordIdKey::Array(a) = &o.key { - //TODO no clone? - return JniTypes::new_value(Value::Array(a.clone()).into()); + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::RecordId(o) = value.as_ref() { + if let RecordIdKey::Array(a) = &o.key { + //TODO no clone? + return JniTypes::new_value(Value::Array(a.clone()).into()); + } } - } - SurrealError::NullPointerException("Id").exception(&mut env, || 0) + SurrealError::NullPointerException("Id").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_toString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::RecordId(o) = value.as_ref() { - let s = o.key.to_sql(); - return new_string!(&mut env, s, null_mut); - } - SurrealError::NullPointerException("Id").exception(&mut env, null_mut) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::RecordId(o) = value.as_ref() { + let s = o.key.to_sql(); + return new_string!(env, s, null_mut); + } + SurrealError::NullPointerException("Id").exception(env, null_mut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_hashCode<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jint { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::RecordId(o) = value.as_ref() { - let mut hasher = DefaultHasher::new(); - o.key.hash(&mut hasher); - let hash64 = hasher.finish(); - return (hash64 & 0xFFFFFFFF) as jint; - } - SurrealError::NullPointerException("Id").exception(&mut env, || 0) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::RecordId(o) = value.as_ref() { + let mut hasher = DefaultHasher::new(); + o.key.hash(&mut hasher); + let hash64 = hasher.finish(); + return (hash64 & 0xFFFFFFFF) as jint; + } + SurrealError::NullPointerException("Id").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Id_equals<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr1: jlong, ptr2: jlong, ) -> jboolean { - let v1 = get_value_instance!(&mut env, ptr1, || false as jboolean); - let v2 = get_value_instance!(&mut env, ptr2, || false as jboolean); - if let (Value::RecordId(t1), Value::RecordId(t2)) = (v1.as_ref(), v2.as_ref()) { - return t1.key.eq(&t2.key) as jboolean; - } - SurrealError::NullPointerException("Id").exception(&mut env, || false as jboolean) + with_env_body!(env, env, { + let v1 = get_value_instance!(env, ptr1, || false as jboolean); + let v2 = get_value_instance!(env, ptr2, || false as jboolean); + if let (Value::RecordId(t1), Value::RecordId(t2)) = (v1.as_ref(), v2.as_ref()) { + return t1.key.eq(&t2.key) as jboolean; + } + SurrealError::NullPointerException("Id").exception(env, || false as jboolean) + }) } diff --git a/src/main/rust/lib.rs b/src/main/rust/lib.rs index 051f5704..27ac3910 100644 --- a/src/main/rust/lib.rs +++ b/src/main/rust/lib.rs @@ -3,7 +3,7 @@ use crate::error::SurrealError; use dashmap::DashMap; use jni::objects::{JObjectArray, JString}; use jni::sys::jlong; -use jni::JNIEnv; +use jni::{Env, EnvOutcome, Outcome}; use once_cell::sync::Lazy; use parking_lot::Mutex; use std::collections::btree_map::IntoIter as BIntoIter; @@ -267,18 +267,34 @@ fn release_instance(ptr: jlong) { } // Function to read a jobjectArray of Strings into a Vec -fn read_string_array(env: &mut JNIEnv, array: JObjectArray) -> Result, SurrealError> { - // Get the array length - let len = env.get_array_length(&array)?; - let mut res = Vec::with_capacity(len as usize); +fn read_string_array( + env: &mut Env, + array: JObjectArray, +) -> Result, SurrealError> { + // Get the array length (jni 0.22: methods moved onto the array type; len() -> usize) + let len = array.len(env)?; + let mut res = Vec::with_capacity(len); for i in 0..len { - // Get the element at index i (as JObject) - let elem = env.get_object_array_element(&array, i)?; - // Cast JObject to JString and convert to Rust String - let s = JString::from(elem); - let s = env.get_string(&s)?; - let s = String::from(s); - res.push(s); + // Typed element access yields a JString directly; convert to a Rust String + let s = array.get_element(env, i)?; + res.push(s.try_to_string(env)?); } Ok(res) } + +/// Resolves an [`EnvOutcome`] produced by `EnvUnowned::with_env` for a native-method +/// body. The body always returns `Ok` (application errors are surfaced as Java +/// exceptions inside the body via [`SurrealError::exception`]), so the `Err` arm is +/// unreachable. Panics are re-raised so they abort at the `extern "system"` boundary, +/// matching pre-migration behaviour. Using `into_outcome` here (rather than +/// `EnvOutcome::resolve`) avoids a `T: Default` bound, letting the exports keep their +/// existing raw `jobject`/`jstring`/`jlong` return types and the structured-exception sink. +pub(crate) fn resolve_outcome(outcome: EnvOutcome<'_, T, jni::errors::Error>) -> T { + match outcome.into_outcome() { + Outcome::Ok(v) => v, + Outcome::Err(_e) => { + unreachable!("JNI errors are surfaced as Java exceptions within the native body") + } + Outcome::Panic(p) => std::panic::resume_unwind(p), + } +} diff --git a/src/main/rust/live.rs b/src/main/rust/live.rs index 4260ac19..ce059eb0 100644 --- a/src/main/rust/live.rs +++ b/src/main/rust/live.rs @@ -1,8 +1,9 @@ use std::sync::Arc; +use crate::with_env_body; use jni::objects::{JObject, JValue}; use jni::sys::{jlong, jobject}; -use jni::JNIEnv; +use jni::{jni_sig, jni_str, EnvUnowned}; use crate::error::SurrealError; use crate::{get_instance, new_string, take_instance, JniTypes, LiveStreamChannel, TOKIO_RUNTIME}; @@ -27,51 +28,57 @@ use crate::{get_instance, new_string, take_instance, JniTypes, LiveStreamChannel /// been closed, guaranteeing `recv()` will have already returned. #[no_mangle] pub extern "system" fn Java_com_surrealdb_LiveStream_nextNative<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: jni::objects::JClass<'local>, handle_ptr: jlong, ) -> jobject { - let (recv_mutex, _join_handle_mux, _shutdown_tx_mux, rx_mux) = - match get_instance::(handle_ptr, JniTypes::LiveStream) { - Ok(r) => r, - Err(e) => return e.exception(&mut env, std::ptr::null_mut), + with_env_body!(env, env, { + let (recv_mutex, _join_handle_mux, _shutdown_tx_mux, rx_mux) = + match get_instance::(handle_ptr, JniTypes::LiveStream) { + Ok(r) => r, + Err(e) => return e.exception(env, std::ptr::null_mut), + }; + let _recv_guard = recv_mutex.lock(); + let rx_opt_guard = rx_mux.lock(); + let rx_ref = match rx_opt_guard.as_ref() { + Some(rx) => rx, + None => return JObject::null().into_raw(), }; - let _recv_guard = recv_mutex.lock(); - let rx_opt_guard = rx_mux.lock(); - let rx_ref = match rx_opt_guard.as_ref() { - Some(rx) => rx, - None => return JObject::null().into_raw(), - }; - let item = match TOKIO_RUNTIME.block_on(rx_ref.recv()) { - Ok(item) => item, - Err(_) => return JObject::null().into_raw(), - }; - let notification = match item { - Ok(n) => n, - Err(e) => return SurrealError::from(e).exception(&mut env, std::ptr::null_mut), - }; - let action_raw = new_string!(&mut env, notification.action.to_string(), || { - std::ptr::null_mut() - }); - let action_str = unsafe { JObject::from_raw(action_raw) }; - let value_ptr = JniTypes::new_value(Arc::new(notification.data)); - let query_id_raw = new_string!(&mut env, notification.query_id.to_string(), || { - std::ptr::null_mut() - }); - let query_id_str = unsafe { JObject::from_raw(query_id_raw) }; - let class = match env.find_class("com/surrealdb/LiveNotification") { - Ok(c) => c, - Err(e) => return SurrealError::from(e).exception(&mut env, std::ptr::null_mut), - }; - let args = [ - JValue::Object(&action_str), - JValue::Long(value_ptr), - JValue::Object(&query_id_str), - ]; - match env.new_object(class, "(Ljava/lang/String;JLjava/lang/String;)V", &args) { - Ok(obj) => obj.into_raw(), - Err(e) => SurrealError::from(e).exception(&mut env, std::ptr::null_mut), - } + let item = match TOKIO_RUNTIME.block_on(rx_ref.recv()) { + Ok(item) => item, + Err(_) => return JObject::null().into_raw(), + }; + let notification = match item { + Ok(n) => n, + Err(e) => return SurrealError::from(e).exception(env, std::ptr::null_mut), + }; + let action_raw = new_string!(env, notification.action.to_string(), || { + std::ptr::null_mut() + }); + let action_str = unsafe { JObject::from_raw(env, action_raw) }; + let value_ptr = JniTypes::new_value(Arc::new(notification.data)); + let query_id_raw = new_string!(env, notification.query_id.to_string(), || { + std::ptr::null_mut() + }); + let query_id_str = unsafe { JObject::from_raw(env, query_id_raw) }; + let class = match env.find_class(jni_str!("com/surrealdb/LiveNotification")) { + Ok(c) => c, + Err(e) => return SurrealError::from(e).exception(env, std::ptr::null_mut), + }; + let args = [ + JValue::Object(&action_str), + JValue::Long(value_ptr), + JValue::Object(&query_id_str), + ]; + match env.new_object( + class, + jni_sig!("(Ljava/lang/String;JLjava/lang/String;)V"), + &args, + ) { + Ok(obj) => obj.into_raw(), + Err(e) => SurrealError::from(e).exception(env, std::ptr::null_mut), + } + }) } /// JNI implementation of `LiveStream.releaseNative(long handle)`. @@ -97,7 +104,7 @@ pub extern "system" fn Java_com_surrealdb_LiveStream_nextNative<'local>( /// call returns, preventing further native access. #[no_mangle] pub extern "system" fn Java_com_surrealdb_LiveStream_releaseNative<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: jni::objects::JClass<'local>, handle_ptr: jlong, ) { diff --git a/src/main/rust/macros.rs b/src/main/rust/macros.rs index e5a62775..99be592c 100644 --- a/src/main/rust/macros.rs +++ b/src/main/rust/macros.rs @@ -1,9 +1,32 @@ +/// Wraps a native-method body for the jni 0.22 `EnvUnowned` calling convention. +/// +/// `$unowned` is the `EnvUnowned` first argument; `$env` is the identifier the body +/// uses for the managed `&mut Env` (pass the same name the body already uses, i.e. +/// `env`). The body is run inside an inner closure so its `return ` statements +/// (including those inside the `get_*!`/`new_*!` macros via `SurrealError::exception`) +/// return the native value as before. See [`crate::resolve_outcome`] for how the +/// outcome (incl. panics) is resolved. +#[macro_export] +macro_rules! with_env_body { + ($unowned:expr, $env:ident, $body:block) => {{ + $crate::resolve_outcome($unowned.with_env( + |$env: &mut jni::Env| -> jni::errors::Result<_> { + // The inner closure lets the body's `return ` statements (incl. those in + // the get_*!/new_*! macros) return the native value; we then wrap it in Ok. + #[allow(clippy::redundant_closure_call)] + let __out = (move || $body)(); + std::result::Result::Ok(__out) + }, + )) + }}; +} + #[macro_export] macro_rules! get_rust_string { ($env:expr, $str:expr, $default_fn:expr) => {{ - match $env.get_string(&$str) { - Ok(s) => String::from(s), - Err(e) => return SurrealError::from(e).exception(&mut $env, $default_fn), + match $str.try_to_string($env) { + Ok(s) => s, + Err(e) => return SurrealError::from(e).exception($env, $default_fn), } }}; } @@ -11,9 +34,9 @@ macro_rules! get_rust_string { #[macro_export] macro_rules! get_rust_string_array { ($env:expr, $strings:expr, $default_fn:expr) => {{ - match $crate::read_string_array(&mut $env, $strings) { + match $crate::read_string_array($env, $strings) { Ok(r) => r, - Err(e) => return e.exception(&mut $env, $default_fn), + Err(e) => return e.exception($env, $default_fn), } }}; } @@ -189,12 +212,13 @@ macro_rules! new_string { #[macro_export] macro_rules! get_long_array { ($env:expr, $ptrs:expr, $default_fn:expr) => {{ - let length = match $env.get_array_length($ptrs) { + // jni 0.22: array region/length APIs moved onto JPrimitiveArray; len() -> usize. + let length = match $ptrs.len($env) { Ok(l) => l, Err(e) => return $crate::SurrealError::from(e).exception($env, $default_fn), }; - let mut long_ptrs: Vec = vec![0; length as usize]; - if let Err(e) = $env.get_long_array_region($ptrs, 0, &mut long_ptrs) { + let mut long_ptrs: Vec = vec![0; length]; + if let Err(e) = $ptrs.get_region($env, 0, &mut long_ptrs) { return $crate::SurrealError::from(e).exception($env, $default_fn); }; long_ptrs @@ -205,12 +229,12 @@ macro_rules! get_long_array { macro_rules! new_jlong_array { ($env:expr, $array:expr, $default_fn:expr) => {{ // Create a new jlongArray with the appropriate length - let mut jarray = match $env.new_long_array($array.len() as jni::sys::jsize) { + let jarray = match jni::objects::JLongArray::new($env, $array.len()) { Ok(a) => a, Err(e) => return $crate::SurrealError::from(e).exception($env, $default_fn), }; // Set the values of the jlongArray - if let Err(e) = $env.set_long_array_region(&mut jarray, 0, $array) { + if let Err(e) = jarray.set_region($env, 0, $array) { return $crate::SurrealError::from(e).exception($env, $default_fn); } // Return the populated jlongArray @@ -221,12 +245,12 @@ macro_rules! new_jlong_array { #[macro_export] macro_rules! new_double_point { ($env:expr, $pt:expr, $default_fn:expr) => {{ - let double_array = match $env.new_double_array(2) { + let double_array = match jni::objects::JDoubleArray::new($env, 2) { Ok(d) => d, Err(e) => return $crate::SurrealError::from(e).exception($env, $default_fn), }; let coordinates: [jni::sys::jdouble; 2] = [$pt.x(), $pt.y()]; - if let Err(e) = $env.set_double_array_region(&double_array, 0, &coordinates) { + if let Err(e) = double_array.set_region($env, 0, &coordinates) { return $crate::SurrealError::from(e).exception($env, $default_fn); } double_array.into_raw() diff --git a/src/main/rust/object.rs b/src/main/rust/object.rs index 5327ea06..2a80cc92 100644 --- a/src/main/rust/object.rs +++ b/src/main/rust/object.rs @@ -2,9 +2,10 @@ use std::hash::{DefaultHasher, Hash, Hasher}; use std::ptr::null_mut; use std::sync::Arc; +use crate::with_env_body; use jni::objects::{JClass, JString}; use jni::sys::{jboolean, jint, jlong, jstring}; -use jni::JNIEnv; +use jni::EnvUnowned; use parking_lot::Mutex; use surrealdb::types::{ToSql, Value}; @@ -13,7 +14,7 @@ use crate::{get_rust_string, get_value_instance, new_string, release_instance, J #[no_mangle] pub extern "system" fn Java_com_surrealdb_Object_deleteInstance<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) { @@ -22,107 +23,121 @@ pub extern "system" fn Java_com_surrealdb_Object_deleteInstance<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_Object_len<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jint { - let value = get_value_instance!(&mut env, ptr, || 0 as jint); - if let Value::Object(o) = value.as_ref() { - o.len() as jint - } else { - SurrealError::NullPointerException("Object").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0 as jint); + if let Value::Object(o) = value.as_ref() { + o.len() as jint + } else { + SurrealError::NullPointerException("Object").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Object_get<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, key: JString<'local>, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Object(o) = value.as_ref() { - let key = get_rust_string!(&mut env, key, || 0); - // TODO Avoid cloning - let val = o.get(&key).cloned().unwrap_or(Value::None); - JniTypes::new_value(val.into()) - } else { - SurrealError::NullPointerException("Object").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Object(o) = value.as_ref() { + let key = get_rust_string!(env, key, || 0); + // TODO Avoid cloning + let val = o.get(&key).cloned().unwrap_or(Value::None); + JniTypes::new_value(val.into()) + } else { + SurrealError::NullPointerException("Object").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Object_iterator<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Object(o) = value.as_ref() { - let iter = o.clone().into_iter(); - JniTypes::new_object_iter(iter) - } else { - SurrealError::NullPointerException("Object").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Object(o) = value.as_ref() { + let iter = o.clone().into_iter(); + JniTypes::new_object_iter(iter) + } else { + SurrealError::NullPointerException("Object").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Object_synchronizedIterator<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Object(o) = value.as_ref() { - let iter = o.clone().into_iter(); - JniTypes::new_sync_object_iter(Arc::new(Mutex::new(iter))) - } else { - SurrealError::NullPointerException("Object").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Object(o) = value.as_ref() { + let iter = o.clone().into_iter(); + JniTypes::new_sync_object_iter(Arc::new(Mutex::new(iter))) + } else { + SurrealError::NullPointerException("Object").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Object_toString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if matches!(value.as_ref(), Value::Object(_)) { - new_string!(&mut env, value.to_sql(), null_mut) - } else { - SurrealError::NullPointerException("Object").exception(&mut env, null_mut) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if matches!(value.as_ref(), Value::Object(_)) { + new_string!(env, value.to_sql(), null_mut) + } else { + SurrealError::NullPointerException("Object").exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Object_hashCode<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jint { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Object(o) = value.as_ref() { - let mut hasher = DefaultHasher::new(); - o.hash(&mut hasher); - let hash64 = hasher.finish(); - return (hash64 & 0xFFFFFFFF) as jint; - } - SurrealError::NullPointerException("Object").exception(&mut env, || 0) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Object(o) = value.as_ref() { + let mut hasher = DefaultHasher::new(); + o.hash(&mut hasher); + let hash64 = hasher.finish(); + return (hash64 & 0xFFFFFFFF) as jint; + } + SurrealError::NullPointerException("Object").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Object_equals<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr1: jlong, ptr2: jlong, ) -> jboolean { - let v1 = get_value_instance!(&mut env, ptr1, || false as jboolean); - let v2 = get_value_instance!(&mut env, ptr2, || false as jboolean); - if let (Value::Object(o1), Value::Object(o2)) = (v1.as_ref(), v2.as_ref()) { - return o1.eq(o2) as jboolean; - } - SurrealError::NullPointerException("Object").exception(&mut env, || false as jboolean) + with_env_body!(env, env, { + let v1 = get_value_instance!(env, ptr1, || false as jboolean); + let v2 = get_value_instance!(env, ptr2, || false as jboolean); + if let (Value::Object(o1), Value::Object(o2)) = (v1.as_ref(), v2.as_ref()) { + return o1.eq(o2) as jboolean; + } + SurrealError::NullPointerException("Object").exception(env, || false as jboolean) + }) } diff --git a/src/main/rust/recordid.rs b/src/main/rust/recordid.rs index 059279b0..2f0ae5ad 100644 --- a/src/main/rust/recordid.rs +++ b/src/main/rust/recordid.rs @@ -2,9 +2,10 @@ use std::hash::{DefaultHasher, Hash, Hasher}; use std::ptr::null_mut; use std::str::FromStr; +use crate::with_env_body; use jni::objects::{JClass, JString}; use jni::sys::{jboolean, jint, jlong, jstring}; -use jni::JNIEnv; +use jni::EnvUnowned; use surrealdb::types::{RecordId, RecordIdKey, ToSql, Uuid, Value}; use crate::error::SurrealError; @@ -12,7 +13,7 @@ use crate::{get_rust_string, get_value_instance, new_string, release_instance, J #[no_mangle] pub extern "system" fn Java_com_surrealdb_RecordId_deleteInstance<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) { @@ -21,150 +22,170 @@ pub extern "system" fn Java_com_surrealdb_RecordId_deleteInstance<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_RecordId_newRecordIdWithLong<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, table: JString<'local>, id: jlong, ) -> jlong { - let table = get_rust_string!(&mut env, table, || 0); - let value = Value::RecordId(RecordId::new(table, RecordIdKey::Number(id))); - JniTypes::new_value(value.into()) + with_env_body!(env, env, { + let table = get_rust_string!(env, table, || 0); + let value = Value::RecordId(RecordId::new(table, RecordIdKey::Number(id))); + JniTypes::new_value(value.into()) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_RecordId_newRecordIdWithString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, table: JString<'local>, id: JString<'local>, ) -> jlong { - let table = get_rust_string!(&mut env, table, || 0); - let id = get_rust_string!(&mut env, id, || 0); - let value = Value::RecordId(RecordId::new(table, RecordIdKey::String(id))); - JniTypes::new_value(value.into()) + with_env_body!(env, env, { + let table = get_rust_string!(env, table, || 0); + let id = get_rust_string!(env, id, || 0); + let value = Value::RecordId(RecordId::new(table, RecordIdKey::String(id))); + JniTypes::new_value(value.into()) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_RecordId_newRecordIdWithUuid<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, table: JString<'local>, id: JString<'local>, ) -> jlong { - let table = get_rust_string!(&mut env, table, || 0); - let id = get_rust_string!(&mut env, id, || 0); - if let Ok(uuid) = Uuid::from_str(&id) { - let value = Value::RecordId(RecordId::new(table, RecordIdKey::Uuid(uuid))); - JniTypes::new_value(value.into()) - } else { - SurrealError::NullPointerException("RecordId").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let table = get_rust_string!(env, table, || 0); + let id = get_rust_string!(env, id, || 0); + if let Ok(uuid) = Uuid::from_str(&id) { + let value = Value::RecordId(RecordId::new(table, RecordIdKey::Uuid(uuid))); + JniTypes::new_value(value.into()) + } else { + SurrealError::NullPointerException("RecordId").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_RecordId_newRecordIdWithArray<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, table: JString<'local>, array_ptr: jlong, ) -> jlong { - let table = get_rust_string!(&mut env, table, || 0); - let value = get_value_instance!(&mut env, array_ptr, || 0); - if let Value::Array(a) = value.as_ref() { - let key = RecordIdKey::Array(a.clone()); - let value = Value::RecordId(RecordId::new(table, key)); - JniTypes::new_value(value.into()) - } else { - SurrealError::NullPointerException("RecordId").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let table = get_rust_string!(env, table, || 0); + let value = get_value_instance!(env, array_ptr, || 0); + if let Value::Array(a) = value.as_ref() { + let key = RecordIdKey::Array(a.clone()); + let value = Value::RecordId(RecordId::new(table, key)); + JniTypes::new_value(value.into()) + } else { + SurrealError::NullPointerException("RecordId").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_RecordId_newRecordIdWithObject<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, table: JString<'local>, object_ptr: jlong, ) -> jlong { - let table = get_rust_string!(&mut env, table, || 0); - let value = get_value_instance!(&mut env, object_ptr, || 0); - if let Value::Object(o) = value.as_ref() { - let key = RecordIdKey::Object(o.clone()); - let value = Value::RecordId(RecordId::new(table, key)); - JniTypes::new_value(value.into()) - } else { - SurrealError::NullPointerException("RecordId").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let table = get_rust_string!(env, table, || 0); + let value = get_value_instance!(env, object_ptr, || 0); + if let Value::Object(o) = value.as_ref() { + let key = RecordIdKey::Object(o.clone()); + let value = Value::RecordId(RecordId::new(table, key)); + JniTypes::new_value(value.into()) + } else { + SurrealError::NullPointerException("RecordId").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_RecordId_getTable<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::RecordId(o) = value.as_ref() { - new_string!(&mut env, o.table.to_sql(), null_mut) - } else { - SurrealError::NullPointerException("RecordId").exception(&mut env, null_mut) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::RecordId(o) = value.as_ref() { + new_string!(env, o.table.to_sql(), null_mut) + } else { + SurrealError::NullPointerException("RecordId").exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_RecordId_getId<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::RecordId(_) = value.as_ref() { - JniTypes::new_value(value) - } else { - SurrealError::NullPointerException("RecordId").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::RecordId(_) = value.as_ref() { + JniTypes::new_value(value) + } else { + SurrealError::NullPointerException("RecordId").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_RecordId_equals<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr1: jlong, ptr2: jlong, ) -> jboolean { - let v1 = get_value_instance!(&mut env, ptr1, || false as jboolean); - let v2 = get_value_instance!(&mut env, ptr2, || false as jboolean); - if let (Value::RecordId(t1), Value::RecordId(t2)) = (v1.as_ref(), v2.as_ref()) { - return t1.eq(t2) as jboolean; - } - SurrealError::NullPointerException("RecordId").exception(&mut env, || false as jboolean) + with_env_body!(env, env, { + let v1 = get_value_instance!(env, ptr1, || false as jboolean); + let v2 = get_value_instance!(env, ptr2, || false as jboolean); + if let (Value::RecordId(t1), Value::RecordId(t2)) = (v1.as_ref(), v2.as_ref()) { + return t1.eq(t2) as jboolean; + } + SurrealError::NullPointerException("RecordId").exception(env, || false as jboolean) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_RecordId_hashCode<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jint { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::RecordId(o) = value.as_ref() { - let mut hasher = DefaultHasher::new(); - o.hash(&mut hasher); - let hash64 = hasher.finish(); - return (hash64 & 0xFFFFFFFF) as jint; - } - SurrealError::NullPointerException("RecordId").exception(&mut env, || 0) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::RecordId(o) = value.as_ref() { + let mut hasher = DefaultHasher::new(); + o.hash(&mut hasher); + let hash64 = hasher.finish(); + return (hash64 & 0xFFFFFFFF) as jint; + } + SurrealError::NullPointerException("RecordId").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_RecordId_toString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::RecordId(o) = value.as_ref() { - return new_string!(&mut env, o.to_sql(), null_mut); - } - SurrealError::NullPointerException("RecordId").exception(&mut env, null_mut) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::RecordId(o) = value.as_ref() { + return new_string!(env, o.to_sql(), null_mut); + } + SurrealError::NullPointerException("RecordId").exception(env, null_mut) + }) } diff --git a/src/main/rust/response.rs b/src/main/rust/response.rs index a0c0cbe8..2df2a4b9 100644 --- a/src/main/rust/response.rs +++ b/src/main/rust/response.rs @@ -1,8 +1,9 @@ use std::sync::Arc; +use crate::with_env_body; use jni::objects::JClass; use jni::sys::{jint, jlong}; -use jni::JNIEnv; +use jni::EnvUnowned; use parking_lot::Mutex; use surrealdb::types::Value; use surrealdb::{IndexedResults, Result}; @@ -12,7 +13,7 @@ use crate::{create_instance, get_response_instance, release_instance, JniTypes}; #[no_mangle] pub extern "system" fn Java_com_surrealdb_Response_deleteInstance<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) { @@ -21,25 +22,29 @@ pub extern "system" fn Java_com_surrealdb_Response_deleteInstance<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_Response_take<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, num: jint, ) -> jlong { - let response = get_response_instance!(&mut env, ptr, || 0); - let value: Value = match response.lock().take(num as usize) { - Ok(r) => r, - Err(e) => return SurrealError::SurrealDB(e).exception(&mut env, || 0), - }; - create_instance(Arc::new(value), JniTypes::Value) + with_env_body!(env, env, { + let response = get_response_instance!(env, ptr, || 0); + let value: Value = match response.lock().take(num as usize) { + Ok(r) => r, + Err(e) => return SurrealError::SurrealDB(e).exception(env, || 0), + }; + create_instance(Arc::new(value), JniTypes::Value) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Response_size<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jint { - let response = get_response_instance!(&mut env, ptr, || 0); - return response.lock().num_statements() as jint; + with_env_body!(env, env, { + let response = get_response_instance!(env, ptr, || 0); + return response.lock().num_statements() as jint; + }) } diff --git a/src/main/rust/surreal.rs b/src/main/rust/surreal.rs index 78e9a5cc..451770eb 100644 --- a/src/main/rust/surreal.rs +++ b/src/main/rust/surreal.rs @@ -4,6 +4,7 @@ use std::ptr::null_mut; use std::sync::Arc; use crate::error::SurrealError; +use crate::with_env_body; use crate::{ build_params_map, check_query_result, convert_up_type, get_long_array, get_rust_string, get_rust_string_array, get_surreal_ref, get_value_instance, get_value_mut_instance, @@ -14,7 +15,7 @@ use crate::{ use futures::StreamExt; use jni::objects::{JClass, JLongArray, JObject, JObjectArray, JString, JValue}; use jni::sys::{jboolean, jint, jlong, jlongArray, jobject, jstring}; -use jni::JNIEnv; +use jni::{jni_sig, jni_str, Env, EnvUnowned}; use parking_lot::Mutex; use serde::Serialize; use std::ops::Bound; @@ -26,32 +27,36 @@ use surrealdb::{IndexedResults, Result, Surreal}; #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_beginTransaction<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let surreal = get_surreal_ref!(&mut env, ptr, || 0); - match TOKIO_RUNTIME.block_on(async { surreal.clone().begin().await }) { - Ok(txn) => JniTypes::new_transaction(txn), - Err(e) => SurrealError::from(e).exception(&mut env, || 0), - } + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, || 0); + match TOKIO_RUNTIME.block_on(async { surreal.clone().begin().await }) { + Ok(txn) => JniTypes::new_transaction(txn), + Err(e) => SurrealError::from(e).exception(env, || 0), + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_cloneSession<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - match crate::clone_surreal_instance(ptr) { - Ok(new_ptr) => new_ptr, - Err(e) => e.exception(&mut env, || 0), - } + with_env_body!(env, env, { + match crate::clone_surreal_instance(ptr) { + Ok(new_ptr) => new_ptr, + Err(e) => e.exception(env, || 0), + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_newInstance<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, ) -> jlong { JniTypes::new_surreal(Surreal::::init()) @@ -59,7 +64,7 @@ pub extern "system" fn Java_com_surrealdb_Surreal_newInstance<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_deleteInstance<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) { @@ -68,55 +73,61 @@ pub extern "system" fn Java_com_surrealdb_Surreal_deleteInstance<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_connect<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, addr: JString<'local>, ) -> jboolean { - let surreal = get_surreal_ref!(&mut env, ptr, || false as jboolean); - let addr = get_rust_string!(env, addr, || false as jboolean); - // Connect - if let Err(err) = TOKIO_RUNTIME.block_on(async { surreal.connect(addr).await }) { - return SurrealError::from(err).exception(&mut env, || false as jboolean); - } - true as jboolean + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, || false as jboolean); + let addr = get_rust_string!(env, addr, || false as jboolean); + // Connect + if let Err(err) = TOKIO_RUNTIME.block_on(async { surreal.connect(addr).await }) { + return SurrealError::from(err).exception(env, || false as jboolean); + } + true as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_version<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let surreal = get_surreal_ref!(&mut env, ptr, null_mut); - let version = match TOKIO_RUNTIME.block_on(async { surreal.version().await }) { - Ok(v) => v.to_string(), - // Embedded / local: no server to ask; report library version (injected at build time) - Err(_) => env!("SURREALDB_VERSION").into(), - }; - new_string!(&mut env, version, null_mut) + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, null_mut); + let version = match TOKIO_RUNTIME.block_on(async { surreal.version().await }) { + Ok(v) => v.to_string(), + // Embedded / local: no server to ask; report library version (injected at build time) + Err(_) => env!("SURREALDB_VERSION").into(), + }; + new_string!(env, version, null_mut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_health<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let surreal = get_surreal_ref!(&mut env, ptr, || false as jboolean); - match TOKIO_RUNTIME.block_on(async { surreal.health().await }) { - Ok(()) => true as jboolean, - Err(e) => SurrealError::from(e).exception(&mut env, || false as jboolean), - } + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, || false as jboolean); + match TOKIO_RUNTIME.block_on(async { surreal.health().await }) { + Ok(()) => true as jboolean, + Err(e) => SurrealError::from(e).exception(env, || false as jboolean), + } + }) } fn new_token_object<'local>( - env: &mut JNIEnv<'local>, + env: &mut Env<'local>, access: String, refresh: Option, ) -> StdResult { let token_class = env - .find_class("com/surrealdb/signin/Token") + .find_class(jni_str!("com/surrealdb/signin/Token")) .map_err(SurrealError::from)?; let access_jstr = env.new_string(access).map_err(SurrealError::from)?; let refresh_jobj: JObject<'local> = match refresh { @@ -130,7 +141,7 @@ fn new_token_object<'local>( let token_obj = env .new_object( token_class, - "(Ljava/lang/String;Ljava/lang/String;)V", + jni_sig!("(Ljava/lang/String;Ljava/lang/String;)V"), &args, ) .map_err(SurrealError::from)?; @@ -138,12 +149,12 @@ fn new_token_object<'local>( } fn new_ns_db_object<'local>( - env: &mut JNIEnv<'local>, + env: &mut Env<'local>, namespace: Option, database: Option, ) -> StdResult { let ns_db_class = env - .find_class("com/surrealdb/NsDb") + .find_class(jni_str!("com/surrealdb/NsDb")) .map_err(SurrealError::from)?; let ns_jobj: JObject<'local> = match namespace { Some(s) => env.new_string(s).map_err(SurrealError::from)?.into(), @@ -160,7 +171,7 @@ fn new_ns_db_object<'local>( let ns_db_obj = env .new_object( ns_db_class, - "(Ljava/lang/String;Ljava/lang/String;)V", + jni_sig!("(Ljava/lang/String;Ljava/lang/String;)V"), &args, ) .map_err(SurrealError::from)?; @@ -169,65 +180,69 @@ fn new_ns_db_object<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_signinRoot<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, username: JString<'local>, password: JString<'local>, ) -> jobject { - let surreal = get_surreal_ref!(&mut env, ptr, null_mut); - let username = get_rust_string!(&mut env, username, null_mut); - let password = get_rust_string!(&mut env, password, null_mut); - match TOKIO_RUNTIME.block_on(async { surreal.signin(Root { username, password }).await }) { - Ok(token) => { - let access = token.access.into_insecure_token(); - let refresh = token.refresh.map(|r| r.into_insecure_token()); - match new_token_object(&mut env, access, refresh) { - Ok(obj) => obj, - Err(e) => e.exception(&mut env, null_mut), + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, null_mut); + let username = get_rust_string!(env, username, null_mut); + let password = get_rust_string!(env, password, null_mut); + match TOKIO_RUNTIME.block_on(async { surreal.signin(Root { username, password }).await }) { + Ok(token) => { + let access = token.access.into_insecure_token(); + let refresh = token.refresh.map(|r| r.into_insecure_token()); + match new_token_object(env, access, refresh) { + Ok(obj) => obj, + Err(e) => e.exception(env, null_mut), + } } + Err(err) => SurrealError::from(err).exception(env, null_mut), } - Err(err) => SurrealError::from(err).exception(&mut env, null_mut), - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_signinNamespace<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, username: JString<'local>, password: JString<'local>, ns: JString<'local>, ) -> jobject { - let surreal = get_surreal_ref!(&mut env, ptr, null_mut); - let username = get_rust_string!(&mut env, username, null_mut); - let password = get_rust_string!(&mut env, password, null_mut); - let namespace = get_rust_string!(&mut env, ns, null_mut); - match TOKIO_RUNTIME.block_on(async { - surreal - .signin(Namespace { - username, - password, - namespace, - }) - .await - }) { - Ok(token) => { - let access = token.access.into_insecure_token(); - let refresh = token.refresh.map(|r| r.into_insecure_token()); - match new_token_object(&mut env, access, refresh) { - Ok(obj) => obj, - Err(e) => e.exception(&mut env, null_mut), + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, null_mut); + let username = get_rust_string!(env, username, null_mut); + let password = get_rust_string!(env, password, null_mut); + let namespace = get_rust_string!(env, ns, null_mut); + match TOKIO_RUNTIME.block_on(async { + surreal + .signin(Namespace { + username, + password, + namespace, + }) + .await + }) { + Ok(token) => { + let access = token.access.into_insecure_token(); + let refresh = token.refresh.map(|r| r.into_insecure_token()); + match new_token_object(env, access, refresh) { + Ok(obj) => obj, + Err(e) => e.exception(env, null_mut), + } } + Err(err) => SurrealError::from(err).exception(env, null_mut), } - Err(err) => SurrealError::from(err).exception(&mut env, null_mut), - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_signinDatabase<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, username: JString<'local>, @@ -235,36 +250,38 @@ pub extern "system" fn Java_com_surrealdb_Surreal_signinDatabase<'local>( ns: JString<'local>, db: JString<'local>, ) -> jobject { - let surreal = get_surreal_ref!(&mut env, ptr, null_mut); - let username = get_rust_string!(&mut env, username, null_mut); - let password = get_rust_string!(&mut env, password, null_mut); - let namespace = get_rust_string!(&mut env, ns, null_mut); - let database = get_rust_string!(&mut env, db, null_mut); - match TOKIO_RUNTIME.block_on(async { - surreal - .signin(Database { - username, - password, - namespace, - database, - }) - .await - }) { - Ok(token) => { - let access = token.access.into_insecure_token(); - let refresh = token.refresh.map(|r| r.into_insecure_token()); - match new_token_object(&mut env, access, refresh) { - Ok(obj) => obj, - Err(e) => e.exception(&mut env, null_mut), + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, null_mut); + let username = get_rust_string!(env, username, null_mut); + let password = get_rust_string!(env, password, null_mut); + let namespace = get_rust_string!(env, ns, null_mut); + let database = get_rust_string!(env, db, null_mut); + match TOKIO_RUNTIME.block_on(async { + surreal + .signin(Database { + username, + password, + namespace, + database, + }) + .await + }) { + Ok(token) => { + let access = token.access.into_insecure_token(); + let refresh = token.refresh.map(|r| r.into_insecure_token()); + match new_token_object(env, access, refresh) { + Ok(obj) => obj, + Err(e) => e.exception(env, null_mut), + } } + Err(err) => SurrealError::from(err).exception(env, null_mut), } - Err(err) => SurrealError::from(err).exception(&mut env, null_mut), - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_signup<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, namespace: JString<'local>, @@ -272,33 +289,35 @@ pub extern "system" fn Java_com_surrealdb_Surreal_signup<'local>( access: JString<'local>, params_value_ptr: jlong, ) -> jobject { - let surreal = get_surreal_ref!(&mut env, ptr, null_mut); - let namespace = get_rust_string!(&mut env, namespace, null_mut); - let database = get_rust_string!(&mut env, database, null_mut); - let access = get_rust_string!(&mut env, access, null_mut); - let params = get_value_mut_instance!(&mut env, params_value_ptr, null_mut).clone(); - let record = AuthRecord { - namespace, - database, - access, - params, - }; - match TOKIO_RUNTIME.block_on(async { surreal.signup(record).await }) { - Ok(token) => { - let access_str = token.access.into_insecure_token(); - let refresh = token.refresh.map(|r| r.into_insecure_token()); - match new_token_object(&mut env, access_str, refresh) { - Ok(obj) => obj, - Err(e) => e.exception(&mut env, null_mut), + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, null_mut); + let namespace = get_rust_string!(env, namespace, null_mut); + let database = get_rust_string!(env, database, null_mut); + let access = get_rust_string!(env, access, null_mut); + let params = get_value_mut_instance!(env, params_value_ptr, null_mut).clone(); + let record = AuthRecord { + namespace, + database, + access, + params, + }; + match TOKIO_RUNTIME.block_on(async { surreal.signup(record).await }) { + Ok(token) => { + let access_str = token.access.into_insecure_token(); + let refresh = token.refresh.map(|r| r.into_insecure_token()); + match new_token_object(env, access_str, refresh) { + Ok(obj) => obj, + Err(e) => e.exception(env, null_mut), + } } + Err(err) => SurrealError::from(err).exception(env, null_mut), } - Err(err) => SurrealError::from(err).exception(&mut env, null_mut), - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_signinRecord<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, namespace: JString<'local>, @@ -306,173 +325,191 @@ pub extern "system" fn Java_com_surrealdb_Surreal_signinRecord<'local>( access: JString<'local>, params_value_ptr: jlong, ) -> jobject { - let surreal = get_surreal_ref!(&mut env, ptr, null_mut); - let namespace = get_rust_string!(&mut env, namespace, null_mut); - let database = get_rust_string!(&mut env, database, null_mut); - let access = get_rust_string!(&mut env, access, null_mut); - let params = get_value_mut_instance!(&mut env, params_value_ptr, null_mut).clone(); - let record = AuthRecord { - namespace, - database, - access, - params, - }; - match TOKIO_RUNTIME.block_on(async { surreal.signin(record).await }) { - Ok(token) => { - let access_str = token.access.into_insecure_token(); - let refresh = token.refresh.map(|r| r.into_insecure_token()); - match new_token_object(&mut env, access_str, refresh) { - Ok(obj) => obj, - Err(e) => e.exception(&mut env, null_mut), + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, null_mut); + let namespace = get_rust_string!(env, namespace, null_mut); + let database = get_rust_string!(env, database, null_mut); + let access = get_rust_string!(env, access, null_mut); + let params = get_value_mut_instance!(env, params_value_ptr, null_mut).clone(); + let record = AuthRecord { + namespace, + database, + access, + params, + }; + match TOKIO_RUNTIME.block_on(async { surreal.signin(record).await }) { + Ok(token) => { + let access_str = token.access.into_insecure_token(); + let refresh = token.refresh.map(|r| r.into_insecure_token()); + match new_token_object(env, access_str, refresh) { + Ok(obj) => obj, + Err(e) => e.exception(env, null_mut), + } } + Err(err) => SurrealError::from(err).exception(env, null_mut), } - Err(err) => SurrealError::from(err).exception(&mut env, null_mut), - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_authenticate<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, token: JString<'local>, ) -> jboolean { - let surreal = get_surreal_ref!(&mut env, ptr, || false as jboolean); - let token_str = get_rust_string!(&mut env, token, || false as jboolean); - if let Err(err) = TOKIO_RUNTIME.block_on(async { surreal.authenticate(token_str).await }) { - return SurrealError::from(err).exception(&mut env, || false as jboolean); - } - true as jboolean + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, || false as jboolean); + let token_str = get_rust_string!(env, token, || false as jboolean); + if let Err(err) = TOKIO_RUNTIME.block_on(async { surreal.authenticate(token_str).await }) { + return SurrealError::from(err).exception(env, || false as jboolean); + } + true as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_invalidate<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let surreal = get_surreal_ref!(&mut env, ptr, || false as jboolean); - if let Err(err) = TOKIO_RUNTIME.block_on(async { surreal.invalidate().await }) { - return SurrealError::from(err).exception(&mut env, || false as jboolean); - } - true as jboolean + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, || false as jboolean); + if let Err(err) = TOKIO_RUNTIME.block_on(async { surreal.invalidate().await }) { + return SurrealError::from(err).exception(env, || false as jboolean); + } + true as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_useNs<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ns: JString<'local>, ) -> jobject { - let surreal = get_surreal_ref!(&mut env, ptr, null_mut); - let ns = get_rust_string!(&mut env, ns, null_mut); - match TOKIO_RUNTIME.block_on(async { surreal.use_ns(ns).await }) { - Ok((namespace, database)) => match new_ns_db_object(&mut env, namespace, database) { - Ok(obj) => obj, - Err(e) => e.exception(&mut env, null_mut), - }, - Err(err) => SurrealError::from(err).exception(&mut env, null_mut), - } + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, null_mut); + let ns = get_rust_string!(env, ns, null_mut); + match TOKIO_RUNTIME.block_on(async { surreal.use_ns(ns).await }) { + Ok((namespace, database)) => match new_ns_db_object(env, namespace, database) { + Ok(obj) => obj, + Err(e) => e.exception(env, null_mut), + }, + Err(err) => SurrealError::from(err).exception(env, null_mut), + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_useDb<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, db: JString<'local>, ) -> jobject { - let surreal = get_surreal_ref!(&mut env, ptr, null_mut); - let db = get_rust_string!(&mut env, db, null_mut); - match TOKIO_RUNTIME.block_on(async { surreal.use_db(db).await }) { - Ok((namespace, database)) => match new_ns_db_object(&mut env, namespace, database) { - Ok(obj) => obj, - Err(e) => e.exception(&mut env, null_mut), - }, - Err(err) => SurrealError::from(err).exception(&mut env, null_mut), - } + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, null_mut); + let db = get_rust_string!(env, db, null_mut); + match TOKIO_RUNTIME.block_on(async { surreal.use_db(db).await }) { + Ok((namespace, database)) => match new_ns_db_object(env, namespace, database) { + Ok(obj) => obj, + Err(e) => e.exception(env, null_mut), + }, + Err(err) => SurrealError::from(err).exception(env, null_mut), + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_useDefaults<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jobject { - let surreal = get_surreal_ref!(&mut env, ptr, null_mut); - match TOKIO_RUNTIME.block_on(async { surreal.use_defaults().await }) { - Ok((namespace, database)) => match new_ns_db_object(&mut env, namespace, database) { - Ok(obj) => obj, - Err(e) => e.exception(&mut env, null_mut), - }, - Err(err) => SurrealError::from(err).exception(&mut env, null_mut), - } + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, null_mut); + match TOKIO_RUNTIME.block_on(async { surreal.use_defaults().await }) { + Ok((namespace, database)) => match new_ns_db_object(env, namespace, database) { + Ok(obj) => obj, + Err(e) => e.exception(env, null_mut), + }, + Err(err) => SurrealError::from(err).exception(env, null_mut), + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_query<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, query: JString<'local>, ) -> jlong { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, ptr, || 0); - // Retrieve the query - let query = get_rust_string!(&mut env, &query, || 0); - // Execute the query - let res = surrealdb_query::<()>(surreal, &query, None); - // Check the result - let res = check_query_result!(&mut env, res, || 0); - // Build a response instance - JniTypes::new_response(Arc::new(Mutex::new(res))) + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, ptr, || 0); + // Retrieve the query + let query = get_rust_string!(env, &query, || 0); + // Execute the query + let res = surrealdb_query::<()>(surreal, &query, None); + // Check the result + let res = check_query_result!(env, res, || 0); + // Build a response instance + JniTypes::new_response(Arc::new(Mutex::new(res))) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_queryWithBindings<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, query: JString<'local>, - params_keys: JObjectArray<'local>, + params_keys: JObjectArray<'local, JString<'local>>, params_values: JLongArray<'local>, ) -> jlong { - let surreal = get_surreal_ref!(&mut env, ptr, || 0); - let query = get_rust_string!(&mut env, &query, || 0); - let params_map = build_params_map!(&mut env, params_keys, params_values, || 0); - let res = surrealdb_query::(surreal, &query, Some(params_map)); - let res = check_query_result!(&mut env, res, || 0); - JniTypes::new_response(Arc::new(Mutex::new(res))) + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, || 0); + let query = get_rust_string!(env, &query, || 0); + let params_map = build_params_map!(env, params_keys, params_values, || 0); + let res = surrealdb_query::(surreal, &query, Some(params_map)); + let res = check_query_result!(env, res, || 0); + JniTypes::new_response(Arc::new(Mutex::new(res))) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_run<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, name: JString<'local>, args_value_ptrs: JLongArray<'local>, ) -> jlong { - let surreal = get_surreal_ref!(&mut env, ptr, || 0); - let name = get_rust_string!(&mut env, &name, || 0); - let value_ptrs = get_long_array!(&mut env, &args_value_ptrs, || 0); - let mut params_map = BTreeMap::::new(); - let mut placeholders = Vec::with_capacity(value_ptrs.len()); - for (i, value_ptr) in value_ptrs.iter().enumerate() { - let key = format!("arg{}", i); - placeholders.push(format!("${}", key)); - let value = get_value_mut_instance!(&mut env, *value_ptr, || 0); - params_map.insert(key, value.clone()); - } - let args_list = placeholders.join(", "); - let query = format!("RETURN {}({})", name, args_list); - let res = surrealdb_query::(surreal, &query, Some(params_map)); - let mut response = check_query_result!(&mut env, res, || 0); - let mut result = take_one_result!(&mut env, response, || 0); - return_value_array_first!(result); - // Single scalar result (e.g. RETURN fn::greet() returns the value directly, not [value]) - JniTypes::new_value(Arc::new(result)) + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, || 0); + let name = get_rust_string!(env, &name, || 0); + let value_ptrs = get_long_array!(env, &args_value_ptrs, || 0); + let mut params_map = BTreeMap::::new(); + let mut placeholders = Vec::with_capacity(value_ptrs.len()); + for (i, value_ptr) in value_ptrs.iter().enumerate() { + let key = format!("arg{}", i); + placeholders.push(format!("${}", key)); + let value = get_value_mut_instance!(env, *value_ptr, || 0); + params_map.insert(key, value.clone()); + } + let args_list = placeholders.join(", "); + let query = format!("RETURN {}({})", name, args_list); + let res = surrealdb_query::(surreal, &query, Some(params_map)); + let mut response = check_query_result!(env, res, || 0); + let mut result = take_one_result!(env, response, || 0); + return_value_array_first!(result); + // Single scalar result (e.g. RETURN fn::greet() returns the value directly, not [value]) + JniTypes::new_value(Arc::new(result)) + }) } /// JNI implementation of `Surreal.selectLive(long ptr, String table)`. @@ -510,105 +547,111 @@ pub extern "system" fn Java_com_surrealdb_Surreal_run<'local>( /// exist) are thrown at call site instead of being deferred to `next()`. #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_selectLive<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, table: JString<'local>, ) -> jlong { - let surreal = get_surreal_ref!(&mut env, ptr, || 0); - let table = get_rust_string!(&mut env, &table, || 0); + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, || 0); + let table = get_rust_string!(env, &table, || 0); - // Notification channel: background thread produces, nextNative consumes. - let (tx, rx) = async_channel::unbounded(); - // Shutdown channel: dropping shutdown_tx signals the background thread to exit. - let (shutdown_tx, shutdown_rx) = async_channel::bounded::<()>(1); - // Readiness channel: background thread confirms subscription before we return. - let (ready_tx, ready_rx) = - std::sync::mpsc::channel::>(); + // Notification channel: background thread produces, nextNative consumes. + let (tx, rx) = async_channel::unbounded(); + // Shutdown channel: dropping shutdown_tx signals the background thread to exit. + let (shutdown_tx, shutdown_rx) = async_channel::bounded::<()>(1); + // Readiness channel: background thread confirms subscription before we return. + let (ready_tx, ready_rx) = + std::sync::mpsc::channel::>(); - let tx_thread = tx.clone(); - let surreal_clone = surreal.clone(); - let join_handle = std::thread::spawn(move || { - TOKIO_RUNTIME.block_on(async move { - let mut stream = match surreal_clone.select(table).live().await { - Ok(s) => { - let _ = ready_tx.send(Ok(())); - s - } - Err(e) => { - let _ = ready_tx.send(Err(e)); - return; - } - }; - loop { - tokio::select! { - _ = shutdown_rx.recv() => break, - item = stream.next() => match item { - Some(i) => { - let _ = tx_thread.send(i).await; - } - None => break, - }, + let tx_thread = tx.clone(); + let surreal_clone = surreal.clone(); + let join_handle = std::thread::spawn(move || { + TOKIO_RUNTIME.block_on(async move { + let mut stream = match surreal_clone.select(table).live().await { + Ok(s) => { + let _ = ready_tx.send(Ok(())); + s + } + Err(e) => { + let _ = ready_tx.send(Err(e)); + return; + } + }; + loop { + tokio::select! { + _ = shutdown_rx.recv() => break, + item = stream.next() => match item { + Some(i) => { + let _ = tx_thread.send(i).await; + } + None => break, + }, + } } - } + }); }); - }); - // Block until the subscription is confirmed or fails. - match ready_rx.recv() { - Ok(Ok(())) => {} - Ok(Err(e)) => { - let _ = join_handle.join(); - return SurrealError::from(e).exception(&mut env, || 0); - } - Err(_) => { - let _ = join_handle.join(); - return SurrealError::SurrealDBJni( - "Live query background thread exited unexpectedly".to_string(), - ) - .exception(&mut env, || 0); + // Block until the subscription is confirmed or fails. + match ready_rx.recv() { + Ok(Ok(())) => {} + Ok(Err(e)) => { + let _ = join_handle.join(); + return SurrealError::from(e).exception(env, || 0); + } + Err(_) => { + let _ = join_handle.join(); + return SurrealError::SurrealDBJni( + "Live query background thread exited unexpectedly".to_string(), + ) + .exception(env, || 0); + } } - } - let recv_mutex = std::sync::Arc::new(parking_lot::Mutex::new(())); - JniTypes::new_live_stream(( - recv_mutex, - parking_lot::Mutex::new(Some(join_handle)), - parking_lot::Mutex::new(Some(shutdown_tx)), - parking_lot::Mutex::new(Some(rx)), - )) + let recv_mutex = std::sync::Arc::new(parking_lot::Mutex::new(())); + JniTypes::new_live_stream(( + recv_mutex, + parking_lot::Mutex::new(Some(join_handle)), + parking_lot::Mutex::new(Some(shutdown_tx)), + parking_lot::Mutex::new(Some(rx)), + )) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_exportSql<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, path: JString<'local>, ) -> jboolean { - let surreal = get_surreal_ref!(&mut env, ptr, || false as jboolean); - let path = get_rust_string!(&mut env, &path, || false as jboolean); - let path = Path::new(&path).to_path_buf(); - match TOKIO_RUNTIME.block_on(async { surreal.export(path).await }) { - Ok(()) => true as jboolean, - Err(e) => SurrealError::from(e).exception(&mut env, || false as jboolean), - } + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, || false as jboolean); + let path = get_rust_string!(env, &path, || false as jboolean); + let path = Path::new(&path).to_path_buf(); + match TOKIO_RUNTIME.block_on(async { surreal.export(path).await }) { + Ok(()) => true as jboolean, + Err(e) => SurrealError::from(e).exception(env, || false as jboolean), + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_importSql<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, path: JString<'local>, ) -> jboolean { - let surreal = get_surreal_ref!(&mut env, ptr, || false as jboolean); - let path = get_rust_string!(&mut env, &path, || false as jboolean); - let path = Path::new(&path).to_path_buf(); - match TOKIO_RUNTIME.block_on(async { surreal.import(path).await }) { - Ok(()) => true as jboolean, - Err(e) => SurrealError::from(e).exception(&mut env, || false as jboolean), - } + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, ptr, || false as jboolean); + let path = get_rust_string!(env, &path, || false as jboolean); + let path = Path::new(&path).to_path_buf(); + match TOKIO_RUNTIME.block_on(async { surreal.import(path).await }) { + Ok(()) => true as jboolean, + Err(e) => SurrealError::from(e).exception(env, || false as jboolean), + } + }) } fn surrealdb_query( @@ -631,230 +674,245 @@ where #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_createRecordIdValue<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, record_id_ptr: jlong, value_ptr: jlong, ) -> jlong { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || 0); - // Extract the record id - let record_id = get_value_instance!(&mut env, record_id_ptr, || 0); - // Get the value - let value = get_value_mut_instance!(&mut env, value_ptr, || 0); - // Execute the query - let query = format!("CREATE {} CONTENT $val", record_id.to_sql()); - let params = BTreeMap::from([("val".to_string(), value.clone())]); - let res = surrealdb_query(surreal, &query, Some(params)); - // Check the result - let mut response = check_query_result!(&mut env, res, || 0); - // There is only one statement - let mut result = take_one_result!(&mut env, response, || 0); - // There should be only one result - return_value_array_first!(result); - // Otherwise we return an error - return_unexpected_result!(&mut env, result.to_sql(), || 0) + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, || 0); + // Extract the record id + let record_id = get_value_instance!(env, record_id_ptr, || 0); + // Get the value + let value = get_value_mut_instance!(env, value_ptr, || 0); + // Execute the query + let query = format!("CREATE {} CONTENT $val", record_id.to_sql()); + let params = BTreeMap::from([("val".to_string(), value.clone())]); + let res = surrealdb_query(surreal, &query, Some(params)); + // Check the result + let mut response = check_query_result!(env, res, || 0); + // There is only one statement + let mut result = take_one_result!(env, response, || 0); + // There should be only one result + return_value_array_first!(result); + // Otherwise we return an error + return_unexpected_result!(env, result.to_sql(), || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_createTargetValues<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, target: JString<'local>, value_ptrs: JLongArray<'local>, ) -> jlongArray { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, null_mut); - // Build the parameters - let target = get_rust_string!(&mut env, target, null_mut); - // Get the pointers - let value_ptrs = get_long_array!(&mut env, &value_ptrs, null_mut); - // Build the queries - let mut queries = Vec::with_capacity(value_ptrs.len()); - let mut params = BTreeMap::new(); - for (idx, value_ptr) in value_ptrs.iter().enumerate() { - queries.push(format!("CREATE {} CONTENT $i{idx}", target)); - let value = get_value_mut_instance!(&mut env, *value_ptr, null_mut); - params.insert(format!("i{idx}"), value.clone()); - } - let query = queries.join(";\n"); - // Execute the query - let res = surrealdb_query(surreal, &query, Some(params)); - // Check the result - let mut res = check_query_result!(&mut env, res, null_mut); - // Prepare the result - let mut value_ptrs: Vec = Vec::with_capacity(res.num_statements()); - // Iterate over the statement - for i in 0..res.num_statements() { - let mut res = match res.take::(i) { - Ok(r) => r, - Err(e) => return SurrealError::SurrealDB(e).exception(&mut env, null_mut), - }; - // There should be only one result per statement - if let Value::Array(ref mut a) = res { - if a.len() != 1 { - return SurrealError::SurrealDBJni(format!("Unexpected result: {}", res.to_sql())) - .exception(&mut env, null_mut); + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, null_mut); + // Build the parameters + let target = get_rust_string!(env, target, null_mut); + // Get the pointers + let value_ptrs = get_long_array!(env, &value_ptrs, null_mut); + // Build the queries + let mut queries = Vec::with_capacity(value_ptrs.len()); + let mut params = BTreeMap::new(); + for (idx, value_ptr) in value_ptrs.iter().enumerate() { + queries.push(format!("CREATE {} CONTENT $i{idx}", target)); + let value = get_value_mut_instance!(env, *value_ptr, null_mut); + params.insert(format!("i{idx}"), value.clone()); + } + let query = queries.join(";\n"); + // Execute the query + let res = surrealdb_query(surreal, &query, Some(params)); + // Check the result + let mut res = check_query_result!(env, res, null_mut); + // Prepare the result + let mut value_ptrs: Vec = Vec::with_capacity(res.num_statements()); + // Iterate over the statement + for i in 0..res.num_statements() { + let mut res = match res.take::(i) { + Ok(r) => r, + Err(e) => return SurrealError::SurrealDB(e).exception(env, null_mut), + }; + // There should be only one result per statement + if let Value::Array(ref mut a) = res { + if a.len() != 1 { + return SurrealError::SurrealDBJni(format!( + "Unexpected result: {}", + res.to_sql() + )) + .exception(env, null_mut); + } + let val = a.remove(0); + let value_ptr = JniTypes::new_value(val.into()); + value_ptrs.push(value_ptr); } - let val = a.remove(0); - let value_ptr = JniTypes::new_value(val.into()); - value_ptrs.push(value_ptr); } - } - new_jlong_array!(&mut env, &value_ptrs, null_mut) + new_jlong_array!(env, &value_ptrs, null_mut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_insertTargetValues<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, target: JString<'local>, value_ptrs: JLongArray<'local>, ) -> jlongArray { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, null_mut); - // Build the parameters - let target = get_rust_string!(&mut env, target, null_mut); - // Get the pointers - let value_ptrs = get_long_array!(&mut env, &value_ptrs, null_mut); - // Build the queries - let mut records = Vec::with_capacity(value_ptrs.len()); - for value_ptr in &value_ptrs { - let value = get_value_mut_instance!(&mut env, *value_ptr, null_mut); - records.push(value.to_sql()); - } - let query = format!("INSERT INTO {} [ {} ]", target, records.join(" , ")); - // Execute the query - let res = surrealdb_query::<()>(surreal, &query, None); - // Check the result - let mut response = check_query_result!(&mut env, res, null_mut); - // There is only one statement - let result = take_one_result!(&mut env, response, null_mut); - if let Value::Array(a) = result { - // Prepare the result - let mut value_ptrs: Vec = Vec::with_capacity(a.len()); - for val in a.into_iter() { - let value_ptr = JniTypes::new_value(val.into()); - value_ptrs.push(value_ptr); + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, null_mut); + // Build the parameters + let target = get_rust_string!(env, target, null_mut); + // Get the pointers + let value_ptrs = get_long_array!(env, &value_ptrs, null_mut); + // Build the queries + let mut records = Vec::with_capacity(value_ptrs.len()); + for value_ptr in &value_ptrs { + let value = get_value_mut_instance!(env, *value_ptr, null_mut); + records.push(value.to_sql()); } - new_jlong_array!(&mut env, &value_ptrs, null_mut) - } else { - SurrealError::SurrealDBJni(format!("Unexpected result: {}", result.to_sql())) - .exception(&mut env, null_mut) - } + let query = format!("INSERT INTO {} [ {} ]", target, records.join(" , ")); + // Execute the query + let res = surrealdb_query::<()>(surreal, &query, None); + // Check the result + let mut response = check_query_result!(env, res, null_mut); + // There is only one statement + let result = take_one_result!(env, response, null_mut); + if let Value::Array(a) = result { + // Prepare the result + let mut value_ptrs: Vec = Vec::with_capacity(a.len()); + for val in a.into_iter() { + let value_ptr = JniTypes::new_value(val.into()); + value_ptrs.push(value_ptr); + } + new_jlong_array!(env, &value_ptrs, null_mut) + } else { + SurrealError::SurrealDBJni(format!("Unexpected result: {}", result.to_sql())) + .exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_insertRelationTargetValue<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, target: JString<'local>, value_ptr: jlong, ) -> jlong { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || 0); - // Build the parameters - let target = get_rust_string!(&mut env, target, || 0); - // Get the value - let value = get_value_mut_instance!(&mut env, value_ptr, || 0); - // Execute the query - let query = format!("INSERT RELATION INTO {} {}", target, value.to_sql()); - let res = surrealdb_query::<()>(surreal, &query, None); - // Check the result - let mut response = check_query_result!(&mut env, res, || 0); - // There is only one statement - let mut result = take_one_result!(&mut env, response, || 0); - // There should be only one result - return_value_array_first!(result); - // Otherwise we return an error - return_unexpected_result!(&mut env, result.to_sql(), || 0) + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, || 0); + // Build the parameters + let target = get_rust_string!(env, target, || 0); + // Get the value + let value = get_value_mut_instance!(env, value_ptr, || 0); + // Execute the query + let query = format!("INSERT RELATION INTO {} {}", target, value.to_sql()); + let res = surrealdb_query::<()>(surreal, &query, None); + // Check the result + let mut response = check_query_result!(env, res, || 0); + // There is only one statement + let mut result = take_one_result!(env, response, || 0); + // There should be only one result + return_value_array_first!(result); + // Otherwise we return an error + return_unexpected_result!(env, result.to_sql(), || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_insertRelationTargetValues<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, target: JString<'local>, value_ptrs: JLongArray<'local>, ) -> jlongArray { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, null_mut); - // Build the parameters - let target = get_rust_string!(&mut env, target, null_mut); - // Get the pointers - let value_ptrs = get_long_array!(&mut env, &value_ptrs, null_mut); - // Build the queries - let mut records = Vec::with_capacity(value_ptrs.len()); - for value_ptr in &value_ptrs { - let value = get_value_mut_instance!(&mut env, *value_ptr, null_mut); - records.push(value.to_sql()); - } - let query = format!( - "INSERT RELATION INTO {} [ {} ]", - target, - records.join(" , ") - ); - // Execute the query - let res = surrealdb_query::<()>(surreal, &query, None); - // Check the result - let mut response = check_query_result!(&mut env, res, null_mut); - // There is only one statement - let result = take_one_result!(&mut env, response, null_mut); - if let Value::Array(a) = result { - // Prepare the result - let mut value_ptrs: Vec = Vec::with_capacity(a.len()); - for val in a.into_iter() { - let value_ptr = JniTypes::new_value(val.into()); - value_ptrs.push(value_ptr); + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, null_mut); + // Build the parameters + let target = get_rust_string!(env, target, null_mut); + // Get the pointers + let value_ptrs = get_long_array!(env, &value_ptrs, null_mut); + // Build the queries + let mut records = Vec::with_capacity(value_ptrs.len()); + for value_ptr in &value_ptrs { + let value = get_value_mut_instance!(env, *value_ptr, null_mut); + records.push(value.to_sql()); } - new_jlong_array!(&mut env, &value_ptrs, null_mut) - } else { - SurrealError::SurrealDBJni(format!("Unexpected result: {}", result.to_sql())) - .exception(&mut env, null_mut) - } + let query = format!( + "INSERT RELATION INTO {} [ {} ]", + target, + records.join(" , ") + ); + // Execute the query + let res = surrealdb_query::<()>(surreal, &query, None); + // Check the result + let mut response = check_query_result!(env, res, null_mut); + // There is only one statement + let result = take_one_result!(env, response, null_mut); + if let Value::Array(a) = result { + // Prepare the result + let mut value_ptrs: Vec = Vec::with_capacity(a.len()); + for val in a.into_iter() { + let value_ptr = JniTypes::new_value(val.into()); + value_ptrs.push(value_ptr); + } + new_jlong_array!(env, &value_ptrs, null_mut) + } else { + SurrealError::SurrealDBJni(format!("Unexpected result: {}", result.to_sql())) + .exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_relate<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, from_ptr: jlong, target: JString<'local>, to_ptr: jlong, ) -> jlong { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || 0); - // Build the parameters - let target = get_rust_string!(&mut env, target, || 0); - // Get from and to - let from_value = get_value_instance!(&mut env, from_ptr, || 0); - let to_value = get_value_instance!(&mut env, to_ptr, || 0); - // Execute the query - let query = format!("RELATE $from->{}->$to", target); - let params = BTreeMap::from([ - ("from".to_string(), from_value), - ("to".to_string(), to_value), - ]); - let res = surrealdb_query(surreal, &query, Some(params)); - // Check the result - let mut response = check_query_result!(&mut env, res, || 0); - // There is only one statement - let mut result = take_one_result!(&mut env, response, || 0); - // There should be only one result - return_value_array_first!(result); - // Otherwise we return an error - return_unexpected_result!(&mut env, result.to_sql(), || 0) + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, || 0); + // Build the parameters + let target = get_rust_string!(env, target, || 0); + // Get from and to + let from_value = get_value_instance!(env, from_ptr, || 0); + let to_value = get_value_instance!(env, to_ptr, || 0); + // Execute the query + let query = format!("RELATE $from->{}->$to", target); + let params = BTreeMap::from([ + ("from".to_string(), from_value), + ("to".to_string(), to_value), + ]); + let res = surrealdb_query(surreal, &query, Some(params)); + // Check the result + let mut response = check_query_result!(env, res, || 0); + // There is only one statement + let mut result = take_one_result!(env, response, || 0); + // There should be only one result + return_value_array_first!(result); + // Otherwise we return an error + return_unexpected_result!(env, result.to_sql(), || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_relateContent<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, from_ptr: jlong, @@ -862,273 +920,293 @@ pub extern "system" fn Java_com_surrealdb_Surreal_relateContent<'local>( to_ptr: jlong, content_ptr: jlong, ) -> jlong { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || 0); - // Build the parameters - let target = get_rust_string!(&mut env, target, || 0); - // Get from and to - let from_value = get_value_instance!(&mut env, from_ptr, || 0); - let to_value = get_value_instance!(&mut env, to_ptr, || 0); - let content_value = get_value_mut_instance!(&mut env, content_ptr, || 0); - // Execute the query - let query = format!( - "RELATE $from->{}->$to CONTENT {}", - target, - content_value.to_sql() - ); - let params = BTreeMap::from([ - ("from".to_string(), from_value), - ("to".to_string(), to_value), - ]); - let res = surrealdb_query(surreal, &query, Some(params)); - // Check the result - let mut response = check_query_result!(&mut env, res, || 0); - // There is only one statement - let mut result = take_one_result!(&mut env, response, || 0); - // There should be only one result - return_value_array_first!(result); - // Otherwise we return an error - return_unexpected_result!(&mut env, result.to_sql(), || 0) + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, || 0); + // Build the parameters + let target = get_rust_string!(env, target, || 0); + // Get from and to + let from_value = get_value_instance!(env, from_ptr, || 0); + let to_value = get_value_instance!(env, to_ptr, || 0); + let content_value = get_value_mut_instance!(env, content_ptr, || 0); + // Execute the query + let query = format!( + "RELATE $from->{}->$to CONTENT {}", + target, + content_value.to_sql() + ); + let params = BTreeMap::from([ + ("from".to_string(), from_value), + ("to".to_string(), to_value), + ]); + let res = surrealdb_query(surreal, &query, Some(params)); + // Check the result + let mut response = check_query_result!(env, res, || 0); + // There is only one statement + let mut result = take_one_result!(env, response, || 0); + // There should be only one result + return_value_array_first!(result); + // Otherwise we return an error + return_unexpected_result!(env, result.to_sql(), || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_selectRecordId<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, record_id_ptr: jlong, ) -> jlong { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || 0); - // Extract the record id - let record_id = get_value_instance!(&mut env, record_id_ptr, || 0); - // Execute the query - let query = format!("SELECT * FROM {}", record_id.to_sql()); - let res = surrealdb_query::<()>(surreal, &query, None); - // Check the result - let mut res = check_query_result!(&mut env, res, || 0); - // There is only one statement - let mut res = take_one_result!(&mut env, res, || 0); - // There should be only one result - return_value_array_first!(res); - // If the array is empty, return null (0) for Optional.empty() - if let Value::Array(ref a) = res { - if a.is_empty() { - return 0; + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, || 0); + // Extract the record id + let record_id = get_value_instance!(env, record_id_ptr, || 0); + // Execute the query + let query = format!("SELECT * FROM {}", record_id.to_sql()); + let res = surrealdb_query::<()>(surreal, &query, None); + // Check the result + let mut res = check_query_result!(env, res, || 0); + // There is only one statement + let mut res = take_one_result!(env, res, || 0); + // There should be only one result + return_value_array_first!(res); + // If the array is empty, return null (0) for Optional.empty() + if let Value::Array(ref a) = res { + if a.is_empty() { + return 0; + } } - } - // Otherwise throw an error - return_unexpected_result!(&mut env, res.to_sql(), || 0) + // Otherwise throw an error + return_unexpected_result!(env, res.to_sql(), || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_selectRecordIds<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, record_id_ptrs: JLongArray<'local>, ) -> jlongArray { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, null_mut); - // Get the record id pointers - let record_id_ptrs = get_long_array!(&mut env, &record_id_ptrs, null_mut); - // Extract the record ids - let mut record_ids = Vec::with_capacity(record_id_ptrs.len()); - for record_id_ptr in record_id_ptrs { - let record_id = get_value_instance!(&mut env, record_id_ptr, null_mut); - record_ids.push(record_id.to_sql()); - } - // Execute the query - let query = format!("SELECT * FROM {}", record_ids.join(",")); - let res = surrealdb_query::<()>(surreal, &query, None); - // Check the result - let mut res = check_query_result!(&mut env, res, null_mut); - // There is only one statement - let res = take_one_result!(&mut env, res, null_mut); - // Prepare the result - if let Value::Array(a) = res { - let mut value_ptrs: Vec = Vec::with_capacity(a.len()); - for value in a { - let value_ptr = JniTypes::new_value(Arc::new(value)); - value_ptrs.push(value_ptr); + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, null_mut); + // Get the record id pointers + let record_id_ptrs = get_long_array!(env, &record_id_ptrs, null_mut); + // Extract the record ids + let mut record_ids = Vec::with_capacity(record_id_ptrs.len()); + for record_id_ptr in record_id_ptrs { + let record_id = get_value_instance!(env, record_id_ptr, null_mut); + record_ids.push(record_id.to_sql()); } - // Return the results - new_jlong_array!(&mut env, &value_ptrs, null_mut) - } else { - SurrealError::SurrealDBJni(format!("Unexpected result: {}", res.to_sql())) - .exception(&mut env, null_mut) - } + // Execute the query + let query = format!("SELECT * FROM {}", record_ids.join(",")); + let res = surrealdb_query::<()>(surreal, &query, None); + // Check the result + let mut res = check_query_result!(env, res, null_mut); + // There is only one statement + let res = take_one_result!(env, res, null_mut); + // Prepare the result + if let Value::Array(a) = res { + let mut value_ptrs: Vec = Vec::with_capacity(a.len()); + for value in a { + let value_ptr = JniTypes::new_value(Arc::new(value)); + value_ptrs.push(value_ptr); + } + // Return the results + new_jlong_array!(env, &value_ptrs, null_mut) + } else { + SurrealError::SurrealDBJni(format!("Unexpected result: {}", res.to_sql())) + .exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_selectTargetsValues<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, - targets: JObjectArray<'local>, + targets: JObjectArray<'local, JString<'local>>, ) -> jlong { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || 0); - // Get the targets - let targets = get_rust_string_array!(&mut env, targets, || 0); - // Prepare the query - let query = format!("SELECT * FROM {}", targets.join(",")); - // Execute the query - let res = surrealdb_query::<()>(surreal, &query, None); - // Check the result - let mut response = check_query_result!(&mut env, res, || 0); - // There is only one statement - let result = take_one_result!(&mut env, response, || 0); - // Return the iterator - return_value_array_iter!(result); - // Otherwise throw an error - return_unexpected_result!(&mut env, result.to_sql(), || 0) + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, || 0); + // Get the targets + let targets = get_rust_string_array!(env, targets, || 0); + // Prepare the query + let query = format!("SELECT * FROM {}", targets.join(",")); + // Execute the query + let res = surrealdb_query::<()>(surreal, &query, None); + // Check the result + let mut response = check_query_result!(env, res, || 0); + // There is only one statement + let result = take_one_result!(env, response, || 0); + // Return the iterator + return_value_array_iter!(result); + // Otherwise throw an error + return_unexpected_result!(env, result.to_sql(), || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_selectTargetsValuesSync<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, - targets: JObjectArray<'local>, + targets: JObjectArray<'local, JString<'local>>, ) -> jlong { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || 0); - // Get the targets - let targets = get_rust_string_array!(&mut env, targets, || 0); - // Prepare the query - let query = format!("SELECT * FROM {}", targets.join(",")); - // Execute the query - let res = surrealdb_query::<()>(surreal, &query, None); - // Check the result - let mut response = check_query_result!(&mut env, res, || 0); - // There is only one statement - let result = take_one_result!(&mut env, response, || 0); - // Return tne sync iterator - return_value_array_iter_sync!(result); - // Otherwise throw an error - return_unexpected_result!(&mut env, result.to_sql(), || 0) + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, || 0); + // Get the targets + let targets = get_rust_string_array!(env, targets, || 0); + // Prepare the query + let query = format!("SELECT * FROM {}", targets.join(",")); + // Execute the query + let res = surrealdb_query::<()>(surreal, &query, None); + // Check the result + let mut response = check_query_result!(env, res, || 0); + // There is only one statement + let result = take_one_result!(env, response, || 0); + // Return tne sync iterator + return_value_array_iter_sync!(result); + // Otherwise throw an error + return_unexpected_result!(env, result.to_sql(), || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_deleteRecordId<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, record_id_ptr: jlong, ) -> jboolean { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || false as jboolean); - // Build the parameters - let record_id = get_value_instance!(&mut env, record_id_ptr, || false as jboolean); - // Prepare the params - let params = BTreeMap::from([("t".to_string(), record_id)]); - // Execute the query - let res = surrealdb_query(surreal, "DELETE $t", Some(params)); - // Check the result - check_query_result!(&mut env, res, || false as jboolean); - true as jboolean + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, || false as jboolean); + // Build the parameters + let record_id = get_value_instance!(env, record_id_ptr, || false as jboolean); + // Prepare the params + let params = BTreeMap::from([("t".to_string(), record_id)]); + // Execute the query + let res = surrealdb_query(surreal, "DELETE $t", Some(params)); + // Check the result + check_query_result!(env, res, || false as jboolean); + true as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_deleteRecordIds<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, record_id_ptrs: JLongArray<'local>, ) -> jboolean { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || false as jboolean); - // Extract the record ids - let record_id_ptrs = get_long_array!(&mut env, &record_id_ptrs, || false as jboolean); - // Prepare the params - let mut targets = Vec::with_capacity(record_id_ptrs.len()); - let mut params = BTreeMap::new(); - for (idx, record_id_ptr) in record_id_ptrs.iter().enumerate() { - let value = get_value_instance!(&mut env, *record_id_ptr, || false as jboolean); - params.insert(format!("t{idx}"), value); - targets.push(format!("$t{idx}")); - } - // Prepare the query - let query = format!("DELETE {}", targets.join(",")); - // Execute the query - let res = surrealdb_query(surreal, &query, Some(params)); - // Check the result - check_query_result!(&mut env, res, || 0); - true as jboolean + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, || false as jboolean); + // Extract the record ids + let record_id_ptrs = get_long_array!(env, &record_id_ptrs, || false as jboolean); + // Prepare the params + let mut targets = Vec::with_capacity(record_id_ptrs.len()); + let mut params = BTreeMap::new(); + for (idx, record_id_ptr) in record_id_ptrs.iter().enumerate() { + let value = get_value_instance!(env, *record_id_ptr, || false as jboolean); + params.insert(format!("t{idx}"), value); + targets.push(format!("$t{idx}")); + } + // Prepare the query + let query = format!("DELETE {}", targets.join(",")); + // Execute the query + let res = surrealdb_query(surreal, &query, Some(params)); + // Check the result + check_query_result!(env, res, || false); + true as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_selectRecordIdRange<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, table: JString<'local>, start_id_ptr: jlong, end_id_ptr: jlong, ) -> jlongArray { - let surreal = get_surreal_ref!(&mut env, surreal_ptr, null_mut); - let table_str = get_rust_string!(&mut env, table, null_mut); - let range_value = match build_range_value(&mut env, &table_str, start_id_ptr, end_id_ptr) { - Ok(v) => v, - Err(e) => return e.exception(&mut env, null_mut), - }; - let params = BTreeMap::from([("_range".to_string(), range_value)]); - let res = surrealdb_query(surreal, "SELECT * FROM $_range", Some(params)); - let mut res = check_query_result!(&mut env, res, null_mut); - let res = take_one_result!(&mut env, res, null_mut); - if let Value::Array(a) = res { - let mut value_ptrs: Vec = Vec::with_capacity(a.len()); - for value in a { - let value_ptr = JniTypes::new_value(Arc::new(value)); - value_ptrs.push(value_ptr); + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, surreal_ptr, null_mut); + let table_str = get_rust_string!(env, table, null_mut); + let range_value = match build_range_value(env, &table_str, start_id_ptr, end_id_ptr) { + Ok(v) => v, + Err(e) => return e.exception(env, null_mut), + }; + let params = BTreeMap::from([("_range".to_string(), range_value)]); + let res = surrealdb_query(surreal, "SELECT * FROM $_range", Some(params)); + let mut res = check_query_result!(env, res, null_mut); + let res = take_one_result!(env, res, null_mut); + if let Value::Array(a) = res { + let mut value_ptrs: Vec = Vec::with_capacity(a.len()); + for value in a { + let value_ptr = JniTypes::new_value(Arc::new(value)); + value_ptrs.push(value_ptr); + } + new_jlong_array!(env, &value_ptrs, null_mut) + } else { + SurrealError::SurrealDBJni(format!("Unexpected result: {}", res.to_sql())) + .exception(env, null_mut) } - new_jlong_array!(&mut env, &value_ptrs, null_mut) - } else { - SurrealError::SurrealDBJni(format!("Unexpected result: {}", res.to_sql())) - .exception(&mut env, null_mut) - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_deleteRecordIdRange<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, table: JString<'local>, start_id_ptr: jlong, end_id_ptr: jlong, ) -> jboolean { - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || false as jboolean); - let table_str = get_rust_string!(&mut env, table, || false as jboolean); - let range_value = match build_range_value(&mut env, &table_str, start_id_ptr, end_id_ptr) { - Ok(v) => v, - Err(e) => return e.exception(&mut env, || false as jboolean), - }; - let params = BTreeMap::from([("_range".to_string(), range_value)]); - let res = surrealdb_query(surreal, "DELETE $_range", Some(params)); - check_query_result!(&mut env, res, || false as jboolean); - true as jboolean + with_env_body!(env, env, { + let surreal = get_surreal_ref!(env, surreal_ptr, || false as jboolean); + let table_str = get_rust_string!(env, table, || false as jboolean); + let range_value = match build_range_value(env, &table_str, start_id_ptr, end_id_ptr) { + Ok(v) => v, + Err(e) => return e.exception(env, || false as jboolean), + }; + let params = BTreeMap::from([("_range".to_string(), range_value)]); + let res = surrealdb_query(surreal, "DELETE $_range", Some(params)); + check_query_result!(env, res, || false as jboolean); + true as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_deleteTarget<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, target: JString<'local>, ) -> jboolean { - // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || false as jboolean); - // Get the targets - let target = get_rust_string!(&mut env, target, || false as jboolean); - // Prepare the query - let query = format!("DELETE FROM {}", target); - // Execute the query - let res = surrealdb_query::<()>(surreal, &query, None); - // Check the result - check_query_result!(&mut env, res, || false as jboolean); - true as jboolean + with_env_body!(env, env, { + // Retrieve the Surreal instance + let surreal = get_surreal_ref!(env, surreal_ptr, || false as jboolean); + // Get the targets + let target = get_rust_string!(env, target, || false as jboolean); + // Prepare the query + let query = format!("DELETE FROM {}", target); + // Execute the query + let res = surrealdb_query::<()>(surreal, &query, None); + // Check the result + check_query_result!(env, res, || false as jboolean); + true as jboolean + }) } /// Converts a Value (e.g. from an Id) to RecordIdKey for range bounds. @@ -1143,7 +1221,7 @@ fn value_to_record_id_key(value: &Value) -> std::result::Result jlong { // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || 0); + let surreal = get_surreal_ref!(env, surreal_ptr, || 0); // Extract the record id - let record_id = get_value_instance!(&mut env, record_id_ptr, || 0); + let record_id = get_value_instance!(env, record_id_ptr, || 0); // Get the value - let value = get_value_mut_instance!(&mut env, value_ptr, || 0); + let value = get_value_mut_instance!(env, value_ptr, || 0); // Check the up type - let up_type = convert_up_type!(&mut env, up_type, || 0); + let up_type = convert_up_type!(env, up_type, || 0); // Execute the query let query = format!("{up} {} {up_type} $val", record_id.to_sql()); let params = BTreeMap::from([("val".to_string(), value.clone())]); let res = surrealdb_query(surreal, &query, Some(params)); // Check the result - let mut response = check_query_result!(&mut env, res, || 0); + let mut response = check_query_result!(env, res, || 0); // There is only one statement - let mut result: Value = take_one_result!(&mut env, response, || 0); + let mut result: Value = take_one_result!(env, response, || 0); // There should be only one result return_value_array_first!(result); // Otherwise we return an error - return_unexpected_result!(&mut env, result.to_sql(), || 0) + return_unexpected_result!(env, result.to_sql(), || 0) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_updateRecordIdValue<'local>( - env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, record_id_ptr: jlong, up_type: jint, value_ptr: jlong, ) -> jlong { - up_record_id_value( - env, - surreal_ptr, - record_id_ptr, - up_type, - value_ptr, - "update", - ) + with_env_body!(env, env, { + up_record_id_value( + env, + surreal_ptr, + record_id_ptr, + up_type, + value_ptr, + "update", + ) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_upsertRecordIdValue<'local>( - env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, record_id_ptr: jlong, up_type: jint, value_ptr: jlong, ) -> jlong { - up_record_id_value( - env, - surreal_ptr, - record_id_ptr, - up_type, - value_ptr, - "upsert", - ) + with_env_body!(env, env, { + up_record_id_value( + env, + surreal_ptr, + record_id_ptr, + up_type, + value_ptr, + "upsert", + ) + }) } #[allow(clippy::too_many_arguments)] fn up_record_id_range_value( - mut env: JNIEnv, + env: &mut Env, surreal_ptr: jlong, table: JString, start_id_ptr: jlong, @@ -1249,21 +1331,21 @@ fn up_record_id_range_value( value_ptr: jlong, up: &str, ) -> jlong { - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || 0); - let table_str = get_rust_string!(&mut env, table, || 0); - let range_value = match build_range_value(&mut env, &table_str, start_id_ptr, end_id_ptr) { + let surreal = get_surreal_ref!(env, surreal_ptr, || 0); + let table_str = get_rust_string!(env, table, || 0); + let range_value = match build_range_value(env, &table_str, start_id_ptr, end_id_ptr) { Ok(v) => v, - Err(e) => return e.exception(&mut env, || 0), + Err(e) => return e.exception(env, || 0), }; - let value = get_value_mut_instance!(&mut env, value_ptr, || 0); - let up_type = convert_up_type!(&mut env, up_type, || 0); + let value = get_value_mut_instance!(env, value_ptr, || 0); + let up_type = convert_up_type!(env, up_type, || 0); let mut params = BTreeMap::new(); params.insert("_range".to_string(), range_value); params.insert("val".to_string(), value.clone()); let query = format!("{up} $_range {up_type} $val"); let res = surrealdb_query(surreal, &query, Some(params)); - let mut response = check_query_result!(&mut env, res, || 0); - let mut result: Value = take_one_result!(&mut env, response, || 0); + let mut response = check_query_result!(env, res, || 0); + let mut result: Value = take_one_result!(env, response, || 0); return_value_array_first!(result); // Range update/upsert can return [] or [one or more records]; return first or None if let Value::Array(ref mut a) = result { @@ -1272,12 +1354,12 @@ fn up_record_id_range_value( } return JniTypes::new_value(Arc::new(a.remove(0))); } - return_unexpected_result!(&mut env, result.to_sql(), || 0) + return_unexpected_result!(env, result.to_sql(), || 0) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_updateRecordIdRangeValue<'local>( - env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, table: JString<'local>, @@ -1286,21 +1368,23 @@ pub extern "system" fn Java_com_surrealdb_Surreal_updateRecordIdRangeValue<'loca up_type: jint, value_ptr: jlong, ) -> jlong { - up_record_id_range_value( - env, - surreal_ptr, - table, - start_id_ptr, - end_id_ptr, - up_type, - value_ptr, - "update", - ) + with_env_body!(env, env, { + up_record_id_range_value( + env, + surreal_ptr, + table, + start_id_ptr, + end_id_ptr, + up_type, + value_ptr, + "update", + ) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_upsertRecordIdRangeValue<'local>( - env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, table: JString<'local>, @@ -1309,20 +1393,22 @@ pub extern "system" fn Java_com_surrealdb_Surreal_upsertRecordIdRangeValue<'loca up_type: jint, value_ptr: jlong, ) -> jlong { - up_record_id_range_value( - env, - surreal_ptr, - table, - start_id_ptr, - end_id_ptr, - up_type, - value_ptr, - "upsert", - ) + with_env_body!(env, env, { + up_record_id_range_value( + env, + surreal_ptr, + table, + start_id_ptr, + end_id_ptr, + up_type, + value_ptr, + "upsert", + ) + }) } fn up_target_value( - mut env: JNIEnv, + env: &mut Env, surreal_ptr: jlong, target: JString, up_type: jint, @@ -1330,53 +1416,57 @@ fn up_target_value( up: &str, ) -> jlong { // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || 0); + let surreal = get_surreal_ref!(env, surreal_ptr, || 0); // Build the parameters - let target = get_rust_string!(&mut env, target, || 0); + let target = get_rust_string!(env, target, || 0); // Get the value - let value = get_value_mut_instance!(&mut env, value_ptr, || 0); + let value = get_value_mut_instance!(env, value_ptr, || 0); // Check the up type - let up_type = convert_up_type!(&mut env, up_type, || 0); + let up_type = convert_up_type!(env, up_type, || 0); // Execute the query let query = format!("{up} {} {up_type} $val", target); let params = BTreeMap::from([("val".to_string(), value.clone())]); let res = surrealdb_query(surreal, &query, Some(params)); // Check the result - let mut response = check_query_result!(&mut env, res, || 0); + let mut response = check_query_result!(env, res, || 0); // There is only one statement - let result: Value = take_one_result!(&mut env, response, || 0); + let result: Value = take_one_result!(env, response, || 0); // There should be only one result return_value_array_iter!(result); // Otherwise we return an error - return_unexpected_result!(&mut env, result.to_sql(), || 0) + return_unexpected_result!(env, result.to_sql(), || 0) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_updateTargetValue<'local>( - env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, target: JString<'local>, up_type: jint, value_ptr: jlong, ) -> jlong { - up_target_value(env, surreal_ptr, target, up_type, value_ptr, "update") + with_env_body!(env, env, { + up_target_value(env, surreal_ptr, target, up_type, value_ptr, "update") + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_upsertTargetValue<'local>( - env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, target: JString<'local>, up_type: jint, value_ptr: jlong, ) -> jlong { - up_target_value(env, surreal_ptr, target, up_type, value_ptr, "upsert") + with_env_body!(env, env, { + up_target_value(env, surreal_ptr, target, up_type, value_ptr, "upsert") + }) } fn up_target_value_sync<'local>( - mut env: JNIEnv<'local>, + env: &mut Env<'local>, surreal_ptr: jlong, target: JString<'local>, up_type: jint, @@ -1384,47 +1474,51 @@ fn up_target_value_sync<'local>( up: &str, ) -> jlong { // Retrieve the Surreal instance - let surreal = get_surreal_ref!(&mut env, surreal_ptr, || 0); + let surreal = get_surreal_ref!(env, surreal_ptr, || 0); // Build the parameters - let target = get_rust_string!(&mut env, target, || 0); + let target = get_rust_string!(env, target, || 0); // Get the value - let value = get_value_mut_instance!(&mut env, value_ptr, || 0); + let value = get_value_mut_instance!(env, value_ptr, || 0); // Check the up type - let up_type = convert_up_type!(&mut env, up_type, || 0); + let up_type = convert_up_type!(env, up_type, || 0); // Execute the query let query = format!("{up} {} {up_type} $val", target); let params = BTreeMap::from([("val".to_string(), value.clone())]); let res = surrealdb_query(surreal, &query, Some(params)); // Check the result - let mut response = check_query_result!(&mut env, res, || 0); + let mut response = check_query_result!(env, res, || 0); // There is only one statement - let result: Value = take_one_result!(&mut env, response, || 0); + let result: Value = take_one_result!(env, response, || 0); // Return tne sync iterator return_value_array_iter_sync!(result); // Otherwise throw an error - return_unexpected_result!(&mut env, result.to_sql(), || 0) + return_unexpected_result!(env, result.to_sql(), || 0) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_updateTargetValueSync<'local>( - env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, target: JString<'local>, up_type: jint, value_ptr: jlong, ) -> jlong { - up_target_value_sync(env, surreal_ptr, target, up_type, value_ptr, "update") + with_env_body!(env, env, { + up_target_value_sync(env, surreal_ptr, target, up_type, value_ptr, "update") + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Surreal_upsertTargetValueSync<'local>( - env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, surreal_ptr: jlong, target: JString<'local>, up_type: jint, value_ptr: jlong, ) -> jlong { - up_target_value_sync(env, surreal_ptr, target, up_type, value_ptr, "upsert") + with_env_body!(env, env, { + up_target_value_sync(env, surreal_ptr, target, up_type, value_ptr, "upsert") + }) } diff --git a/src/main/rust/syncentryiterator.rs b/src/main/rust/syncentryiterator.rs index dc5c2003..21ad26a5 100644 --- a/src/main/rust/syncentryiterator.rs +++ b/src/main/rust/syncentryiterator.rs @@ -1,34 +1,39 @@ use std::sync::Arc; +use crate::with_env_body; use jni::objects::JClass; use jni::sys::{jboolean, jlong}; -use jni::JNIEnv; +use jni::EnvUnowned; use crate::error::SurrealError; use crate::{get_sync_entry_iterator_instance, JniTypes}; #[no_mangle] pub extern "system" fn Java_com_surrealdb_SynchronizedEntryIterator_hasNext<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let iter = get_sync_entry_iterator_instance!(&mut env, ptr, || false as jboolean); - let iter = iter.lock(); - (iter.len() > 0) as jboolean + with_env_body!(env, env, { + let iter = get_sync_entry_iterator_instance!(env, ptr, || false as jboolean); + let iter = iter.lock(); + (iter.len() > 0) as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_SynchronizedEntryIterator_next<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let iter = get_sync_entry_iterator_instance!(&mut env, ptr, || 0); - let mut iter = iter.lock(); - if let Some((key, value)) = iter.next() { - JniTypes::new_key_value(key, value.into()) - } else { - SurrealError::NoSuchElementException.exception(&mut env, || 0) - } + with_env_body!(env, env, { + let iter = get_sync_entry_iterator_instance!(env, ptr, || 0); + let mut iter = iter.lock(); + if let Some((key, value)) = iter.next() { + JniTypes::new_key_value(key, value.into()) + } else { + SurrealError::NoSuchElementException.exception(env, || 0) + } + }) } diff --git a/src/main/rust/syncvalueiterator.rs b/src/main/rust/syncvalueiterator.rs index f69a0cc0..0a10f0c0 100644 --- a/src/main/rust/syncvalueiterator.rs +++ b/src/main/rust/syncvalueiterator.rs @@ -1,34 +1,39 @@ use std::sync::Arc; +use crate::with_env_body; use jni::objects::JClass; use jni::sys::{jboolean, jlong}; -use jni::JNIEnv; +use jni::EnvUnowned; use crate::error::SurrealError; use crate::{get_sync_value_iterator_instance, JniTypes}; #[no_mangle] pub extern "system" fn Java_com_surrealdb_SynchronizedValueIterator_hasNext<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let iter = get_sync_value_iterator_instance!(&mut env, ptr, || false as jboolean); - let iter = iter.lock(); - (iter.len() > 0) as jboolean + with_env_body!(env, env, { + let iter = get_sync_value_iterator_instance!(env, ptr, || false as jboolean); + let iter = iter.lock(); + (iter.len() > 0) as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_SynchronizedValueIterator_next<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let iter = get_sync_value_iterator_instance!(&mut env, ptr, || 0); - let mut iter = iter.lock(); - if let Some(v) = iter.next() { - JniTypes::new_value(v.into()) - } else { - SurrealError::NoSuchElementException.exception(&mut env, || 0) - } + with_env_body!(env, env, { + let iter = get_sync_value_iterator_instance!(env, ptr, || 0); + let mut iter = iter.lock(); + if let Some(v) = iter.next() { + JniTypes::new_value(v.into()) + } else { + SurrealError::NoSuchElementException.exception(env, || 0) + } + }) } diff --git a/src/main/rust/transaction.rs b/src/main/rust/transaction.rs index bde0b3c5..649d0e4c 100644 --- a/src/main/rust/transaction.rs +++ b/src/main/rust/transaction.rs @@ -3,20 +3,21 @@ use std::sync::Arc; use crate::error::SurrealError; +use crate::with_env_body; use crate::{ build_params_map, check_query_result, get_rust_string, get_transaction_ref, release_instance, take_instance, JniTypes, TOKIO_RUNTIME, }; use jni::objects::{JClass, JLongArray, JObjectArray, JString}; use jni::sys::{jboolean, jlong}; -use jni::JNIEnv; +use jni::EnvUnowned; use parking_lot::Mutex; use surrealdb::engine::any::Any; use surrealdb::method::Transaction; #[no_mangle] pub extern "system" fn Java_com_surrealdb_Transaction_nativeDeleteInstance<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) { @@ -25,65 +26,73 @@ pub extern "system" fn Java_com_surrealdb_Transaction_nativeDeleteInstance<'loca #[no_mangle] pub extern "system" fn Java_com_surrealdb_Transaction_commit<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - // take_instance must be first: Transaction::commit(self) takes ownership. On commit failure we drop the txn (rollback). - let txn = match take_instance::>(ptr, JniTypes::Transaction) { - Ok(t) => t, - Err(e) => return e.exception(&mut env, || false as jboolean), - }; - if let Err(e) = TOKIO_RUNTIME.block_on(async { txn.commit().await }) { - return SurrealError::from(e).exception(&mut env, || false as jboolean); - } - true as jboolean + with_env_body!(env, env, { + // take_instance must be first: Transaction::commit(self) takes ownership. On commit failure we drop the txn (rollback). + let txn = match take_instance::>(ptr, JniTypes::Transaction) { + Ok(t) => t, + Err(e) => return e.exception(env, || false as jboolean), + }; + if let Err(e) = TOKIO_RUNTIME.block_on(async { txn.commit().await }) { + return SurrealError::from(e).exception(env, || false as jboolean); + } + true as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Transaction_cancel<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - // take_instance must be first: Transaction::cancel(self) takes ownership. On cancel failure we drop the txn. - let txn = match take_instance::>(ptr, JniTypes::Transaction) { - Ok(t) => t, - Err(e) => return e.exception(&mut env, || false as jboolean), - }; - if let Err(e) = TOKIO_RUNTIME.block_on(async { txn.cancel().await }) { - return SurrealError::from(e).exception(&mut env, || false as jboolean); - } - true as jboolean + with_env_body!(env, env, { + // take_instance must be first: Transaction::cancel(self) takes ownership. On cancel failure we drop the txn. + let txn = match take_instance::>(ptr, JniTypes::Transaction) { + Ok(t) => t, + Err(e) => return e.exception(env, || false as jboolean), + }; + if let Err(e) = TOKIO_RUNTIME.block_on(async { txn.cancel().await }) { + return SurrealError::from(e).exception(env, || false as jboolean); + } + true as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Transaction_query<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, query: JString<'local>, ) -> jlong { - let txn = get_transaction_ref!(&mut env, ptr, || 0); - let query = get_rust_string!(&mut env, query, || 0); - let res = TOKIO_RUNTIME.block_on(async { txn.query(&query).await }); - let res = check_query_result!(&mut env, res, || 0); - JniTypes::new_response(Arc::new(Mutex::new(res))) + with_env_body!(env, env, { + let txn = get_transaction_ref!(env, ptr, || 0); + let query = get_rust_string!(env, query, || 0); + let res = TOKIO_RUNTIME.block_on(async { txn.query(&query).await }); + let res = check_query_result!(env, res, || 0); + JniTypes::new_response(Arc::new(Mutex::new(res))) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Transaction_queryWithBindings<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, query: JString<'local>, - params_keys: JObjectArray<'local>, + params_keys: JObjectArray<'local, JString<'local>>, params_values: JLongArray<'local>, ) -> jlong { - let txn = get_transaction_ref!(&mut env, ptr, || 0); - let query = get_rust_string!(&mut env, query, || 0); - let params_map = build_params_map!(&mut env, params_keys, params_values, || 0); - let res = TOKIO_RUNTIME.block_on(async { txn.query(&query).bind(params_map).await }); - let res = check_query_result!(&mut env, res, || 0); - JniTypes::new_response(Arc::new(Mutex::new(res))) + with_env_body!(env, env, { + let txn = get_transaction_ref!(env, ptr, || 0); + let query = get_rust_string!(env, query, || 0); + let params_map = build_params_map!(env, params_keys, params_values, || 0); + let res = TOKIO_RUNTIME.block_on(async { txn.query(&query).bind(params_map).await }); + let res = check_query_result!(env, res, || 0); + JniTypes::new_response(Arc::new(Mutex::new(res))) + }) } diff --git a/src/main/rust/value.rs b/src/main/rust/value.rs index b9223929..d240485a 100644 --- a/src/main/rust/value.rs +++ b/src/main/rust/value.rs @@ -3,9 +3,10 @@ use std::ops::Bound; use std::ptr::null_mut; use std::sync::Arc; +use crate::with_env_body; use jni::objects::{AsJArrayRaw, JClass}; use jni::sys::{jboolean, jbyteArray, jdouble, jint, jlong, jlongArray, jstring}; -use jni::JNIEnv; +use jni::EnvUnowned; use surrealdb::types::{Number, ToSql, Value}; use crate::error::SurrealError; @@ -13,7 +14,7 @@ use crate::{get_value_instance, new_jlong_array, new_string, release_instance, J #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_deleteInstance<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { @@ -23,466 +24,542 @@ pub extern "system" fn Java_com_surrealdb_Value_deleteInstance<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isArray<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_array() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_array() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getArray<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if value.is_array() { - JniTypes::new_value(value) - } else { - SurrealError::NullPointerException("Array").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if value.is_array() { + JniTypes::new_value(value) + } else { + SurrealError::NullPointerException("Array").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isObject<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_object() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_object() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getObject<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if value.is_object() { - JniTypes::new_value(value) - } else { - SurrealError::NullPointerException("Object").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if value.is_object() { + JniTypes::new_value(value) + } else { + SurrealError::NullPointerException("Object").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isBoolean<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_bool() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_bool() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getBoolean<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Bool(b) = value.as_ref() { - *b as jboolean - } else { - SurrealError::NullPointerException("Boolean").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false); + if let Value::Bool(b) = value.as_ref() { + *b as jboolean + } else { + SurrealError::NullPointerException("Boolean").exception(env, || false) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isBytes<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_bytes() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_bytes() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getBytes<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jbyteArray { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::Bytes(bytes) = value.as_ref() { - match env.byte_array_from_slice(bytes) { - Ok(a) => a.as_jarray_raw(), - Err(_) => null_mut(), + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::Bytes(bytes) = value.as_ref() { + match env.byte_array_from_slice(bytes) { + Ok(a) => a.as_jarray_raw(), + Err(_) => null_mut(), + } + } else { + SurrealError::NullPointerException("Bytes").exception(env, null_mut) } - } else { - SurrealError::NullPointerException("Bytes").exception(&mut env, null_mut) - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isLong<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_int() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_int() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getLong<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Number(Number::Int(i)) = value.as_ref() { - *i - } else { - SurrealError::NullPointerException("Long").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Number(Number::Int(i)) = value.as_ref() { + *i + } else { + SurrealError::NullPointerException("Long").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isDateTime<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_datetime() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_datetime() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getDateTime<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlongArray { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::Datetime(dt) = value.as_ref() { - let seconds = dt.timestamp(); - let nanos = dt.timestamp_subsec_nanos() as i64; - let buf: [jlong; 2] = [seconds, nanos]; - new_jlong_array!(&mut env, &buf, null_mut) - } else { - SurrealError::NullPointerException("Geometry").exception(&mut env, null_mut) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::Datetime(dt) = value.as_ref() { + let seconds = dt.timestamp(); + let nanos = dt.timestamp_subsec_nanos() as i64; + let buf: [jlong; 2] = [seconds, nanos]; + new_jlong_array!(env, &buf, null_mut) + } else { + SurrealError::NullPointerException("Geometry").exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isDuration<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_duration() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_duration() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getDuration<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Duration(d) = value.as_ref() { - d.as_millis() as jlong - } else { - SurrealError::NullPointerException("Geometry").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Duration(d) = value.as_ref() { + d.as_millis() as jlong + } else { + SurrealError::NullPointerException("Geometry").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isBigDecimal<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_decimal() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_decimal() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getBigDecimal<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::Number(Number::Decimal(d)) = value.as_ref() { - new_string!(&mut env, &d.to_string(), null_mut) - } else { - SurrealError::NullPointerException("BigDecimal").exception(&mut env, null_mut) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::Number(Number::Decimal(d)) = value.as_ref() { + new_string!(env, &d.to_string(), null_mut) + } else { + SurrealError::NullPointerException("BigDecimal").exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isDouble<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_float() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_float() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getDouble<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jdouble { - let value = get_value_instance!(&mut env, ptr, || 0.0); - if let Value::Number(Number::Float(f)) = value.as_ref() { - *f - } else { - SurrealError::NullPointerException("Double").exception(&mut env, || 0.0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0.0); + if let Value::Number(Number::Float(f)) = value.as_ref() { + *f + } else { + SurrealError::NullPointerException("Double").exception(env, || 0.0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isGeometry<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_geometry() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_geometry() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getGeometry<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Geometry(_) = value.as_ref() { - JniTypes::new_value(value) - } else { - SurrealError::NullPointerException("Geometry").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Geometry(_) = value.as_ref() { + JniTypes::new_value(value) + } else { + SurrealError::NullPointerException("Geometry").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isNone<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_none() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_none() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isNull<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_null() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_null() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_string() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_string() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::String(s) = value.as_ref() { - new_string!(&mut env, s.clone(), null_mut) - } else { - SurrealError::NullPointerException("String").exception(&mut env, null_mut) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::String(s) = value.as_ref() { + new_string!(env, s.clone(), null_mut) + } else { + SurrealError::NullPointerException("String").exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isRecordId<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - matches!(value.as_ref(), Value::RecordId(_)) as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + matches!(value.as_ref(), Value::RecordId(_)) as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getRecordId<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::RecordId(_) = value.as_ref() { - JniTypes::new_value(value) - } else { - SurrealError::NullPointerException("RecordId").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::RecordId(_) = value.as_ref() { + JniTypes::new_value(value) + } else { + SurrealError::NullPointerException("RecordId").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isUuid<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_uuid() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_uuid() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getUuid<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::Uuid(uuid) = value.as_ref() { - new_string!(&mut env, uuid.to_string(), null_mut) - } else { - SurrealError::NullPointerException("Uuid").exception(&mut env, null_mut) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::Uuid(uuid) = value.as_ref() { + new_string!(env, uuid.to_string(), null_mut) + } else { + SurrealError::NullPointerException("Uuid").exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isFile<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_file() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_file() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getFile<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if value.is_file() { - JniTypes::new_value(value) - } else { - SurrealError::NullPointerException("File").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if value.is_file() { + JniTypes::new_value(value) + } else { + SurrealError::NullPointerException("File").exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isRange<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_range() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_range() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getRangeStart<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Range(r) = value.as_ref() { - match &r.start { - Bound::Included(v) | Bound::Excluded(v) => JniTypes::new_value(Arc::new(v.clone())), - Bound::Unbounded => 0, + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Range(r) = value.as_ref() { + match &r.start { + Bound::Included(v) | Bound::Excluded(v) => JniTypes::new_value(Arc::new(v.clone())), + Bound::Unbounded => 0, + } + } else { + SurrealError::NullPointerException("Range").exception(env, || 0) } - } else { - SurrealError::NullPointerException("Range").exception(&mut env, || 0) - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getRangeEnd<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::Range(r) = value.as_ref() { - match &r.end { - Bound::Included(v) | Bound::Excluded(v) => JniTypes::new_value(Arc::new(v.clone())), - Bound::Unbounded => 0, + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::Range(r) = value.as_ref() { + match &r.end { + Bound::Included(v) | Bound::Excluded(v) => JniTypes::new_value(Arc::new(v.clone())), + Bound::Unbounded => 0, + } + } else { + SurrealError::NullPointerException("Range").exception(env, || 0) } - } else { - SurrealError::NullPointerException("Range").exception(&mut env, || 0) - } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_isTable<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let value = get_value_instance!(&mut env, ptr, || false as jboolean); - value.is_table() as jboolean + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || false as jboolean); + value.is_table() as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_getTable<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - if let Value::Table(t) = value.as_ref() { - new_string!(&mut env, t.as_str().to_string(), null_mut) - } else { - SurrealError::NullPointerException("Table").exception(&mut env, null_mut) - } + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + if let Value::Table(t) = value.as_ref() { + new_string!(env, t.as_str(), null_mut) + } else { + SurrealError::NullPointerException("Table").exception(env, null_mut) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_toString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_instance!(&mut env, ptr, null_mut); - let s = value.to_sql(); - new_string!(&mut env, s, null_mut) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, null_mut); + let s = value.to_sql(); + new_string!(env, s, null_mut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_hashCode<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jint { - let value = get_value_instance!(&mut env, ptr, || 0); - let mut hasher = DefaultHasher::new(); - value.hash(&mut hasher); - let hash64 = hasher.finish(); - (hash64 & 0xFFFFFFFF) as jint + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + let mut hasher = DefaultHasher::new(); + value.hash(&mut hasher); + let hash64 = hasher.finish(); + (hash64 & 0xFFFFFFFF) as jint + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_Value_equals<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr1: jlong, ptr2: jlong, ) -> jboolean { - let v1 = get_value_instance!(&mut env, ptr1, || false as jboolean); - let v2 = get_value_instance!(&mut env, ptr2, || false as jboolean); - v1.eq(&v2) as jboolean + with_env_body!(env, env, { + let v1 = get_value_instance!(env, ptr1, || false as jboolean); + let v2 = get_value_instance!(env, ptr2, || false as jboolean); + v1.eq(&v2) as jboolean + }) } diff --git a/src/main/rust/valueiterator.rs b/src/main/rust/valueiterator.rs index 14cc7be3..a5757c7c 100644 --- a/src/main/rust/valueiterator.rs +++ b/src/main/rust/valueiterator.rs @@ -1,30 +1,35 @@ +use crate::with_env_body; use jni::objects::JClass; use jni::sys::{jboolean, jlong}; -use jni::JNIEnv; +use jni::EnvUnowned; use crate::error::SurrealError; use crate::{get_value_iterator_instance, get_value_iterator_mut_instance, JniTypes}; #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueIterator_hasNext<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jboolean { - let iter = get_value_iterator_instance!(&mut env, ptr, || false as jboolean); - (iter.len() > 0) as jboolean + with_env_body!(env, env, { + let iter = get_value_iterator_instance!(env, ptr, || false as jboolean); + (iter.len() > 0) as jboolean + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueIterator_next<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let iter = get_value_iterator_mut_instance!(&mut env, ptr, || 0); - if let Some(v) = iter.next() { - JniTypes::new_value(v.into()) - } else { - SurrealError::NullPointerException("Value").exception(&mut env, || 0) - } + with_env_body!(env, env, { + let iter = get_value_iterator_mut_instance!(env, ptr, || 0); + if let Some(v) = iter.next() { + JniTypes::new_value(v.into()) + } else { + SurrealError::NullPointerException("Value").exception(env, || 0) + } + }) } diff --git a/src/main/rust/valuemut.rs b/src/main/rust/valuemut.rs index e258e547..dbe87bac 100644 --- a/src/main/rust/valuemut.rs +++ b/src/main/rust/valuemut.rs @@ -3,10 +3,11 @@ use std::hash::{DefaultHasher, Hash, Hasher}; use std::ptr::null_mut; use std::str::FromStr; +use crate::with_env_body; use chrono::DateTime; use jni::objects::{JClass, JLongArray, JString}; use jni::sys::{jboolean, jdouble, jint, jlong, jstring}; -use jni::JNIEnv; +use jni::EnvUnowned; use rust_decimal::Decimal; use surrealdb::types::{ Array, Datetime, Duration, File, Number, Object, Table, ToSql, Uuid, Value, @@ -20,7 +21,7 @@ use crate::{ #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newNone<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, ) -> jlong { create_instance(Value::None, JniTypes::ValueMut) @@ -28,7 +29,7 @@ pub extern "system" fn Java_com_surrealdb_ValueMut_newNone<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newNull<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, ) -> jlong { create_instance(Value::Null, JniTypes::ValueMut) @@ -36,18 +37,20 @@ pub extern "system" fn Java_com_surrealdb_ValueMut_newNull<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, s: JString<'local>, ) -> jlong { - let s = get_rust_string!(&mut env, s, || 0); - let value = Value::String(s); - create_instance(value, JniTypes::ValueMut) + with_env_body!(env, env, { + let s = get_rust_string!(env, s, || 0); + let value = Value::String(s); + create_instance(value, JniTypes::ValueMut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newLong<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, l: jlong, ) -> jlong { @@ -57,7 +60,7 @@ pub extern "system" fn Java_com_surrealdb_ValueMut_newLong<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newDouble<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, d: jdouble, ) -> jlong { @@ -67,32 +70,34 @@ pub extern "system" fn Java_com_surrealdb_ValueMut_newDouble<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newBoolean<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, b: jboolean, ) -> jlong { - let value = Value::Bool(b == 1); + let value = Value::Bool(b); JniTypes::new_value_mut(value) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newDecimal<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, s: JString<'local>, ) -> jlong { - let s = get_rust_string!(&mut env, s, || 0); - let d = match Decimal::from_str(&s) { - Ok(d) => d, - Err(e) => return SurrealError::SurrealDBJni(e.to_string()).exception(&mut env, || 0), - }; - let value = Value::Number(Number::Decimal(d)); - JniTypes::new_value_mut(value) + with_env_body!(env, env, { + let s = get_rust_string!(env, s, || 0); + let d = match Decimal::from_str(&s) { + Ok(d) => d, + Err(e) => return SurrealError::SurrealDBJni(e.to_string()).exception(env, || 0), + }; + let value = Value::Number(Number::Decimal(d)); + JniTypes::new_value_mut(value) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newDuration<'local>( - _env: JNIEnv<'local>, + _env: EnvUnowned<'local>, _class: JClass<'local>, millis: jlong, ) -> jlong { @@ -102,189 +107,215 @@ pub extern "system" fn Java_com_surrealdb_ValueMut_newDuration<'local>( #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newDatetime<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, seconds: jlong, nanos: jlong, ) -> jlong { - if let Some(d) = DateTime::from_timestamp(seconds, nanos as u32) { - let value = Value::Datetime(Datetime::from(d)); - JniTypes::new_value_mut(value) - } else { - SurrealError::SurrealDBJni(format!( - "Can't create the Datetime from seconds: {seconds}, nanos: {nanos}" - )) - .exception(&mut env, || 0) - } + with_env_body!(env, env, { + if let Some(d) = DateTime::from_timestamp(seconds, nanos as u32) { + let value = Value::Datetime(Datetime::from(d)); + JniTypes::new_value_mut(value) + } else { + SurrealError::SurrealDBJni(format!( + "Can't create the Datetime from seconds: {seconds}, nanos: {nanos}" + )) + .exception(env, || 0) + } + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newId<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if let Value::RecordId(record_id) = value.as_ref() { - // Extract just the key part from the RecordId, not the whole RecordId with empty table - let key_value = match &record_id.key { - surrealdb::types::RecordIdKey::Number(n) => Value::Number(Number::Int(*n)), - surrealdb::types::RecordIdKey::String(s) => Value::String(s.clone()), - surrealdb::types::RecordIdKey::Uuid(u) => Value::Uuid(*u), - surrealdb::types::RecordIdKey::Array(a) => Value::Array(a.clone()), - surrealdb::types::RecordIdKey::Object(o) => Value::Object(o.clone()), - surrealdb::types::RecordIdKey::Range(_) => { - return SurrealError::SurrealDBJni( - "Range-based IDs are not supported for Id serialization".to_string(), - ) - .exception(&mut env, || 0); - } - }; - return JniTypes::new_value_mut(key_value); - } - SurrealError::NullPointerException("ID").exception(&mut env, || 0) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if let Value::RecordId(record_id) = value.as_ref() { + // Extract just the key part from the RecordId, not the whole RecordId with empty table + let key_value = match &record_id.key { + surrealdb::types::RecordIdKey::Number(n) => Value::Number(Number::Int(*n)), + surrealdb::types::RecordIdKey::String(s) => Value::String(s.clone()), + surrealdb::types::RecordIdKey::Uuid(u) => Value::Uuid(*u), + surrealdb::types::RecordIdKey::Array(a) => Value::Array(a.clone()), + surrealdb::types::RecordIdKey::Object(o) => Value::Object(o.clone()), + surrealdb::types::RecordIdKey::Range(_) => { + return SurrealError::SurrealDBJni( + "Range-based IDs are not supported for Id serialization".to_string(), + ) + .exception(env, || 0); + } + }; + return JniTypes::new_value_mut(key_value); + } + SurrealError::NullPointerException("ID").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newUuid<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, s: JString<'local>, ) -> jlong { - let s = get_rust_string!(&mut env, s, || 0); - if let Ok(uuid) = Uuid::from_str(&s) { - let value = Value::Uuid(uuid); - return create_instance(value, JniTypes::ValueMut); - } - SurrealError::NullPointerException("Uuid").exception(&mut env, || 0) + with_env_body!(env, env, { + let s = get_rust_string!(env, s, || 0); + if let Ok(uuid) = Uuid::from_str(&s) { + let value = Value::Uuid(uuid); + return create_instance(value, JniTypes::ValueMut); + } + SurrealError::NullPointerException("Uuid").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newRecordId<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if matches!(value.as_ref(), Value::RecordId(_)) { - return JniTypes::new_value_mut(value.as_ref().clone()); - } - SurrealError::NullPointerException("RecordId").exception(&mut env, || 0) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if matches!(value.as_ref(), Value::RecordId(_)) { + return JniTypes::new_value_mut(value.as_ref().clone()); + } + SurrealError::NullPointerException("RecordId").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newArray<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if matches!(value.as_ref(), Value::Array(_)) { - return JniTypes::new_value_mut(value.as_ref().clone()); - } - SurrealError::NullPointerException("Array").exception(&mut env, || 0) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if matches!(value.as_ref(), Value::Array(_)) { + return JniTypes::new_value_mut(value.as_ref().clone()); + } + SurrealError::NullPointerException("Array").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newArrayOf<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptrs: JLongArray<'local>, ) -> jlong { - let ptrs = get_long_array!(&mut env, &ptrs, || 0); - let mut values = Vec::with_capacity(ptrs.len()); - for ptr in ptrs { - let value = take_value_mut_instance!(&mut env, ptr, || 0); - values.push(value); - } - let value = Value::Array(Array::from(values)); - JniTypes::new_value_mut(value) + with_env_body!(env, env, { + let ptrs = get_long_array!(env, &ptrs, || 0); + let mut values = Vec::with_capacity(ptrs.len()); + for ptr in ptrs { + let value = take_value_mut_instance!(env, ptr, || 0); + values.push(value); + } + let value = Value::Array(Array::from(values)); + JniTypes::new_value_mut(value) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newObject<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jlong { - let value = get_value_instance!(&mut env, ptr, || 0); - if matches!(value.as_ref(), Value::Object(_)) { - return JniTypes::new_value_mut(value.as_ref().clone()); - } - SurrealError::NullPointerException("Object").exception(&mut env, || 0) + with_env_body!(env, env, { + let value = get_value_instance!(env, ptr, || 0); + if matches!(value.as_ref(), Value::Object(_)) { + return JniTypes::new_value_mut(value.as_ref().clone()); + } + SurrealError::NullPointerException("Object").exception(env, || 0) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newObjectOf<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptrs: JLongArray<'local>, ) -> jlong { - let ptrs = get_long_array!(&mut env, &ptrs, || 0); - let mut map = BTreeMap::new(); - for ptr in ptrs { - let (key, value) = take_entry_mut_instance!(&mut env, ptr, || 0); - map.insert(key, value); - } - let value = Value::Object(Object::from(map)); - create_instance(value, JniTypes::ValueMut) + with_env_body!(env, env, { + let ptrs = get_long_array!(env, &ptrs, || 0); + let mut map = BTreeMap::new(); + for ptr in ptrs { + let (key, value) = take_entry_mut_instance!(env, ptr, || 0); + map.insert(key, value); + } + let value = Value::Object(Object::from(map)); + create_instance(value, JniTypes::ValueMut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newFile<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, bucket: JString<'local>, key: JString<'local>, ) -> jlong { - let bucket = get_rust_string!(&mut env, bucket, || 0); - let key = get_rust_string!(&mut env, key, || 0); - let value = Value::File(File::new(bucket, key)); - JniTypes::new_value_mut(value) + with_env_body!(env, env, { + let bucket = get_rust_string!(env, bucket, || 0); + let key = get_rust_string!(env, key, || 0); + let value = Value::File(File::new(bucket, key)); + JniTypes::new_value_mut(value) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_newTable<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, name: JString<'local>, ) -> jlong { - let name = get_rust_string!(&mut env, name, || 0); - let value = Value::Table(Table::new(name)); - JniTypes::new_value_mut(value) + with_env_body!(env, env, { + let name = get_rust_string!(env, name, || 0); + let value = Value::Table(Table::new(name)); + JniTypes::new_value_mut(value) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_toString<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jstring { - let value = get_value_mut_instance!(&mut env, ptr, null_mut); - new_string!(&mut env, value.to_sql(), null_mut) + with_env_body!(env, env, { + let value = get_value_mut_instance!(env, ptr, null_mut); + new_string!(env, value.to_sql(), null_mut) + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_hashCode<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr: jlong, ) -> jint { - let value = get_value_mut_instance!(&mut env, ptr, || 0); - let mut hasher = DefaultHasher::new(); - value.hash(&mut hasher); - let hash64 = hasher.finish(); - (hash64 & 0xFFFFFFFF) as jint + with_env_body!(env, env, { + let value = get_value_mut_instance!(env, ptr, || 0); + let mut hasher = DefaultHasher::new(); + value.hash(&mut hasher); + let hash64 = hasher.finish(); + (hash64 & 0xFFFFFFFF) as jint + }) } #[no_mangle] pub extern "system" fn Java_com_surrealdb_ValueMut_equals<'local>( - mut env: JNIEnv<'local>, + mut env: EnvUnowned<'local>, _class: JClass<'local>, ptr1: jlong, ptr2: jlong, ) -> jboolean { - let v1 = get_value_mut_instance!(&mut env, ptr1, || false as jboolean); - let v2 = get_value_mut_instance!(&mut env, ptr2, || false as jboolean); - v1.eq(v2) as jboolean + with_env_body!(env, env, { + let v1 = get_value_mut_instance!(env, ptr1, || false as jboolean); + let v2 = get_value_mut_instance!(env, ptr2, || false as jboolean); + v1.eq(v2) as jboolean + }) }