A lot more work on WASM script execution
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2022-09-07 18:01:26 +02:00
parent f9761f61da
commit b1890681a1
102 changed files with 748 additions and 202 deletions

View File

@@ -0,0 +1,71 @@
use crate::dynamic_data::{
Battle, BattleParty, BattleRandom, BattleSide, ChoiceQueue, DynamicLibrary, Pokemon, PokemonParty,
};
use crate::script_implementations::wasm::export_registry::register;
use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef};
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
use wasmer::FunctionEnvMut;
register! {
fn battle_get_parties(
env: FunctionEnvMut<WebAssemblyEnv>,
battle: ExternRef<Battle>,
) -> VecExternRef<BattleParty> {
VecExternRef::new(env.data().data().as_ref(), battle.value_func(&env).unwrap().parties())
}
fn battle_get_choice_queue(
env: FunctionEnvMut<WebAssemblyEnv>,
battle: ExternRef<Battle>,
) -> ExternRef<ChoiceQueue> {
let queue = battle.value_func(&env).unwrap().current_turn_queue().read();
if let Some(queue) = queue.as_ref() {
ExternRef::func_new(&env, queue)
} else {
ExternRef::null()
}
}
fn battle_get_library(
env: FunctionEnvMut<WebAssemblyEnv>,
battle: ExternRef<Battle>,
) -> ExternRef<DynamicLibrary> {
ExternRef::func_new(&env, battle.value_func(&env).unwrap().library().as_ref())
}
fn battle_get_sides(
env: FunctionEnvMut<WebAssemblyEnv>,
battle: ExternRef<Battle>,
) -> VecExternRef<BattleSide> {
VecExternRef::new(env.data().data().as_ref(), battle.value_func(&env).unwrap().sides())
}
fn battle_get_random(
env: FunctionEnvMut<WebAssemblyEnv>,
battle: ExternRef<Battle>,
) -> ExternRef<BattleRandom> {
ExternRef::func_new(&env, battle.value_func(&env).unwrap().random())
}
fn battle_find_party_for_pokemon(
env: FunctionEnvMut<WebAssemblyEnv>,
battle: ExternRef<Battle>,
pokemon: ExternRef<Pokemon>
) -> ExternRef<BattleParty> {
let battle = battle.value_func(&env).unwrap();
let pokemon = pokemon.value_func(&env).unwrap();
for party in battle.parties() {
if party.party().has_pokemon(pokemon) {
return ExternRef::func_new(&env, party);
}
}
ExternRef::null()
}
fn battle_party_get_party(
env: FunctionEnvMut<WebAssemblyEnv>,
battle_party: ExternRef<BattleParty>,
) -> ExternRef<PokemonParty> {
ExternRef::func_new(&env, battle_party.value_func(&env).unwrap().party().as_ref())
}
}

View File

@@ -0,0 +1,30 @@
use crate::dynamic_data::BattleRandom;
use crate::script_implementations::wasm::export_registry::register;
use crate::script_implementations::wasm::extern_ref::ExternRef;
use wasmer::FunctionEnvMut;
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
register! {
fn battle_random_get(
env: FunctionEnvMut<WebAssemblyEnv>,
battle_random: ExternRef<BattleRandom>,
) -> i32 {
battle_random.value_func(&env).unwrap().get()
}
fn battle_random_get_max(
env: FunctionEnvMut<WebAssemblyEnv>,
battle_random: ExternRef<BattleRandom>,
max: i32
) -> i32 {
battle_random.value_func(&env).unwrap().get_max(max)
}
fn battle_random_get_between(
env: FunctionEnvMut<WebAssemblyEnv>,
battle_random: ExternRef<BattleRandom>,
min: i32,
max: i32
) -> i32 {
battle_random.value_func(&env).unwrap().get_between(min, max)
}
}

View File

