use std::path::Path;
use jni::{JValue, jni_sig, jni_str, objects::JObject};
use crate::{
Error, Result,
runtime::{self, GhidraRuntime, LaunchOptions},
};
use super::support::{bridge_jar_path, find_bridge_class, jni_error, path_string};
pub fn start(mut options: LaunchOptions) -> Result<GhidraRuntime> {
let bridge_jar = bridge_jar_path()?;
options.extra_classpaths.push(bridge_jar);
let runtime = runtime::start(options)
.map_err(|source| Error::runtime("failed to start embedded Ghidra runtime", source))?;
attach_bridge(&runtime)?;
Ok(runtime)
}
pub fn started() -> bool {
runtime::started()
}
pub(crate) fn attach_bridge(runtime: &GhidraRuntime) -> Result<()> {
let bridge_jar = bridge_jar_path()?;
add_bridge_jar(runtime, &bridge_jar)?;
initialize_ghidra(runtime)?;
Ok(())
}
fn initialize_ghidra(runtime: &GhidraRuntime) -> Result<()> {
runtime.with_attached(|env| -> Result<()> {
let install_dir = env.new_string(path_string(runtime.install_dir()))?;
let install_dir = JObject::from(install_dir);
let class = find_bridge_class(env)?;
if let Err(error) = env.call_static_method(
class,
jni_str!("initialize"),
jni_sig!("(Ljava/lang/String;)V"),
&[JValue::Object(&install_dir)],
) {
return Err(jni_error(env, "failed to initialize Ghidra", error));
}
Ok(())
})
}
fn add_bridge_jar(runtime: &GhidraRuntime, bridge_jar: &Path) -> Result<()> {
runtime.with_attached(|env| -> Result<()> {
let loader = env
.call_static_method(
jni_str!("java/lang/ClassLoader"),
jni_str!("getSystemClassLoader"),
jni_sig!("()Ljava/lang/ClassLoader;"),
&[],
)?
.l()?;
let bridge_jar = env.new_string(path_string(bridge_jar))?;
let bridge_jar = JObject::from(bridge_jar);
env.call_method(
&loader,
jni_str!("addPath"),
jni_sig!("(Ljava/lang/String;)Z"),
&[JValue::Object(&bridge_jar)],
)?;
Ok(())
})
}