feat: simplify IntoJava family

basically it's all IntoJava, but there's another trait layer,
IntoJavaObject, which downstream library users should implement. Every
IntoJavaObject automatically implements IntoJava too
This commit is contained in:
əlemi 2024-09-23 17:59:31 +02:00
parent 50be86d2a4
commit 5e69eca9d6
Signed by: alemi
GPG key ID: A4895B84D311642C
8 changed files with 187 additions and 210 deletions

View file

@ -1,5 +1,5 @@
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::TokenStreamExt; use quote::{ToTokens, TokenStreamExt};
use syn::Ident; use syn::Ident;
pub(crate) struct ArgumentOptions { pub(crate) struct ArgumentOptions {
@ -109,7 +109,7 @@ impl ArgumentOptions {
}, },
}; };
}); });
incoming.append_all(quote::quote!( mut #pat: <#ty as jni_toolbox::FromJava<'local>>::T,)); incoming.append_all(quote::quote!( #pat: <#ty as jni_toolbox::FromJava<'local>>::From,));
forwarding.append_all(quote::quote!( #new_pat,)); forwarding.append_all(quote::quote!( #new_pat,));
} }
@ -121,3 +121,25 @@ struct SingleArgument {
pat: syn::Ident, pat: syn::Ident,
ty: Box<syn::Type>, ty: Box<syn::Type>,
} }
#[allow(unused)]
fn bare_type(t: syn::Type) -> TokenStream {
match t {
syn::Type::Array(x) => bare_type(*x.elem),
syn::Type::BareFn(f) => f.to_token_stream(),
syn::Type::Group(x) => bare_type(*x.elem),
syn::Type::ImplTrait(t) => t.to_token_stream(),
syn::Type::Infer(x) => x.to_token_stream(),
syn::Type::Macro(x) => x.to_token_stream(),
syn::Type::Never(x) => x.to_token_stream(),
syn::Type::Paren(p) => bare_type(*p.elem),
syn::Type::Path(p) => p.to_token_stream(),
syn::Type::Ptr(x) => bare_type(*x.elem),
syn::Type::Reference(r) => bare_type(*r.elem),
syn::Type::Slice(s) => bare_type(*s.elem),
syn::Type::TraitObject(t) => t.to_token_stream(),
syn::Type::Tuple(x) => x.to_token_stream(),
syn::Type::Verbatim(x) => x.to_token_stream(),
_ => todo!(),
}
}

View file

@ -4,7 +4,6 @@ pub(crate) struct AttrsOptions {
pub(crate) package: String, pub(crate) package: String,
pub(crate) class: String, pub(crate) class: String,
pub(crate) exception: Option<String>, pub(crate) exception: Option<String>,
pub(crate) return_pointer: bool,
} }
impl AttrsOptions { impl AttrsOptions {
@ -14,7 +13,6 @@ impl AttrsOptions {
let mut package = None; let mut package = None;
let mut class = None; let mut class = None;
let mut exception = None; let mut exception = None;
let mut return_pointer = false;
for attr in attrs { for attr in attrs {
match what_next { match what_next {
@ -24,7 +22,7 @@ impl AttrsOptions {
"package" => what_next = WhatNext::Package, "package" => what_next = WhatNext::Package,
"class" => what_next = WhatNext::Class, "class" => what_next = WhatNext::Class,
"exception" => what_next = WhatNext::Exception, "exception" => what_next = WhatNext::Exception,
"ptr" => return_pointer = true, "ptr" => {}, // accepted for backwards compatibility
_ => return Err(syn::Error::new(Span::call_site(), "unexpected attribute on macro: {attr}")), _ => return Err(syn::Error::new(Span::call_site(), "unexpected attribute on macro: {attr}")),
} }
} }
@ -53,7 +51,7 @@ impl AttrsOptions {
let Some(package) = package else { return Err(syn::Error::new(Span::call_site(), "missing required attribute 'package'")) }; let Some(package) = package else { return Err(syn::Error::new(Span::call_site(), "missing required attribute 'package'")) };
let Some(class) = class else { return Err(syn::Error::new(Span::call_site(), "missing required attribute 'class'")) }; let Some(class) = class else { return Err(syn::Error::new(Span::call_site(), "missing required attribute 'class'")) };
Ok(Self { package, class, exception, return_pointer }) Ok(Self { package, class, exception })
} }
} }

View file

@ -6,13 +6,16 @@ use syn::{ReturnType, Type};
pub(crate) struct ReturnOptions { pub(crate) struct ReturnOptions {
pub(crate) ty: Option<Box<Type>>, pub(crate) ty: Option<Box<Type>>,
pub(crate) result: bool, pub(crate) result: bool,
pub(crate) pointer: bool,
pub(crate) void: bool, pub(crate) void: bool,
} }
const PRIMITIVE_TYPES: [&str; 7] = ["i8", "i16", "i32", "i64", "f32", "f64", "bool"];
impl ReturnOptions { impl ReturnOptions {
pub(crate) fn parse_signature(ret: &ReturnType) -> Result<Self, syn::Error> { pub(crate) fn parse_signature(ret: &ReturnType) -> Result<Self, syn::Error> {
match ret { match ret {
syn::ReturnType::Default => Ok(Self { ty: None, result: false, void: true }), syn::ReturnType::Default => Ok(Self { ty: None, result: false, void: true, pointer: false }),
syn::ReturnType::Type(_tok, ty) => match *ty.clone() { syn::ReturnType::Type(_tok, ty) => match *ty.clone() {
syn::Type::Path(path) => { syn::Type::Path(path) => {
let Some(last) = path.path.segments.last() else { let Some(last) = path.path.segments.last() else {
@ -26,14 +29,19 @@ impl ReturnOptions {
syn::PathArguments::AngleBracketed(ref generics) => for generic in generics.args.iter() { syn::PathArguments::AngleBracketed(ref generics) => for generic in generics.args.iter() {
match generic { match generic {
syn::GenericArgument::Lifetime(_) => continue, syn::GenericArgument::Lifetime(_) => continue,
syn::GenericArgument::Type(ty) => return Ok(Self { ty: Some(Box::new(ty.clone())), result: true, void: is_void(ty) }), syn::GenericArgument::Type(ty) => {
_ => return Err(syn::Error::new(Span::call_site(), "unexpected type in Result")), // TODO checking by making ty a token stream and then string equals is not exactly great!
let pointer = !PRIMITIVE_TYPES.iter().any(|t| ty.to_token_stream().to_string() == *t);
return Ok(Self { ty: Some(Box::new(ty.clone())), result: true, void: is_void(ty), pointer });
},
_ => return Err(syn::Error::new(Span::call_site(), "unexpected type in Result"))
} }
} }
} }
} }
Ok(Self { ty: Some(Box::new(Type::Path(path.clone()))), result: false, void: false }) let pointer = !PRIMITIVE_TYPES.iter().any(|t| last.ident == t);
Ok(Self { ty: Some(Box::new(Type::Path(path.clone()))), result: false, void: false, pointer })
}, },
_ => Err(syn::Error::new(Span::call_site(), "unsupported return type")), _ => Err(syn::Error::new(Span::call_site(), "unsupported return type")),
}, },
@ -43,13 +51,7 @@ impl ReturnOptions {
pub(crate) fn tokens(&self) -> TokenStream { pub(crate) fn tokens(&self) -> TokenStream {
match &self.ty { // TODO why do we need to invoke syn::Token! macro ??? match &self.ty { // TODO why do we need to invoke syn::Token! macro ???
None => ReturnType::Default.to_token_stream(), None => ReturnType::Default.to_token_stream(),
Some(t) => { Some(t) => quote::quote!( -> <#t as jni_toolbox::IntoJava<'local>>::Ret )
if t.to_token_stream().to_string() == "bool" {// TODO: there DEFINITELY is a better way
quote::quote!( -> jni::sys::jboolean )
} else {
quote::quote!( -> <#t as jni_toolbox::IntoJavaRaw>::T )
}
}
} }
} }
} }

View file

@ -15,7 +15,7 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re
let ret = ReturnOptions::parse_signature(&fn_item.sig.output)?; let ret = ReturnOptions::parse_signature(&fn_item.sig.output)?;
let return_expr = if ret.void { let return_expr = if ret.void {
quote::quote!( () ) quote::quote!( () )
} else if attrs.return_pointer { } else if ret.pointer {
quote::quote!( std::ptr::null_mut() ) quote::quote!( std::ptr::null_mut() )
} else { } else {
quote::quote!( 0 ) quote::quote!( 0 )
@ -25,11 +25,6 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re
let args = ArgumentOptions::parse_args(&fn_item, return_expr.clone())?; let args = ArgumentOptions::parse_args(&fn_item, return_expr.clone())?;
let return_type = ret.tokens(); let return_type = ret.tokens();
let return_type_import = if attrs.return_pointer {
syn::Ident::new("IntoJavaObject", Span::call_site())
} else {
syn::Ident::new("IntoJavaPrimitive", Span::call_site())
};
let name = fn_item.sig.ident.to_string(); let name = fn_item.sig.ident.to_string();
let name_jni = name.replace("_", "_1"); let name_jni = name.replace("_", "_1");
@ -38,55 +33,43 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re
let incoming = args.incoming; let incoming = args.incoming;
// V----------------------------------V // V----------------------------------V
let header = if attrs.return_pointer { let header = quote::quote! {
quote::quote! {
#[no_mangle] #[no_mangle]
#[allow(unused_mut)] #[allow(unused_unit)]
pub extern "system" fn #fn_name<'local>(#incoming) -> jni::sys::jobject
}
} else {
quote::quote! {
#[no_mangle]
#[allow(unused_mut)]
pub extern "system" fn #fn_name<'local>(#incoming) #return_type pub extern "system" fn #fn_name<'local>(#incoming) #return_type
}
}; };
// ^----------------------------------^ let transforming = args.transforming;
let transformations = quote::quote! {
use jni_toolbox::{JniToolboxError, FromJava, IntoJava};
#transforming
};
let env_ident = args.env; let env_ident = args.env;
let forwarding = args.forwarding; let forwarding = args.forwarding;
let transforming = args.transforming; let invocation = quote::quote! {
let body = if ret.result { // wrap errors let mut env_copy = unsafe { #env_ident.unsafe_clone() };
let result = #fn_name_inner(#forwarding);
};
let error_handling = if ret.result {
if let Some(exception) = attrs.exception { if let Some(exception) = attrs.exception {
// V----------------------------------V
quote::quote! { quote::quote! {
{ let ret = match result {
use jni_toolbox::#return_type_import; Ok(x) => x,
use jni_toolbox::{JniToolboxError, FromJava}; Err(e) => match env_copy.throw_new(#exception, format!("{e:?}")) {
#transforming
match #fn_name_inner(#forwarding) {
Ok(ret) => ret,
Err(e) => match #env_ident.throw_new(#exception, format!("{e:?}")) {
Ok(_) => return #return_expr, Ok(_) => return #return_expr,
Err(e) => panic!("error throwing java exception: {e}"), Err(e) => panic!("error throwing java exception: {e}"),
} }
};
} }
}
}
// ^----------------------------------^
} else { } else {
// V----------------------------------V
quote::quote! { quote::quote! {
{ let ret = match result {
use jni_toolbox::#return_type_import; Ok(x) => x,
use jni_toolbox::{JniToolboxError, FromJava, IntoJavaRaw};
// NOTE: this should be SAFE! the cloned env reference lives less than the actual one, we just lack a
// way to get it back from the called function and thus resort to unsafe cloning
let mut env_copy = unsafe { #env_ident.unsafe_clone() };
#transforming
match #fn_name_inner(#forwarding) {
Err(e) => match env_copy.find_class(e.jclass()) { Err(e) => match env_copy.find_class(e.jclass()) {
Err(e) => panic!("error throwing Java exception -- failed resolving error class: {e}"), Err(e) => panic!("error throwing Java exception -- failed resolving error class: {e}"),
Ok(class) => match env_copy.new_string(format!("{e:?}")) { Ok(class) => match env_copy.new_string(format!("{e:?}")) {
@ -100,38 +83,38 @@ pub(crate) fn generate_jni_wrapper(attrs: TokenStream, input: TokenStream) -> Re
}, },
}, },
} }
Ok(ret) => match ret.into_java(&mut env_copy) { };
Ok(fin) => return fin.into_java_raw(),
Err(e) => {
// TODO should we panic instead?
let _ = env_copy.throw_new("java/lang/RuntimeException", format!("{e:?}"));
return #return_expr;
}
},
}
}
} }
} }
} else { } else {
// V----------------------------------V quote::quote!( let ret = result; )
quote::quote! { };
{
use jni_toolbox::#return_type_import;
use jni_toolbox::{JniToolboxError, FromJava, IntoJavaRaw}; let reverse_transformations = quote::quote! {
#transforming match ret.into_java(&mut env_copy) {
match #fn_name_inner(#forwarding).into_java(&mut #env_ident) { Ok(fin) => fin,
Ok(res) => return res.into_java_raw(),
Err(e) => { Err(e) => {
// TODO should we panic instead? // TODO should we panic instead?
let _ = #env_ident.throw_new("java/lang/RuntimeException", format!("{e:?}")); let _ = env_copy.throw_new("java/lang/RuntimeException", format!("{e:?}"));
return #return_expr; #return_expr
},
} }
} }
}
// ^----------------------------------^
}; };
let body = quote::quote! {
{
#transformations
#invocation
#error_handling
#reverse_transformations
}
};
out.append_all(input); out.append_all(input);
out.append_all(header); out.append_all(header);
out.append_all(body); out.append_all(body);

View file

@ -1,27 +1,27 @@
use jni::objects::{JObject, JString}; use jni::objects::{JObject, JObjectArray, JPrimitiveArray, JString, TypeArray};
/// Used in the generated code to have proper type bindings. You probably didn't want /// Used in the generated code to have proper type bindings. You probably didn't want
/// to call this directly. /// to call this directly.
pub fn from_java_static<'j, T: FromJava<'j>>(env: &mut jni::JNIEnv<'j>, val: T::T) -> Result<T, jni::errors::Error> { pub fn from_java_static<'j, T: FromJava<'j>>(env: &mut jni::JNIEnv<'j>, val: T::From) -> Result<T, jni::errors::Error> {
T::from_java(env, val) T::from_java(env, val)
} }
/// Specifies how a Java type should be converted before being fed to Rust. /// Specifies how a Java type should be converted before being fed to Rust.
pub trait FromJava<'j> : Sized { pub trait FromJava<'j> : Sized {
/// The JNI type representing the input. /// The JNI type representing the input.
type T : Sized; type From : Sized;
/// Attempts to convert this Java object into its Rust counterpart. /// Attempts to convert this Java object into its Rust counterpart.
fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result<Self, jni::errors::Error>; fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::From) -> Result<Self, jni::errors::Error>;
} }
macro_rules! auto_from_java { macro_rules! auto_from_java {
($t: ty, $j: ty) => { ($t: ty, $j: ty) => {
impl<'j> FromJava<'j> for $t { impl<'j> FromJava<'j> for $t {
type T = $j; type From = $j;
#[inline] #[inline]
fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result<Self, jni::errors::Error> { fn from_java(_: &mut jni::JNIEnv, value: Self::From) -> Result<Self, jni::errors::Error> {
Ok(value) Ok(value)
} }
} }
@ -35,38 +35,75 @@ auto_from_java!(i8, jni::sys::jbyte);
auto_from_java!(f32, jni::sys::jfloat); auto_from_java!(f32, jni::sys::jfloat);
auto_from_java!(f64, jni::sys::jdouble); auto_from_java!(f64, jni::sys::jdouble);
auto_from_java!(JObject<'j>, JObject<'j>); auto_from_java!(JObject<'j>, JObject<'j>);
auto_from_java!(JString<'j>, JString<'j>);
auto_from_java!(JObjectArray<'j>, JObjectArray<'j>);
impl<'j> FromJava<'j> for bool { impl<'j, T: TypeArray> FromJava<'j> for JPrimitiveArray<'j, T> {
type T = jni::sys::jboolean; type From = JPrimitiveArray<'j, T>;
#[inline] #[inline]
fn from_java(_: &mut jni::JNIEnv, value: Self::T) -> Result<Self, jni::errors::Error> { fn from_java(_: &mut jni::JNIEnv, value: Self::From) -> Result<Self, jni::errors::Error> {
Ok(value)
}
}
impl<'j> FromJava<'j> for char {
type From = jni::sys::jchar;
#[inline]
fn from_java(_: &mut jni::JNIEnv, value: Self::From) -> Result<Self, jni::errors::Error> {
char::from_u32(value.into()).ok_or_else(|| jni::errors::Error::WrongJValueType("char", "invalid u16"))
}
}
impl<'j> FromJava<'j> for bool {
type From = jni::sys::jboolean;
#[inline]
fn from_java(_: &mut jni::JNIEnv, value: Self::From) -> Result<Self, jni::errors::Error> {
Ok(value == 1) Ok(value == 1)
} }
} }
impl<'j> FromJava<'j> for String { impl<'j> FromJava<'j> for String {
type T = JString<'j>; type From = JString<'j>;
fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result<Self, jni::errors::Error> { fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::From) -> Result<Self, jni::errors::Error> {
if value.is_null() { return Err(jni::errors::Error::NullPtr("string can't be null")) }; if value.is_null() { return Err(jni::errors::Error::NullPtr("string can't be null")) };
Ok(env.get_string(&value)?.into()) Ok(env.get_string(&value)?.into())
} }
} }
impl<'j, T: FromJava<'j, T: std::convert::AsRef<JObject<'j>>>> FromJava<'j> for Option<T> { impl<'j, T> FromJava<'j> for Option<T>
type T = T::T; where
T: FromJava<'j, From: AsRef<JObject<'j>>>,
{
type From = T::From;
fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::T) -> Result<Self, jni::errors::Error> { fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::From) -> Result<Self, jni::errors::Error> {
if value.as_ref().is_null() { return Ok(None) }; if value.as_ref().is_null() { return Ok(None) };
Ok(Some(T::from_java(env, value)?)) Ok(Some(T::from_java(env, value)?))
} }
} }
impl<'j, T: FromJava<'j, From = JObject<'j>>> FromJava<'j> for Vec<T> {
type From = JObjectArray<'j>;
fn from_java(env: &mut jni::JNIEnv<'j>, value: Self::From) -> Result<Self, jni::errors::Error> {
let len = env.get_array_length(&value)?;
let mut out = Vec::new();
for i in 0..len {
let el = env.get_object_array_element(&value, i)?;
out.push(T::from_java(env, el)?);
}
Ok(out)
}
}
#[cfg(feature = "uuid")] #[cfg(feature = "uuid")]
impl<'j> FromJava<'j> for uuid::Uuid { impl<'j> FromJava<'j> for uuid::Uuid {
type T = JObject<'j>; type From = JObject<'j>;
fn from_java(env: &mut jni::JNIEnv<'j>, uuid: Self::T) -> Result<Self, jni::errors::Error> { fn from_java(env: &mut jni::JNIEnv<'j>, uuid: Self::From) -> Result<Self, jni::errors::Error> {
let lsb = u64::from_ne_bytes( let lsb = u64::from_ne_bytes(
env.call_method(&uuid, "getLeastSignificantBits", "()J", &[])? env.call_method(&uuid, "getLeastSignificantBits", "()J", &[])?
.j()? .j()?

View file

@ -1,22 +1,20 @@
use jni::objects::{JObject, JObjectArray, JString}; use jni::objects::JObject;
use crate::raw_java::FromJavaRaw;
/// Specifies how a Rust type should be converted into a Java primitive. /// Specifies how a Rust type should be converted into a Java primitive.
pub trait IntoJavaPrimitive<'j> { pub trait IntoJava<'j> {
/// The JNI type representing the output. /// The JNI type representing the output.
type T; type Ret;
/// Attempts to convert this Rust object into a Java primitive. /// Attempts to convert this Rust object into a Java primitive.
fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error>; fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result<Self::Ret, jni::errors::Error>;
} }
macro_rules! auto_into_java { macro_rules! auto_into_java {
($t: ty, $j: ty) => { ($t: ty, $j: ty) => {
impl<'j> IntoJavaPrimitive<'j> for $t { impl<'j> IntoJava<'j> for $t {
type T = $j; type Ret = $j;
fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> { fn into_java(self, _: &mut jni::JNIEnv<'j>) -> Result<Self::Ret, jni::errors::Error> {
Ok(self) Ok(self)
} }
} }
@ -33,69 +31,71 @@ auto_into_java!(f32, jni::sys::jfloat);
auto_into_java!(f64, jni::sys::jdouble); auto_into_java!(f64, jni::sys::jdouble);
auto_into_java!((), ()); auto_into_java!((), ());
impl<'j> IntoJavaPrimitive<'j> for bool { impl<'j> IntoJava<'j> for bool {
type T = jni::sys::jboolean; type Ret = jni::sys::jboolean;
#[inline] #[inline]
fn into_java(self, _: &mut jni::JNIEnv) -> Result<Self::T, jni::errors::Error> { fn into_java(self, _: &mut jni::JNIEnv) -> Result<Self::Ret, jni::errors::Error> {
Ok(if self { 1 } else { 0 }) Ok(if self { 1 } else { 0 })
} }
} }
impl<'j, X: IntoJavaObject<'j>> IntoJava<'j> for X {
type Ret = jni::sys::jobject;
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::Ret, jni::errors::Error> {
Ok(self.into_java_object(env)?.as_raw())
}
}
/// Specifies how a Rust type should be converted into a Java object. /// Specifies how a Rust type should be converted into a Java object.
pub trait IntoJavaObject<'j> { pub trait IntoJavaObject<'j> {
type T: std::convert::AsRef<JObject<'j>>;
/// The Java class associated with this type. /// The Java class associated with this type.
const CLASS: &'static str; const CLASS: &'static str;
/// Attempts to convert this Rust object into a Java object. /// Attempts to convert this Rust object into a Java object.
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error>; fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<JObject<'j>, jni::errors::Error>;
} }
impl<'j> IntoJavaObject<'j> for &str { impl<'j> IntoJavaObject<'j> for &str {
type T = JString<'j>;
const CLASS: &'static str = "java/lang/String"; const CLASS: &'static str = "java/lang/String";
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> { fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<JObject<'j>, jni::errors::Error> {
env.new_string(self) Ok(env.new_string(self)?.into())
} }
} }
impl<'j> IntoJavaObject<'j> for String { impl<'j> IntoJavaObject<'j> for String {
type T = JString<'j>;
const CLASS: &'static str = "java/lang/String"; const CLASS: &'static str = "java/lang/String";
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> { fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<JObject<'j>, jni::errors::Error> {
self.as_str().into_java(env) self.as_str().into_java_object(env)
} }
} }
impl<'j, E: IntoJavaObject<'j>> IntoJavaObject<'j> for Vec<E> { impl<'j, T: IntoJavaObject<'j>> IntoJavaObject<'j> for Vec<T> {
type T = JObjectArray<'j>; const CLASS: &'static str = T::CLASS; // TODO shouldnt it be 'Object[]' ?
const CLASS: &'static str = E::CLASS; fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<JObject<'j>, jni::errors::Error> {
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> { let mut array = env.new_object_array(self.len() as i32, T::CLASS, JObject::null())?;
let mut array = env.new_object_array(self.len() as i32, E::CLASS, JObject::null())?;
for (n, el) in self.into_iter().enumerate() { for (n, el) in self.into_iter().enumerate() {
let el = el.into_java(env)?; let el = el.into_java_object(env)?;
env.set_object_array_element(&mut array, n as i32, &el)?; env.set_object_array_element(&mut array, n as i32, &el)?;
} }
Ok(array) Ok(array.into())
} }
} }
impl<'j, E: std::convert::AsRef<JObject<'j>> + FromJavaRaw, T: IntoJavaObject<'j, T = E>> IntoJavaObject<'j> for Option<T> { impl<'j, T: IntoJavaObject<'j>> IntoJavaObject<'j> for Option<T> {
type T = E;
const CLASS: &'static str = T::CLASS; const CLASS: &'static str = T::CLASS;
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> { fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<JObject<'j>, jni::errors::Error> {
match self { match self {
Some(x) => x.into_java(env), Some(x) => x.into_java_object(env),
None => Ok(unsafe { E::from_java_raw(std::ptr::null_mut()) }) // safe, that's what JObject::null does None => Ok(JObject::null())
} }
} }
} }
#[cfg(feature = "uuid")] #[cfg(feature = "uuid")]
impl<'j> IntoJavaObject<'j> for uuid::Uuid { impl<'j> IntoJavaObject<'j> for uuid::Uuid {
type T = jni::objects::JObject<'j>;
const CLASS: &'static str = "java/util/UUID"; const CLASS: &'static str = "java/util/UUID";
fn into_java(self, env: &mut jni::JNIEnv<'j>) -> Result<Self::T, jni::errors::Error> { fn into_java_object(self, env: &mut jni::JNIEnv<'j>) -> Result<JObject<'j>, jni::errors::Error> {
let class = env.find_class(Self::CLASS)?; let class = env.find_class(Self::CLASS)?;
let (msb, lsb) = self.as_u64_pair(); let (msb, lsb) = self.as_u64_pair();
let msb = i64::from_ne_bytes(msb.to_ne_bytes()); let msb = i64::from_ne_bytes(msb.to_ne_bytes());

View file

@ -1,11 +1,9 @@
pub mod into_java; pub mod into_java;
pub mod from_java; pub mod from_java;
pub mod raw_java;
pub use jni_toolbox_macro::jni; pub use jni_toolbox_macro::jni;
pub use into_java::{IntoJavaObject, IntoJavaPrimitive}; pub use into_java::{IntoJavaObject, IntoJava};
pub use from_java::{FromJava, from_java_static}; pub use from_java::{FromJava, from_java_static};
pub use raw_java::IntoJavaRaw;
/// An error that is meant to be used with jni-toolbox. /// An error that is meant to be used with jni-toolbox.

View file

@ -1,63 +0,0 @@
use jni::objects::{JObject, JObjectArray, JString};
/// Needed internally to perform some operations.
pub trait FromJavaRaw {
unsafe fn from_java_raw(raw: jni::sys::jobject) -> Self;
}
macro_rules! auto_from_raw {
($type: ty) => {
impl FromJavaRaw for $type {
#[inline]
unsafe fn from_java_raw(raw: jni::sys::jobject) -> Self {
Self::from_raw(raw)
}
}
};
}
auto_from_raw!(JObject<'_>);
auto_from_raw!(JString<'_>);
auto_from_raw!(JObjectArray<'_>);
/// Intermediate trait used to guess the JNI return type.
/// Usually doesn't need to be manually implemented.
pub trait IntoJavaRaw {
type T;
fn into_java_raw(self) -> Self::T;
}
macro_rules! auto_into_raw_primitive {
($type: ty) => {
impl IntoJavaRaw for $type {
type T = $type;
fn into_java_raw(self) -> Self::T {
self
}
}
}
}
auto_into_raw_primitive!(jni::sys::jlong);
auto_into_raw_primitive!(jni::sys::jint);
auto_into_raw_primitive!(jni::sys::jshort);
auto_into_raw_primitive!(jni::sys::jbyte);
auto_into_raw_primitive!(jni::sys::jdouble);
auto_into_raw_primitive!(jni::sys::jfloat);
auto_into_raw_primitive!(());
macro_rules! auto_into_raw_object {
($lt: lifetime, $type: ty) => {
impl<'j> IntoJavaRaw for $type {
type T = jni::sys::jobject;
fn into_java_raw(self) -> Self::T {
self.as_raw()
}
}
};
}
auto_into_raw_object!('j, JObject<'j>);
auto_into_raw_object!('j, JString<'j>);
auto_into_raw_object!('j, JObjectArray<'j>);