diff options
Diffstat (limited to 'src/script/wasm.rs')
| -rw-r--r-- | src/script/wasm.rs | 361 |
1 files changed, 221 insertions, 140 deletions
diff --git a/src/script/wasm.rs b/src/script/wasm.rs index 25632fa..9b9cd91 100644 --- a/src/script/wasm.rs +++ b/src/script/wasm.rs @@ -1,41 +1,54 @@ -use core::slice; use std::time::UNIX_EPOCH; use crossterm::{ event::KeyCode, style::{Attribute, Attributes, Color, ContentStyle}, }; -use wasmi::{Caller, Engine, Linker}; +use tinywasm::{FuncContext, HostFunction, Imports, ModuleInstance, Store}; use crate::{buffer::Cell, script::FullScriptContext}; use super::Operation; -unsafe fn str_from_memory<'a>( - caller: &'a Caller<FullScriptContext<'_>>, - offset: u32, - length: u32, -) -> &'a str { - let memory = caller.get_export("memory").unwrap().into_memory().unwrap(); +pub struct WasmModule { + pub instance: ModuleInstance, +} + +fn str_from_memory(caller: &FuncContext<'_>, offset: u32, length: u32) -> tinywasm::Result<String> { + let memory = caller.memory("memory").unwrap(); + memory.read_string(caller.store(), offset as usize, length as usize) +} + +fn context_mut<'b>(address: u64) -> &'b mut FullScriptContext<'b> { unsafe { - str::from_utf8_unchecked(slice::from_raw_parts( - memory.data_ptr(caller).byte_add(offset as usize), - length as usize, - )) + std::ptr::with_exposed_provenance_mut::<FullScriptContext<'b>>(address as usize) + .as_mut() + .unwrap_unchecked() + } +} + +fn context_ref<'b>(address: u64) -> &'b FullScriptContext<'b> { + unsafe { + std::ptr::with_exposed_provenance::<FullScriptContext<'b>>(address as usize) + .as_ref() + .unwrap_unchecked() } } -#[allow(clippy::too_many_arguments)] fn add_cell( - mut caller: Caller<FullScriptContext<'_>>, - character: u32, - location: u32, - z: u32, - foreground_color: u32, - background_color: u32, - underline_color: u32, - attributes: u32, -) { + _caller: FuncContext<'_>, + ( + context, + character, + location, + z, + foreground_color, + background_color, + underline_color, + attributes, + ): (u64, u32, u32, u32, u32, u32, u32, u32), +) -> tinywasm::Result<()> { + puffin::profile_function!(); const STANDARD_COLOR: u32 = 1 << 24; const ANSI_COLOR: u32 = 2 << 24; const RGB_COLOR: u32 = 3 << 24; @@ -127,8 +140,7 @@ fn add_cell( attribute_set.set(Attribute::OverLined); } - caller - .data_mut() + context_mut(context) .operation_queue .push(Operation::AddCell(Cell { character: char::from_u32(character).unwrap_or_default(), @@ -142,14 +154,20 @@ fn add_cell( y: (location & 0xffff) as u16, z: z as u8, })); + Ok(()) } -fn window_size(caller: Caller<FullScriptContext<'_>>) -> u32 { - let window_size = &caller.data().buffer.window_size; - ((window_size.rows as u32) << 16) | (window_size.columns as u32) +fn window_size(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<u32> { + puffin::profile_function!(); + let window_size = &context_ref(context).buffer.window_size; + Ok(((window_size.rows as u32) << 16) | (window_size.columns as u32)) } -fn is_key_pressed_this_frame(caller: Caller<FullScriptContext<'_>>, key_code: u32) -> u32 { +fn is_key_pressed_this_frame( + _caller: FuncContext<'_>, + (context, key_code): (u64, u32), +) -> tinywasm::Result<u32> { + puffin::profile_function!(); const BACKSPACE: u32 = 1 << 24; const ENTER: u32 = 2 << 24; const LEFT: u32 = 3 << 24; @@ -189,21 +207,24 @@ fn is_key_pressed_this_frame(caller: Caller<FullScriptContext<'_>>, key_code: u3 _ => KeyCode::Null, }; - if caller.data().input.is_key_pressed_this_frame(key_code) { - 1 - } else { - 0 - } + Ok( + if context_ref(context) + .input + .is_key_pressed_this_frame(key_code) + { + 1 + } else { + 0 + }, + ) } -fn log(caller: Caller<FullScriptContext<'_>>, severity: u32, offset: u32, length: u32) { - let memory = caller.get_export("memory").unwrap().into_memory().unwrap(); - let message = unsafe { - str::from_utf8_unchecked(slice::from_raw_parts( - memory.data_ptr(&caller).byte_add(offset as usize), - length as usize, - )) - }; +fn log( + caller: FuncContext<'_>, + (severity, offset, length): (u32, u32, u32), +) -> tinywasm::Result<()> { + puffin::profile_function!(); + let message = str_from_memory(&caller, offset, length)?; let severity = match severity { 1 => log::Level::Error, 2 => log::Level::Warn, @@ -213,103 +234,129 @@ fn log(caller: Caller<FullScriptContext<'_>>, severity: u32, offset: u32, length _ => log::Level::Error, }; log::log!(severity, "{message}"); + Ok(()) } -fn exit(mut caller: Caller<FullScriptContext<'_>>) { - caller.data_mut().operation_queue.push(Operation::Exit) +fn exit(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<()> { + puffin::profile_function!(); + context_mut(context).operation_queue.push(Operation::Exit); + Ok(()) } -fn random_u64() -> u64 { - getrandom::u64().unwrap_or(4) +fn random_u64(_caller: FuncContext<'_>, (): ()) -> tinywasm::Result<u64> { + puffin::profile_function!(); + Ok(getrandom::u64().unwrap_or(4)) } -fn scene_id_by_name(caller: Caller<FullScriptContext<'_>>, offset: u32, length: u32) -> u32 { - let name = unsafe { str_from_memory(&caller, offset, length) }; - crate::scene::scene_id_by_name(&caller.data().manifest.scenes, name) - .map(|u| u as u32) - .unwrap_or(u32::MAX) +fn scene_id_by_name( + caller: FuncContext<'_>, + (context, offset, length): (u64, u32, u32), +) -> tinywasm::Result<u32> { + puffin::profile_function!(); + let name = str_from_memory(&caller, offset, length)?; + Ok( + crate::scene::scene_id_by_name(&context_ref(context).manifest.scenes, &name) + .map(|u| u as u32) + .unwrap_or(u32::MAX), + ) } -fn preload_scene(mut caller: Caller<FullScriptContext<'_>>, id: u32) { - caller - .data_mut() +fn preload_scene(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> { + puffin::profile_function!(); + context_mut(context) .operation_queue - .push(Operation::PreloadScene(id as usize)) + .push(Operation::PreloadScene(id as usize)); + Ok(()) } -fn is_scene_loaded(caller: Caller<FullScriptContext<'_>>, id: u32) -> u32 { - let context = caller.data(); - crate::scene::is_scene_loaded( +fn is_scene_loaded(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<u32> { + puffin::profile_function!(); + let context = context_ref(context); + Ok(crate::scene::is_scene_loaded( &context.manifest.scenes, context.scripts, - context.files, + &context.files, id as usize, - ) as u32 + ) as u32) } -fn switch_scene(mut caller: Caller<FullScriptContext<'_>>, id: u32) { - caller - .data_mut() +fn switch_scene(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> { + puffin::profile_function!(); + context_mut(context) .operation_queue .push(Operation::SwitchScene(id as usize)); + Ok(()) } -fn script_id_by_name(caller: Caller<FullScriptContext<'_>>, offset: u32, length: u32) -> u32 { - let context = caller.data(); - let name = unsafe { str_from_memory(&caller, offset, length) }; - context +fn script_id_by_name( + caller: FuncContext<'_>, + (context, offset, length): (u64, u32, u32), +) -> tinywasm::Result<u32> { + puffin::profile_function!(); + let name = str_from_memory(&caller, offset, length)?; + let context = context_ref(context); + Ok(context .scripts - .script_id_by_name(name) + .script_id_by_name(&name) .map(|u| u as u32) - .unwrap_or(u32::MAX) + .unwrap_or(u32::MAX)) } -fn preload_script(mut caller: Caller<FullScriptContext<'_>>, id: u32) { - caller - .data_mut() +fn preload_script(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> { + puffin::profile_function!(); + context_mut(context) .operation_queue .push(Operation::PreloadScript(id as usize)); + Ok(()) } -fn is_script_loaded(caller: Caller<FullScriptContext<'_>>, id: u32) -> u32 { - let context = caller.data(); - context.scripts.is_script_loaded(id as usize, context.files) as u32 +fn is_script_loaded(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<u32> { + puffin::profile_function!(); + let context = context_ref(context); + Ok(context + .scripts + .is_script_loaded(id as usize, &context.files) as u32) } -fn unload_script(mut caller: Caller<FullScriptContext<'_>>, id: u32) { - caller - .data_mut() +fn unload_script(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> { + puffin::profile_function!(); + context_mut(context) .operation_queue .push(Operation::UnloadScript(id as usize)); + Ok(()) } -fn activate_script(mut caller: Caller<FullScriptContext<'_>>, id: u32) { - caller - .data_mut() +fn activate_script(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> { + puffin::profile_function!(); + context_mut(context) .operation_queue .push(Operation::ActivateScript(id as usize)); + Ok(()) } -fn deactivate_script(mut caller: Caller<FullScriptContext<'_>>, id: u32) { - caller - .data_mut() +fn deactivate_script(_caller: FuncContext<'_>, (context, id): (u64, u32)) -> tinywasm::Result<()> { + puffin::profile_function!(); + context_mut(context) .operation_queue .push(Operation::DeactivateScript(id as usize)); + Ok(()) } -fn state_size(caller: Caller<FullScriptContext<'_>>) -> u32 { - let context = caller.data(); - context +fn state_size(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<u32> { + puffin::profile_function!(); + let context = context_ref(context); + Ok(context .scripts .script_states .get(&context.script_id) .map(|state| state.len()) - .unwrap_or_default() as u32 + .unwrap_or_default() as u32) } -fn load_state(mut caller: Caller<FullScriptContext<'_>>, offset: u32) { - let memory = caller.get_export("memory").unwrap().into_memory().unwrap(); - let context = caller.data(); +fn load_state(mut caller: FuncContext<'_>, (context, offset): (u64, u32)) -> tinywasm::Result<()> { + puffin::profile_function!(); + let memory = caller.memory("memory").unwrap(); + let context = context_ref(context); let state = context .scripts .script_states @@ -317,119 +364,153 @@ fn load_state(mut caller: Caller<FullScriptContext<'_>>, offset: u32) { .map(|state| &**state) .unwrap_or(&[]); memory.write(&mut caller, offset as usize, state).unwrap(); + Ok(()) } -fn save_state(mut caller: Caller<FullScriptContext<'_>>, offset: u32, length: u32) { +fn save_state( + mut caller: FuncContext<'_>, + (context, offset, length): (u64, u32, u32), +) -> tinywasm::Result<()> { + puffin::profile_function!(); let offset = offset as usize; let length = length as usize; - let memory = caller.get_export("memory").unwrap().into_memory().unwrap(); - let new_state = Box::from(&memory.data(&caller)[offset..(offset + length)]); - let context = caller.data_mut(); + let memory = caller.memory("memory").unwrap(); + let new_state = memory + .read_vec(caller.store_mut(), offset, length) + .unwrap() + .into_boxed_slice(); + let context = context_mut(context); context.operation_queue.push(Operation::UpdateState { script_id: context.script_id, new_state, }); + Ok(()) } -fn entity_size(caller: Caller<FullScriptContext<'_>>, name: u32, name_len: u32) -> u32 { - let context = caller.data(); - let name = unsafe { str_from_memory(&caller, name, name_len) }; - context +fn entity_size( + caller: FuncContext<'_>, + (context, name, name_len): (u64, u32, u32), +) -> tinywasm::Result<u32> { + puffin::profile_function!(); + let name = str_from_memory(&caller, name, name_len)?; + let context = context_ref(context); + Ok(context .scripts .entities - .get(name) + .get(&*name) .map(|entity| entity.len()) - .unwrap_or_default() as u32 + .unwrap_or_default() as u32) } -fn load_entity(mut caller: Caller<FullScriptContext<'_>>, name: u32, name_len: u32, offset: u32) { - let memory = caller.get_export("memory").unwrap().into_memory().unwrap(); - let name = unsafe { str_from_memory(&caller, name, name_len) }; - let context = caller.data(); +fn load_entity( + mut caller: FuncContext<'_>, + (context, name, name_len, offset): (u64, u32, u32, u32), +) -> tinywasm::Result<()> { + puffin::profile_function!(); + let name = str_from_memory(&caller, name, name_len)?; + let memory = caller.memory("memory").unwrap(); + let context = context_ref(context); let entity = context .scripts .entities - .get(name) + .get(&*name) .map(|entity| &**entity) .unwrap_or(&[]); memory.write(&mut caller, offset as usize, entity).unwrap(); + Ok(()) } fn save_entity( - mut caller: Caller<FullScriptContext<'_>>, - name: u32, - name_len: u32, - offset: u32, - length: u32, -) { + mut caller: FuncContext<'_>, + (context, name, name_len, offset, length): (u64, u32, u32, u32, u32), +) -> tinywasm::Result<()> { + puffin::profile_function!(); let offset = offset as usize; let length = length as usize; - let memory = caller.get_export("memory").unwrap().into_memory().unwrap(); - let new_state = Box::from(&memory.data(&caller)[offset..(offset + length)]); - let name = Box::from(unsafe { str_from_memory(&caller, name, name_len) }); - let context = caller.data_mut(); + let memory = caller.memory("memory").unwrap(); + let new_state = memory + .read_vec(caller.store_mut(), offset, length) + .unwrap() + .into_boxed_slice(); + let name = str_from_memory(&caller, name, name_len)?.into_boxed_str(); + let context = context_mut(context); context .operation_queue .push(Operation::UpdateEntity { name, new_state }); + Ok(()) } -fn storage_size(caller: Caller<FullScriptContext<'_>>) -> u32 { - caller - .data() +fn storage_size(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<u32> { + puffin::profile_function!(); + Ok(context_ref(context) .storage .get() .map(|data| data.len()) - .unwrap_or_default() as u32 + .unwrap_or_default() as u32) } -fn load_storage(mut caller: Caller<FullScriptContext<'_>>, offset: u32) { - let memory = caller.get_export("memory").unwrap().into_memory().unwrap(); - let context = caller.data(); +fn load_storage( + mut caller: FuncContext<'_>, + (context, offset): (u64, u32), +) -> tinywasm::Result<()> { + puffin::profile_function!(); + let memory = caller.memory("memory").unwrap(); + let context = context_ref(context); let state = context.storage.get().unwrap_or(&[]); memory.write(&mut caller, offset as usize, state).unwrap(); + Ok(()) } -fn save_storage(mut caller: Caller<FullScriptContext<'_>>, offset: u32, length: u32) { +fn save_storage( + mut caller: FuncContext<'_>, + (context, offset, length): (u64, u32, u32), +) -> tinywasm::Result<()> { + puffin::profile_function!(); let offset = offset as usize; let length = length as usize; - let memory = caller.get_export("memory").unwrap().into_memory().unwrap(); - let new_save = Box::from(&memory.data(&caller)[offset..(offset + length)]); - let context = caller.data_mut(); + let memory = caller.memory("memory").unwrap(); + let new_save = memory + .read_vec(caller.store_mut(), offset, length) + .unwrap() + .into_boxed_slice(); + let context = context_mut(context); context .operation_queue .push(Operation::UpdateStorage { new_save }); + Ok(()) } -fn system_now(caller: Caller<FullScriptContext<'_>>) -> u64 { - caller - .data() +fn system_now(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<u64> { + puffin::profile_function!(); + Ok(context_ref(context) .time .system_now() .duration_since(UNIX_EPOCH) .unwrap() - .as_nanos() as u64 + .as_nanos() as u64) } -fn monotonic_now(caller: Caller<FullScriptContext<'_>>) -> u64 { - caller.data().time.monotonic_now().as_nanos() as u64 +fn monotonic_now(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<u64> { + puffin::profile_function!(); + Ok(context_ref(context).time.monotonic_now().as_nanos() as u64) } -fn delta_time(caller: Caller<FullScriptContext<'_>>) -> u64 { - caller.data().time.delta_time().as_nanos() as u64 +fn delta_time(_caller: FuncContext<'_>, context: u64) -> tinywasm::Result<u64> { + puffin::profile_function!(); + Ok(context_ref(context).time.delta_time().as_nanos() as u64) } -pub fn linker<'ctx>( - engine: &Engine, -) -> Result<wasmi::Linker<FullScriptContext<'ctx>>, wasmi::Error> { +pub fn linker(store: &mut Store) -> Result<Imports, tinywasm::Error> { macro_rules! add_fns { - ($linker: expr, [$($func: ident),*]) => { - $($linker.func_wrap("tortuise", stringify!($func), $func)?;)* + ($linker: expr, $store: expr, [$($func: ident),*]) => { + $($linker.define("tortuise", stringify!($func), HostFunction::from($store, $func));)* }; } - let mut linker = Linker::new(engine); + let mut linker = Imports::new(); add_fns!( linker, + store, [ add_cell, window_size, |
