use crate::Error::InternalError;
use crate::{JitValue, Result, Value};
#[cfg(all(
not(target_family = "wasm"),
target_endian = "little",
not(any(
target_arch = "mips",
target_arch = "mips64",
target_os = "dragonfly",
target_os = "solaris"
))
))]
use cranelift::jit::JITModule;
use std::fmt::{Debug, Formatter};
#[cfg(all(
not(target_family = "wasm"),
target_endian = "little",
not(any(
target_arch = "mips",
target_arch = "mips64",
target_os = "dragonfly",
target_os = "solaris"
))
))]
use std::sync::{Arc, Mutex};
#[derive(Clone)]
pub struct Function {
function: unsafe extern "C" fn(*const JitValue, usize, *mut JitValue, *const u8),
#[cfg(all(
not(target_family = "wasm"),
target_endian = "little",
not(any(
target_arch = "mips",
target_arch = "mips64",
target_os = "dragonfly",
target_os = "solaris"
))
))]
module: Arc<JitModuleOwner>,
}
#[cfg(all(
not(target_family = "wasm"),
target_endian = "little",
not(any(
target_arch = "mips",
target_arch = "mips64",
target_os = "dragonfly",
target_os = "solaris"
))
))]
struct JitModuleOwner {
module: Mutex<Option<JITModule>>,
}
#[cfg(all(
not(target_family = "wasm"),
target_endian = "little",
not(any(
target_arch = "mips",
target_arch = "mips64",
target_os = "dragonfly",
target_os = "solaris"
))
))]
impl JitModuleOwner {
fn new(module: Option<JITModule>) -> Self {
Self {
module: Mutex::new(module),
}
}
}
#[cfg(all(
not(target_family = "wasm"),
target_endian = "little",
not(any(
target_arch = "mips",
target_arch = "mips64",
target_os = "dragonfly",
target_os = "solaris"
))
))]
impl Drop for JitModuleOwner {
fn drop(&mut self) {
let module = match self.module.get_mut() {
Ok(module) => module,
Err(error) => error.into_inner(),
};
if let Some(module) = module.take() {
unsafe { module.free_memory() };
}
}
}
impl Debug for Function {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Function")
.field("function", &(self.function as *const ()))
.finish_non_exhaustive()
}
}
impl Function {
#[cfg(test)]
pub(crate) fn new(
function: unsafe extern "C" fn(*const JitValue, usize, *mut JitValue, *const u8),
) -> Self {
Self {
function,
#[cfg(all(
not(target_family = "wasm"),
target_endian = "little",
not(any(
target_arch = "mips",
target_arch = "mips64",
target_os = "dragonfly",
target_os = "solaris"
))
))]
module: Arc::new(JitModuleOwner::new(None)),
}
}
#[cfg(all(
not(target_family = "wasm"),
target_endian = "little",
not(any(
target_arch = "mips",
target_arch = "mips64",
target_os = "dragonfly",
target_os = "solaris"
))
))]
pub(crate) fn with_module(
function: unsafe extern "C" fn(*const JitValue, usize, *mut JitValue, *const u8),
module: JITModule,
) -> Self {
Self {
function,
module: Arc::new(JitModuleOwner::new(Some(module))),
}
}
#[expect(
clippy::not_unsafe_ptr_arg_deref,
reason = "the JIT crate owns the validated generated-code ABI boundary"
)]
pub fn execute(&self, arguments: &[Value], context: *const u8) -> Result<Option<Value>> {
let mut stack_buf = [JitValue::new(); 8];
let mut result = JitValue::new();
if arguments.len() <= stack_buf.len() {
for (i, arg) in arguments.iter().enumerate() {
let slot = stack_buf
.get_mut(i)
.ok_or_else(|| InternalError(format!("Invalid stack argument index {i}")))?;
*slot = JitValue::from(arg.clone());
}
unsafe {
(self.function)(
stack_buf.as_ptr(),
arguments.len(),
&raw mut result,
context,
);
}
} else {
let heap_args: Vec<JitValue> = arguments.iter().cloned().map(JitValue::from).collect();
unsafe {
(self.function)(
heap_args.as_ptr(),
heap_args.len(),
&raw mut result,
context,
);
}
}
result.try_into()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::JitValue;
unsafe extern "C" fn return_42(
_arguments: *const JitValue,
_arguments_len: usize,
jit_value: *mut JitValue,
_context: *const u8,
) {
unsafe {
*jit_value = JitValue::from(42i64);
}
}
#[test]
fn test_function() -> Result<()> {
let function = Function::new(return_42);
assert!(format!("{function:?}").contains("Function"));
let cloned_function = function.clone();
drop(function);
let value = cloned_function
.execute(&[], std::ptr::null())?
.expect("value");
assert_eq!(value, Value::I64(42));
Ok(())
}
#[test]
fn test_function_with_heap_arguments() -> Result<()> {
let function = Function::new(return_42);
let arguments = vec![Value::I32(1); 9];
let value = function
.execute(&arguments, std::ptr::null())?
.expect("value");
assert_eq!(value, Value::I64(42));
Ok(())
}
}