diff --git a/.drone.yml b/.drone.yml index d86a8c5..1189acc 100644 --- a/.drone.yml +++ b/.drone.yml @@ -22,15 +22,6 @@ steps: - cargo miri test --release --color=always depends_on: - test-debug-linux - - name: test-address-sanitizer - image: deukhoofd/linux64builder - environment: - RUSTFLAGS: -Zsanitizer=address - RUSTDOCFLAGS: -Zsanitizer=address - commands: - - cargo test -Zbuild-std --target x86_64-unknown-linux-gnu --color=always -- --test-threads=1 - depends_on: - - test-debug-linux - name: test-coverage image: deukhoofd/linux64builder commands: diff --git a/Cargo.toml b/Cargo.toml index d11a670..d2f1d2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ indexmap = "1.8.2" parking_lot = "0.12.1" conquer-once = "0.3.2" serde = { version = "1.0.137", optional = true, features = ["derive"] } -wasmer = { version = "2.3.0", optional = true, default-features = false, features = ["default-cranelift", "universal", "experimental-reference-types-extern-ref"] } +wasmer = { version = "3.0.0-beta", optional = true, default-features = true } unique-type-id = { version = "1.0.0", optional = true } unique-type-id-derive = { version = "1.0.0", optional = true } paste = { version = "1.0.8" } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/learned_move.rs b/src/script_implementations/wasm/export_registry/dynamic_data/learned_move.rs index 87693d1..b28e498 100644 --- a/src/script_implementations/wasm/export_registry/dynamic_data/learned_move.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/learned_move.rs @@ -5,21 +5,22 @@ use crate::script_implementations::wasm::export_registry::register; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::MoveData; +use wasmer::FunctionEnvMut; register! { fn learned_move_get_learn_method( - env: &WebAssemblyEnv, + env: FunctionEnvMut, turn_choice: ExternRef, ) -> u8 { unsafe { - transmute(turn_choice.value(env).unwrap().learn_method()) + transmute(turn_choice.value(&env).unwrap().learn_method()) } } fn learned_move_get_move_data( - env: &WebAssemblyEnv, + env: FunctionEnvMut, turn_choice: ExternRef, ) -> ExternRef { - ExternRef::new(env.data().as_ref(), turn_choice.value(env).unwrap().move_data()) + ExternRef::func_new(&env, turn_choice.value(&env).unwrap().move_data()) } } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs b/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs index c4ba1b9..6ae6ed5 100644 --- a/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs @@ -1,7 +1,7 @@ use crate::dynamic_data::DynamicLibrary; use crate::script_implementations::wasm::export_registry::register; use crate::script_implementations::wasm::extern_ref::ExternRef; -use wasmer::{Exports, Store}; +use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut}; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::StaticData; @@ -15,17 +15,17 @@ mod turn_choice; register! { fn dynamic_library_get_static_data( - env: &WebAssemblyEnv, + env: FunctionEnvMut, dynamic_lib: ExternRef, ) -> ExternRef { - ExternRef::new(env.data().as_ref(), dynamic_lib.value(env).unwrap().static_data()) + ExternRef::func_new(&env, dynamic_lib.value(&env).unwrap().static_data()) } manual manual_register } /// Additional required manual registration -fn manual_register(exports: &mut Exports, store: &Store, env: WebAssemblyEnv) { - turn_choice::register(exports, store, env.clone()); - pokemon::register(exports, store, env.clone()); - learned_move::register(exports, store, env); +fn manual_register(imports: &mut Imports, store: &mut StoreMut, env: &FunctionEnv) { + turn_choice::register(imports, store, env); + pokemon::register(imports, store, env); + learned_move::register(imports, store, env); } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs b/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs index f24af61..85a9736 100644 --- a/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs @@ -6,72 +6,73 @@ use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::StatisticSet; use crate::static_data::{ClampedStatisticSet, Species}; +use wasmer::FunctionEnvMut; register! { fn pokemon_get_library( - env: &WebAssemblyEnv, + env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef { - let lib = pokemon.value(env).unwrap().library(); - ExternRef::new(env.data().as_ref(), lib) + let lib = pokemon.value(&env).unwrap().library(); + ExternRef::func_new(&env, lib) } fn pokemon_get_boosted_stats( - env: &WebAssemblyEnv, + env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef> { - let statistic_set = pokemon.value(env).unwrap().boosted_stats(); - ExternRef::new(env.data().as_ref(), statistic_set) + let statistic_set = pokemon.value(&env).unwrap().boosted_stats(); + ExternRef::func_new(&env, statistic_set) } fn pokemon_get_flat_stats( - env: &WebAssemblyEnv, + env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef> { - let statistic_set = pokemon.value(env).unwrap().flat_stats(); - ExternRef::new(env.data().as_ref(), statistic_set) + let statistic_set = pokemon.value(&env).unwrap().flat_stats(); + ExternRef::func_new(&env, statistic_set) } fn pokemon_get_stat_boosts( - env: &WebAssemblyEnv, + env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef> { - let statistic_set = pokemon.value(env).unwrap().stat_boosts(); - ExternRef::new(env.data().as_ref(), statistic_set) + let statistic_set = pokemon.value(&env).unwrap().stat_boosts(); + ExternRef::func_new(&env, statistic_set) } fn pokemon_get_individual_values( - env: &WebAssemblyEnv, + env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef> { - let statistic_set = pokemon.value(env).unwrap().individual_values(); - ExternRef::new(env.data().as_ref(), statistic_set) + let statistic_set = pokemon.value(&env).unwrap().individual_values(); + ExternRef::func_new(&env, statistic_set) } fn pokemon_get_effort_values( - env: &WebAssemblyEnv, + env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef> { - let statistic_set = pokemon.value(env).unwrap().effort_values(); - ExternRef::new(env.data().as_ref(), statistic_set) + let statistic_set = pokemon.value(&env).unwrap().effort_values(); + ExternRef::func_new(&env, statistic_set) } fn pokemon_get_species( - env: &WebAssemblyEnv, + env: FunctionEnvMut, pokemon: ExternRef, ) -> ExternRef { - let species = pokemon.value(env).unwrap().species(); - ExternRef::new(env.data().as_ref(), species) + let species = pokemon.value(&env).unwrap().species(); + ExternRef::func_new(&env, species) } fn pokemon_damage( - env: &WebAssemblyEnv, + env: FunctionEnvMut, pokemon: ExternRef, damage: u32, source: u8 ) { unsafe{ - pokemon.value(env).unwrap().damage(damage, transmute(source)); + pokemon.value(&env).unwrap().damage(damage, transmute(source)); } } } diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs b/src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs index ecb9773..25ba52d 100644 --- a/src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/turn_choice.rs @@ -4,22 +4,23 @@ use crate::dynamic_data::{LearnedMove, Pokemon, TurnChoice}; use crate::script_implementations::wasm::export_registry::register; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; +use wasmer::FunctionEnvMut; register! { fn turn_choice_get_user( - env: &WebAssemblyEnv, + env: FunctionEnvMut, turn_choice: ExternRef, ) -> ExternRef { - let turn_choice = turn_choice.value(env).unwrap(); - ExternRef::new(env.data().as_ref(), turn_choice.user().as_ref().deref()) + let turn_choice = turn_choice.value(&env).unwrap(); + ExternRef::func_new(&env, turn_choice.user().as_ref().deref()) } fn turn_choice_get_kind( - env: &WebAssemblyEnv, + env: FunctionEnvMut, turn_choice: ExternRef, ) -> u8 { - match turn_choice.value(env).unwrap() { + match turn_choice.value(&env).unwrap() { TurnChoice::Move(_) => 0, TurnChoice::Item(_) => 1, TurnChoice::Switch(_) => 2, @@ -29,30 +30,30 @@ register! { } fn turn_choice_move_used_move( - env: &WebAssemblyEnv, + env: FunctionEnvMut, turn_choice: ExternRef, ) -> ExternRef { - if let TurnChoice::Move(d) = turn_choice.value(env).unwrap() { - return ExternRef::new(env.data().as_ref(), d.used_move().as_ref()); + if let TurnChoice::Move(d) = turn_choice.value(&env).unwrap() { + return ExternRef::func_new(&env, d.used_move().as_ref()); } panic!("Invalid turn choice"); } fn turn_choice_move_target_side( - env: &WebAssemblyEnv, + env: FunctionEnvMut, turn_choice: ExternRef, ) -> u8 { - if let TurnChoice::Move(d) = turn_choice.value(env).unwrap() { + if let TurnChoice::Move(d) = turn_choice.value(&env).unwrap() { return d.target_side(); } panic!("Invalid turn choice"); } fn turn_choice_move_target_index( - env: &WebAssemblyEnv, + env: FunctionEnvMut, turn_choice: ExternRef, ) -> u8 { - if let TurnChoice::Move(d) = turn_choice.value(env).unwrap() { + if let TurnChoice::Move(d) = turn_choice.value(&env).unwrap() { return d.target_index(); } panic!("Invalid turn choice"); diff --git a/src/script_implementations/wasm/export_registry/mod.rs b/src/script_implementations/wasm/export_registry/mod.rs index fac9c6b..a5a1df4 100644 --- a/src/script_implementations/wasm/export_registry/mod.rs +++ b/src/script_implementations/wasm/export_registry/mod.rs @@ -1,7 +1,7 @@ use std::ffi::CString; use std::mem::{align_of, forget}; -use wasmer::{Exports, Store}; +use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut}; use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; @@ -14,19 +14,12 @@ mod dynamic_data; mod static_data; /// Register a single function. -#[allow(unused_macros)] -macro_rules! register_func { - ($exports: ident, $store: ident, $func: ident) => { - $exports.insert(stringify!($func), Function::new_native($store, $func)); - }; -} - -/// Register a single function that requires environment data. macro_rules! register_func_with_env { - ($exports: ident, $store: ident, $func: ident, $env: expr) => { - $exports.insert( + ($imports: ident, $store: ident, $func: ident, $env: expr) => { + $imports.define( + "env", stringify!($func), - wasmer::Function::new_native_with_env($store, $env.clone(), $func), + wasmer::Function::new_typed_with_env($store, $env, $func), ); }; } @@ -41,19 +34,22 @@ macro_rules! register { manual $manual_name:ident )? ) => { - pub(crate) fn register(exports: &mut crate::script_implementations::wasm::export_registry::Exports, - store: &crate::script_implementations::wasm::export_registry::Store, - env: crate::script_implementations::wasm::script_resolver::WebAssemblyEnv) { - $( + $( fn $name( $( $par: $par_type, )* ) $(-> $return)* $block - crate::script_implementations::wasm::export_registry::register_func_with_env!(exports, store, $name, env); )* - $( $manual_name(exports, store, env) )* + + 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) )* } }; } @@ -62,27 +58,31 @@ pub(crate) use register; pub(crate) use register_func_with_env; /// Register the functions we expose to WASM. -pub(crate) fn register_webassembly_funcs(exports: &mut Exports, store: &Store, env: WebAssemblyEnv) { - register_func_with_env!(exports, store, _print, env); - register_func_with_env!(exports, store, _error, env); - register_func_with_env!(exports, store, _vec_extern_ref_get_value, env); +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(exports, store, env.clone()); - dynamic_data::register(exports, store, env.clone()); + static_data::register(imports, store, env); + dynamic_data::register(imports, store, env); - register_func_with_env!(exports, store, string_key_get_hash, env); - register_func_with_env!(exports, store, string_key_get_str, env); - register_func_with_env!(exports, store, effect_parameter_get_type, env); - register_func_with_env!(exports, store, effect_parameter_as_bool, env); - register_func_with_env!(exports, store, effect_parameter_as_int, env); - register_func_with_env!(exports, store, effect_parameter_as_float, env); - register_func_with_env!(exports, store, effect_parameter_as_string, 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: &WebAssemblyEnv, p: u32, len: u32) { +fn _print(env: FunctionEnvMut, p: u32, len: u32) { unsafe { - let mem: *mut u8 = env.data().memory().data_ptr().offset(p as isize); + 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); @@ -91,11 +91,19 @@ fn _print(env: &WebAssemblyEnv, p: u32, len: u32) { /// Triggers when WASM panics. #[track_caller] -fn _error(env: &WebAssemblyEnv, message: u32, message_len: u32, file: u32, file_len: u32, line: u32, position: u32) { +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().memory().data_ptr().offset(message as isize); + 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().memory().data_ptr().offset(file as isize); + 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: {}:{}", @@ -105,20 +113,21 @@ fn _error(env: &WebAssemblyEnv, message: u32, message_len: u32, file: u32, file_ } /// Get a single item from an earlier passed VecExternRef -fn _vec_extern_ref_get_value(env: &WebAssemblyEnv, reference: u32, index: u32) -> u32 { - env.data().get_extern_vec_ref_extern_ref(reference, index) +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: &WebAssemblyEnv, string_key: ExternRef) -> u32 { - string_key.value(env).unwrap().hash() +fn string_key_get_hash(env: FunctionEnvMut, string_key: ExternRef) -> u32 { + string_key.value(&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: &WebAssemblyEnv, string_key: ExternRef) -> u32 { - let string_key = string_key.value(env).unwrap().str(); +fn string_key_get_str(env: FunctionEnvMut, string_key: ExternRef) -> u32 { + let string_key = string_key.value(&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 = @@ -131,8 +140,8 @@ fn string_key_get_str(env: &WebAssemblyEnv, string_key: ExternRef) -> } /// Gets the type of an EffectParameter -fn effect_parameter_get_type(env: &WebAssemblyEnv, parameter: ExternRef) -> u8 { - let v = parameter.value(env).unwrap(); +fn effect_parameter_get_type(env: FunctionEnvMut, parameter: ExternRef) -> u8 { + let v = parameter.value(&env).unwrap(); match v { EffectParameter::Bool(_) => 1, EffectParameter::Int(_) => 2, @@ -142,8 +151,8 @@ fn effect_parameter_get_type(env: &WebAssemblyEnv, parameter: ExternRef) -> u8 { - let v = parameter.value(env).unwrap(); +fn effect_parameter_as_bool(env: FunctionEnvMut, parameter: ExternRef) -> u8 { + let v = parameter.value(&env).unwrap(); match v { EffectParameter::Bool(b) => { if *b { @@ -157,8 +166,8 @@ fn effect_parameter_as_bool(env: &WebAssemblyEnv, parameter: ExternRef) -> i64 { - let v = parameter.value(env).unwrap(); +fn effect_parameter_as_int(env: FunctionEnvMut, parameter: ExternRef) -> i64 { + let v = parameter.value(&env).unwrap(); match v { EffectParameter::Int(i) => *i, _ => panic!("Unexpected parameter type!"), @@ -166,8 +175,8 @@ fn effect_parameter_as_int(env: &WebAssemblyEnv, parameter: ExternRef) -> f32 { - let v = parameter.value(env).unwrap(); +fn effect_parameter_as_float(env: FunctionEnvMut, parameter: ExternRef) -> f32 { + let v = parameter.value(&env).unwrap(); match v { EffectParameter::Float(f) => *f, _ => panic!("Unexpected parameter type!"), @@ -175,10 +184,13 @@ fn effect_parameter_as_float(env: &WebAssemblyEnv, parameter: ExternRef) -> ExternRef { - let v = parameter.value(env).unwrap(); +fn effect_parameter_as_string( + env: FunctionEnvMut, + parameter: ExternRef, +) -> ExternRef { + let v = parameter.value(&env).unwrap(); match v { - EffectParameter::String(s) => ExternRef::new(env.data().as_ref(), s), + EffectParameter::String(s) => ExternRef::func_new(&env, s), _ => panic!("Unexpected parameter type!"), } } diff --git a/src/script_implementations/wasm/export_registry/static_data/mod.rs b/src/script_implementations/wasm/export_registry/static_data/mod.rs index 2643b19..a0ee609 100644 --- a/src/script_implementations/wasm/export_registry/static_data/mod.rs +++ b/src/script_implementations/wasm/export_registry/static_data/mod.rs @@ -1,4 +1,4 @@ -use wasmer::{Exports, Store}; +use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut}; use crate::defines::LevelInt; use crate::script_implementations::wasm::export_registry::register; @@ -12,41 +12,41 @@ mod moves; mod species; register! { - fn static_data_get_move_library(env: &WebAssemblyEnv, data_library: ExternRef) -> ExternRef { - ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().moves()) + fn static_data_get_move_library(env: FunctionEnvMut, data_library: ExternRef) -> ExternRef { + ExternRef::func_new(&env, data_library.value(&env).unwrap().moves()) } fn static_data_get_species_library( - env: &WebAssemblyEnv, + env: FunctionEnvMut, data_library: ExternRef, ) -> ExternRef { - ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().species()) + ExternRef::func_new(&env, data_library.value(&env).unwrap().species()) } - fn static_data_get_item_library(env: &WebAssemblyEnv, data_library: ExternRef) -> ExternRef { - ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().items()) + fn static_data_get_item_library(env: FunctionEnvMut, data_library: ExternRef) -> ExternRef { + ExternRef::func_new(&env, data_library.value(&env).unwrap().items()) } - fn static_data_get_type_library(env: &WebAssemblyEnv, data_library: ExternRef) -> ExternRef { - ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().types()) + fn static_data_get_type_library(env: FunctionEnvMut, data_library: ExternRef) -> ExternRef { + ExternRef::func_new(&env, data_library.value(&env).unwrap().types()) } fn static_data_get_library_settings( - env: &WebAssemblyEnv, + env: FunctionEnvMut, data_library: ExternRef, ) -> ExternRef { - ExternRef::new(env.data().as_ref(), data_library.value(env).unwrap().settings()) + ExternRef::func_new(&env, data_library.value(&env).unwrap().settings()) } - fn library_settings_get_maximum_level(env: &WebAssemblyEnv, data_library: ExternRef) -> LevelInt { - data_library.value(env).unwrap().maximum_level() + fn library_settings_get_maximum_level(env: FunctionEnvMut, data_library: ExternRef) -> LevelInt { + data_library.value(&env).unwrap().maximum_level() } manual manual_registration } /// Additional required manual registration. -fn manual_registration(exports: &mut Exports, store: &Store, env: WebAssemblyEnv) { - moves::register(exports, store, env.clone()); - species::register(exports, store, env); +fn manual_registration(imports: &mut Imports, store: &mut StoreMut, env: &FunctionEnv) { + moves::register(imports, store, env); + species::register(imports, store, env); } diff --git a/src/script_implementations/wasm/export_registry/static_data/moves.rs b/src/script_implementations/wasm/export_registry/static_data/moves.rs index 5e3a34e..a0a64a9 100644 --- a/src/script_implementations/wasm/export_registry/static_data/moves.rs +++ b/src/script_implementations/wasm/export_registry/static_data/moves.rs @@ -3,66 +3,67 @@ use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::{DataLibrary, MoveData, MoveLibrary}; use crate::StringKey; +use wasmer::FunctionEnvMut; register! { fn move_library_get_move( - env: &WebAssemblyEnv, + env: FunctionEnvMut, lib: ExternRef, string_key: ExternRef, ) -> ExternRef { - let lib = lib.value(env).unwrap(); - let m = lib.get(string_key.value(env).unwrap()); + let lib = lib.value(&env).unwrap(); + let m = lib.get(string_key.value(&env).unwrap()); if let Some(v) = m { - ExternRef::new(env.data().as_ref(), v) + ExternRef::func_new(&env, v) } else { ExternRef::null() } } -fn move_library_get_move_by_hash(env: &WebAssemblyEnv, lib: ExternRef, hash: u32) -> ExternRef { - let lib = lib.value(env).unwrap(); +fn move_library_get_move_by_hash(env: FunctionEnvMut, lib: ExternRef, hash: u32) -> ExternRef { + let lib = lib.value(&env).unwrap(); let m = lib.get_by_hash(hash); if let Some(v) = m { - ExternRef::new(env.data().as_ref(), v) + ExternRef::func_new(&env, v) } else { ExternRef::null() } } -fn move_data_get_name(env: &WebAssemblyEnv, move_data: ExternRef) -> ExternRef { - ExternRef::new(env.data().as_ref(), move_data.value(env).unwrap().name()) +fn move_data_get_name(env: FunctionEnvMut, move_data: ExternRef) -> ExternRef { + ExternRef::func_new(&env, move_data.value(&env).unwrap().name()) } -fn move_data_get_type(env: &WebAssemblyEnv, move_data: ExternRef) -> u8 { - move_data.value(env).unwrap().move_type().into() +fn move_data_get_type(env: FunctionEnvMut, move_data: ExternRef) -> u8 { + move_data.value(&env).unwrap().move_type().into() } -fn move_data_get_category(env: &WebAssemblyEnv, move_data: ExternRef) -> u8 { - move_data.value(env).unwrap().category() as u8 +fn move_data_get_category(env: FunctionEnvMut, move_data: ExternRef) -> u8 { + move_data.value(&env).unwrap().category() as u8 } -fn move_data_get_base_power(env: &WebAssemblyEnv, move_data: ExternRef) -> u8 { - move_data.value(env).unwrap().base_power() +fn move_data_get_base_power(env: FunctionEnvMut, move_data: ExternRef) -> u8 { + move_data.value(&env).unwrap().base_power() } -fn move_data_get_accuracy(env: &WebAssemblyEnv, move_data: ExternRef) -> u8 { - move_data.value(env).unwrap().accuracy() +fn move_data_get_accuracy(env: FunctionEnvMut, move_data: ExternRef) -> u8 { + move_data.value(&env).unwrap().accuracy() } -fn move_data_get_base_usages(env: &WebAssemblyEnv, move_data: ExternRef) -> u8 { - move_data.value(env).unwrap().base_usages() +fn move_data_get_base_usages(env: FunctionEnvMut, move_data: ExternRef) -> u8 { + move_data.value(&env).unwrap().base_usages() } -fn move_data_get_target(env: &WebAssemblyEnv, move_data: ExternRef) -> u8 { - move_data.value(env).unwrap().target() as u8 +fn move_data_get_target(env: FunctionEnvMut, move_data: ExternRef) -> u8 { + move_data.value(&env).unwrap().target() as u8 } -fn move_data_get_priority(env: &WebAssemblyEnv, move_data: ExternRef) -> i8 { - move_data.value(env).unwrap().priority() +fn move_data_get_priority(env: FunctionEnvMut, move_data: ExternRef) -> i8 { + move_data.value(&env).unwrap().priority() } -fn move_data_has_flag(env: &WebAssemblyEnv, move_data: ExternRef, flag: ExternRef) -> u8 { - if move_data.value(env).unwrap().has_flag(flag.value(env).unwrap()) { +fn move_data_has_flag(env: FunctionEnvMut, move_data: ExternRef, flag: ExternRef) -> u8 { + if move_data.value(&env).unwrap().has_flag(flag.value(&env).unwrap()) { 1 } else { 0 } } -fn move_data_has_flag_by_hash(env: &WebAssemblyEnv, move_data: ExternRef, flag_hash: u32) -> u8 { - if move_data.value(env).unwrap().has_flag_by_hash(flag_hash) { +fn move_data_has_flag_by_hash(env: FunctionEnvMut, move_data: ExternRef, flag_hash: u32) -> u8 { + if move_data.value(&env).unwrap().has_flag_by_hash(flag_hash) { 1 } else { 0 diff --git a/src/script_implementations/wasm/export_registry/static_data/species.rs b/src/script_implementations/wasm/export_registry/static_data/species.rs index 253b36f..b72779f 100644 --- a/src/script_implementations/wasm/export_registry/static_data/species.rs +++ b/src/script_implementations/wasm/export_registry/static_data/species.rs @@ -3,58 +3,59 @@ use crate::script_implementations::wasm::extern_ref::ExternRef; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; use crate::static_data::{DataLibrary, Species, SpeciesLibrary}; use crate::StringKey; +use wasmer::FunctionEnvMut; register! { fn species_library_get_species( - env: &WebAssemblyEnv, + env: FunctionEnvMut, lib: ExternRef, string_key: ExternRef, ) -> ExternRef { - let lib = lib.value(env).unwrap(); - let m = lib.get(string_key.value(env).unwrap()); + let lib = lib.value(&env).unwrap(); + let m = lib.get(string_key.value(&env).unwrap()); if let Some(v) = m { - ExternRef::new(env.data().as_ref(), v) + ExternRef::func_new(&env, v) } else { ExternRef::null() } } fn species_get_capture_rate( - env: &WebAssemblyEnv, + env: FunctionEnvMut, species: ExternRef, ) -> u8 { - species.value(env).unwrap().capture_rate() + species.value(&env).unwrap().capture_rate() } fn species_get_growth_rate( - env: &WebAssemblyEnv, + env: FunctionEnvMut, species: ExternRef, ) -> ExternRef { - let species = species.value(env).unwrap(); - ExternRef::new(env.data().as_ref(), species.growth_rate()) + let species = species.value(&env).unwrap(); + ExternRef::func_new(&env, species.growth_rate()) } fn species_get_gender_rate( - env: &WebAssemblyEnv, + env: FunctionEnvMut, species: ExternRef, ) -> f32 { - species.value(env).unwrap().gender_rate() + species.value(&env).unwrap().gender_rate() } fn species_get_name( - env: &WebAssemblyEnv, + env: FunctionEnvMut, species: ExternRef, ) -> ExternRef { - let species = species.value(env).unwrap(); - ExternRef::new(env.data().as_ref(), species.name()) + let species = species.value(&env).unwrap(); + ExternRef::func_new(&env, species.name()) } fn species_get_id( - env: &WebAssemblyEnv, + env: FunctionEnvMut, species: ExternRef, ) -> u16 { - species.value(env).unwrap().id() + species.value(&env).unwrap().id() } diff --git a/src/script_implementations/wasm/extern_ref.rs b/src/script_implementations/wasm/extern_ref.rs index f55980f..5db2d86 100644 --- a/src/script_implementations/wasm/extern_ref.rs +++ b/src/script_implementations/wasm/extern_ref.rs @@ -1,12 +1,12 @@ use std::marker::PhantomData; use std::mem::transmute; -use unique_type_id::UniqueTypeId; -use wasmer::FromToNativeWasmType; - use crate::script_implementations::wasm::script_resolver::{ WebAssemblyEnv, WebAssemblyEnvironmentData, WebAssemblyScriptResolver, }; +use unique_type_id::UniqueTypeId; +use wasmer::FromToNativeWasmType; +use wasmer::FunctionEnvMut; /// An Extern Ref allows us to pass objects to WASM without actually passing raw memory, or /// requiring us to make copies. Instead, we pass a simple increment index, that we can then use @@ -28,6 +28,15 @@ impl> ExternRef { } } + /// Instantiates a new ExternRef for a bit of data using the function environment. If we already + /// have made an Extern Ref for this data and type, we use that instead. + pub fn func_new(env: &FunctionEnvMut, value: &T) -> Self { + Self { + index: env.data().data().get_extern_ref_index(value), + _phantom: Default::default(), + } + } + /// Creates an ExternRef with a given resolver. This can be used in cases where we do not have an environment variable. pub(crate) fn new_with_resolver(resolver: &WebAssemblyScriptResolver, value: &T) -> Self { Self { @@ -46,8 +55,8 @@ impl> ExternRef { /// Returns the real value for a given ExternRef. Note that the requested type must be the same as the type of the /// value when it was passed before. If these types do not match, this will panic. - pub fn value<'a, 'b>(&'a self, env: &'b WebAssemblyEnv) -> Option<&'b T> { - let ptr = env.data().get_extern_ref_value(self.index) as *const T; + pub fn value<'a, 'b>(&'a self, env: &'b FunctionEnvMut) -> Option<&'b T> { + let ptr = env.data().data().get_extern_ref_value(self.index) as *const T; unsafe { ptr.as_ref() } } } diff --git a/src/script_implementations/wasm/script.rs b/src/script_implementations/wasm/script.rs index 4bfd1e9..f132255 100644 --- a/src/script_implementations/wasm/script.rs +++ b/src/script_implementations/wasm/script.rs @@ -73,7 +73,7 @@ impl Script for WebAssemblyScript { let env = self.environment.upgrade().unwrap(); let func = env.script_function_cache().stack(&env); if let Some(func) = func { - func.call(self.self_ptr).unwrap(); + func.call(&mut env.store_mut(), self.self_ptr).unwrap(); } } @@ -84,7 +84,7 @@ impl Script for WebAssemblyScript { let env = self.environment.upgrade().unwrap(); let func = env.script_function_cache().on_remove(&env); if let Some(func) = func { - func.call(self.self_ptr).unwrap(); + func.call(&mut env.store_mut(), self.self_ptr).unwrap(); } } @@ -97,6 +97,7 @@ impl Script for WebAssemblyScript { let func = env.script_function_cache().on_initialize(&env); if let Some(func) = func { func.call( + &mut env.store_mut(), self.self_ptr, ExternRef::new(env.as_ref(), library), VecExternRef::new(env.as_ref(), pars), @@ -112,7 +113,12 @@ impl Script for WebAssemblyScript { let env = self.environment.upgrade().unwrap(); let func = env.script_function_cache().on_before_turn(&env); if let Some(func) = func { - func.call(self.self_ptr, ExternRef::new(env.as_ref(), choice)).unwrap(); + func.call( + &mut env.store_mut(), + self.self_ptr, + ExternRef::new(env.as_ref(), choice), + ) + .unwrap(); } } @@ -125,8 +131,13 @@ impl Script for WebAssemblyScript { let func = env.script_function_cache().change_speed(&env); if let Some(func) = func { let ptr = env.temp_allocate_mem_typed::(*speed); - func.call(self.self_ptr, ExternRef::new(env.as_ref(), choice), ptr.wasm_pointer) - .unwrap(); + func.call( + &mut env.store_mut(), + self.self_ptr, + ExternRef::new(env.as_ref(), choice), + ptr.wasm_pointer, + ) + .unwrap(); *speed = *ptr.value(); } } @@ -143,8 +154,13 @@ impl Script for WebAssemblyScript { let func = env.script_function_cache().change_priority(&env); if let Some(func) = func { let ptr = env.temp_allocate_mem_typed::(*priority); - func.call(self.self_ptr, ExternRef::new(env.as_ref(), choice), ptr.wasm_pointer) - .unwrap(); + func.call( + &mut env.store_mut(), + self.self_ptr, + ExternRef::new(env.as_ref(), choice), + ptr.wasm_pointer, + ) + .unwrap(); *priority = *ptr.value(); } } diff --git a/src/script_implementations/wasm/script_function_cache.rs b/src/script_implementations/wasm/script_function_cache.rs index 16ae8f7..072c2bc 100644 --- a/src/script_implementations/wasm/script_function_cache.rs +++ b/src/script_implementations/wasm/script_function_cache.rs @@ -2,13 +2,13 @@ use std::sync::Arc; use parking_lot::RwLock; use paste::paste; -use wasmer::NativeFunc; use crate::dynamic_data::{DynamicLibrary, TurnChoice}; use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef}; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData; use crate::static_data::EffectParameter; use crate::StringKey; +use wasmer::TypedFunction; /// A macro to generate the script function cache a bit easier. macro_rules! script_function_cache { @@ -21,7 +21,7 @@ macro_rules! script_function_cache { #[allow(unused_parens)] pub struct ScriptFunctionCache { $( - $name: RwLock>>, + $name: RwLock>>, )* } @@ -34,7 +34,7 @@ macro_rules! script_function_cache { let exported = env.exported_functions(); let f = exported.get::(&stringify!([< script_ $name >]).into()); if let Some(f) = f { - let func: NativeFunc<(u32 $(,$par_type)*), ()> = f.native().unwrap(); + let func: TypedFunction<(u32 $(,$par_type)*), ()> = f.typed(&env.store_ref()).unwrap(); let _ = self.$name.write().insert(func); } } @@ -44,7 +44,7 @@ macro_rules! script_function_cache { pub(crate) fn [<$name>]( &self, env: &Arc, - ) -> Option> { + ) -> Option> { { let read_lock = self.$name.read(); if let Some(f) = read_lock.as_ref() { diff --git a/src/script_implementations/wasm/script_resolver.rs b/src/script_implementations/wasm/script_resolver.rs index 8971e7c..716ad9f 100644 --- a/src/script_implementations/wasm/script_resolver.rs +++ b/src/script_implementations/wasm/script_resolver.rs @@ -1,14 +1,14 @@ use std::fmt::{Debug, Formatter}; -use std::mem::{align_of, size_of}; +use std::mem::{align_of, forget, size_of}; use std::sync::{Arc, Weak}; use hashbrown::{HashMap, HashSet}; -use parking_lot::lock_api::{MappedRwLockReadGuard, RwLockReadGuard}; +use parking_lot::lock_api::RwLockReadGuard; use parking_lot::{RawRwLock, RwLock}; use unique_type_id::UniqueTypeId; use wasmer::{ - Cranelift, Exports, Extern, Features, Function, ImportObject, Instance, Memory, Module, NativeFunc, Store, - Universal, Value, WasmerEnv, + AsStoreMut, AsStoreRef, Cranelift, EngineBuilder, Extern, Features, Function, FunctionEnv, Imports, Instance, + Memory, Module, Store, StoreMut, StoreRef, TypedFunction, Value, }; use crate::dynamic_data::{ItemScript, Script, ScriptResolver}; @@ -24,7 +24,7 @@ use crate::{PkmnResult, ScriptCategory, StringKey}; /// A WebAssembly script resolver implements the dynamic scripts functionality with WebAssembly. pub struct WebAssemblyScriptResolver { /// The global state storage of WASM. - store: Store, + _store: *mut Store, /// The WASM modules we have loaded. modules: Vec, /// Our currently loaded WASM instances. Empty until finalize() is called, after which the loaded modules get turned @@ -32,7 +32,7 @@ pub struct WebAssemblyScriptResolver { instances: Vec, /// This is the WASM function to load a script. - load_script_fn: Option), u32>>, + load_script_fn: Option), u32>>, /// Script capabilities tell us which functions are implemented on a given script. This allows us to skip unneeded /// WASM calls. @@ -54,39 +54,55 @@ struct ScriptCapabilitiesKey { impl WebAssemblyScriptResolver { /// Instantiates a new WebAssemblyScriptResolver. pub fn new() -> Box { - let config = Cranelift::default(); + let compiler = Cranelift::default(); let mut features = Features::new(); features.multi_value = true; features.reference_types = true; - let universal = Universal::new(config).features(features); - let engine = universal.engine(); - let store = Store::new(&engine); + let engine = EngineBuilder::new(compiler).set_features(Some(features)); + let mut store = Box::new(Store::new(engine)); + let store_ptr: *mut Store = store.as_mut(); + forget(store); + let s = Self { - store, + _store: store_ptr, modules: Default::default(), instances: Default::default(), load_script_fn: None, script_capabilities: Default::default(), - environment_data: Arc::new(Default::default()), + environment_data: Arc::new(WebAssemblyEnvironmentData::new(store_ptr)), }; + Box::new(s) } + /// Get an immutable reference to the current WASM Store. + fn store_ref(&self) -> StoreRef<'_> { + unsafe { self._store.as_ref().unwrap().as_store_ref() } + } + + /// Get a mutable reference to the current WASM Store. + fn store_mut(&self) -> StoreMut<'_> { + unsafe { self._store.as_mut().unwrap().as_store_mut() } + } + /// Load a compiled WASM module. pub fn load_wasm_from_bytes(&mut self, bytes: &[u8]) { // FIXME: Error handling - let module = Module::new(&self.store, bytes).unwrap(); + let module = Module::new(&self.store_ref(), bytes).unwrap(); self.modules.push(module); } /// Initialise all the data we need. pub fn finalize(&mut self) { - let mut imports = ImportObject::new(); - let mut exports = Exports::new(); + let mut imports = Imports::new(); + //let mut exports = Exports::new(); - let env = WebAssemblyEnv::new(Arc::downgrade(&self.environment_data)); - register_webassembly_funcs(&mut exports, &self.store, env); - imports.register("env", exports); + let env = FunctionEnv::new( + &mut self.store_mut(), + WebAssemblyEnv::new(Arc::downgrade(&self.environment_data)), + ); + register_webassembly_funcs(&mut imports, &mut self.store_mut(), &env); + //imports.register("env", exports); for module in &self.modules { for import in module.imports() { @@ -99,12 +115,12 @@ impl WebAssemblyScriptResolver { } } - let instance = Instance::new(module, &imports).unwrap(); + let instance = Instance::new(&mut self.store_mut(), module, &imports).unwrap(); let exports = &instance.exports; let init_fn = exports.get_extern("_init"); if let Some(Extern::Function(init_fn)) = init_fn { - init_fn.call(&[]).unwrap(); + init_fn.call(&mut self.store_mut(), &[]).unwrap(); } let mut exported_functions = self.environment_data.exported_functions.write(); @@ -120,17 +136,17 @@ impl WebAssemblyScriptResolver { } } if let Some(m) = &self.environment_data.memory.read().as_ref() { - m.grow(32).unwrap(); + m.grow(&mut self.store_mut(), 32).unwrap(); } if let Some(f) = exported_functions.get::(&"load_script".into()) { - self.load_script_fn = Some(f.native().unwrap()) + self.load_script_fn = Some(f.typed(&self.store_ref()).unwrap()) } if let Some(f) = exported_functions.get::(&"allocate_mem".into()) { let _ = self .environment_data .allocate_mem_fn .write() - .insert(f.native().unwrap()); + .insert(f.typed(&self.store_ref()).unwrap()); let temp_memory_slab = self.environment_data.allocate_mem(128, 1); let _ = self @@ -160,7 +176,11 @@ impl ScriptResolver for WebAssemblyScriptResolver { .load_script_fn .as_ref() .unwrap() - .call(category as u8, ExternRef::new_with_resolver(self, script_key)) + .call( + &mut self.store_mut(), + category as u8, + ExternRef::new_with_resolver(self, script_key), + ) .unwrap(); if script == 0 { return Ok(None); @@ -180,9 +200,10 @@ impl ScriptResolver for WebAssemblyScriptResolver { .read() .get::(&"get_script_capabilities".into()) { - let res = get_cap.call(&[Value::I32(script as i32)]).unwrap(); - let ptr = (self.environment_data.memory.read().as_ref().unwrap().data_ptr() - as *const WebAssemblyScriptCapabilities) + let res = get_cap + .call(&mut self.store_mut(), &[Value::I32(script as i32)]) + .unwrap(); + let ptr = (self.environment_data.memory() as *const WebAssemblyScriptCapabilities) .offset(res[0].i32().unwrap() as isize); let length = res[1].i32().unwrap() as usize; for i in 0..length { @@ -218,8 +239,15 @@ impl Debug for WebAssemblyScriptResolver { } } +impl Drop for WebAssemblyScriptResolver { + fn drop(&mut self) { + unsafe { + drop(Box::from_raw(self._store)); + } + } +} + /// This data is what is passed to every function that requires access to the global runtime context. -#[derive(Default)] pub struct WebAssemblyEnvironmentData { /// We currently have a hacky implementation of extern refs while we're waiting for ExternRef support to hit the /// wasm32-unknown-unknown target of Rust. As we don't want to pass raw memory pointers to WASM for security reasons, @@ -248,10 +276,13 @@ pub struct WebAssemblyEnvironmentData { script_function_cache: ScriptFunctionCache, /// This is the WASM function to allocate memory inside the WASM container. - allocate_mem_fn: RwLock>>, + allocate_mem_fn: RwLock>>, /// An allocator for quick short lifetime allocations within WASM. temp_allocator: RwLock>, + + /// The WASM store. + store: *mut Store, } /// A quick lookup so we can find the extern ref of the value. @@ -266,9 +297,25 @@ struct ExternRefLookupKey { } impl WebAssemblyEnvironmentData { + /// Instantiates new Environment data with a given store. + pub(crate) fn new(store: *mut Store) -> Self { + Self { + extern_ref_pointers: Default::default(), + extern_ref_pointers_lookup: Default::default(), + extern_ref_type_lookup: Default::default(), + extern_vec_ref_lookup: Default::default(), + memory: Default::default(), + exported_functions: Default::default(), + script_function_cache: Default::default(), + allocate_mem_fn: Default::default(), + temp_allocator: Default::default(), + store, + } + } + /// This returns the memory of the WASM container. - pub fn memory(&self) -> MappedRwLockReadGuard<'_, RawRwLock, Memory> { - RwLockReadGuard::map(self.memory.read(), |a| a.as_ref().unwrap()) + pub fn memory(&self) -> *mut u8 { + self.memory.read().as_ref().unwrap().view(&self.store_ref()).data_ptr() } /// This returns the functions exported from WASM. @@ -286,18 +333,14 @@ impl WebAssemblyEnvironmentData { /// The return is a tuple containing both the actual pointer to the memory (usable by the host), /// and the WASM offset to the memory (usable by the client). pub fn allocate_mem(&self, size: u32, align: u32) -> (*mut u8, u32) { - let wasm_ptr = self.allocate_mem_fn.read().as_ref().unwrap().call(size, align).unwrap(); - unsafe { - ( - self.memory - .read() - .as_ref() - .unwrap() - .data_ptr() - .offset(wasm_ptr as isize), - wasm_ptr, - ) - } + let wasm_ptr = self + .allocate_mem_fn + .read() + .as_ref() + .unwrap() + .call(&mut self.store_mut(), size, align) + .unwrap(); + unsafe { (self.memory().offset(wasm_ptr as isize), wasm_ptr) } } /// Allocates memory inside the WASM container with a given size and alignment. This memory is @@ -310,19 +353,9 @@ impl WebAssemblyEnvironmentData { .read() .as_ref() .unwrap() - .call(size_of::() as u32, align_of::() as u32) + .call(&mut self.store_mut(), size_of::() as u32, align_of::() as u32) .unwrap(); - unsafe { - ( - self.memory - .read() - .as_ref() - .unwrap() - .data_ptr() - .offset(wasm_ptr as isize), - wasm_ptr, - ) - } + unsafe { (self.memory().offset(wasm_ptr as isize), wasm_ptr) } } /// Allocate a piece of memory inside WASM with a very short lifespan. This is mainly used for @@ -419,6 +452,16 @@ impl WebAssemblyEnvironmentData { unsafe { (*ptr as *const T).as_ref().unwrap() } } + + /// The WASM store. + pub fn store_ref(&self) -> StoreRef<'_> { + unsafe { self.store.as_ref().unwrap().as_store_ref() } + } + + /// The mutable WASM store. + pub fn store_mut(&self) -> StoreMut<'_> { + unsafe { self.store.as_mut().unwrap().as_store_mut() } + } } /// The runtime environment for script execution. This is passed to most of the host functions being called. @@ -447,5 +490,3 @@ unsafe impl Send for WebAssemblyEnv {} unsafe impl Sync for WebAssemblyEnvironmentData {} unsafe impl Send for WebAssemblyEnvironmentData {} - -impl WasmerEnv for WebAssemblyEnv {}