@@ -0,0 +1,123 @@
use crate::dynamic_data::{Battle, BattleSide, Pokemon, VolatileScriptsOwner};
use crate::script_implementations::wasm::export_registry::register;
use crate::script_implementations::wasm::extern_ref::ExternRef;
use crate::script_implementations::wasm::script::WebAssemblyScript;
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
use crate::{ScriptCategory, StringKey};
use std::ffi::CString;
use wasmer::{FunctionEnvMut, Value};
register! {
fn battleside_has_fled_battle(
env: FunctionEnvMut<WebAssemblyEnv>,
side: ExternRef<BattleSide>,
) -> u8 {
if side.value_func(&env).unwrap().has_fled_battle() { 1 } else { 0 }
}
fn battleside_is_defeated(
env: FunctionEnvMut<WebAssemblyEnv>,
side: ExternRef<BattleSide>,
) -> u8 {
if side.value_func(&env).unwrap().is_defeated() { 1 } else { 0 }
}
fn battleside_get_side_index(
env: FunctionEnvMut<WebAssemblyEnv>,
side: ExternRef<BattleSide>,
) -> u8 {
side.value_func(&env).unwrap().index()
}
fn battleside_get_pokemon_per_side(
env: FunctionEnvMut<WebAssemblyEnv>,
side: ExternRef<BattleSide>,
) -> u8 {
side.value_func(&env).unwrap().pokemon_per_side()
}
fn battleside_get_battle(
env: FunctionEnvMut<WebAssemblyEnv>,
side: ExternRef<BattleSide>,
) -> ExternRef<Battle> {
ExternRef::func_new(&env, side.value_func(&env).unwrap().battle())
}
fn battleside_get_pokemon(
env: FunctionEnvMut<WebAssemblyEnv>,
side: ExternRef<BattleSide>,
index: u32
) -> ExternRef<Pokemon> {
if let Some(Some(p)) = side.value_func(&env).unwrap().pokemon().get(index as usize) {
ExternRef::func_new(&env, p.as_ref())
} else {
ExternRef::null()
}
}
fn battleside_add_volatile_by_name(
env: FunctionEnvMut<WebAssemblyEnv>,
side: ExternRef<BattleSide>,
name_ptr: u32
) -> u32 {
unsafe {
let c_name = CString::from_raw(env.data().data().get_raw_pointer(name_ptr));
let script = side.value_func(&env).unwrap().add_volatile_script(&c_name.into()).unwrap();
if let Some(script) = script {
let script = script.get_as::<WebAssemblyScript>();
script.get_wasm_pointer()
} else {
0
}
}
}
fn battleside_add_volatile(
env: FunctionEnvMut<WebAssemblyEnv>,
side: ExternRef<BattleSide>,
script_ptr: u32
) -> u32 {
let side = side.value_func(&env).unwrap();
let name_ptr = env.data().data().exported_functions().get(&StringKey::new("script_get_name")).unwrap().call(&mut env.data().data().store_mut(), &[Value::I32(script_ptr as i32)]).unwrap().get(0).unwrap().i32().unwrap() as u32;
unsafe{
let name_ptr: CString = CString::from_raw(env.data().data().get_raw_pointer(name_ptr));
let script = env.data().data().setup_script(script_ptr, ScriptCategory::Side, &name_ptr.into(), side.into()).unwrap();
if let Some(script) = script {
let script = side.add_volatile_script_with_script(script);
script.unwrap().unwrap().get_as::<WebAssemblyScript>().get_wasm_pointer()
} else {
0
}
}
}
fn battleside_get_volatile(
env: FunctionEnvMut<WebAssemblyEnv>,
side: ExternRef<BattleSide>,
name_ptr: u32
) -> u32 {
unsafe {
let c_name = CString::from_raw(env.data().data().get_raw_pointer(name_ptr));
let script = side.value_func(&env).unwrap().get_volatile_script(&c_name.into());
if let Some(script) = script {
let script = script.get_as::<WebAssemblyScript>();
script.get_wasm_pointer()
} else {
0
}
}
}
fn battleside_remove_volatile(
env: FunctionEnvMut<WebAssemblyEnv>,
side: ExternRef<BattleSide>,
name_ptr: u32
) {
unsafe {
let c_name = CString::from_raw(env.data().data().get_raw_pointer(name_ptr));
side.value_func(&env).unwrap().remove_volatile_script(&c_name.into());
}
}
}

View File

