feat!: move to async execution (#44)

* compiling async interpreter

* compiling

* fix after merge

* added lifetimes to allocatable

* move from async_trait to BoxFuture

* self-review fixes

* fix lifetime naming

* make simple instructions sync

* fmt
This commit is contained in:
Valery Antopol 2024-01-25 14:43:07 +03:00 committed by GitHub
parent 2a2288f2c1
commit 63b4395ab5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 1017 additions and 408 deletions

160
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -41,9 +41,6 @@ impl<'r, R: RecordResolvable, MV: MemoryView<Store>, 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 }
}
}

View File

@ -38,7 +38,8 @@ impl LoweredArray {
}
}
pub fn array_lower_memory<
#[async_recursion::async_recursion]
pub async fn array_lower_memory<
A: Allocatable<MV, Store>,
MV: MemoryView<Store>,
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());
}
}

View File

@ -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, Store>,
MV: MemoryView<Store>,
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)
}

View File

@ -46,25 +46,27 @@ impl<'i, A: Allocatable<MV, Store>, MV: MemoryView<Store>, Store: it_memory_trai
Ok(writer)
}
pub fn write_bytes(
pub async fn write_bytes<'store, 'store_inner: 'store>(
&mut self,
store: &mut <Store as it_memory_traits::Store>::ActualStore<'_>,
store: &'store mut <Store as it_memory_traits::Store>::ActualStore<'store_inner>,
bytes: &[u8],
) -> LoResult<u32> {
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 <Store as it_memory_traits::Store>::ActualStore<'_>,
size: u32,
type_tag: u32,
) -> LoResult<SequentialWriter> {
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)

View File

@ -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<MV: MemoryView<Store>, Store: it_memory_traits::Store> {
fn allocate(
&mut self,
store: &mut <Store as it_memory_traits::Store>::ActualStore<'_>,
pub trait Allocatable<MV: MemoryView<Store>, Store: it_memory_traits::Store>: Send {
fn allocate<'this, 'store: 'this, 'store_inner: 'this>(
&'this mut self,
store: &'store mut <Store as it_memory_traits::Store>::ActualStore<'store_inner>,
size: u32,
type_tag: u32,
) -> Result<(u32, MV), AllocatableError>;
) -> BoxFuture<'this, Result<(u32, MV), AllocatableError>>;
}
#[derive(Debug, ThisError)]

View File

@ -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<Store: self::Store> {
@ -70,7 +70,9 @@ pub trait MemoryWritable<Store: self::Store> {
);
}
pub trait MemoryView<Store: self::Store>: MemoryWritable<Store> + MemoryReadable<Store> {
pub trait MemoryView<Store: self::Store>:
Send + MemoryWritable<Store> + MemoryReadable<Store>
{
/// 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<Store: self::Store>: MemoryWritable<Store> + MemoryReadable
) -> Result<(), MemoryAccessError>;
}
pub trait Memory<View, Store: self::Store>
pub trait Memory<View, Store: self::Store>: Send
where
View: MemoryView<Store>,
{

3
rust-toolchain.toml Normal file
View File

@ -0,0 +1,3 @@
[toolchain]
channel = "nightly-2023-09-17"
targets = [ "x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "wasm32-wasi" ]

View File

@ -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"

View File

@ -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;

View File

@ -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<Instance, Export, LocalImport, Memory, MemoryView, Store>(
instruction: Instruction,
value_type: IType,
) -> crate::interpreter::ExecutableInstruction<
Instance,
Export,
LocalImport,
Memory,
MemoryView,
Store,
>
) -> ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView, Store>
where
Export: crate::interpreter::wasm::structures::Export,
LocalImport: crate::interpreter::wasm::structures::LocalImport<Store>,
@ -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<Instance, Export, LocalImport, Memory, MemoryView, Store>
AsyncExecutableInstructionImpl<Instance, Export, LocalImport, Memory, MemoryView, Store>
for ArrayLiftMemoryAsync
where
Export: crate::interpreter::wasm::structures::Export,
LocalImport: crate::interpreter::wasm::structures::LocalImport<Store>,
Memory: crate::interpreter::wasm::structures::Memory<MemoryView, Store>,
MemoryView: crate::interpreter::wasm::structures::MemoryView<Store>,
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<Instance, Export, LocalImport, Memory, MemoryView, Store>,
) -> BoxFuture<InstructionResult<()>> {
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<Instance, Export, LocalImport, Memory, MemoryView, Store>(
instruction: Instruction,
value_type: IType,
) -> crate::interpreter::ExecutableInstruction<
Instance,
Export,
LocalImport,
Memory,
MemoryView,
Store,
>
) -> ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView, Store>
where
Export: crate::interpreter::wasm::structures::Export,
LocalImport: crate::interpreter::wasm::structures::LocalImport<Store>,
@ -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<Instance, Export, LocalImport, Memory, MemoryView, Store>
AsyncExecutableInstructionImpl<Instance, Export, LocalImport, Memory, MemoryView, Store>
for ArrayLowerMemoryAsync
where
Export: crate::interpreter::wasm::structures::Export,
LocalImport: crate::interpreter::wasm::structures::LocalImport<Store>,
Memory: crate::interpreter::wasm::structures::Memory<MemoryView, Store>,
MemoryView: crate::interpreter::wasm::structures::MemoryView<Store>,
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<Instance, Export, LocalImport, Memory, MemoryView, Store>,
) -> BoxFuture<InstructionResult<()>> {
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()
}
}

