mod argument_get;
mod arrays;
mod byte_arrays;
mod call_core;
mod dup;
pub(self) mod lilo;
mod numbers;
mod push;
mod records;
mod strings;
mod swap2;
use crate::errors::{
InstructionError, InstructionErrorKind, InstructionResult, WasmValueNativeCastError,
};
use crate::interpreter::wasm;
use crate::IType;
use crate::IValue;
use crate::NEVec;
pub(crate) use argument_get::argument_get;
pub(crate) use arrays::*;
pub(crate) use byte_arrays::*;
pub(crate) use call_core::call_core;
pub(crate) use dup::dup;
pub(crate) use numbers::*;
pub(crate) use push::*;
pub(crate) use records::*;
pub(crate) use strings::*;
pub(crate) use swap2::swap2;
use fluence_it_types::NativeType;
use serde::Deserialize;
use serde::Serialize;
use std::convert::TryFrom;
pub(self) const ALLOCATE_FUNC_INDEX: u32 = 0;
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
pub enum Instruction {
ArgumentGet {
index: u32,
},
CallCore {
function_index: u32,
},
BoolFromI32,
S8FromI32,
S8FromI64,
S16FromI32,
S16FromI64,
S32FromI32,
S32FromI64,
S64FromI32,
S64FromI64,
I32FromBool,
I32FromS8,
I32FromS16,
I32FromS32,
I32FromS64,
I64FromS8,
I64FromS16,
I64FromS32,
I64FromS64,
U8FromI32,
U8FromI64,
U16FromI32,
U16FromI64,
U32FromI32,
U32FromI64,
U64FromI32,
U64FromI64,
I32FromU8,
I32FromU16,
I32FromU32,
I32FromU64,
I64FromU8,
I64FromU16,
I64FromU32,
I64FromU64,
StringLiftMemory,
StringLowerMemory,
ByteArraySize,
ByteArrayLiftMemory,
ByteArrayLowerMemory,
StringSize,
ArrayLiftMemory {
value_type: IType,
},
ArrayLowerMemory {
value_type: IType,
},
RecordLiftMemory {
record_type_id: u32,
},
RecordLowerMemory {
record_type_id: u32,
},
PushI32 {
value: i32,
},
PushI64 {
value: i64,
},
Dup,
Swap2,
}
pub(crate) fn to_native<'a, T>(wit_value: IValue, instruction: Instruction) -> InstructionResult<T>
where
T: NativeType + TryFrom<IValue, Error = WasmValueNativeCastError>,
{
T::try_from(wit_value).map_err(|error| {
InstructionError::from_error_kind(instruction, InstructionErrorKind::ToNative(error))
})
}
pub(crate) fn check_function_signature<
'instance,
Instance,
Export,
LocalImport,
Memory,
MemoryView,
Store,
>(
instance: &'instance Instance,
local_import: &LocalImport,
values: &[IValue],
) -> Result<(), InstructionErrorKind>
where
Export: wasm::structures::Export + 'instance,
LocalImport: wasm::structures::LocalImport<Store> + 'instance,
Memory: wasm::structures::Memory<MemoryView, Store> + 'instance,
MemoryView: wasm::structures::MemoryView<Store>,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
Store: wasm::structures::Store,
{
let func_inputs = local_import.arguments();
for (func_input_arg, value) in func_inputs.iter().zip(values.iter()) {
is_value_compatible_to_type(instance, &func_input_arg.ty, value)?;
}
Ok(())
}
pub(crate) fn is_value_compatible_to_type<
'instance,
Instance,
Export,
LocalImport,
Memory,
MemoryView,
Store,
>(
instance: &'instance Instance,
interface_type: &IType,
interface_value: &IValue,
) -> Result<(), InstructionErrorKind>
where
Export: wasm::structures::Export + 'instance,
LocalImport: wasm::structures::LocalImport<Store> + 'instance,
Memory: wasm::structures::Memory<MemoryView, Store> + 'instance,
MemoryView: wasm::structures::MemoryView<Store>,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
Store: wasm::structures::Store,
{
match (&interface_type, interface_value) {
(IType::Boolean, IValue::Boolean(_)) => Ok(()),
(IType::S8, IValue::S8(_)) => Ok(()),
(IType::S16, IValue::S16(_)) => Ok(()),
(IType::S32, IValue::S32(_)) => Ok(()),
(IType::S64, IValue::S64(_)) => Ok(()),
(IType::U8, IValue::U8(_)) => Ok(()),
(IType::U16, IValue::U16(_)) => Ok(()),
(IType::U32, IValue::U32(_)) => Ok(()),
(IType::U64, IValue::U64(_)) => Ok(()),
(IType::I32, IValue::I32(_)) => Ok(()),
(IType::I64, IValue::I64(_)) => Ok(()),
(IType::F32, IValue::F32(_)) => Ok(()),
(IType::F64, IValue::F64(_)) => Ok(()),
(IType::String, IValue::String(_)) => Ok(()),
(IType::ByteArray, IValue::ByteArray(_)) => Ok(()),
(IType::Array(ty), IValue::Array(values)) => {
for value in values {
is_value_compatible_to_type(instance, ty, value)?
}
Ok(())
}
(IType::ByteArray, IValue::Array(values)) => {
for value in values {
is_value_compatible_to_type(instance, &IType::U8, value)?
}
Ok(())
}
(IType::Array(ty), IValue::ByteArray(_)) => {
if ty.as_ref() == &IType::U8 {
return Ok(());
}
Err(InstructionErrorKind::InvalidValueOnTheStack {
expected_type: interface_type.clone(),
received_value: interface_value.clone(),
})
}
(IType::Record(ref record_type_id), IValue::Record(record_fields)) => {
is_record_fields_compatible_to_type(instance, *record_type_id, record_fields)?;
Ok(())
}
_ => Err(InstructionErrorKind::InvalidValueOnTheStack {
expected_type: interface_type.clone(),
received_value: interface_value.clone(),
}),
}
}
pub(crate) fn is_record_fields_compatible_to_type<
'instance,
Instance,
Export,
LocalImport,
Memory,
MemoryView,
Store,
>(
instance: &'instance Instance,
record_type_id: u64,
record_fields: &[IValue],
) -> Result<(), InstructionErrorKind>
where
Export: wasm::structures::Export + 'instance,
LocalImport: wasm::structures::LocalImport<Store> + 'instance,
Memory: wasm::structures::Memory<MemoryView, Store> + 'instance,
MemoryView: wasm::structures::MemoryView<Store>,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView, Store>,
Store: wasm::structures::Store,
{
let record_type = instance
.wit_record_by_id(record_type_id)
.ok_or(InstructionErrorKind::RecordTypeByNameIsMissing { record_type_id })?;
if record_fields.len() != record_type.fields.len() {
return Err(InstructionErrorKind::InvalidValueOnTheStack {
expected_type: IType::Record(record_type_id),
received_value: IValue::Record(NEVec::new(record_fields.to_vec()).unwrap()),
});
}
for (record_type_field, record_value_field) in
record_type.fields.iter().zip(record_fields.iter())
{
is_value_compatible_to_type(instance, &record_type_field.ty, record_value_field)?;
}
Ok(())
}
#[cfg(test)]
pub(crate) mod tests {
use crate::{ast::*, interpreter::wasm, types::*, values::*};
use std::{cell::Cell, collections::HashMap, convert::TryInto, ops::Deref, rc::Rc};
pub(crate) struct Export {
pub(crate) inputs: Vec<IType>,
pub(crate) outputs: Vec<IType>,
pub(crate) function: fn(arguments: &[IValue]) -> Result<Vec<IValue>, ()>,
}
impl wasm::structures::Export for Export {
fn inputs_cardinality(&self) -> usize {
self.inputs.len() as usize
}
fn outputs_cardinality(&self) -> usize {
self.outputs.len()
}
fn arguments(&self) -> &[IType] {
&self.inputs
}
fn outputs(&self) -> &[IType] {
&self.outputs
}
fn call(&self, arguments: &[IValue]) -> Result<Vec<IValue>, ()> {
(self.function)(arguments)
}
}
pub(crate) struct LocalImport {
pub(crate) inputs: Vec<IType>,
pub(crate) outputs: Vec<IType>,
pub(crate) function: fn(arguments: &[IValue]) -> Result<Vec<IValue>, ()>,
}
impl wasm::structures::LocalImport for LocalImport {
fn inputs_cardinality(&self) -> usize {
self.inputs.len()
}
fn outputs_cardinality(&self) -> usize {
self.outputs.len()
}
fn arguments(&self) -> &[IType] {
&self.inputs
}
fn outputs(&self) -> &[IType] {
&self.outputs
}
fn call(&self, arguments: &[IValue]) -> Result<Vec<IValue>, ()> {
(self.function)(arguments)
}
}
#[derive(Default, Clone)]
pub(crate) struct MemoryView(Rc<Vec<Cell<u8>>>);
impl wasm::structures::MemoryView for MemoryView {}
impl Deref for MemoryView {
type Target = [Cell<u8>];
fn deref(&self) -> &Self::Target {
self.0.as_slice()
}
}
#[derive(Default)]
pub(crate) struct Memory {
pub(crate) view: MemoryView,
}
impl Memory {
pub(crate) fn new(data: Vec<Cell<u8>>) -> Self {
Self {
view: MemoryView(Rc::new(data)),
}
}
}
impl wasm::structures::Memory<MemoryView, Store> for Memory {
fn view(&self) -> MemoryView {
self.view.clone()
}
}
#[derive(Default)]
pub(crate) struct Instance {
pub(crate) exports: HashMap<String, Export>,
pub(crate) locals_or_imports: HashMap<usize, LocalImport>,
pub(crate) memory: Memory,
pub(crate) wit_types: Vec<Type>,
}
impl Instance {
pub(crate) fn new() -> Self {
Self {
exports: {
let mut hashmap = HashMap::new();
hashmap.insert(
"sum".into(),
Export {
inputs: vec![IType::I32, IType::I32],
outputs: vec![IType::I32],
function: |arguments: &[IValue]| {
let a: i32 = (&arguments[0]).try_into().unwrap();
let b: i32 = (&arguments[1]).try_into().unwrap();
Ok(vec![IValue::I32(a + b)])
},
},
);
hashmap
},
locals_or_imports: {
let mut hashmap = HashMap::new();
hashmap.insert(
42,
LocalImport {
inputs: vec![IType::I32, IType::I32],
outputs: vec![IType::I32],
function: |arguments: &[IValue]| {
let a: i32 = (&arguments[0]).try_into().unwrap();
let b: i32 = (&arguments[1]).try_into().unwrap();
Ok(vec![IValue::I32(a * b)])
},
},
);
hashmap.insert(
43,
LocalImport {
inputs: vec![IType::I32],
outputs: vec![IType::I32],
function: |arguments: &[IValue]| {
let _size: i32 = (&arguments[0]).try_into().unwrap();
Ok(vec![IValue::I32(0)])
},
},
);
hashmap
},
memory: Memory::new(vec![Cell::new(0); 128]),
wit_types: vec![Type::Record(RecordType {
name: String::from("RecordType0"),
fields: vec1![
RecordFieldType {
name: String::from("field_0"),
ty: IType::I32,
},
RecordFieldType {
name: String::from("field_1"),
ty: IType::Record(RecordType {
name: String::from("RecordType1"),
fields: vec1![
RecordFieldType {
name: String::from("field_0"),
ty: IType::String,
},
RecordFieldType {
name: String::from("field1"),
ty: IType::F32
}
],
}),
},
RecordFieldType {
name: String::from("field_2"),
ty: IType::I64,
}
],
})],
}
}
}
impl wasm::structures::Instance<Export, LocalImport, Memory, MemoryView> for Instance {
fn export(&self, export_name: &str) -> Option<&Export> {
self.exports.get(export_name)
}
fn local_or_import<I: wasm::structures::TypedIndex + wasm::structures::LocalImportIndex>(
&mut self,
index: I,
) -> Option<&LocalImport> {
self.locals_or_imports.get(&index.index())
}
fn memory(&self, _index: usize) -> Option<&Memory> {
Some(&self.memory)
}
fn wit_type_by_id(&self, index: u32) -> Option<&Type> {
self.wit_types.get(index as usize)
}
fn wit_record_by_id(&self, index: u64) -> Option<&RecordType> {
self.wit_types.get(index as _)
}
}
}