@@ -0,0 +1,19 @@
use crate::dynamic_data::{ChoiceQueue, Pokemon};
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 choice_queue_move_pokemon_choice_next(
env: FunctionEnvMut<WebAssemblyEnv>,
battle_random: ExternRef<ChoiceQueue>,
pokemon: ExternRef<Pokemon>
) -> u8 {
if battle_random.value_func(&env).unwrap().move_pokemon_choice_next(pokemon.value_func(&env).unwrap()) {
1
} else {
0
}
}
}

View File

@@ -0,0 +1,45 @@
use crate::dynamic_data::{ExecutingMove, HitData, LearnedMove, Pokemon};
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 executing_move_get_user(
env: FunctionEnvMut<WebAssemblyEnv>,
executing_move: ExternRef<ExecutingMove>,
) -> ExternRef<Pokemon> {
ExternRef::func_new(&env, executing_move.value_func(&env).unwrap().user().as_ref())
}
fn executing_move_get_use_move(
env: FunctionEnvMut<WebAssemblyEnv>,
executing_move: ExternRef<ExecutingMove>,
) -> ExternRef<MoveData> {
ExternRef::func_new(&env, executing_move.value_func(&env).unwrap().use_move().as_ref())
}
fn executing_move_get_chosen_move(
env: FunctionEnvMut<WebAssemblyEnv>,
executing_move: ExternRef<ExecutingMove>,
) -> ExternRef<LearnedMove> {
ExternRef::func_new(&env, executing_move.value_func(&env).unwrap().chosen_move().as_ref())
}
fn executing_move_get_number_of_hits(
env: FunctionEnvMut<WebAssemblyEnv>,
executing_move: ExternRef<ExecutingMove>,
) -> u8 {
executing_move.value_func(&env).unwrap().number_of_hits()
}
fn executing_move_get_hit_data(
env: FunctionEnvMut<WebAssemblyEnv>,
executing_move: ExternRef<ExecutingMove>,
target: ExternRef<Pokemon>,
hit: u8
) -> ExternRef<HitData> {
ExternRef::func_new(&env, executing_move.value_func(&env).unwrap().get_hit_data(target.value_func(&env).unwrap(), hit).unwrap())
}
}

View File

@@ -0,0 +1,14 @@
use crate::dynamic_data::HitData;
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 hit_data_fail(
env: FunctionEnvMut<WebAssemblyEnv>,
hit: ExternRef<HitData>,
) {
hit.value_func(&env).unwrap().fail()
}
}

View File

@@ -1,31 +1,67 @@
use crate::dynamic_data::DynamicLibrary;
use crate::dynamic_data::{DynamicLibrary, ScriptOwnerData};
use crate::script_implementations::wasm::export_registry::register;
use crate::script_implementations::wasm::extern_ref::ExternRef;
use std::sync::atomic::Ordering;
use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut};
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
use crate::static_data::StaticData;
mod battle_random;
/// Battle side registration
mod battle_side;
mod choice_queue;
mod executing_move;
/// Learned move registration
mod learned_move;
mod party;
/// Pokemon registration
mod pokemon;
/// Turn choice registration
mod turn_choice;
mod hit_data;
mod battle;
register! {
fn dynamic_library_get_static_data(
env: FunctionEnvMut<WebAssemblyEnv>,
dynamic_lib: ExternRef<DynamicLibrary>,
) -> ExternRef<StaticData> {
ExternRef::func_new(&env, dynamic_lib.value_func(&env).unwrap().static_data())
}
env: FunctionEnvMut<WebAssemblyEnv>,
dynamic_lib: ExternRef<DynamicLibrary>,
) -> ExternRef<StaticData> {
ExternRef::func_new(&env, dynamic_lib.value_func(&env).unwrap().static_data())
}
fn script_get_owner(
env: FunctionEnvMut<WebAssemblyEnv>,
script: u32,
) -> u32 {
unsafe {
let script = env.data().data().get_loaded_script(script);
if let Some(script) = script {
match script.get_owner() {
ScriptOwnerData::Pokemon(p) => env.data().data().get_extern_ref_index(p.load(Ordering::Relaxed).as_ref().unwrap()),
ScriptOwnerData::BattleSide(p) => env.data().data().get_extern_ref_index(p.load(Ordering::Relaxed).as_ref().unwrap()),
ScriptOwnerData::Battle(p) => env.data().data().get_extern_ref_index(p.load(Ordering::Relaxed).as_ref().unwrap()),
ScriptOwnerData::None => 0,
}
} else {
0
}
}
}
manual manual_register
}
/// Additional required manual registration
fn manual_register(imports: &mut Imports, store: &mut StoreMut, env: &FunctionEnv<WebAssemblyEnv>) {
battle::register(imports, store, env);
turn_choice::register(imports, store, env);
pokemon::register(imports, store, env);
learned_move::register(imports, store, env);
battle_side::register(imports, store, env);
battle_random::register(imports, store, env);
choice_queue::register(imports, store, env);
party::register(imports, store, env);
executing_move::register(imports, store, env);
hit_data::register(imports, store, env);
}

