diff --git a/Cargo.lock b/Cargo.lock index 9fc9e97..415e75d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,23 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "async-recursion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "cfg-if" version = "1.0.0" @@ -31,12 +48,103 @@ dependencies = [ "wast", ] +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "it-lilo" version = "0.6.0" dependencies = [ "anyhow", + "async-recursion", "fluence-it-types", + "futures", "it-memory-traits", "log", "paste", @@ -108,24 +216,36 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -165,7 +285,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -179,6 +299,15 @@ dependencies = [ "serde", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "syn" version = "1.0.107" @@ -190,6 +319,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "thiserror" version = "1.0.38" @@ -207,7 +347,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -223,7 +363,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae2faf80ac463422992abf4de234731279c058aaf33171ca70277c98406b124" dependencies = [ "quote", - "syn", + "syn 1.0.107", ] [[package]] @@ -232,12 +372,14 @@ version = "0.27.0" dependencies = [ "anyhow", "fluence-it-types", + "futures", "it-lilo", "it-memory-traits", "it-to-bytes", "itertools", "log", "nom", + "paste", "safe-transmute", "semver", "serde", diff --git a/crates/it-lilo/Cargo.toml b/crates/it-lilo/Cargo.toml index 63fc8df..dad00e6 100644 --- a/crates/it-lilo/Cargo.toml +++ b/crates/it-lilo/Cargo.toml @@ -11,10 +11,12 @@ name = "it_lilo" path = "src/lib.rs" [dependencies] -fluence-it-types = { path = "../it-types/", version = "0.4.1" } +fluence-it-types = { path = "../it-types", version = "0.4.1" } it-memory-traits = { path = "../it-memory-traits", version = "0.4.0" } anyhow = "1.0.75" paste = "1.0.11" thiserror = "1.0.38" log = "0.4.17" +async-recursion = "1.0.5" +futures = "0.3.29" diff --git a/crates/it-lilo/src/lifter/mod.rs b/crates/it-lilo/src/lifter/mod.rs index d48d261..ee78e7f 100644 --- a/crates/it-lilo/src/lifter/mod.rs +++ b/crates/it-lilo/src/lifter/mod.rs @@ -41,9 +41,6 @@ impl<'r, R: RecordResolvable, MV: MemoryView, Store: it_memory_traits::St { pub fn new(view: MV, resolver: &'r R) -> Self { let reader = MemoryReader::new(view); - Self { - reader, - resolver, - } + Self { reader, resolver } } } diff --git a/crates/it-lilo/src/lowerer/lower_array.rs b/crates/it-lilo/src/lowerer/lower_array.rs index e75db1a..3aeb6e1 100644 --- a/crates/it-lilo/src/lowerer/lower_array.rs +++ b/crates/it-lilo/src/lowerer/lower_array.rs @@ -38,7 +38,8 @@ impl LoweredArray { } } -pub fn array_lower_memory< +#[async_recursion::async_recursion] +pub async fn array_lower_memory< A: Allocatable, MV: MemoryView, Store: it_memory_traits::Store, @@ -54,32 +55,57 @@ pub fn array_lower_memory< let elements_count = array_values.len() as u32; let size = ser_value_size(&array_values[0]) * elements_count; let type_tag = type_tag_form_ivalue(&array_values[0]); - let seq_writer = lowerer.writer.sequential_writer(store, size, type_tag)?; + let seq_writer = lowerer + .writer + .sequential_writer(store, size, type_tag) + .await?; // here it's known that all interface values have the same type for value in array_values { match value { IValue::Boolean(value) => seq_writer.write_u8(store, &lowerer.writer, value as _), IValue::S8(value) => seq_writer.write_u8(store, &lowerer.writer, value as _), - IValue::S16(value) => seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()), - IValue::S32(value) => seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()), - IValue::S64(value) => seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()), - IValue::U8(value) => seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()), - IValue::U16(value) => seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()), - IValue::U32(value) => seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()), - IValue::U64(value) => seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()), - IValue::I32(value) => seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()), - IValue::I64(value) => seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()), - IValue::F32(value) => seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()), - IValue::F64(value) => seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()), + IValue::S16(value) => { + seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()) + } + IValue::S32(value) => { + seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()) + } + IValue::S64(value) => { + seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()) + } + IValue::U8(value) => { + seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()) + } + IValue::U16(value) => { + seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()) + } + IValue::U32(value) => { + seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()) + } + IValue::U64(value) => { + seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()) + } + IValue::I32(value) => { + seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()) + } + IValue::I64(value) => { + seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()) + } + IValue::F32(value) => { + seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()) + } + IValue::F64(value) => { + seq_writer.write_bytes(store, &lowerer.writer, &value.to_le_bytes()) + } IValue::String(value) => { - let offset = lowerer.writer.write_bytes(store, &value.as_bytes())? as u32; + let offset = lowerer.writer.write_bytes(store, &value.as_bytes()).await? as u32; seq_writer.write_bytes(store, &lowerer.writer, &offset.to_le_bytes()); seq_writer.write_bytes(store, &lowerer.writer, &(value.len() as u32).to_le_bytes()); } IValue::ByteArray(values) => { - let offset = lowerer.writer.write_bytes(store, &values)? as u32; + let offset = lowerer.writer.write_bytes(store, &values).await? as u32; seq_writer.write_bytes(store, &lowerer.writer, &offset.to_le_bytes()); seq_writer.write_bytes( @@ -89,13 +115,14 @@ pub fn array_lower_memory< ); } IValue::Array(values) => { - let LoweredArray { offset, size } = array_lower_memory(store, lowerer, values)?; + let LoweredArray { offset, size } = + array_lower_memory(store, lowerer, values).await?; seq_writer.write_bytes(store, &lowerer.writer, &(offset as u32).to_le_bytes()); seq_writer.write_bytes(store, &lowerer.writer, &(size as u32).to_le_bytes()); } IValue::Record(values) => { - let offset = super::record_lower_memory(store, lowerer, values)? as u32; + let offset = super::record_lower_memory(store, lowerer, values).await? as u32; seq_writer.write_bytes(store, &lowerer.writer, &offset.to_le_bytes()); } } diff --git a/crates/it-lilo/src/lowerer/lower_record.rs b/crates/it-lilo/src/lowerer/lower_record.rs index 1098978..068a401 100644 --- a/crates/it-lilo/src/lowerer/lower_record.rs +++ b/crates/it-lilo/src/lowerer/lower_record.rs @@ -23,7 +23,8 @@ use crate::NEVec; use it_memory_traits::MemoryView; -pub fn record_lower_memory< +#[async_recursion::async_recursion] +pub async fn record_lower_memory< A: Allocatable, MV: MemoryView, Store: it_memory_traits::Store, @@ -52,13 +53,13 @@ pub fn record_lower_memory< IValue::F32(value) => result.extend_from_slice(&value.to_le_bytes()), IValue::F64(value) => result.extend_from_slice(&value.to_le_bytes()), IValue::String(value) => { - let offset = lowerer.writer.write_bytes(store, value.as_bytes())?; + let offset = lowerer.writer.write_bytes(store, value.as_bytes()).await?; result.extend_from_slice(&offset.to_le_bytes()); result.extend_from_slice(&(value.len() as u32).to_le_bytes()); } IValue::ByteArray(value) => { - let offset = lowerer.writer.write_bytes(store, &value)?; + let offset = lowerer.writer.write_bytes(store, &value).await?; result.extend_from_slice(&offset.to_le_bytes()); result.extend_from_slice(&(value.len() as u32).to_le_bytes()); @@ -66,21 +67,21 @@ pub fn record_lower_memory< IValue::Array(values) => { let LoweredArray { offset, size } = - super::array_lower_memory(store, lowerer, values)?; + super::array_lower_memory(store, lowerer, values).await?; result.extend_from_slice(&(offset).to_le_bytes()); result.extend_from_slice(&(size).to_le_bytes()); } IValue::Record(values) => { - let offset = record_lower_memory(store, lowerer, values)?; + let offset = record_lower_memory(store, lowerer, values).await?; result.extend_from_slice(&offset.to_le_bytes()); } } } - let result_pointer = lowerer.writer.write_bytes(store, &result)?; + let result_pointer = lowerer.writer.write_bytes(store, &result).await?; Ok(result_pointer) } diff --git a/crates/it-lilo/src/lowerer/memory_writer.rs b/crates/it-lilo/src/lowerer/memory_writer.rs index d3075c5..4596346 100644 --- a/crates/it-lilo/src/lowerer/memory_writer.rs +++ b/crates/it-lilo/src/lowerer/memory_writer.rs @@ -46,25 +46,27 @@ impl<'i, A: Allocatable, MV: MemoryView, Store: it_memory_trai Ok(writer) } - pub fn write_bytes( + pub async fn write_bytes<'store, 'store_inner: 'store>( &mut self, - store: &mut ::ActualStore<'_>, + store: &'store mut ::ActualStore<'store_inner>, bytes: &[u8], ) -> LoResult { let byte_type_tag = type_tag_form_itype(&crate::IType::U8); - let seq_writer = self.sequential_writer(store, bytes.len() as u32, byte_type_tag)?; + let seq_writer = self + .sequential_writer(store, bytes.len() as u32, byte_type_tag) + .await?; seq_writer.write_bytes(store, &self, bytes); Ok(seq_writer.start_offset()) } - pub fn sequential_writer( + pub async fn sequential_writer( &mut self, store: &mut ::ActualStore<'_>, size: u32, type_tag: u32, ) -> LoResult { - let (offset, view) = self.heap_manager.allocate(store, size, type_tag)?; + let (offset, view) = self.heap_manager.allocate(store, size, type_tag).await?; self.view.replace(view); let seq_writer = SequentialWriter::new(offset); Ok(seq_writer) diff --git a/crates/it-lilo/src/traits/allocatable.rs b/crates/it-lilo/src/traits/allocatable.rs index 3855f09..ead91c1 100644 --- a/crates/it-lilo/src/traits/allocatable.rs +++ b/crates/it-lilo/src/traits/allocatable.rs @@ -14,18 +14,19 @@ * limitations under the License. */ +use futures::future::BoxFuture; use it_memory_traits::MemoryView; use thiserror::Error as ThisError; pub const DEFAULT_MEMORY_INDEX: usize = 0; -pub trait Allocatable, Store: it_memory_traits::Store> { - fn allocate( - &mut self, - store: &mut ::ActualStore<'_>, +pub trait Allocatable, Store: it_memory_traits::Store>: Send { + fn allocate<'this, 'store: 'this, 'store_inner: 'this>( + &'this mut self, + store: &'store mut ::ActualStore<'store_inner>, size: u32, type_tag: u32, - ) -> Result<(u32, MV), AllocatableError>; + ) -> BoxFuture<'this, Result<(u32, MV), AllocatableError>>; } #[derive(Debug, ThisError)] diff --git a/crates/it-memory-traits/src/lib.rs b/crates/it-memory-traits/src/lib.rs index 6045dab..beb92f9 100644 --- a/crates/it-memory-traits/src/lib.rs +++ b/crates/it-memory-traits/src/lib.rs @@ -18,8 +18,8 @@ mod errors; pub use errors::MemoryAccessError; -pub trait Store { - type ActualStore<'c>; +pub trait Store: Send { + type ActualStore<'c>: Send; } pub trait MemoryReadable { @@ -70,7 +70,9 @@ pub trait MemoryWritable { ); } -pub trait MemoryView: MemoryWritable + MemoryReadable { +pub trait MemoryView: + Send + MemoryWritable + MemoryReadable +{ /// For optimization purposes, user must check bounds first, then try read-write to memory /// `MemoryWritable` and `MemoryReadable` functions will panic in case of out of bounds access` fn check_bounds( @@ -81,7 +83,7 @@ pub trait MemoryView: MemoryWritable + MemoryReadable ) -> Result<(), MemoryAccessError>; } -pub trait Memory +pub trait Memory: Send where View: MemoryView, { diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..b30f74c --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly-2023-09-17" +targets = [ "x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "wasm32-wasi" ] diff --git a/wasmer-it/Cargo.toml b/wasmer-it/Cargo.toml index 95370e0..cc85c86 100644 --- a/wasmer-it/Cargo.toml +++ b/wasmer-it/Cargo.toml @@ -26,6 +26,8 @@ serde_json = "1.0" safe-transmute = "0.11.2" log = "0.4.17" itertools = "0.10.5" +futures = "0.3.29" +paste = "1.0.14" thiserror = "1.0.38" semver = "1.0.16" diff --git a/wasmer-it/src/interpreter/instructions/argument_get.rs b/wasmer-it/src/interpreter/instructions/argument_get.rs index f0c4be4..6f0b08f 100644 --- a/wasmer-it/src/interpreter/instructions/argument_get.rs +++ b/wasmer-it/src/interpreter/instructions/argument_get.rs @@ -1,7 +1,8 @@ use crate::instr_error; -use crate::{errors::InstructionErrorKind, interpreter::Instruction}; +use crate::interpreter::instructions::InstructionErrorKind; +use crate::interpreter::Instruction; -executable_instruction!( +impl_sync_executable_instruction!( argument_get(index: u32, instruction: Instruction) -> _ { move |runtime| -> _ { let invocation_inputs = runtime.invocation_inputs; diff --git a/wasmer-it/src/interpreter/instructions/arrays.rs b/wasmer-it/src/interpreter/instructions/arrays.rs index ca8cf1f..2e2d6fb 100644 --- a/wasmer-it/src/interpreter/instructions/arrays.rs +++ b/wasmer-it/src/interpreter/instructions/arrays.rs @@ -1,7 +1,10 @@ use super::lilo; +use crate::errors::InstructionResult; use crate::instr_error; use crate::interpreter::instructions::to_native; +use crate::interpreter::stack::Stackable; +use crate::interpreter::{AsyncExecutableInstructionImpl, ExecutableInstruction, Runtime}; use crate::{ errors::{InstructionError, InstructionErrorKind}, interpreter::Instruction, @@ -12,17 +15,17 @@ use it_lilo::lowerer::ILowerer; use it_lilo::lowerer::LoweredArray; use it_lilo::traits::DEFAULT_MEMORY_INDEX; +use futures::future::BoxFuture; +use futures::FutureExt; + +struct ArrayLiftMemoryAsync { + instruction: Instruction, + value_type: IType, +} pub(crate) fn array_lift_memory( instruction: Instruction, value_type: IType, -) -> crate::interpreter::ExecutableInstruction< - Instance, - Export, - LocalImport, - Memory, - MemoryView, - Store, -> +) -> ExecutableInstruction where Export: crate::interpreter::wasm::structures::Export, LocalImport: crate::interpreter::wasm::structures::LocalImport, @@ -37,10 +40,37 @@ where >, Store: crate::interpreter::wasm::structures::Store, { - #[allow(unused_imports)] - use crate::interpreter::stack::Stackable; - Box::new({ - move |runtime| -> _ { + ExecutableInstruction::Async(Box::new(ArrayLiftMemoryAsync { + instruction, + value_type, + })) +} + +impl + AsyncExecutableInstructionImpl + for ArrayLiftMemoryAsync +where + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: crate::interpreter::wasm::structures::Instance< + Export, + LocalImport, + Memory, + MemoryView, + Store, + >, + Store: crate::interpreter::wasm::structures::Store, +{ + fn execute<'args>( + &'args self, + runtime: &'args mut Runtime, + ) -> BoxFuture> { + async move { + let value_type = &self.value_type; + let instruction = &self.instruction; + let mut inputs = runtime.stack.pop(2).ok_or_else(|| { InstructionError::from_error_kind( instruction.clone(), @@ -74,29 +104,33 @@ where let li_helper = lilo::LiHelper::new(&**instance); let lifter = ILifter::new(memory_view, &li_helper); - let array = - it_lilo::lifter::array_lift_memory(runtime.store, &lifter, &value_type, offset, size) - .map_err(|e| InstructionError::from_li(instruction.clone(), e))?; + let array = it_lilo::lifter::array_lift_memory( + runtime.store, + &lifter, + &value_type, + offset, + size, + ) + .map_err(|e| InstructionError::from_li(instruction.clone(), e))?; log::trace!("array.lift_memory: pushing {:?} on the stack", array); runtime.stack.push(array); Ok(()) } - }) + .boxed() + } +} + +struct ArrayLowerMemoryAsync { + instruction: Instruction, + value_type: IType, } pub(crate) fn array_lower_memory( instruction: Instruction, value_type: IType, -) -> crate::interpreter::ExecutableInstruction< - Instance, - Export, - LocalImport, - Memory, - MemoryView, - Store, -> +) -> ExecutableInstruction where Export: crate::interpreter::wasm::structures::Export, LocalImport: crate::interpreter::wasm::structures::LocalImport, @@ -111,10 +145,37 @@ where >, Store: crate::interpreter::wasm::structures::Store, { - #[allow(unused_imports)] - use crate::interpreter::stack::Stackable; - Box::new({ - move |runtime| -> _ { + ExecutableInstruction::Async(Box::new(ArrayLowerMemoryAsync { + instruction, + value_type, + })) +} + +impl + AsyncExecutableInstructionImpl + for ArrayLowerMemoryAsync +where + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: crate::interpreter::wasm::structures::Instance< + Export, + LocalImport, + Memory, + MemoryView, + Store, + >, + Store: crate::interpreter::wasm::structures::Store, +{ + fn execute<'args>( + &'args self, + runtime: &'args mut Runtime, + ) -> BoxFuture> { + let value_type = &self.value_type; + let instruction = &self.instruction; + + async move { let instance = &mut runtime.wasm_instance; let stack_value = runtime.stack.pop1().ok_or_else(|| { InstructionError::from_error_kind( @@ -125,7 +186,11 @@ where match stack_value { IValue::Array(values) => { - log::trace!("array.lower_memory: obtained {:?} values on the stack for interface type {:?}", values, value_type); + log::trace!( + "array.lower_memory: obtained {:?} values on the stack for interface type {:?}", + values, + value_type + ); for value in values.iter() { super::is_value_compatible_to_type(&**instance, &value_type, &value) @@ -150,6 +215,7 @@ where let LoweredArray { offset, size } = it_lilo::lowerer::array_lower_memory(runtime.store, &mut lowerer, values) + .await .map_err(|e| InstructionError::from_lo(instruction.clone(), e))?; log::trace!( @@ -181,6 +247,7 @@ where let offset = lowerer .writer .write_bytes(runtime.store, &bytearray) + .await .map_err(|e| InstructionError::from_lo(instruction.clone(), e))?; let size = bytearray.len(); @@ -203,5 +270,6 @@ where ), } } - }) + .boxed() + } } diff --git a/wasmer-it/src/interpreter/instructions/byte_arrays.rs b/wasmer-it/src/interpreter/instructions/byte_arrays.rs index 2c7be43..c13b166 100644 --- a/wasmer-it/src/interpreter/instructions/byte_arrays.rs +++ b/wasmer-it/src/interpreter/instructions/byte_arrays.rs @@ -3,136 +3,173 @@ use crate::instr_error; use crate::IType; use crate::IValue; use crate::{ - errors::{InstructionError, InstructionErrorKind}, + errors::{InstructionError, InstructionErrorKind, InstructionResult}, + interpreter::stack::Stackable, interpreter::Instruction, + interpreter::Runtime, }; use it_lilo::traits::DEFAULT_MEMORY_INDEX; -executable_instruction!( - byte_array_lift_memory(instruction: Instruction) -> _ { - move |runtime| -> _ { - let mut inputs = runtime.stack.pop(2).ok_or_else(|| { - InstructionError::from_error_kind( - instruction.clone(), - InstructionErrorKind::StackIsTooSmall { needed: 2 }, - ) - })?; +use futures::future::BoxFuture; +use futures::FutureExt; - let memory_index = DEFAULT_MEMORY_INDEX; - let memory = runtime - .wasm_instance - .memory(memory_index) - .ok_or_else(|| { +struct ByteArrayLiftMemoryAsync { + instruction: Instruction, +} + +impl_async_executable_instruction!( + byte_array_lift_memory(instruction: Instruction) -> _ { + Box::new(ByteArrayLiftMemoryAsync{instruction}) + } + ByteArrayLiftMemoryAsync { + fn execute<'args>(&'args self, runtime: &'args mut Runtime) + -> BoxFuture> { + async move { + let mut inputs = runtime.stack.pop(2).ok_or_else(|| { InstructionError::from_error_kind( - instruction.clone(), - InstructionErrorKind::MemoryIsMissing { memory_index }, + self.instruction.clone(), + InstructionErrorKind::StackIsTooSmall { needed: 2 }, ) })?; - let pointer = to_native::(inputs.remove(0), instruction.clone())? as u32; - let length = to_native::(inputs.remove(0), instruction.clone())? as u32; + let memory_index = DEFAULT_MEMORY_INDEX; + let memory = runtime + .wasm_instance + .memory(memory_index) + .ok_or_else(|| { + InstructionError::from_error_kind( + self.instruction.clone(), + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })?; - let memory_view = memory.view(); + let pointer = to_native::(inputs.remove(0), self.instruction.clone())? as u32; + let length = to_native::(inputs.remove(0), self.instruction.clone())? as u32; - if length == 0 { - runtime.stack.push(IValue::ByteArray(vec![])); + let memory_view = memory.view(); - return Ok(()) - } + if length == 0 { + runtime.stack.push(IValue::ByteArray(vec![])); - memory_view - .check_bounds(runtime.store, pointer, length) - .map_err(|e| InstructionError::from_memory_access(instruction.clone(), e))?; + return Ok(()) + } - let data = memory_view.read_vec(runtime.store, pointer, length); + memory_view + .check_bounds(runtime.store, pointer, length) + .map_err(|e| InstructionError::from_memory_access(self.instruction.clone(), e))?; - log::debug!("byte_array.lift_memory: pushing {:?} on the stack", data); - runtime.stack.push(IValue::ByteArray(data)); + let data = memory_view.read_vec(runtime.store, pointer, length); - Ok(()) + log::debug!("byte_array.lift_memory: pushing {:?} on the stack", data); + runtime.stack.push(IValue::ByteArray(data)); + + Ok(()) + }.boxed() } } ); -executable_instruction!( +struct ByteArrayLowerMemoryAsync { + instruction: Instruction, +} + +impl_async_executable_instruction!( byte_array_lower_memory(instruction: Instruction) -> _ { - move |runtime| -> _ { - let mut inputs = runtime.stack.pop(2).ok_or_else(|| { - InstructionError::from_error_kind( - instruction.clone(), - InstructionErrorKind::StackIsTooSmall { needed: 2 }, - ) - })?; + Box::new(ByteArrayLowerMemoryAsync{instruction}) + } - let array_pointer = to_native::(inputs.remove(0), instruction.clone())? as u32; - let array: Vec = to_native(inputs.remove(0), instruction.clone())?; - let length = array.len() as u32; - - let instance = &mut runtime.wasm_instance; - let memory_index = DEFAULT_MEMORY_INDEX; - let memory_view = instance - .memory(memory_index) - .ok_or_else(|| { + ByteArrayLowerMemoryAsync { + fn execute<'args>(&'args self, runtime: &'args mut Runtime) -> BoxFuture> { + async move { + let instruction = &self.instruction; + let mut inputs = runtime.stack.pop(2).ok_or_else(|| { InstructionError::from_error_kind( instruction.clone(), - InstructionErrorKind::MemoryIsMissing { memory_index }, + InstructionErrorKind::StackIsTooSmall { needed: 2 }, ) - })? - .view(); + })?; - memory_view - .check_bounds(runtime.store, array_pointer, array.len() as u32) - .map_err(|e| InstructionError::from_memory_access(instruction.clone(), e))?; + let array_pointer = to_native::(inputs.remove(0), instruction.clone())? as u32; + let array: Vec = to_native(inputs.remove(0), instruction.clone())?; + let length = array.len() as u32; - memory_view.write_bytes(runtime.store, array_pointer, &array); + let instance = &mut runtime.wasm_instance; + let memory_index = DEFAULT_MEMORY_INDEX; + let memory_view = instance + .memory(memory_index) + .ok_or_else(|| { + InstructionError::from_error_kind( + instruction.clone(), + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })? + .view(); - log::debug!("string.lower_memory: pushing {}, {} on the stack", array_pointer, length); - runtime.stack.push(IValue::I32(array_pointer as i32)); - runtime.stack.push(IValue::I32(length as i32)); + memory_view + .check_bounds(runtime.store, array_pointer, array.len() as u32) + .map_err(|e| InstructionError::from_memory_access(instruction.clone(), e))?; - Ok(()) + memory_view.write_bytes(runtime.store, array_pointer, &array); + + log::debug!("string.lower_memory: pushing {}, {} on the stack", array_pointer, length); + runtime.stack.push(IValue::I32(array_pointer as i32)); + runtime.stack.push(IValue::I32(length as i32)); + + Ok(()) + }.boxed() } } ); -executable_instruction!( +struct ByteArraySizeAsync { + instruction: Instruction, +} + +impl_async_executable_instruction!( byte_array_size(instruction: Instruction) -> _ { - move |runtime| -> _ { - match runtime.stack.pop1() { - Some(IValue::ByteArray(array)) => { - let length = array.len() as i32; + Box::new(ByteArraySizeAsync{instruction}) + } - log::debug!("byte_array.size: pushing {} on the stack", length); - runtime.stack.push(IValue::I32(length)); + ByteArraySizeAsync { + fn execute<'args>(&'args self, runtime: &'args mut Runtime) -> BoxFuture> { + async move { + let instruction = &self.instruction; + match runtime.stack.pop1() { + Some(IValue::ByteArray(array)) => { + let length = array.len() as i32; - Ok(()) - }, + log::debug!("byte_array.size: pushing {} on the stack", length); + runtime.stack.push(IValue::I32(length)); - Some(IValue::Array(array)) => { - let array = check_array_type(array, &instruction)?; + Ok(()) + }, - let length = array.len() as i32; + Some(IValue::Array(array)) => { + let array = check_array_type(array, &instruction)?; - log::debug!("byte_array.size: pushing {} on the stack", length); - runtime.stack.push(IValue::I32(length)); + let length = array.len() as i32; - Ok(()) - }, + log::debug!("byte_array.size: pushing {} on the stack", length); + runtime.stack.push(IValue::I32(length)); - Some(value) => instr_error!( - instruction.clone(), - InstructionErrorKind::InvalidValueOnTheStack { - expected_type: IType::ByteArray, - received_value: (&value).clone(), - } - ), + Ok(()) + }, - None => instr_error!( - instruction.clone(), - InstructionErrorKind::StackIsTooSmall { needed: 1 } - ), - } + Some(value) => instr_error!( + instruction.clone(), + InstructionErrorKind::InvalidValueOnTheStack { + expected_type: IType::ByteArray, + received_value: (&value).clone(), + } + ), + + None => instr_error!( + instruction.clone(), + InstructionErrorKind::StackIsTooSmall { needed: 1 } + ), + } + }.boxed() } } ); diff --git a/wasmer-it/src/interpreter/instructions/call_core.rs b/wasmer-it/src/interpreter/instructions/call_core.rs index 5c8045a..add0f1c 100644 --- a/wasmer-it/src/interpreter/instructions/call_core.rs +++ b/wasmer-it/src/interpreter/instructions/call_core.rs @@ -1,56 +1,79 @@ use crate::{ - errors::{InstructionError, InstructionErrorKind}, + errors::{InstructionError, InstructionErrorKind, InstructionResult}, + interpreter::stack::Stackable, interpreter::wasm::structures::{FunctionIndex, TypedIndex}, interpreter::Instruction, + interpreter::Runtime, }; -executable_instruction!( +use futures::future::BoxFuture; +use futures::FutureExt; + +struct CallCoreAsync { + function_index: u32, + instruction: Instruction, +} + +impl_async_executable_instruction!( call_core(function_index: u32, instruction: Instruction) -> _ { - move |runtime| -> _ { - let instance = &runtime.wasm_instance; - let index = FunctionIndex::new(function_index as usize); + Box::new(CallCoreAsync{function_index, instruction}) + } - let local_or_import = instance.local_or_import(index).ok_or_else(|| { - InstructionError::from_error_kind( - instruction.clone(), - InstructionErrorKind::LocalOrImportIsMissing { - function_index, - }, - ) - })?; - let inputs_cardinality = local_or_import.inputs_cardinality(); + CallCoreAsync { + fn execute<'args>(&'args self, runtime: &'args mut Runtime) + -> BoxFuture> { + async move { + let instruction = &self.instruction; + let function_index = self.function_index; - let inputs = runtime.stack.pop(inputs_cardinality).ok_or_else(|| { - InstructionError::from_error_kind( - instruction.clone(), - InstructionErrorKind::StackIsTooSmall { - needed: inputs_cardinality, - }, - ) - })?; + let instance = &runtime.wasm_instance; + let index = FunctionIndex::new(function_index as usize); - super::check_function_signature(&**instance, local_or_import, &inputs) - .map_err(|e| InstructionError::from_error_kind(instruction.clone(), e))?; + let local_or_import = instance.local_or_import(index).ok_or_else(|| { + InstructionError::from_error_kind( + instruction.clone(), + InstructionErrorKind::LocalOrImportIsMissing { + function_index, + }, + ) + })?; + let inputs_cardinality = local_or_import.inputs_cardinality(); - log::debug!("call-core: calling {} with arguments: {:?}", local_or_import.name(), inputs); + let inputs = runtime.stack.pop(inputs_cardinality).ok_or_else(|| { + InstructionError::from_error_kind( + instruction.clone(), + InstructionErrorKind::StackIsTooSmall { + needed: inputs_cardinality, + }, + ) + })?; - let outputs = local_or_import.call(runtime.store, &inputs).map_err(|e| { - InstructionError::from_error_kind( - instruction.clone(), - InstructionErrorKind::LocalOrImportCall { - function_name: local_or_import.name().to_string(), - reason: e - }, - ) - })?; + super::check_function_signature(&**instance, local_or_import, &inputs) + .map_err(|e| InstructionError::from_error_kind(instruction.clone(), e))?; - log::debug!("call-core: call to {} succeeded with result {:?}", local_or_import.name(), outputs); + log::debug!("call-core: calling {} with arguments: {:?}", local_or_import.name(), inputs); - for output in outputs.into_iter() { - runtime.stack.push(output) - } + let outputs = local_or_import + .call_async(runtime.store, &inputs) + .await + .map_err(|e| { + InstructionError::from_error_kind( + instruction.clone(), + InstructionErrorKind::LocalOrImportCall { + function_name: local_or_import.name().to_string(), + reason: e + }, + ) + })?; - Ok(()) + log::debug!("call-core: call to {} succeeded with result {:?}", local_or_import.name(), outputs); + + for output in outputs.into_iter() { + runtime.stack.push(output) + } + + Ok(()) + }.boxed() } } ); diff --git a/wasmer-it/src/interpreter/instructions/dup.rs b/wasmer-it/src/interpreter/instructions/dup.rs index c726c69..3ca1c38 100644 --- a/wasmer-it/src/interpreter/instructions/dup.rs +++ b/wasmer-it/src/interpreter/instructions/dup.rs @@ -3,7 +3,7 @@ use crate::{ interpreter::Instruction, }; -executable_instruction!( +impl_sync_executable_instruction!( dup(instruction: Instruction) -> _ { move |runtime| -> _ { let value = runtime.stack.peek1().ok_or_else(|| { diff --git a/wasmer-it/src/interpreter/instructions/lilo/lo_helper.rs b/wasmer-it/src/interpreter/instructions/lilo/lo_helper.rs index 97d3085..fb7209a 100644 --- a/wasmer-it/src/interpreter/instructions/lilo/lo_helper.rs +++ b/wasmer-it/src/interpreter/instructions/lilo/lo_helper.rs @@ -6,13 +6,16 @@ use it_lilo::traits::Allocatable; use it_lilo::traits::AllocatableError; use it_lilo::traits::DEFAULT_MEMORY_INDEX; +use futures::future::BoxFuture; +use futures::FutureExt; + use std::marker::PhantomData; pub struct LoHelper<'i, Instance, Export, LocalImport, Memory, MemoryView, Store> where - Export: wasm::structures::Export + 'i, - LocalImport: wasm::structures::LocalImport + 'i, - Memory: wasm::structures::Memory + 'i, + Export: wasm::structures::Export, + LocalImport: wasm::structures::LocalImport, + Memory: wasm::structures::Memory, MemoryView: wasm::structures::MemoryView, Instance: wasm::structures::Instance, Store: wasm::structures::Store, @@ -28,9 +31,9 @@ where impl<'i, Instance, Export, LocalImport, Memory, MemoryView, Store> LoHelper<'i, Instance, Export, LocalImport, Memory, MemoryView, Store> where - Export: wasm::structures::Export + 'i, - LocalImport: wasm::structures::LocalImport + 'i, - Memory: wasm::structures::Memory + 'i, + Export: wasm::structures::Export, + LocalImport: wasm::structures::LocalImport, + Memory: wasm::structures::Memory, MemoryView: wasm::structures::MemoryView, Instance: wasm::structures::Instance, Store: wasm::structures::Store, @@ -50,61 +53,64 @@ where impl<'i, Instance, Export, LocalImport, Memory, MemoryView, Store> Allocatable for LoHelper<'i, Instance, Export, LocalImport, Memory, MemoryView, Store> where - Export: wasm::structures::Export + 'i, - LocalImport: wasm::structures::LocalImport + 'i, - Memory: wasm::structures::Memory + 'i, + Export: wasm::structures::Export, + LocalImport: wasm::structures::LocalImport, + Memory: wasm::structures::Memory, MemoryView: wasm::structures::MemoryView, Instance: wasm::structures::Instance, Store: wasm::structures::Store, { - fn allocate( - &mut self, - store: &mut ::ActualStore<'_>, + fn allocate<'this, 'store: 'this, 'store_inner: 'this>( + &'this mut self, + store: &'store mut ::ActualStore<'store_inner>, size: u32, type_tag: u32, - ) -> Result<(u32, MemoryView), AllocatableError> { - use AllocatableError::*; + ) -> BoxFuture<'this, Result<(u32, MemoryView), AllocatableError>> { + async move { + use crate::interpreter::instructions::ALLOCATE_FUNC_INDEX; + use crate::interpreter::wasm::structures::TypedIndex; + use AllocatableError::*; - use crate::interpreter::instructions::ALLOCATE_FUNC_INDEX; - use crate::interpreter::wasm::structures::TypedIndex; + let index = FunctionIndex::new(ALLOCATE_FUNC_INDEX as usize); + let local_or_import = + self.instance + .local_or_import(index) + .ok_or(AllocateFuncIsMissing { + function_index: ALLOCATE_FUNC_INDEX, + })?; - let index = FunctionIndex::new(ALLOCATE_FUNC_INDEX as usize); - let local_or_import = - self.instance - .local_or_import(index) - .ok_or(AllocateFuncIsMissing { - function_index: ALLOCATE_FUNC_INDEX, - })?; + let inputs = vec![IValue::I32(size as _), IValue::I32(type_tag as _)]; + // TODO: we could check it only once on the module startup or memorize check result + crate::interpreter::instructions::check_function_signature( + self.instance, + local_or_import, + &inputs, + ) + .map_err(|_| AllocateFuncIncompatibleSignature)?; - let inputs = vec![IValue::I32(size as _), IValue::I32(type_tag as _)]; - // TODO: we could check it only once on the module startup or memorize check result - crate::interpreter::instructions::check_function_signature( - self.instance, - local_or_import, - &inputs, - ) - .map_err(|_| AllocateFuncIncompatibleSignature)?; + let outcome = local_or_import + .call_async(store, &inputs) + .await + .map_err(|e| AllocateCallFailed { reason: e })?; - let outcome = local_or_import - .call(store, &inputs) - .map_err(|e| AllocateCallFailed { reason: e })?; - - if outcome.len() != 1 { - return Err(AllocateFuncIncompatibleOutput); - } - - match outcome[0] { - IValue::I32(offset) => { - let view = - self.instance - .memory_view(DEFAULT_MEMORY_INDEX) - .ok_or(MemoryIsMissing { - memory_index: DEFAULT_MEMORY_INDEX, - })?; - - Ok((offset as _, view)) + if outcome.len() != 1 { + return Err(AllocateFuncIncompatibleOutput); + } + + match outcome[0] { + IValue::I32(offset) => { + let view = + self.instance + .memory_view(DEFAULT_MEMORY_INDEX) + .ok_or(MemoryIsMissing { + memory_index: DEFAULT_MEMORY_INDEX, + })?; + + Ok((offset as _, view)) + } + _ => Err(AllocateFuncIncompatibleOutput), } - _ => Err(AllocateFuncIncompatibleOutput), } + .boxed() } } diff --git a/wasmer-it/src/interpreter/instructions/numbers.rs b/wasmer-it/src/interpreter/instructions/numbers.rs index e6edb2b..d709342 100644 --- a/wasmer-it/src/interpreter/instructions/numbers.rs +++ b/wasmer-it/src/interpreter/instructions/numbers.rs @@ -10,7 +10,7 @@ use std::convert::TryInto; macro_rules! lowering_lifting { ($instruction_function_name:ident, $instruction_name:expr, $to_variant:ident, $from_variant:ident) => { - executable_instruction!( + impl_sync_executable_instruction!( $instruction_function_name(instruction: Instruction) -> _ { move |runtime| -> _ { match runtime.stack.pop1() { @@ -94,7 +94,7 @@ lowering_lifting!(i64_from_u16, "i64.from_u16", I64, U16); lowering_lifting!(i64_from_u32, "i64.from_u32", I64, U32); lowering_lifting!(i64_from_u64, "i64.from_u64", I64, U64); -executable_instruction!( +impl_sync_executable_instruction!( bool_from_i32(instruction: Instruction) -> _ { move |runtime| -> _ { match runtime.stack.pop1() { diff --git a/wasmer-it/src/interpreter/instructions/push.rs b/wasmer-it/src/interpreter/instructions/push.rs index 0281335..e3d5291 100644 --- a/wasmer-it/src/interpreter/instructions/push.rs +++ b/wasmer-it/src/interpreter/instructions/push.rs @@ -1,6 +1,6 @@ use crate::IValue; -executable_instruction!( +impl_sync_executable_instruction!( push_i32(value: i32) -> _ { move |runtime| -> _ { @@ -12,7 +12,7 @@ executable_instruction!( } ); -executable_instruction!( +impl_sync_executable_instruction!( push_i64(value: i64) -> _ { move |runtime| -> _ { diff --git a/wasmer-it/src/interpreter/instructions/records.rs b/wasmer-it/src/interpreter/instructions/records.rs index 6d5c064..e93827c 100644 --- a/wasmer-it/src/interpreter/instructions/records.rs +++ b/wasmer-it/src/interpreter/instructions/records.rs @@ -5,21 +5,25 @@ use crate::IType; use crate::IValue; use crate::{errors::InstructionError, errors::InstructionErrorKind, interpreter::Instruction}; +use crate::errors::InstructionResult; +use crate::interpreter::stack::Stackable; +use crate::interpreter::{AsyncExecutableInstructionImpl, ExecutableInstruction, Runtime}; use it_lilo::lifter::ILifter; use it_lilo::lowerer::ILowerer; use it_lilo::traits::DEFAULT_MEMORY_INDEX; +use futures::future::BoxFuture; +use futures::FutureExt; + +struct RecordLiftMemoryAsync { + record_type_id: u64, + instruction: Instruction, +} + pub(crate) fn record_lift_memory( record_type_id: u64, instruction: Instruction, -) -> crate::interpreter::ExecutableInstruction< - Instance, - Export, - LocalImport, - Memory, - MemoryView, - Store, -> +) -> ExecutableInstruction where Export: crate::interpreter::wasm::structures::Export, LocalImport: crate::interpreter::wasm::structures::LocalImport, @@ -34,10 +38,37 @@ where >, Store: crate::interpreter::wasm::structures::Store, { - #[allow(unused_imports)] - use crate::interpreter::stack::Stackable; - Box::new({ - move |runtime| -> _ { + ExecutableInstruction::Async(Box::new(RecordLiftMemoryAsync { + record_type_id, + instruction, + })) +} + +impl + AsyncExecutableInstructionImpl + for RecordLiftMemoryAsync +where + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: crate::interpreter::wasm::structures::Instance< + Export, + LocalImport, + Memory, + MemoryView, + Store, + >, + Store: crate::interpreter::wasm::structures::Store, +{ + fn execute<'args>( + &'args self, + runtime: &'args mut Runtime, + ) -> BoxFuture> { + async move { + let record_type_id = self.record_type_id; + let instruction = &self.instruction; + let mut inputs = runtime.stack.pop(1).ok_or_else(|| { InstructionError::from_error_kind( instruction.clone(), @@ -75,28 +106,28 @@ where let li_helper = lilo::LiHelper::new(&**instance); let lifter = ILifter::new(memory_view, &li_helper); - let record = it_lilo::lifter::record_lift_memory(runtime.store, &lifter, record_type, offset) - .map_err(|e| InstructionError::from_li(instruction.clone(), e))?; + let record = + it_lilo::lifter::record_lift_memory(runtime.store, &lifter, record_type, offset) + .map_err(|e| InstructionError::from_li(instruction.clone(), e))?; log::debug!("record.lift_memory: pushing {:?} on the stack", record); runtime.stack.push(record); Ok(()) } - }) + .boxed() + } +} + +struct RecordLowerMemoryAsync { + record_type_id: u64, + instruction: Instruction, } pub(crate) fn record_lower_memory( record_type_id: u64, instruction: Instruction, -) -> crate::interpreter::ExecutableInstruction< - Instance, - Export, - LocalImport, - Memory, - MemoryView, - Store, -> +) -> ExecutableInstruction where Export: crate::interpreter::wasm::structures::Export, LocalImport: crate::interpreter::wasm::structures::LocalImport, @@ -111,10 +142,36 @@ where >, Store: crate::interpreter::wasm::structures::Store, { - #[allow(unused_imports)] - use crate::interpreter::stack::Stackable; - Box::new({ - move |runtime| -> _ { + ExecutableInstruction::Async(Box::new(RecordLowerMemoryAsync { + record_type_id, + instruction, + })) +} + +impl + AsyncExecutableInstructionImpl + for RecordLowerMemoryAsync +where + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: crate::interpreter::wasm::structures::Instance< + Export, + LocalImport, + Memory, + MemoryView, + Store, + >, + Store: crate::interpreter::wasm::structures::Store, +{ + fn execute<'args>( + &'args self, + runtime: &'args mut Runtime, + ) -> BoxFuture> { + async move { + let record_type_id = self.record_type_id; + let instruction = &self.instruction; let instance = &mut runtime.wasm_instance; match runtime.stack.pop1() { @@ -126,7 +183,11 @@ where ) .map_err(|e| InstructionError::from_error_kind(instruction.clone(), e))?; - log::debug!("record.lower_memory: obtained {:?} values on the stack for record type = {}", record_fields, record_type_id); + log::debug!( + "record.lower_memory: obtained {:?} values on the stack for record type = {}", + record_fields, + record_type_id + ); let memory_index = DEFAULT_MEMORY_INDEX; let memory_view = instance @@ -142,11 +203,13 @@ where let mut lo_helper = lilo::LoHelper::new(&**instance); let mut memory_writer = ILowerer::new(memory_view, &mut lo_helper) .map_err(|e| InstructionError::from_lo(instruction.clone(), e))?; + let offset = it_lilo::lowerer::record_lower_memory( runtime.store, &mut memory_writer, record_fields, ) + .await .map_err(|e| InstructionError::from_lo(instruction.clone(), e))?; log::debug!("record.lower_memory: pushing {} on the stack", offset); @@ -167,5 +230,6 @@ where ), } } - }) + .boxed() + } } diff --git a/wasmer-it/src/interpreter/instructions/strings.rs b/wasmer-it/src/interpreter/instructions/strings.rs index 972702e..6346c93 100644 --- a/wasmer-it/src/interpreter/instructions/strings.rs +++ b/wasmer-it/src/interpreter/instructions/strings.rs @@ -3,103 +3,138 @@ use crate::instr_error; use crate::IType; use crate::IValue; use crate::{ - errors::{InstructionError, InstructionErrorKind}, + errors::{InstructionError, InstructionErrorKind, InstructionResult}, + interpreter::stack::Stackable, interpreter::Instruction, + interpreter::Runtime, }; use it_lilo::traits::DEFAULT_MEMORY_INDEX; -executable_instruction!( +use futures::future::BoxFuture; +use futures::FutureExt; + +struct StringLiftMemory { + instruction: Instruction, +} + +impl_async_executable_instruction!( string_lift_memory(instruction: Instruction) -> _ { - move |runtime| -> _ { - let mut inputs = runtime.stack.pop(2).ok_or_else(|| { - InstructionError::from_error_kind( - instruction.clone(), - InstructionErrorKind::StackIsTooSmall { needed: 2 }, - ) - })?; - - let memory_index = DEFAULT_MEMORY_INDEX; - let memory = runtime - .wasm_instance - .memory(memory_index) - .ok_or_else(|| { + Box::new(StringLiftMemory{instruction}) + } + StringLiftMemory { + fn execute<'args>(&'args self, runtime: &'args mut Runtime) -> BoxFuture> { + async move { + let instruction = &self.instruction; + let mut inputs = runtime.stack.pop(2).ok_or_else(|| { InstructionError::from_error_kind( instruction.clone(), - InstructionErrorKind::MemoryIsMissing { memory_index }, + InstructionErrorKind::StackIsTooSmall { needed: 2 }, ) })?; - let pointer = to_native::(inputs.remove(0), instruction.clone())? as u32; - let length = to_native::(inputs.remove(0), instruction.clone())? as u32; - let memory_view = memory.view(); + let memory_index = DEFAULT_MEMORY_INDEX; + let memory = runtime + .wasm_instance + .memory(memory_index) + .ok_or_else(|| { + InstructionError::from_error_kind( + instruction.clone(), + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })?; - if length == 0 { - runtime.stack.push(IValue::String("".into())); + let pointer = to_native::(inputs.remove(0), instruction.clone())? as u32; + let length = to_native::(inputs.remove(0), instruction.clone())? as u32; + let memory_view = memory.view(); - return Ok(()) - } + if length == 0 { + runtime.stack.push(IValue::String("".into())); - memory_view - .check_bounds(runtime.store, pointer, length) - .map_err(|e| InstructionError::from_memory_access(instruction.clone(), e))?; + return Ok(()) + } - let data = memory_view.read_vec(runtime.store, pointer, length); - let string = String::from_utf8(data) - .map_err(|error| InstructionError::from_error_kind(instruction.clone(), InstructionErrorKind::String(error)))?; + memory_view + .check_bounds(runtime.store, pointer, length) + .map_err(|e| InstructionError::from_memory_access(instruction.clone(), e))?; - log::debug!("string.lift_memory: pushing {:?} on the stack", string); - runtime.stack.push(IValue::String(string)); + let data = memory_view.read_vec(runtime.store, pointer, length); + let string = String::from_utf8(data) + .map_err(|error| InstructionError::from_error_kind(instruction.clone(), InstructionErrorKind::String(error)))?; - Ok(()) + log::debug!("string.lift_memory: pushing {:?} on the stack", string); + runtime.stack.push(IValue::String(string)); + + Ok(()) + }.boxed() } } ); -executable_instruction!( +struct StringLowerMemoryAsync { + instruction: Instruction, +} + +impl_async_executable_instruction!( string_lower_memory(instruction: Instruction) -> _ { - move |runtime| -> _ { - let mut inputs = runtime.stack.pop(2).ok_or_else(|| { - InstructionError::from_error_kind( - instruction.clone(), - InstructionErrorKind::StackIsTooSmall { needed: 2 }, - ) - })?; - - let string_pointer = to_native::(inputs.remove(0), instruction.clone())? as u32; - let string: String = to_native(inputs.remove(0), instruction.clone())?; - let string_bytes = string.as_bytes(); - let string_length: u32 = string_bytes.len() as u32; - - let instance = &mut runtime.wasm_instance; - let memory_index = DEFAULT_MEMORY_INDEX; - let memory_view = instance - .memory_view(memory_index) - .ok_or_else(|| { + Box::new(StringLowerMemoryAsync {instruction}) + } + StringLowerMemoryAsync { + fn execute<'args>(&'args self, runtime: &'args mut Runtime) -> BoxFuture> { + async move { + let instruction = &self.instruction; + let mut inputs = runtime.stack.pop(2).ok_or_else(|| { InstructionError::from_error_kind( instruction.clone(), - InstructionErrorKind::MemoryIsMissing { memory_index }, + InstructionErrorKind::StackIsTooSmall { needed: 2 }, ) })?; - memory_view - .check_bounds(runtime.store, string_pointer, string_length) - .map_err(|e| InstructionError::from_memory_access(instruction.clone(), e))?; + let string_pointer = to_native::(inputs.remove(0), instruction.clone())? as u32; + let string: String = to_native(inputs.remove(0), instruction.clone())?; + let string_bytes = string.as_bytes(); + let string_length: u32 = string_bytes.len() as u32; - memory_view.write_bytes(runtime.store, string_pointer, string_bytes); + let instance = &mut runtime.wasm_instance; + let memory_index = DEFAULT_MEMORY_INDEX; + let memory_view = instance + .memory_view(memory_index) + .ok_or_else(|| { + InstructionError::from_error_kind( + instruction.clone(), + InstructionErrorKind::MemoryIsMissing { memory_index }, + ) + })?; - log::debug!("string.lower_memory: pushing {}, {} on the stack", string_pointer, string_length); - runtime.stack.push(IValue::I32(string_pointer as i32)); - runtime.stack.push(IValue::I32(string_length as i32)); + memory_view + .check_bounds(runtime.store, string_pointer, string_length) + .map_err(|e| InstructionError::from_memory_access(instruction.clone(), e))?; - Ok(()) + memory_view.write_bytes(runtime.store, string_pointer, string_bytes); + + log::debug!("string.lower_memory: pushing {}, {} on the stack", string_pointer, string_length); + runtime.stack.push(IValue::I32(string_pointer as i32)); + runtime.stack.push(IValue::I32(string_length as i32)); + + Ok(()) + }.boxed() } } ); -executable_instruction!( +struct StringSize { + instruction: Instruction, +} + +impl_async_executable_instruction!( string_size(instruction: Instruction) -> _ { - move |runtime| -> _ { + Box::new( StringSize{instruction } ) + } + + StringSize { + fn execute<'args>(&'args self, runtime: &'args mut Runtime) -> BoxFuture> { + async move { + let instruction = &self.instruction; match runtime.stack.pop1() { Some(IValue::String(string)) => { let length = string.len() as i32; @@ -122,7 +157,8 @@ executable_instruction!( instruction.clone(), InstructionErrorKind::StackIsTooSmall { needed: 1 } ), - } + } + }.boxed() } } ); diff --git a/wasmer-it/src/interpreter/instructions/swap2.rs b/wasmer-it/src/interpreter/instructions/swap2.rs index fc855a8..0621b77 100644 --- a/wasmer-it/src/interpreter/instructions/swap2.rs +++ b/wasmer-it/src/interpreter/instructions/swap2.rs @@ -3,7 +3,7 @@ use crate::{ interpreter::Instruction, }; -executable_instruction!( +impl_sync_executable_instruction!( swap2(instruction: Instruction) -> _ { move |runtime| -> _ { let mut values = runtime.stack.pop(2).ok_or_else(|| { diff --git a/wasmer-it/src/interpreter/mod.rs b/wasmer-it/src/interpreter/mod.rs index 0f611fe..1b6f35c 100644 --- a/wasmer-it/src/interpreter/mod.rs +++ b/wasmer-it/src/interpreter/mod.rs @@ -9,6 +9,9 @@ pub use instructions::Instruction; use crate::errors::{InstructionResult, InterpreterResult}; use crate::IValue; use stack::Stack; + +use futures::future::BoxFuture; + use std::{convert::TryFrom, marker::PhantomData}; /// Represents the `Runtime`, which is used by an adapter to execute @@ -49,14 +52,71 @@ pub(crate) struct Runtime< _phantom: PhantomData<(Export, LocalImport, Memory, MemoryView, Store)>, } +/// Type alias for an executable instruction. It's an implementation +/// details, but an instruction is a boxed dyn AsyncExecutableInstructionImpl instance. +pub(crate) type AsyncExecutableInstruction< + Instance, + Export, + LocalImport, + Memory, + MemoryView, + Store, +> = Box< + dyn AsyncExecutableInstructionImpl + + Send + + Sync, +>; + +pub(crate) type SyncExecutableInstruction< + Instance, + Export, + LocalImport, + Memory, + MemoryView, + Store, +> = Box< + dyn Fn( + &mut Runtime, + ) -> InstructionResult<()> + + Send + + Sync, +>; + /// Type alias for an executable instruction. It's an implementation /// details, but an instruction is a boxed closure instance. -pub(crate) type ExecutableInstruction = - Box< - dyn Fn(&mut Runtime, ) -> InstructionResult<()> - + Send - + Sync, - >; +pub(crate) enum ExecutableInstruction +where + Export: wasm::structures::Export, + LocalImport: wasm::structures::LocalImport, + Memory: wasm::structures::Memory, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, + Store: wasm::structures::Store, +{ + Sync(SyncExecutableInstruction), + Async(AsyncExecutableInstruction), +} + +pub(crate) trait AsyncExecutableInstructionImpl< + Instance, + Export, + LocalImport, + Memory, + MemoryView, + Store, +> where + Export: wasm::structures::Export, + LocalImport: wasm::structures::LocalImport, + Memory: wasm::structures::Memory, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, + Store: wasm::structures::Store, +{ + fn execute<'args>( + &'args self, + runtime: &'args mut Runtime, + ) -> BoxFuture<'args, InstructionResult<()>>; +} /// An interpreter is the central piece of this crate. It is a set of /// executable instructions. Each instruction takes the runtime as @@ -172,7 +232,7 @@ where /// 2. Create a fresh stack, /// 3. Execute the instructions one after the other, and /// returns the stack. - pub fn run( + pub async fn run( &self, invocation_inputs: &[IValue], wasm_instance: &mut Instance, @@ -187,13 +247,117 @@ where }; for executable_instruction in self.iter() { - executable_instruction(&mut runtime)?; + match &executable_instruction { + ExecutableInstruction::Sync(instruction) => instruction(&mut runtime)?, + ExecutableInstruction::Async(instruction) => { + instruction.execute(&mut runtime).await? + } + } } Ok(runtime.stack) } } - +/* +/// Transforms a `Vec` into an `Interpreter`. +impl TryFrom> + for Interpreter +where + Export: wasm::structures::Export, + LocalImport: wasm::structures::LocalImport, + Memory: wasm::structures::Memory, + MemoryView: wasm::structures::MemoryView, + Instance: wasm::structures::Instance, + Store: wasm::structures::Store, +{ + type Error = (); + + fn try_from(instructions: Vec) -> Result { + let executable_instructions = instructions + .into_iter() + .map(|instruction| match instruction { + Instruction::ArgumentGet { index } => { + instructions::argument_get(index, instruction) + } + + Instruction::CallCore { function_index } => { + instructions::call_core(function_index, instruction) + } + + Instruction::BoolFromI32 => instructions::bool_from_i32(instruction), + Instruction::S8FromI32 => instructions::s8_from_i32(instruction), + Instruction::S8FromI64 => instructions::s8_from_i64(instruction), + Instruction::S16FromI32 => instructions::s16_from_i32(instruction), + Instruction::S16FromI64 => instructions::s16_from_i64(instruction), + Instruction::S32FromI32 => instructions::s32_from_i32(instruction), + Instruction::S32FromI64 => instructions::s32_from_i64(instruction), + Instruction::S64FromI32 => instructions::s64_from_i32(instruction), + Instruction::S64FromI64 => instructions::s64_from_i64(instruction), + Instruction::I32FromBool => instructions::i32_from_bool(instruction), + Instruction::I32FromS8 => instructions::i32_from_s8(instruction), + Instruction::I32FromS16 => instructions::i32_from_s16(instruction), + Instruction::I32FromS32 => instructions::i32_from_s32(instruction), + Instruction::I32FromS64 => instructions::i32_from_s64(instruction), + Instruction::I64FromS8 => instructions::i64_from_s8(instruction), + Instruction::I64FromS16 => instructions::i64_from_s16(instruction), + Instruction::I64FromS32 => instructions::i64_from_s32(instruction), + Instruction::I64FromS64 => instructions::i64_from_s64(instruction), + Instruction::U8FromI32 => instructions::u8_from_i32(instruction), + Instruction::U8FromI64 => instructions::u8_from_i64(instruction), + Instruction::U16FromI32 => instructions::u16_from_i32(instruction), + Instruction::U16FromI64 => instructions::u16_from_i64(instruction), + Instruction::U32FromI32 => instructions::u32_from_i32(instruction), + Instruction::U32FromI64 => instructions::u32_from_i64(instruction), + Instruction::U64FromI32 => instructions::u64_from_i32(instruction), + Instruction::U64FromI64 => instructions::u64_from_i64(instruction), + Instruction::I32FromU8 => instructions::i32_from_u8(instruction), + Instruction::I32FromU16 => instructions::i32_from_u16(instruction), + Instruction::I32FromU32 => instructions::i32_from_u32(instruction), + Instruction::I32FromU64 => instructions::i32_from_u64(instruction), + Instruction::I64FromU8 => instructions::i64_from_u8(instruction), + Instruction::I64FromU16 => instructions::i64_from_u16(instruction), + Instruction::I64FromU32 => instructions::i64_from_u32(instruction), + Instruction::I64FromU64 => instructions::i64_from_u64(instruction), + Instruction::PushI32 { value } => instructions::push_i32(value), + Instruction::PushI64 { value } => instructions::push_i64(value), + + Instruction::StringLiftMemory => instructions::string_lift_memory(instruction), + Instruction::StringLowerMemory => instructions::string_lower_memory(instruction), + Instruction::StringSize => instructions::string_size(instruction), + + Instruction::ByteArrayLiftMemory => { + instructions::byte_array_lift_memory(instruction) + } + Instruction::ByteArrayLowerMemory => { + instructions::byte_array_lower_memory(instruction) + } + Instruction::ByteArraySize => instructions::byte_array_size(instruction), + + Instruction::ArrayLiftMemory { ref value_type } => { + let value_type = value_type.clone(); + instructions::array_lift_memory(instruction, value_type) + } + Instruction::ArrayLowerMemory { ref value_type } => { + let value_type = value_type.clone(); + instructions::array_lower_memory(instruction, value_type) + } + Instruction::RecordLiftMemory { record_type_id } => { + instructions::record_lift_memory(record_type_id as _, instruction) + } + Instruction::RecordLowerMemory { record_type_id } => { + instructions::record_lower_memory(record_type_id as _, instruction) + } + Instruction::Dup => instructions::dup(instruction), + Instruction::Swap2 => instructions::swap2(instruction), + }) + .collect(); + + Ok(Interpreter { + executable_instructions, + }) + } +} +*/ /// Transforms a `Vec` into an `Interpreter`. impl TryFrom> for Interpreter diff --git a/wasmer-it/src/interpreter/wasm/structures.rs b/wasmer-it/src/interpreter/wasm/structures.rs index e4c59f4..22eebc9 100644 --- a/wasmer-it/src/interpreter/wasm/structures.rs +++ b/wasmer-it/src/interpreter/wasm/structures.rs @@ -4,6 +4,10 @@ use crate::ast::FunctionArg; use crate::IRecordType; use crate::IType; use crate::IValue; + +use futures::future::BoxFuture; +use futures::FutureExt; + use std::sync::Arc; pub use it_memory_traits::{Memory, MemoryAccessError, MemoryView}; @@ -45,31 +49,34 @@ impl LocalImportIndex for FunctionIndex { type Import = ImportFunctionIndex; } -pub trait Export { +pub trait Export: Send { fn name(&self) -> &str; fn inputs_cardinality(&self) -> usize; fn outputs_cardinality(&self) -> usize; fn arguments(&self) -> &[FunctionArg]; fn outputs(&self) -> &[IType]; - fn call(&self, arguments: &[IValue]) -> Result, anyhow::Error>; + fn call_async<'args>( + &'args self, + arguments: &'args [IValue], + ) -> BoxFuture<'args, Result, anyhow::Error>>; } -pub trait LocalImport { +pub trait LocalImport: Send + Sync { fn name(&self) -> &str; fn inputs_cardinality(&self) -> usize; fn outputs_cardinality(&self) -> usize; fn arguments(&self) -> &[FunctionArg]; fn outputs(&self) -> &[IType]; - fn call( - &self, - store: &mut ::ActualStore<'_>, - arguments: &[IValue], - ) -> Result, anyhow::Error>; + fn call_async<'args>( + &'args self, + store: &'args mut ::ActualStore<'_>, + arguments: &'args [IValue], + ) -> BoxFuture, anyhow::Error>>; } pub use it_memory_traits::Store; -pub trait Instance +pub trait Instance: Send + Sync where E: Export, LI: LocalImport, @@ -84,32 +91,6 @@ where fn wit_record_by_id(&self, index: u64) -> Option<&Arc>; } -impl Export for () { - fn name(&self) -> &str { - "" - } - - fn inputs_cardinality(&self) -> usize { - 0 - } - - fn outputs_cardinality(&self) -> usize { - 0 - } - - fn arguments(&self) -> &[FunctionArg] { - &[] - } - - fn outputs(&self) -> &[IType] { - &[] - } - - fn call(&self, _arguments: &[IValue]) -> Result, anyhow::Error> { - Err(anyhow::anyhow!("some error")) - } -} - impl LocalImport for () { fn name(&self) -> &str { "" @@ -131,12 +112,38 @@ impl LocalImport for () { &[] } - fn call( + fn call_async( &self, - _store: &mut ::ActualStore<'_>, + _store: &mut ::ActualStore<'_>, _arguments: &[IValue], - ) -> Result, anyhow::Error> { - Err(anyhow::anyhow!("some error")) + ) -> BoxFuture, anyhow::Error>> { + async { Err(anyhow::anyhow!("some error")) }.boxed() + } +} + +impl Export for () { + fn name(&self) -> &str { + "" + } + + fn inputs_cardinality(&self) -> usize { + 0 + } + + fn outputs_cardinality(&self) -> usize { + 0 + } + + fn arguments(&self) -> &[FunctionArg] { + &[] + } + + fn outputs(&self) -> &[IType] { + &[] + } + + fn call_async(&self, _arguments: &[IValue]) -> BoxFuture, anyhow::Error>> { + async { Err(anyhow::anyhow!("some error")) }.boxed() } } diff --git a/wasmer-it/src/macros.rs b/wasmer-it/src/macros.rs index b791fe0..2edde80 100644 --- a/wasmer-it/src/macros.rs +++ b/wasmer-it/src/macros.rs @@ -56,7 +56,7 @@ macro_rules! consume { /// // ^ the `x` argument /// /// // an executable instruction is a closure that takes a `Runtime` instance -/// move |runtime| -> _ { +/// move |runtime| -> Pin> + 'static>> _ { /// // Do something. /// /// Ok(()) @@ -65,7 +65,31 @@ macro_rules! consume { /// ``` /// /// Check the existing executable instruction to get more examples. -macro_rules! executable_instruction { +macro_rules! impl_async_executable_instruction { + ($getter_name:ident($($argument_name:ident: $argument_type:ty),*) -> _ $getter_implementation:block $name:ident $($implementation:tt)*) => { + impl crate::interpreter::AsyncExecutableInstructionImpl for $name + where + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: crate::interpreter::wasm::structures::Instance, + Store: crate::interpreter::wasm::structures::Store, $($implementation)* + + pub(crate) fn $getter_name($($argument_name: $argument_type),*) -> crate::interpreter::ExecutableInstruction + where + Export: crate::interpreter::wasm::structures::Export, + LocalImport: crate::interpreter::wasm::structures::LocalImport, + Memory: crate::interpreter::wasm::structures::Memory, + MemoryView: crate::interpreter::wasm::structures::MemoryView, + Instance: crate::interpreter::wasm::structures::Instance, + Store: crate::interpreter::wasm::structures::Store, { + crate::interpreter::ExecutableInstruction::Async($getter_implementation) + } + } +} + +macro_rules! impl_sync_executable_instruction { ($name:ident ( $($argument_name:ident: $argument_type:ty),* ) -> _ $implementation:block ) => { pub(crate) fn $name( $($argument_name: $argument_type),* @@ -81,7 +105,7 @@ macro_rules! executable_instruction { #[allow(unused_imports)] use crate::interpreter::{stack::Stackable}; - Box::new($implementation) + crate::interpreter::ExecutableInstruction::Sync(Box::new($implementation)) } }; }