Skip to content

Commit

Permalink
Merge pull request #1276 from viperproject/improvements
Browse files Browse the repository at this point in the history
Optimize lookups in code generated by jni-gen
  • Loading branch information
fpoli authored Jan 10, 2023
2 parents a4ac54b + 4cb1917 commit 3b66dee
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 113 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion jni-gen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ The generated code uses the `jni` crate to interact with the JVM.
Requirements
------------

`libjvm.so` needs to be in the `LD_LIBRARY_PATH` environment variable.
* `libjvm.so` needs to be in the `LD_LIBRARY_PATH` environment variable.
* The crate that makes use of `jni-gen` should have the `once_cell` dependency.
5 changes: 5 additions & 0 deletions jni-gen/src/generators/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,17 @@ impl<'a> ClassGenerator<'a> {
fn generate_imports(&self) -> String {
vec![
"use jni::JNIEnv;",
"use jni::objects::GlobalRef;",
"use jni::objects::JObject;",
"use jni::objects::JMethodID;",
"use jni::objects::JStaticFieldID;",
"use jni::objects::JStaticMethodID;",
"use jni::objects::JValue;",
"use jni::objects::JClass;",
"use jni::errors::Result as JNIResult;",
"use jni::sys::*;",
"use jni::signature::*;",
"use once_cell::sync::OnceCell;",
"use std::str::FromStr;",
]
.join("\n")
Expand Down
69 changes: 44 additions & 25 deletions jni-gen/src/generators/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,40 +165,48 @@ fn generate(
code.push(") -> JNIResult<JObject<'a>> {".to_string());

// Generate dynamic type check for the arguments
if cfg!(debug_assertions) {
for i in 0..parameter_names.len() {
let par_name = &parameter_names[i];
let par_sign = &parameter_signatures[i];
if par_sign.starts_with('L') {
let par_class = &par_sign[1..(par_sign.len() - 1)];
code.push(" debug_assert!(".to_string());
code.push(format!(
" self.env.is_instance_of({}, self.env.find_class(\"{}\")?)?",
par_name, par_class
));
code.push(" );".to_string());
}
for i in 0..parameter_names.len() {
let par_name = &parameter_names[i];
let par_sign = &parameter_signatures[i];
if par_sign.starts_with('L') {
let par_class = &par_sign[1..(par_sign.len() - 1)];
code.push(" debug_assert!(".to_string());
code.push(format!(
" self.env.is_instance_of({}, self.env.find_class(\"{}\")?)?",
par_name, par_class
));
code.push(" );".to_string());
}
}
code.push("".to_string());

code.push(format!(
" let class = self.env.find_class(\"{}\")?;",
class.path()
));

code.push(format!(" let class_name = \"{}\";", class.path()));
code.push(" let method_name = \"<init>\";".to_string());
code.push(format!(
" let method_signature = \"{}\";",
constructor_signature
));

code.push(" let method_id = self.env.get_method_id(".to_string());
code.push(" class,".to_string());
code.push(" \"<init>\",".to_string());
code.push(" method_signature".to_string());
code.push(" )?;".to_string());
code.push(
r#"static CLASS: OnceCell<GlobalRef> = OnceCell::new();
static METHOD_ID: OnceCell<JMethodID> = OnceCell::new();
let class = CLASS.get_or_try_init(|| {
let class = self.env.find_class(class_name)?;
self.env.new_global_ref(class)
})?;
let method_id = *METHOD_ID.get_or_try_init(|| {
self.env.get_method_id(
class_name,
method_name,
method_signature
)
})?;
"#
.to_string(),
);

code.push(" let result = self.env.new_object_unchecked(".to_string());
code.push(" class,".to_string());
code.push(" JClass::from(class.as_obj()),".to_string());
code.push(" method_id,".to_string());
code.push(" &[".to_string());

Expand All @@ -211,8 +219,19 @@ fn generate(

code.push(" ]".to_string());
code.push(" );".to_string());
code.push("".to_string());

// Generate dynamic type check for the result
code.push(" if let Ok(result) = result {".to_string());
code.push(" debug_assert!(".to_string());
code.push(" self.env.is_instance_of(".to_string());
code.push(" result,".to_string());
code.push(" self.env.find_class(class_name)?,".to_string());
code.push(" )?".to_string());
code.push(" );".to_string());
code.push(" }".to_string());
code.push("".to_string());

code.push(" self.env.delete_local_ref(class.into()).unwrap();".to_string());
code.push(" result".to_string());
code.push("}".to_string());

Expand Down
187 changes: 111 additions & 76 deletions jni-gen/src/generators/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,35 +223,7 @@ fn generate(

code.push(format!(") -> JNIResult<{}> {{", return_type));

// Generate dynamic type check for the arguments
if cfg!(debug_assertions) {
for i in 0..parameter_names.len() {
let par_name = &parameter_names[i];
let par_sign = &parameter_signatures[i];
if par_sign.starts_with('L') {
let par_class = &par_sign[1..(par_sign.len() - 1)];
code.push(" debug_assert!(".to_string());
code.push(format!(
" self.env.is_instance_of({}, self.env.find_class(\"{}\")?)?",
par_name, par_class
));
code.push(" );".to_string());
}
}
}

code.push(format!(
" let class = self.env.find_class(\"{}\")?;",
class.path()
));

// Generate dynamic type check for `receiver`
if cfg!(debug_assertions) {
code.push(" debug_assert!(".to_string());
code.push(" self.env.is_instance_of(receiver, class)?".to_string());
code.push(" );".to_string());
}

code.push(format!(" let class_name = \"{}\";", class.path()));
code.push(format!(" let method_name = \"{}\";", method_name));
code.push(format!(
" let method_signature = \"{}\";",
Expand All @@ -261,14 +233,53 @@ fn generate(
" let return_signature = \"{}\";",
return_signature
));
code.push("".to_string());

// Generate dynamic type check for the arguments
for i in 0..parameter_names.len() {
let par_name = &parameter_names[i];
let par_sign = &parameter_signatures[i];
if par_sign.starts_with('L') {
let par_class = &par_sign[1..(par_sign.len() - 1)];
code.push(" debug_assert!(".to_string());
code.push(" self.env.is_instance_of(".to_string());
code.push(format!(" {},", par_name));
code.push(format!(
" self.env.find_class(\"{}\")?,",
par_class
));
code.push(" )?".to_string());
code.push(" );".to_string());
}
}
code.push("".to_string());

code.push(" let method_id = self.env.get_method_id(".to_string());
code.push(" class,".to_string());
code.push(" method_name,".to_string());
code.push(" method_signature".to_string());
code.push(" )?;".to_string());
// Generate dynamic type check for `receiver`
code.push(" debug_assert!(".to_string());
code.push(" self.env.is_instance_of(".to_string());
code.push(" receiver,".to_string());
code.push(" self.env.find_class(class_name)?,".to_string());
code.push(" )?".to_string());
code.push(" );".to_string());
code.push("".to_string());

code.push(
r#" static METHOD_ID: OnceCell<JMethodID> = OnceCell::new();
static RETURN_TYPE: OnceCell<ReturnType> = OnceCell::new();
let method_id = *METHOD_ID.get_or_try_init(|| {
self.env.get_method_id(
class_name,
method_name,
method_signature
)
})?;
let return_type = RETURN_TYPE.get_or_try_init(|| {
ReturnType::from_str(return_signature)
})?.clone();
"#
.to_string(),
);

code.push(" let return_type = ReturnType::from_str(return_signature)?;".to_string());
code.push(" let result = self.env.call_method_unchecked(".to_string());
code.push(" receiver,".to_string());
code.push(" method_id,".to_string());
Expand All @@ -287,21 +298,25 @@ fn generate(
" ).and_then(|x| x.{}());",
generate_jni_type_char(&return_signature)
));
code.push("".to_string());

// Generate dynamic type check for the result
if cfg!(debug_assertions) && return_signature.starts_with('L') {
if return_signature.starts_with('L') {
let return_class = &return_signature[1..(return_signature.len() - 1)];
code.push(" if let Ok(result) = result {".to_string());
code.push(" debug_assert!(".to_string());
code.push(" self.env.is_instance_of(".to_string());
code.push(" result,".to_string());
code.push(format!(
" self.env.is_instance_of(result, self.env.find_class(\"{}\")?)?",
" self.env.find_class(\"{}\")?,",
return_class
));
code.push(" )?".to_string());
code.push(" );".to_string());
code.push(" }".to_string());
code.push("".to_string());
}

code.push(" self.env.delete_local_ref(class.into()).unwrap();".to_string());
code.push(" result".to_string());
code.push("}".to_string());

Expand Down Expand Up @@ -358,27 +373,7 @@ fn generate_static(

code.push(format!(") -> JNIResult<{}> {{", return_type));

// Generate dynamic type check for the arguments
/*for i in 0..parameter_names.len() {
let par_name = &parameter_names[i];
let par_sign = &parameter_signatures[i];
if par_sign.chars().next() == Some('L') {
let par_class = &par_sign[1..(par_sign.len()-1)];
code.push(" debug_assert!(".to_string());
code.push(format!(
" self.env.is_instance_of({}, self.env.find_class(\"{}\")?)?",
par_name,
par_class
));
code.push(" );".to_string());
}
}*/

code.push(format!(
" let class =self.env.find_class(\"{}\")?;",
class.path()
));

code.push(format!(" let class_name = \"{}\";", class.path()));
code.push(format!(" let method_name = \"{}\";", method_name));
code.push(format!(
" let method_signature = \"{}\";",
Expand All @@ -388,45 +383,85 @@ fn generate_static(
" let return_signature = \"{}\";",
return_signature
));
code.push("".to_string());

code.push(" let method_id = self.env.get_static_method_id(".to_string());
code.push(" class,".to_string());
code.push(" method_name,".to_string());
code.push(" method_signature".to_string());
code.push(" )?;".to_string());
// Generate dynamic type check for the arguments
for i in 0..parameter_names.len() {
let par_name = &parameter_names[i];
let par_sign = &parameter_signatures[i];
if par_sign.starts_with('L') {
let par_class = &par_sign[1..(par_sign.len() - 1)];
code.push(" debug_assert!(".to_string());
code.push(" self.env.is_instance_of(".to_string());
code.push(format!(" {},", par_name));
code.push(format!(
" self.env.find_class(\"{}\")?,",
par_class
));
code.push(" )?".to_string());
code.push(" );".to_string());
}
}
code.push("".to_string());

code.push(
r#" static CLASS: OnceCell<GlobalRef> = OnceCell::new();
static STATIC_METHOD_ID: OnceCell<JStaticMethodID> = OnceCell::new();
static RETURN_TYPE: OnceCell<ReturnType> = OnceCell::new();
let class = CLASS.get_or_try_init(|| {
let class = self.env.find_class(class_name)?;
self.env.new_global_ref(class)
})?;
let static_method_id = *STATIC_METHOD_ID.get_or_try_init(|| {
self.env.get_static_method_id(
class_name,
method_name,
method_signature
)
})?;
let return_type = RETURN_TYPE.get_or_try_init(|| {
ReturnType::from_str(return_signature)
})?.clone();
"#
.to_string(),
);
code.push("".to_string());

code.push(" let return_type = ReturnType::from_str(return_signature)?;".to_string());
code.push(" let result = self.env.call_static_method_unchecked(".to_string());
code.push(" class,".to_string());
code.push(" method_id,".to_string());
code.push(" JClass::from(class.as_obj()),".to_string());
code.push(" static_method_id,".to_string());
code.push(" return_type,".to_string());
code.push(" &[".to_string());

for i in 0..parameter_names.len() {
let par_name = &parameter_names[i];
let par_sign = &parameter_signatures[i];
let par_jvalue = generate_jvalue_wrapper(par_name, par_sign);
code.push(format!(" {}.into(),", par_jvalue));
}

code.push(" ]".to_string());
code.push(format!(
" ).and_then(|x| x.{}());",
generate_jni_type_char(&return_signature)
));
code.push("".to_string());

// Generate dynamic type check for the result
/*if return_signature.chars().next() == Some('L') {
let return_class = &return_signature[1..(return_signature.len()-1)];
code.push(" debug_assert!(".to_string());
if return_signature.starts_with('L') {
let return_class = &return_signature[1..(return_signature.len() - 1)];
code.push(" if let Ok(result) = result {".to_string());
code.push(" debug_assert!(".to_string());
code.push(" self.env.is_instance_of(".to_string());
code.push(" result,".to_string());
code.push(format!(
" self.env.is_instance_of(result, self.env.find_class(\"{}\")?)?",
" self.env.find_class(\"{}\")?,",
return_class
));
code.push(" );".to_string());
}*/
code.push(" )?".to_string());
code.push(" );".to_string());
code.push(" }".to_string());
code.push("".to_string());
}

code.push(" self.env.delete_local_ref(class.into()).unwrap();".to_string());
code.push(" result".to_string());
code.push("}".to_string());

Expand Down
Loading

0 comments on commit 3b66dee

Please sign in to comment.