View File

@@ -0,0 +1,26 @@
use crate::dynamic_data::{Pokemon, PokemonParty};
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 party_get_pokemon(
env: FunctionEnvMut<WebAssemblyEnv>,
party: ExternRef<PokemonParty>,
index: u32
) -> ExternRef<Pokemon> {
if let Some(v) = &party.value_func(&env).unwrap().pokemon()[index as usize] {
ExternRef::func_new(&env, v.as_ref())
} else {
ExternRef::null()
}
}
fn party_get_length(
env: FunctionEnvMut<WebAssemblyEnv>,
party: ExternRef<PokemonParty>,
) -> u32 {
party.value_func(&env).unwrap().length() as u32
}
}

View File

@@ -1,11 +1,11 @@
use std::mem::transmute;
use crate::dynamic_data::{DynamicLibrary, Pokemon};
use crate::dynamic_data::{Battle, DynamicLibrary, LearnedMove, Pokemon};
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::StatisticSet;
use crate::static_data::{ClampedStatisticSet, Species};
use crate::static_data::{Item, StatisticSet};
use wasmer::FunctionEnvMut;
register! {
@@ -75,4 +75,70 @@ register! {
pokemon.value_func(&env).unwrap().damage(damage, transmute(source));
}
}
fn pokemon_get_learned_move(
env: FunctionEnvMut<WebAssemblyEnv>,
pokemon: ExternRef<Pokemon>,
index: u32
) -> ExternRef<LearnedMove> {
let read_lock = pokemon.value_func(&env).unwrap().learned_moves().read();
let mv = read_lock.get(index as usize);
if let Some(Some(mv)) = mv {
ExternRef::func_new(&env, mv)
}
else{
ExternRef::null()
}
}
fn pokemon_change_stat_boost(
env: FunctionEnvMut<WebAssemblyEnv>,
pokemon: ExternRef<Pokemon>,
stat: u8,
amount: i8,
self_inflicted: u8
) -> u8 {
unsafe{
if pokemon.value_func(&env).unwrap().change_stat_boost(transmute(stat), amount, self_inflicted == 1) {
1
} else {
0
}
}
}
fn pokemon_get_battle(
env: FunctionEnvMut<WebAssemblyEnv>,
pokemon: ExternRef<Pokemon>,
) -> ExternRef<Battle> {
if let Some(battle) = pokemon.value_func(&env).unwrap().get_battle() {
ExternRef::func_new(&env, battle)
} else {
ExternRef::null()
}
}
fn pokemon_get_battle_side_index(
env: FunctionEnvMut<WebAssemblyEnv>,
pokemon: ExternRef<Pokemon>,
) -> u8 {
if let Some(i) = pokemon.value_func(&env).unwrap().get_battle_index() {
i
} else {
0
}
}
fn pokemon_get_held_item(
env: FunctionEnvMut<WebAssemblyEnv>,
pokemon: ExternRef<Pokemon>,
) -> ExternRef<Item> {
let read_lock = pokemon.value_func(&env).unwrap().held_item().read();
if let Some(item) = read_lock.as_ref() {
ExternRef::func_new(&env, item.as_ref())
} else {
ExternRef::null()
}
}
}

View File

@@ -59,4 +59,11 @@ register! {
panic!("Invalid turn choice");
}
fn turn_choice_fail(
env: FunctionEnvMut<WebAssemblyEnv>,
turn_choice: ExternRef<TurnChoice>,
) {
turn_choice.value_func(&env).unwrap().fail();
}
}

