Skip to content

Commit

Permalink
moved java object easy interop
Browse files Browse the repository at this point in the history
  • Loading branch information
dileping committed Feb 21, 2023
1 parent 54b12e5 commit 4c1a376
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package one.tesseract.interop.rust

open class RustObject(protected val handle: Long) {
private external fun drop()

protected fun finalize() {
drop()
}
}
42 changes: 42 additions & 0 deletions rust/interop/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===------------ error.rs --------------------------------------------===//
// Copyright 2022, Tesseract Systems, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//

use std::{default::Default, error::Error};

use jni::JNIEnv;

pub fn deresultify<T, I, F>(env: &JNIEnv, fun: F) -> T
where
T: Default,
I: Into<T>,
F: FnOnce() -> Result<I, Box<dyn Error>>,
{
match fun() {
Err(err) => {
//temporary solution. need a proper conversion to Exception with a class
let message: &str = &err.to_string();

match env.throw(message) {
Ok(_) => T::default(),
Err(e) => {
debug!("Error '{}' occured, but couldn't be thrown as Exception because JNI returned: {}", message, e.to_string());
panic!("Error '{}' occured, but couldn't be thrown as Exception because JNI returned: {}", message, e.to_string())
},
}
}
Ok(value) => value.into()
}
}
8 changes: 7 additions & 1 deletion rust/interop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,30 @@
// limitations under the License.
//===----------------------------------------------------------------------===//

#![feature(iterator_try_collect)]

#[macro_use]
extern crate log;
extern crate android_log;

pub mod bi_consumer;
mod contexted_global;
pub mod env;
pub mod errors;
pub mod error;
mod exception;
mod jfuture;
pub mod thread_pool;
pub mod pointer;
pub mod future;
pub mod object;

pub use contexted_global::ContextedGlobal;
pub use exception::Exception;
pub use error::deresultify;
pub use jfuture::JFuture;

pub use object::{JavaDesc, JavaWrappableDesc, JavaWrappable, JavaConvertibleDesc, JavaConvertible};

#[cfg(test)]
mod tests {
#[test]
Expand Down
79 changes: 79 additions & 0 deletions rust/interop/src/object/convertible.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//===------------ convertible.rs --------------------------------------------===//
// Copyright 2022, Tesseract Systems, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//

use crate::env::AndroidEnv;
use jni::{JNIEnv, objects::{JObject, JValue}, errors::Result};

pub trait JavaConvertibleDesc: Sized {
fn java_class<'a>(&'a self) -> &'a str;

fn fields() -> Vec<(&'static str, &'static str)>; //(name, type)

fn into_values<'a: 'b, 'b>(self, env: &'b JNIEnv<'a>) -> Result<Vec<JValue<'a>>>;
fn from_values<'a: 'b, 'b>(env: &'b JNIEnv<'a>, values: &[JValue<'a>]) -> Result<Self>;
}

pub trait JavaConvertible: Sized {
fn into_java<'a: 'b, 'b>(self, env: &'b JNIEnv<'a>) -> Result<JObject<'a>>;
fn from_java<'a: 'b, 'b>(env: &'b JNIEnv<'a>, object: JObject<'a>) -> Result<Self>;
}

impl<T> JavaConvertible for T where T: JavaConvertibleDesc {
fn into_java<'a: 'b, 'b>(self, env: &'b JNIEnv<'a>) -> Result<JObject<'a>> {
let clazz = env.find_class_android(self.java_class())?;

let types: Vec<&str> = Self::fields().into_iter().map(|field| field.1).collect();
let sig = format!("({})V", types.join(""));

let value = self.into_values(env)?;

env.new_object(clazz, sig, &value)
}

fn from_java<'a: 'b, 'b>(env: &'b JNIEnv<'a>, object: JObject<'a>) -> Result<Self> {
fn uppercase_first_letter(s: &str) -> String {
let mut c = s.chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
}
}

fn getter_prefix(ret: &str) -> &'static str {
let mut c = ret.chars();
match c.next() {
None => panic!("Put a sig"),
Some(f) => {
match f {
'Z' => "is",
_ => "get"
}
}
}
}

let fields = Self::fields();

let values : Vec<JValue> = fields.into_iter().map(|(field_name, ret)| {
let getter = format!("{}{}", getter_prefix(ret), uppercase_first_letter(field_name));
let sig = format!("(){}", ret);

env.call_method(object, &getter, sig, &[])
}).try_collect()?;

