blob: b869947ebabf5f265c848707d3ff070baf2072ad [file]
use crate::{
exception::{joption_or_throw, runtime_error, Error},
memory,
memory::Memory,
types::{jptr, Pointer},
value::{Value, DOUBLE_CLASS, FLOAT_CLASS, INT_CLASS, LONG_CLASS},
};
use jni::{
objects::{GlobalRef, JClass, JObject, JString, JValue},
sys::{jbyteArray, jobjectArray},
JNIEnv,
};
use std::{collections::HashMap, convert::TryFrom, panic, rc::Rc};
use wasmer_runtime::{imports, instantiate, Export, Value as WasmValue};
use wasmer_runtime_core as core;
pub struct Instance {
pub java_instance_object: GlobalRef,
pub instance: Rc<core::Instance>,
pub memories: HashMap<String, Memory>,
}
impl Instance {
fn new(java_instance_object: GlobalRef, module_bytes: Vec<u8>) -> Result<Self, Error> {
let module_bytes = module_bytes.as_slice();
let imports = imports! {};
let instance = match instantiate(module_bytes, &imports) {
Ok(instance) => Rc::new(instance),
Err(e) => {
return Err(runtime_error(format!(
"Failed to instantiate the module: {}",
e
)))
}
};
let memories: HashMap<String, Memory> = instance
.exports()
.filter_map(|(export_name, export)| match export {
Export::Memory(memory) => Some((export_name, Memory::new(Rc::new(memory)))),
_ => None,
})
.collect();
Ok(Self {
java_instance_object,
instance,
memories,
})
}
fn call_exported_function(
&self,
export_name: String,
arguments: Vec<WasmValue>,
) -> Result<Vec<WasmValue>, Error> {
let function = self.instance.dyn_func(&export_name).map_err(|_| {
runtime_error(format!(
"Exported function `{}` does not exist",
export_name
))
})?;
function
.call(arguments.as_slice())
.map_err(|e| runtime_error(format!("{}", e)))
}
}
#[no_mangle]
pub extern "system" fn Java_org_wasmer_Instance_nativeInstantiate(
env: JNIEnv,
_class: JClass,
this: JObject,
module_bytes: jbyteArray,
) -> jptr {
let output = panic::catch_unwind(|| {
let module_bytes = env.convert_byte_array(module_bytes)?;
let java_instance = env.new_global_ref(this)?;
let instance = Instance::new(java_instance, module_bytes)?;
Ok(Pointer::new(instance).into())
});
joption_or_throw(&env, output).unwrap_or(0)
}
#[no_mangle]
pub extern "system" fn Java_org_wasmer_Instance_nativeDrop(
_env: JNIEnv,
_class: JClass,
instance_pointer: jptr,
) {
let _: Pointer<Instance> = instance_pointer.into();
}
#[no_mangle]
pub extern "system" fn Java_org_wasmer_Instance_nativeCall<'a>(
env: JNIEnv<'a>,
_class: JClass,
instance_pointer: jptr,
export_name: JString,
arguments_pointer: jobjectArray,
) -> jobjectArray {
let output = panic::catch_unwind(|| {
let instance: &Instance = Into::<Pointer<Instance>>::into(instance_pointer).borrow();
let export_name: String = env
.get_string(export_name)
.expect("Failed to get Java string")
.into();
let arguments_length = env.get_array_length(arguments_pointer).unwrap();
let arguments: Vec<JObject> = (0..arguments_length)
.map(|i| {
env.get_object_array_element(arguments_pointer, i)
.expect("Failed to get Java object")
})
.collect();
let results = instance.call_exported_function(
export_name,
arguments
.iter()
.map(|argument| {
Value::try_from((&env, *argument))
.expect("Failed to convert an argument to a WebAssembly value")
.inner()
})
.collect(),
)?;
let obj_array = env
.new_object_array(
i32::try_from(results.len()).unwrap(),
"java/lang/Object",
JObject::null(),
)
.expect("Failed to create a Java object array");
if results.len() > 0 {
for (i, result) in results.iter().enumerate() {
let obj = match result {
WasmValue::I32(val) => env.new_object(INT_CLASS, "(I)V", &[JValue::from(*val)]),
WasmValue::I64(val) => {
env.new_object(LONG_CLASS, "(J)V", &[JValue::from(*val)])
}
WasmValue::F32(val) => {
env.new_object(FLOAT_CLASS, "(F)V", &[JValue::from(*val)])
}
WasmValue::F64(val) => {
env.new_object(DOUBLE_CLASS, "(D)V", &[JValue::from(*val)])
}
_ => unreachable!(),
};
env.set_object_array_element(
obj_array,
i as i32,
obj.expect("Failed to create a Java object"),
)
.expect("Failed to set a Java object element");
}
Ok(obj_array)
} else {
Ok(JObject::null().into_inner())
}
});
joption_or_throw(&env, output).unwrap_or(JObject::null().into_inner())
}
#[no_mangle]
pub extern "system" fn Java_org_wasmer_Instance_nativeInitializeExportedFunctions(
env: JNIEnv,
_class: JClass,
instance_pointer: jptr,
) {
let output = panic::catch_unwind(|| {
let instance: &Instance = Into::<Pointer<Instance>>::into(instance_pointer).borrow();
let exports_object: JObject = env
.get_field(
instance.java_instance_object.as_obj(),
"exports",
"Lorg/wasmer/Exports;",
)?
.l()?;
for (export_name, export) in instance.instance.exports() {
if let Export::Function { .. } = export {
let name = env.new_string(export_name)?;
env.call_method(
exports_object,
"addFunction",
"(Ljava/lang/String;)V",
&[JObject::from(name).into()],
)?;
}
}
Ok(())
});
joption_or_throw(&env, output).unwrap_or(())
}
#[no_mangle]
pub extern "system" fn Java_org_wasmer_Instance_nativeInitializeExportedMemories(
env: JNIEnv,
_class: JClass,
instance_pointer: jptr,
) {
let output = panic::catch_unwind(|| {
let instance: &Instance = Into::<Pointer<Instance>>::into(instance_pointer).borrow();
memory::java::initialize_memories(&env, instance)?;
Ok(())
});
joption_or_throw(&env, output).unwrap_or(())
}