0
src/script_implementations/wasm/export_registry/mod.rs Normal file → Executable file
View File

View File

@@ -0,0 +1,38 @@
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::Item;
use crate::StringKey;
use std::mem::transmute;
use wasmer::FunctionEnvMut;
register! {
fn item_get_price(
env: FunctionEnvMut<WebAssemblyEnv>,
item: ExternRef<Item>,
) -> i32 {
item.value_func(&env).unwrap().price()
}
fn item_get_name(
env: FunctionEnvMut<WebAssemblyEnv>,
item: ExternRef<Item>,
) -> ExternRef<StringKey> {
ExternRef::func_new(&env, item.value_func(&env).unwrap().name())
}
fn item_get_category(
env: FunctionEnvMut<WebAssemblyEnv>,
item: ExternRef<Item>,
) -> u8 {
unsafe { transmute(item.value_func(&env).unwrap().category()) }
}
fn item_get_battle_category(
env: FunctionEnvMut<WebAssemblyEnv>,
item: ExternRef<Item>,
) -> u8 {
unsafe { transmute(item.value_func(&env).unwrap().battle_category()) }
}
}

View File

@@ -6,6 +6,7 @@ use crate::script_implementations::wasm::extern_ref::ExternRef;
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv;
use crate::static_data::{ItemLibrary, LibrarySettings, MoveLibrary, SpeciesLibrary, StaticData, TypeLibrary};
mod item;
/// Moves data registration
mod moves;
/// Species data registration
@@ -49,4 +50,5 @@ register! {
fn manual_registration(imports: &mut Imports, store: &mut StoreMut, env: &FunctionEnv<WebAssemblyEnv>) {
moves::register(imports, store, env);
species::register(imports, store, env);
item::register(imports, store, env);
}

View File

View File

0
src/script_implementations/wasm/extern_ref.rs Normal file → Executable file
View File

0
src/script_implementations/wasm/mod.rs Normal file → Executable file
View File

21
src/script_implementations/wasm/script.rs Normal file → Executable file
View File

@@ -1,10 +1,12 @@
use std::any::Any;
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize};
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::Arc;
use hashbrown::HashSet;
use crate::dynamic_data::{Battle, DamageSource, DynamicLibrary, ExecutingMove, Pokemon, Script, TurnChoice};
use crate::dynamic_data::{
Battle, DamageSource, DynamicLibrary, ExecutingMove, Pokemon, Script, ScriptOwnerData, TurnChoice,
};
use crate::script_implementations::wasm::extern_ref::{ExternRef, VecExternRef};
use crate::script_implementations::wasm::script_resolver::WebAssemblyEnvironmentData;
use crate::script_implementations::wasm::WebAssemblyScriptCapabilities;
@@ -23,7 +25,7 @@ pub struct WebAssemblyScript {
/// we will not execute its methods. This holds the number of suppressions on the script.
suppressed_count: AtomicUsize,
/// The owner of this script (where the script is attached to)
_owner_ptr: AtomicPtr<u8>,
owner: ScriptOwnerData,
/// Pointer inside WebAssembly memory where the data is for this script.
self_ptr: u32,
/// Capabilities define which functions we actually implement.
@@ -35,7 +37,7 @@ pub struct WebAssemblyScript {
impl WebAssemblyScript {
/// Instantiates a new WebAssemblyScript.
pub fn new(
owner_ptr: *mut u8,
owner: ScriptOwnerData,
self_ptr: u32,
capabilities: Arc<HashSet<WebAssemblyScriptCapabilities>>,
environment: Arc<WebAssemblyEnvironmentData>,
@@ -45,18 +47,27 @@ impl WebAssemblyScript {
name,
marked_for_deletion: Default::default(),
suppressed_count: Default::default(),
_owner_ptr: AtomicPtr::new(owner_ptr),
owner,
self_ptr,
capabilities,
environment,
}
}
pub(crate) fn get_wasm_pointer(&self) -> u32 {
self.self_ptr
}
/// Check if this script implements a certain capability.
#[inline(always)]
fn has_capability(&self, cap: &WebAssemblyScriptCapabilities) -> bool {
self.capabilities.contains(cap)
}
/// Gets the thing the script is owned by.
pub fn get_owner(&self) -> &ScriptOwnerData {
&self.owner
}
}
/// Util macro to reduce function call verbosity.

View File

152
src/script_implementations/wasm/script_resolver.rs Normal file → Executable file
View File

@@ -11,7 +11,7 @@ use wasmer::{
Memory, Module, Store, StoreMut, StoreRef, TypedFunction, Value,
};
use crate::dynamic_data::{ItemScript, Script, ScriptResolver};
use crate::dynamic_data::{ItemScript, Script, ScriptOwnerData, ScriptResolver};
use crate::script_implementations::wasm::export_registry::register_webassembly_funcs;
use crate::script_implementations::wasm::extern_ref::ExternRef;
use crate::script_implementations::wasm::script::WebAssemblyScript;
@@ -34,10 +34,6 @@ pub struct WebAssemblyScriptResolver {
/// This is the WASM function to load a script.
load_script_fn: Option<TypedFunction<(u8, ExternRef<StringKey>), u32>>,
/// Script capabilities tell us which functions are implemented on a given script. This allows us to skip unneeded
/// WASM calls.
script_capabilities: RwLock<HashMap<ScriptCapabilitiesKey, Arc<HashSet<WebAssemblyScriptCapabilities>>>>,
/// The data for use in the scripting function calls.
environment_data: Arc<WebAssemblyEnvironmentData>,
}
@@ -63,13 +59,15 @@ impl WebAssemblyScriptResolver {
let store_ptr: *mut Store = store.as_mut();
forget(store);
let environment = Arc::new(WebAssemblyEnvironmentData::new(store_ptr));
environment.self_arc.write().replace(Arc::downgrade(&environment));
let s = Self {
_store: store_ptr,
modules: Default::default(),
instances: Default::default(),
load_script_fn: None,
script_capabilities: Default::default(),
environment_data: Arc::new(WebAssemblyEnvironmentData::new(store_ptr)),
environment_data: environment,
};
Box::new(s)
@@ -168,7 +166,7 @@ impl WebAssemblyScriptResolver {
impl ScriptResolver for WebAssemblyScriptResolver {
fn load_script(
&self,
owner: *const u8,
owner: ScriptOwnerData,
category: ScriptCategory,
script_key: &StringKey,
) -> PkmnResult<Option<Arc<dyn Script>>> {
@@ -182,50 +180,7 @@ impl ScriptResolver for WebAssemblyScriptResolver {
ExternRef::new_with_resolver(self, script_key),
)
.unwrap();
if script == 0 {
return Ok(None);
}
let key = ScriptCapabilitiesKey {
category,
script_key: script_key.clone(),
};
if !self.script_capabilities.read().contains_key(&key) {
let mut capabilities = HashSet::new();
unsafe {
if let Some(get_cap) = self
.environment_data
.exported_functions
.read()
.get::<StringKey>(&"get_script_capabilities".into())
{
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 {
capabilities.insert(*ptr.add(i));
}
}
}
self.script_capabilities
.write()
.insert(key.clone(), Arc::new(capabilities));
}
let read_guard = self.script_capabilities.read();
let capabilities = read_guard.get(&key).unwrap();
Ok(Some(Arc::new(WebAssemblyScript::new(
owner as *mut u8,
script,
capabilities.clone(),
self.environment_data.clone(),
script_key.clone(),
))))
self.environment_data.setup_script(script, category, script_key, owner)
}
fn load_item_script(&self, _key: &Item) -> PkmnResult<Option<Arc<dyn ItemScript>>> {
@@ -283,6 +238,16 @@ pub struct WebAssemblyEnvironmentData {
/// The WASM store.
store: *mut Store,
/// Script capabilities tell us which functions are implemented on a given script. This allows us to skip unneeded
/// WASM calls.
script_capabilities: RwLock<HashMap<ScriptCapabilitiesKey, Arc<HashSet<WebAssemblyScriptCapabilities>>>>,
/// A weak reference to ourselves.
self_arc: RwLock<Option<Weak<Self>>>,
/// A lookup from WASM memory pointer to their actual script wrappers.
loaded_scripts: RwLock<HashMap<u32, Weak<WebAssemblyScript>>>,
}
/// A quick lookup so we can find the extern ref of the value.
@@ -310,6 +275,9 @@ impl WebAssemblyEnvironmentData {
allocate_mem_fn: Default::default(),
temp_allocator: Default::default(),
store,
script_capabilities: Default::default(),
self_arc: Default::default(),
loaded_scripts: Default::default(),
}
}
@@ -318,6 +286,19 @@ impl WebAssemblyEnvironmentData {
self.memory.read().as_ref().unwrap().view(&self.store_ref()).data_ptr()
}
/// Return a pointer to something inside the WASM memory.
pub fn get_raw_pointer<T>(&self, offset: u32) -> *mut T {
unsafe {
self.memory
.read()
.as_ref()
.unwrap()
.view(&self.store_ref())
.data_ptr()
.offset(offset as isize) as *mut T
}
}
/// This returns the functions exported from WASM.
pub fn exported_functions(&self) -> RwLockReadGuard<'_, RawRwLock, HashMap<StringKey, Function>> {
self.exported_functions.read()
@@ -371,6 +352,7 @@ impl WebAssemblyEnvironmentData {
/// access can be touched through this, and we ensure the value is the correct type. In the future,
/// when extern refs get actually properly implemented at compile time we might want to get rid
/// of this code.
#[inline(always)]
pub fn get_extern_ref_index<T: UniqueTypeId<u64> + ?Sized>(&self, value: &T) -> u32 {
self.get_extern_ref_from_ptr(value as *const T as *const u8, T::id().0, false)
}
@@ -400,6 +382,7 @@ impl WebAssemblyEnvironmentData {
/// Gets the extern ref index belonging to a specific pointer. If none exists, this will create
/// a new one.
#[inline(always)]
fn get_extern_ref_from_ptr(&self, ptr: *const u8, type_id: u64, is_vec: bool) -> u32 {
if let Some(v) = self.extern_ref_pointers_lookup.read().get(&ExternRefLookupKey {
ptr,
@@ -462,6 +445,71 @@ impl WebAssemblyEnvironmentData {
pub fn store_mut(&self) -> StoreMut<'_> {
unsafe { self.store.as_mut().unwrap().as_store_mut() }
}
/// Find a loaded script based on the pointer in WASM memory.
pub(crate) fn get_loaded_script(&self, wasm_ptr: u32) -> Option<Arc<WebAssemblyScript>> {
if let Some(script) = self.loaded_scripts.read().get(&wasm_ptr) {
script.upgrade()
} else {
None
}
}
/// Wrap a script pointer in WASM memory into a host managed script.
pub fn setup_script(
&self,
script_ptr: u32,
category: ScriptCategory,
script_key: &StringKey,
owner: ScriptOwnerData,
) -> PkmnResult<Option<Arc<dyn Script>>> {
if script_ptr == 0 {
return Ok(None);
}
let key = ScriptCapabilitiesKey {
category,
script_key: script_key.clone(),
};
if !self.script_capabilities.read().contains_key(&key) {
let mut capabilities = HashSet::new();
unsafe {
if let Some(get_cap) = self
.exported_functions
.read()
.get::<StringKey>(&"get_script_capabilities".into())
{
let res = get_cap
.call(&mut self.store_mut(), &[Value::I32(script_ptr as i32)])
.unwrap();
let ptr =
(self.memory() as *const WebAssemblyScriptCapabilities).offset(res[0].i32().unwrap() as isize);
let length = res[1].i32().unwrap() as usize;
for i in 0..length {
capabilities.insert(*ptr.add(i));
}
}
}
self.script_capabilities
.write()
.insert(key.clone(), Arc::new(capabilities));
}
let read_guard = self.script_capabilities.read();
let capabilities = read_guard.get(&key).unwrap();
let script = Arc::new(WebAssemblyScript::new(
owner,
script_ptr,
capabilities.clone(),
self.self_arc.read().as_ref().unwrap().upgrade().unwrap(),
script_key.clone(),
));
self.loaded_scripts.write().insert(script_ptr, Arc::downgrade(&script));
Ok(Some(script))
}
}
/// The runtime environment for script execution. This is passed to most of the host functions being called.

0
src/script_implementations/wasm/temp_wasm_allocator.rs Normal file → Executable file
View File