diff --git a/Cargo.lock b/Cargo.lock index 42faa81a4e5..59833d7021d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2774,6 +2774,7 @@ dependencies = [ "error-chain", "jni", "jni-gen", + "once_cell", "tempfile", "ureq", ] @@ -3198,6 +3199,7 @@ dependencies = [ "jni", "jni-gen", "log", + "once_cell", "tempfile", "ureq", ] diff --git a/jni-gen/README.md b/jni-gen/README.md index 2109f860c8d..849de08bc03 100644 --- a/jni-gen/README.md +++ b/jni-gen/README.md @@ -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. diff --git a/jni-gen/src/generators/class.rs b/jni-gen/src/generators/class.rs index 085d3f57a6d..1a74c26683f 100644 --- a/jni-gen/src/generators/class.rs +++ b/jni-gen/src/generators/class.rs @@ -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") diff --git a/jni-gen/src/generators/constructor.rs b/jni-gen/src/generators/constructor.rs index 5b8470a0e2b..449d8aabd19 100644 --- a/jni-gen/src/generators/constructor.rs +++ b/jni-gen/src/generators/constructor.rs @@ -165,40 +165,48 @@ fn generate( code.push(") -> JNIResult> {".to_string()); // Generate dynamic type check for the arguments - if cfg!(debug_assertions) { - for i in 0..parameter_names.len() { - let par_name = ¶meter_names[i]; - let par_sign = ¶meter_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 = ¶meter_names[i]; + let par_sign = ¶meter_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 = \"\";".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(" \"\",".to_string()); - code.push(" method_signature".to_string()); - code.push(" )?;".to_string()); + code.push( + r#"static CLASS: OnceCell = OnceCell::new(); + static METHOD_ID: OnceCell = 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()); @@ -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()); diff --git a/jni-gen/src/generators/method.rs b/jni-gen/src/generators/method.rs index d7743d5a207..b3cc488ed4d 100644 --- a/jni-gen/src/generators/method.rs +++ b/jni-gen/src/generators/method.rs @@ -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 = ¶meter_names[i]; - let par_sign = ¶meter_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 = \"{}\";", @@ -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 = ¶meter_names[i]; + let par_sign = ¶meter_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 = OnceCell::new(); + static RETURN_TYPE: OnceCell = 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()); @@ -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()); @@ -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 = ¶meter_names[i]; - let par_sign = ¶meter_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 = \"{}\";", @@ -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 = ¶meter_names[i]; + let par_sign = ¶meter_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 = OnceCell::new(); + static STATIC_METHOD_ID: OnceCell = OnceCell::new(); + static RETURN_TYPE: OnceCell = 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 = ¶meter_names[i]; let par_sign = ¶meter_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()); diff --git a/jni-gen/src/generators/scala_object_getter.rs b/jni-gen/src/generators/scala_object_getter.rs index 82a683a4b8f..6374f7910fc 100644 --- a/jni-gen/src/generators/scala_object_getter.rs +++ b/jni-gen/src/generators/scala_object_getter.rs @@ -26,16 +26,33 @@ pub fn generate_scala_object_getter(env: &JNIEnv, class_name: &ClassName) -> Res class_name.path() ), "pub fn singleton(&self) -> JNIResult> {".to_string(), - format!( - " let class = self.env.find_class(\"{}\")?;", - class_name.path() - ), - " let result = self.env.get_static_field(".to_string(), - " class,".to_string(), - " \"MODULE$\",".to_string(), - format!(" \"L{};\",", class_name.path()), + format!(" let class_name = \"{}\";", class_name.path()), + " let field_name = \"MODULE$\";".to_string(), + format!(" let field_signature = \"L{};\";", class_name.path()), + "".to_string(), + "static CLASS: OnceCell = OnceCell::new();".to_string(), + "static STATIC_FIELD_ID: OnceCell = OnceCell::new();".to_string(), + "static FIELD_TYPE: OnceCell = OnceCell::new();".to_string(), + "let class = CLASS.get_or_try_init(|| {".to_string(), + " let class = self.env.find_class(class_name)?;".to_string(), + " self.env.new_global_ref(class)".to_string(), + "})?;".to_string(), + "let static_field_id = *STATIC_FIELD_ID.get_or_try_init(|| {".to_string(), + " self.env.get_static_field_id(".to_string(), + " class_name,".to_string(), + " field_name,".to_string(), + " field_signature,".to_string(), + " )".to_string(), + "})?;".to_string(), + "let field_type = FIELD_TYPE.get_or_try_init(|| {".to_string(), + " JavaType::from_str(field_signature)".to_string(), + "})?.clone();".to_string(), + "".to_string(), + " let result = self.env.get_static_field_unchecked(".to_string(), + " JClass::from(class.as_obj()),".to_string(), + " static_field_id,".to_string(), + " field_type,".to_string(), " ).and_then(|x| x.l());".to_string(), - " self.env.delete_local_ref(class.into()).unwrap();".to_string(), " result".to_string(), "}".to_string(), ] diff --git a/jni-gen/systest/Cargo.toml b/jni-gen/systest/Cargo.toml index c1e43465249..5c8b0f9b777 100644 --- a/jni-gen/systest/Cargo.toml +++ b/jni-gen/systest/Cargo.toml @@ -14,3 +14,4 @@ tempfile = "3.3" [dependencies] error-chain = "0.12" jni = { version = "0.20", features = ["invocation"] } +once_cell = "1.16" diff --git a/jni-gen/systest/tests/jvm_builtin_classes.rs b/jni-gen/systest/tests/jvm_builtin_classes.rs index a6067f076f7..c1ad07848d4 100644 --- a/jni-gen/systest/tests/jvm_builtin_classes.rs +++ b/jni-gen/systest/tests/jvm_builtin_classes.rs @@ -39,8 +39,8 @@ fn test_jvm_builtin_classes() { )?; let int_array = unsafe { JObject::from_raw(int_array) }; - let result = - java::util::Arrays::with(&env).call_binarySearch(int_array, integer_value)?; + let result = java::util::Arrays::with(&env) + .call_binarySearch(int_array, integer_value)?; assert!(0 <= result && result < array_length); diff --git a/viper-sys/Cargo.toml b/viper-sys/Cargo.toml index 87bed382a3b..d74daa8a593 100644 --- a/viper-sys/Cargo.toml +++ b/viper-sys/Cargo.toml @@ -20,3 +20,4 @@ error-chain = "0.12.0" env_logger = "0.9" jni = { version = "0.20", features = ["invocation"] } log = { version = "0.4", features = ["release_max_level_info"] } +once_cell = "1.16"