View File

@ -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<Instance, Export, LocalImport, Memory, MemoryView, Store>)
-> BoxFuture<InstructionResult<()>> {
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::<i32>(inputs.remove(0), instruction.clone())? as u32;
let length = to_native::<i32>(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::<i32>(inputs.remove(0), self.instruction.clone())? as u32;
let length = to_native::<i32>(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::<i32>(inputs.remove(0), instruction.clone())? as u32;
let array: Vec<u8> = 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<Instance, Export, LocalImport, Memory, MemoryView, Store>) -> BoxFuture<InstructionResult<()>> {
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::<i32>(inputs.remove(0), instruction.clone())? as u32;
let array: Vec<u8> = 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<Instance, Export, LocalImport, Memory, MemoryView, Store>) -> BoxFuture<InstructionResult<()>> {
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()
}
}
);

View File

@ -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<Instance, Export, LocalImport, Memory, MemoryView, Store>)
-> BoxFuture<InstructionResult<()>> {
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()
}
}
);

View File

@ -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(|| {

View File

@ -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<Store> + 'i,
Memory: wasm::structures::Memory<MemoryView, Store> + 'i,
Export: wasm::structures::Export,
LocalImport: wasm::structures::LocalImport<Store>,
Memory: wasm::structures::Memory<MemoryView, Store>,
MemoryView: wasm::structures::MemoryView<Store>,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
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<Store> + 'i,
Memory: wasm::structures::Memory<MemoryView, Store> + 'i,
Export: wasm::structures::Export,
LocalImport: wasm::structures::LocalImport<Store>,
Memory: wasm::structures::Memory<MemoryView, Store>,
MemoryView: wasm::structures::MemoryView<Store>,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
Store: wasm::structures::Store,
@ -50,61 +53,64 @@ where
impl<'i, Instance, Export, LocalImport, Memory, MemoryView, Store> Allocatable<MemoryView, Store>
for LoHelper<'i, Instance, Export, LocalImport, Memory, MemoryView, Store>
where
Export: wasm::structures::Export + 'i,
LocalImport: wasm::structures::LocalImport<Store> + 'i,
Memory: wasm::structures::Memory<MemoryView, Store> + 'i,
Export: wasm::structures::Export,
LocalImport: wasm::structures::LocalImport<Store>,
Memory: wasm::structures::Memory<MemoryView, Store>,
MemoryView: wasm::structures::MemoryView<Store>,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
Store: wasm::structures::Store,
{
fn allocate(
&mut self,
store: &mut <Store as wasm::structures::Store>::ActualStore<'_>,
fn allocate<'this, 'store: 'this, 'store_inner: 'this>(
&'this mut self,
store: &'store mut <Store as wasm::structures::Store>::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()
}
}

View File

@ -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() {

View File

@ -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| -> _ {

View File

@ -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<Instance, Export, LocalImport, Memory, MemoryView, Store>(
record_type_id: u64,
instruction: Instruction,
) -> crate::interpreter::ExecutableInstruction<
Instance,
Export,
LocalImport,
Memory,
MemoryView,
Store,
>
) -> ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView, Store>
where
Export: crate::interpreter::wasm::structures::Export,
LocalImport: crate::interpreter::wasm::structures::LocalImport<Store>,
@ -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<Instance, Export, LocalImport, Memory, MemoryView, Store>
AsyncExecutableInstructionImpl<Instance, Export, LocalImport, Memory, MemoryView, Store>
for RecordLiftMemoryAsync
where
Export: crate::interpreter::wasm::structures::Export,
LocalImport: crate::interpreter::wasm::structures::LocalImport<Store>,
Memory: crate::interpreter::wasm::structures::Memory<MemoryView, Store>,
MemoryView: crate::interpreter::wasm::structures::MemoryView<Store>,
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<Instance, Export, LocalImport, Memory, MemoryView, Store>,
) -> BoxFuture<InstructionResult<()>> {
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<Instance, Export, LocalImport, Memory, MemoryView, Store>(
record_type_id: u64,
instruction: Instruction,
) -> crate::interpreter::ExecutableInstruction<
Instance,
Export,
LocalImport,
Memory,
MemoryView,
Store,
>
) -> ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView, Store>
where
Export: crate::interpreter::wasm::structures::Export,
LocalImport: crate::interpreter::wasm::structures::LocalImport<Store>,
@ -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<Instance, Export, LocalImport, Memory, MemoryView, Store>
AsyncExecutableInstructionImpl<Instance, Export, LocalImport, Memory, MemoryView, Store>
for RecordLowerMemoryAsync
where
Export: crate::interpreter::wasm::structures::Export,
LocalImport: crate::interpreter::wasm::structures::LocalImport<Store>,
Memory: crate::interpreter::wasm::structures::Memory<MemoryView, Store>,
MemoryView: crate::interpreter::wasm::structures::MemoryView<Store>,
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<Instance, Export, LocalImport, Memory, MemoryView, Store>,
) -> BoxFuture<InstructionResult<()>> {
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()
}
}