Self::from_values(env, &values)
}
}
21 changes: 4 additions & 17 deletions rust/interop/src/errors.rs → rust/interop/src/object/desc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//===------------ errors.rs --------------------------------------------===//
//===------------ desc.rs --------------------------------------------===//
// Copyright 2022, Tesseract Systems, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -14,19 +14,6 @@
// limitations under the License.
//===----------------------------------------------------------------------===//

/*use thiserror::Error;
#[derive(Debug, Error)]
enum JError {
#[error("An error came from Rust JNI: {0}")]
Jni(jni::errors::Error),
#[error("An error came from Rust JNI: {0:?}")]
Exception(jni::objects::JThrowable),
}
impl<T> From<::jni::errors::Error> for Error {
fn from(_: ::std::sync::TryLockError<T>) -> Self {
Error::TryLock
}
}*/
pub trait JavaDesc {
fn java_class<'a>(&'a self) -> &'a str;
}
7 changes: 7 additions & 0 deletions rust/interop/src/object/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mod convertible;
mod wrappable;
mod desc;

pub use desc::{JavaDesc};
pub use wrappable::{JavaWrappableDesc, JavaWrappable};
pub use convertible::{JavaConvertibleDesc, JavaConvertible};
109 changes: 109 additions & 0 deletions rust/interop/src/object/wrappable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//===------------ wrappable.rs --------------------------------------------===//
// Copyright 2022, Tesseract Systems, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//

use std::sync::Arc;

use jni::objects::{JObject, JValue};
use jni::JNIEnv;
use jni::errors::Result;

use jni_fn::jni_fn;

use crate::env::AndroidEnv;
use crate::pointer::ArcPointer;
use crate::error::deresultify;

use super::desc::JavaDesc;

struct WrappableHandle {
pointer: i64,
dropper: Option<Box<dyn FnOnce(i64)>>
}

impl WrappableHandle {
fn from_arc<T: JavaWrappable>(arc: Arc<T>) -> Self {
let long_p: i64 = ArcPointer::new(arc).into();

Self { pointer: long_p, dropper: Some(Box::new(|pointer| {
let arc_pointer = ArcPointer::<T>::of(pointer);
arc_pointer.destroy()
}))}
}

fn from_java_ref(object: JObject, env: &JNIEnv) -> Result<Box<Self>> {
let handle_lp = env
.call_method(object, "getHandle", "()J", &[])?
.j()?;

let handle_p = handle_lp as *mut WrappableHandle;
Ok(unsafe { Box::from_raw(handle_p) })
}

fn arc<T: JavaWrappable>(&self) -> Arc<T> {
ArcPointer::of(self.pointer).arc()
}
}

impl Drop for WrappableHandle {
fn drop(&mut self) {
let dropper = self.dropper.take();

if let Some(dropper) = dropper {
dropper(self.pointer)
}
}
}

pub trait JavaWrappableDesc: JavaDesc {
}

pub trait JavaWrappable {
fn java_ref<'a: 'b, 'b, D: JavaDesc>(self: Arc<Self>, env: &'b JNIEnv<'a>, desc: Option<D>) -> Result<JObject<'a>>;
fn from_java_ref(object: JObject, env: &JNIEnv) -> Result<Arc<Self>>;
}

impl<T> JavaWrappable for T where T: JavaWrappableDesc {
fn java_ref<'a: 'b, 'b, D: JavaDesc>(self: Arc<Self>, env: &'b JNIEnv<'a>, desc: Option<D>) -> Result<JObject<'a>> {
let clazz = match &desc {
Some(desc) => desc.java_class(),
None => self.java_class(),
};

let clazz = env
.find_class_android(clazz)?;

let handle = WrappableHandle::from_arc(self);
let handle_p = Box::into_raw(Box::new(handle)) as *const () as i64;

let obj = env.new_object(clazz, "(J)V", &[JValue::from(handle_p)])?;

Ok(obj)
}

fn from_java_ref(object: JObject, env: &JNIEnv) -> Result<Arc<Self>> {
let handle= Box::leak(WrappableHandle::from_java_ref(object, env)?);
Ok(handle.arc())
}
}

#[jni_fn("one.tesseract.interop.rust.RustObject")]
pub fn drop(env: JNIEnv, this: JObject) {
deresultify(&env, || {
let handle = WrappableHandle::from_java_ref(this, &env)?;
Ok(drop(handle))
})
}

0 comments on commit 4c1a376

Please sign in to comment.