use std::ffi::CString; use std::mem::{align_of, forget}; use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::EffectParameter; use crate::StringKey; /// Dynamic data registration mod dynamic_data; /// Static data registration mod static_data; /// Register a single function. macro_rules! register_func_with_env { ($imports: ident, $store: ident, $func: ident, $env: expr) => { $imports.define( "env", stringify!($func), wasmer::Function::new_typed_with_env($store, $env, $func), ); }; } /// Utility macro to register a bunch of WASM functions easily. macro_rules! register { ( $( fn $name:ident($($par:ident: $par_type:ty),*$(,)?) $(-> $return:ty)? $block:block )* $( manual $manual_name:ident )? ) => { $( fn $name( $( $par: $par_type, )* ) $(-> $return)* $block )* pub(crate) fn register(imports: &mut wasmer::Imports, store: &mut wasmer::StoreMut, env: &wasmer::FunctionEnv) { $( crate::script_implementations::wasm::export_registry::register_func_with_env!(imports, store, $name, env); )* $( $manual_name(imports, store, env) )* } }; } pub(crate) use register; pub(crate) use register_func_with_env; /// Register the functions we expose to WASM. pub(crate) fn register_webassembly_funcs( imports: &mut Imports, store: &mut StoreMut, env: &FunctionEnv, ) { register_func_with_env!(imports, store, _print, env); register_func_with_env!(imports, store, _error, env); register_func_with_env!(imports, store, _vec_extern_ref_get_value, env); static_data::register(imports, store, env); dynamic_data::register(imports, store, env); register_func_with_env!(imports, store, string_key_get_hash, env); register_func_with_env!(imports, store, string_key_get_str, env); register_func_with_env!(imports, store, effect_parameter_get_type, env); register_func_with_env!(imports, store, effect_parameter_as_bool, env); register_func_with_env!(imports, store, effect_parameter_as_int, env); register_func_with_env!(imports, store, effect_parameter_as_float, env); register_func_with_env!(imports, store, effect_parameter_as_string, env); } /// Logging function for WASM. fn _print(env: FunctionEnvMut, p: u32, len: u32) { unsafe { let mem: *mut u8 = env.data().data().memory().offset(p as isize); let s = String::from_raw_parts(mem, len as usize, len as usize); println!("{}", s); forget(s); } } /// Triggers when WASM panics. #[track_caller] fn _error( env: FunctionEnvMut, message: u32, message_len: u32, file: u32, file_len: u32, line: u32, position: u32, ) { unsafe { let mem: *mut u8 = env.data().data().memory().offset(message as isize); let message_str = String::from_raw_parts(mem, message_len as usize, message_len as usize); let mem: *mut u8 = env.data().data().memory().offset(file as isize); let file_str = String::from_raw_parts(mem, file_len as usize, file_len as usize); panic!( "Error: {} in file {}, line: {}:{}", message_str, file_str, line, position ); } } /// Get a single item from an earlier passed VecExternRef fn _vec_extern_ref_get_value(env: FunctionEnvMut, reference: u32, index: u32) -> u32 { env.data().data().get_extern_vec_ref_extern_ref(reference, index) } /// Gets the hash value of a StringKey. fn string_key_get_hash(env: FunctionEnvMut, string_key: ExternRef) -> u32 { string_key.value_func(&env).unwrap().hash() } /// Get a null-terminated C string from a StringKey. Note that this involves a copy into WASM /// memory, so this is relatively heavy. fn string_key_get_str(env: FunctionEnvMut, string_key: ExternRef) -> u32 { let string_key = string_key.value_func(&env).unwrap().str(); let wasm_string_ptr = env .data() .data() .allocate_mem((string_key.len() + 1) as u32, align_of::() as u32); let mut wasm_string: Vec = unsafe { Vec::from_raw_parts(wasm_string_ptr.0, string_key.len() + 1, string_key.len() + 1) }; wasm_string.resize(string_key.len() + 1, 0); string_key.as_bytes().clone_into(&mut wasm_string); wasm_string.insert(string_key.len(), 0_u8); forget(wasm_string); wasm_string_ptr.1 } /// Gets the type of an EffectParameter fn effect_parameter_get_type(env: FunctionEnvMut, parameter: ExternRef) -> u8 { let v = parameter.value_func(&env).unwrap(); match v { EffectParameter::Bool(_) => 1, EffectParameter::Int(_) => 2, EffectParameter::Float(_) => 3, EffectParameter::String(_) => 4, } } /// Gets the inner bool data of an EffectParameter. Panics if it's not a bool. fn effect_parameter_as_bool(env: FunctionEnvMut, parameter: ExternRef) -> u8 { let v = parameter.value_func(&env).unwrap(); match v { EffectParameter::Bool(b) => { if *b { 1 } else { 0 } } _ => panic!("Unexpected parameter type!"), } } /// Gets the inner int data of an EffectParameter. Panics if it's not an int. fn effect_parameter_as_int(env: FunctionEnvMut, parameter: ExternRef) -> i64 { let v = parameter.value_func(&env).unwrap(); match v { EffectParameter::Int(i) => *i, _ => panic!("Unexpected parameter type!"), } } /// Gets the inner float data of an EffectParameter. Panics if it's not a float. fn effect_parameter_as_float(env: FunctionEnvMut, parameter: ExternRef) -> f32 { let v = parameter.value_func(&env).unwrap(); match v { EffectParameter::Float(f) => *f, _ => panic!("Unexpected parameter type!"), } } /// Gets the inner string data of an EffectParameter. Panics if it's not a string. fn effect_parameter_as_string( env: FunctionEnvMut, parameter: ExternRef, ) -> ExternRef { let v = parameter.value_func(&env).unwrap(); match v { EffectParameter::String(s) => ExternRef::func_new(&env, s), _ => panic!("Unexpected parameter type!"), } }