View File

@ -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<Instance, Export, LocalImport, Memory, MemoryView, Store>) -> BoxFuture<InstructionResult<()>> {
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::<i32>(inputs.remove(0), instruction.clone())? as u32;
let length = to_native::<i32>(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::<i32>(inputs.remove(0), instruction.clone())? as u32;
let length = to_native::<i32>(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::<i32>(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<Instance, Export, LocalImport, Memory, MemoryView, Store>) -> BoxFuture<InstructionResult<()>> {
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::<i32>(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<Instance, Export, LocalImport, Memory, MemoryView, Store>) -> BoxFuture<InstructionResult<()>> {
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()
}
}
);

View File

@ -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(|| {

View File

@ -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<Instance, Export, LocalImport, Memory, MemoryView, Store>
+ Send
+ Sync,
>;
pub(crate) type SyncExecutableInstruction<
Instance,
Export,
LocalImport,
Memory,
MemoryView,
Store,
> = Box<
dyn Fn(
&mut Runtime<Instance, Export, LocalImport, Memory, MemoryView, Store>,
) -> 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<Instance, Export, LocalImport, Memory, MemoryView, Store> =
Box<
dyn Fn(&mut Runtime<Instance, Export, LocalImport, Memory, MemoryView, Store>, ) -> InstructionResult<()>
+ Send
+ Sync,
>;
pub(crate) enum ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView, Store>
where
Export: wasm::structures::Export,
LocalImport: wasm::structures::LocalImport<Store>,
Memory: wasm::structures::Memory<MemoryView, Store>,
MemoryView: wasm::structures::MemoryView<Store>,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
Store: wasm::structures::Store,
{
Sync(SyncExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView, Store>),
Async(AsyncExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView, Store>),
}
pub(crate) trait AsyncExecutableInstructionImpl<
Instance,
Export,
LocalImport,
Memory,
MemoryView,
Store,
> where
Export: wasm::structures::Export,
LocalImport: wasm::structures::LocalImport<Store>,
Memory: wasm::structures::Memory<MemoryView, Store>,
MemoryView: wasm::structures::MemoryView<Store>,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
Store: wasm::structures::Store,
{
fn execute<'args>(
&'args self,
runtime: &'args mut Runtime<Instance, Export, LocalImport, Memory, MemoryView, Store>,
) -> 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<Instruction>` into an `Interpreter`.
impl<Instance, Export, LocalImport, Memory, MemoryView, Store> TryFrom<Vec<Instruction>>
for Interpreter<Instance, Export, LocalImport, Memory, MemoryView, Store>
where
Export: wasm::structures::Export,
LocalImport: wasm::structures::LocalImport<Store>,
Memory: wasm::structures::Memory<MemoryView, Store>,
MemoryView: wasm::structures::MemoryView<Store>,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
Store: wasm::structures::Store,
{
type Error = ();
fn try_from(instructions: Vec<Instruction>) -> Result<Self, Self::Error> {
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<Instruction>` into an `Interpreter`.
impl<Instance, Export, LocalImport, Memory, MemoryView, Store> TryFrom<Vec<Instruction>>
for Interpreter<Instance, Export, LocalImport, Memory, MemoryView, Store>

View File

@ -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<Vec<IValue>, anyhow::Error>;
fn call_async<'args>(
&'args self,
arguments: &'args [IValue],
) -> BoxFuture<'args, Result<Vec<IValue>, anyhow::Error>>;
}
pub trait LocalImport<Store: self::Store> {
pub trait LocalImport<Store: self::Store>: 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 <Store as self::Store>::ActualStore<'_>,
arguments: &[IValue],
) -> Result<Vec<IValue>, anyhow::Error>;
fn call_async<'args>(
&'args self,
store: &'args mut <Store as self::Store>::ActualStore<'_>,
arguments: &'args [IValue],
) -> BoxFuture<Result<Vec<IValue>, anyhow::Error>>;
}
pub use it_memory_traits::Store;
pub trait Instance<E, LI, M, MV, S>
pub trait Instance<E, LI, M, MV, S>: Send + Sync
where
E: Export,
LI: LocalImport<S>,
@ -84,32 +91,6 @@ where
fn wit_record_by_id(&self, index: u64) -> Option<&Arc<IRecordType>>;
}
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<Vec<IValue>, anyhow::Error> {
Err(anyhow::anyhow!("some error"))
}
}
impl<Store: self::Store> LocalImport<Store> for () {
fn name(&self) -> &str {
""
@ -131,12 +112,38 @@ impl<Store: self::Store> LocalImport<Store> for () {
&[]
}
fn call(
fn call_async(
&self,
_store: &mut <Store as self::Store>::ActualStore<'_>,
_store: &mut <Store as it_memory_traits::Store>::ActualStore<'_>,
_arguments: &[IValue],
) -> Result<Vec<IValue>, anyhow::Error> {
Err(anyhow::anyhow!("some error"))
) -> BoxFuture<Result<Vec<IValue>, 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<Result<Vec<IValue>, anyhow::Error>> {
async { Err(anyhow::anyhow!("some error")) }.boxed()
}
}

View File

@ -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<Box<dyn Future<Output = Result<(), InstructionError>> + '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<Instance, Export, LocalImport, Memory, MemoryView, Store> crate::interpreter::AsyncExecutableInstructionImpl<Instance, Export, LocalImport, Memory, MemoryView, Store> for $name
where
Export: crate::interpreter::wasm::structures::Export,
LocalImport: crate::interpreter::wasm::structures::LocalImport<Store>,
Memory: crate::interpreter::wasm::structures::Memory<MemoryView, Store>,
MemoryView: crate::interpreter::wasm::structures::MemoryView<Store>,
Instance: crate::interpreter::wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
Store: crate::interpreter::wasm::structures::Store, $($implementation)*
pub(crate) fn $getter_name<Instance, Export, LocalImport, Memory, MemoryView, Store>($($argument_name: $argument_type),*) -> crate::interpreter::ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView, Store>
where
Export: crate::interpreter::wasm::structures::Export,
LocalImport: crate::interpreter::wasm::structures::LocalImport<Store>,
Memory: crate::interpreter::wasm::structures::Memory<MemoryView, Store>,
MemoryView: crate::interpreter::wasm::structures::MemoryView<Store>,
Instance: crate::interpreter::wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
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<Instance, Export, LocalImport, Memory, MemoryView, Store>(
$($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))
}
};
}