mod argument_get;
mod arrays;
mod call_core;
mod dup;
mod numbers;
mod records;
mod strings;
mod swap2;
mod utils;
use crate::interpreter::wasm;
use crate::types::InterfaceType;
use crate::vec1::Vec1;
use crate::{
errors::{InstructionError, InstructionErrorKind, InstructionResult, WasmValueNativeCastError},
values::{InterfaceValue, NativeType},
};
pub(crate) use argument_get::argument_get;
pub(crate) use arrays::*;
pub(crate) use call_core::call_core;
pub(crate) use dup::dup;
pub(crate) use numbers::*;
pub(crate) use records::*;
use std::convert::TryFrom;
pub(crate) use strings::*;
pub(crate) use swap2::swap2;
pub(self) use utils::*;
use serde::Deserialize;
use serde::Serialize;
pub(self) const ALLOCATE_FUNC_INDEX: u32 = 0;
pub(self) const DEALLOCATE_FUNC_INDEX: u32 = 1;
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
pub enum Instruction {
ArgumentGet {
index: u32,
},
CallCore {
function_index: u32,
},
S8FromI32,
S8FromI64,
S16FromI32,
S16FromI64,
S32FromI32,
S32FromI64,
S64FromI32,
S64FromI64,
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,
StringSize,
ArrayLiftMemory {
value_type: InterfaceType,
},
ArrayLowerMemory {
value_type: InterfaceType,
},
RecordLiftMemory {
record_type_id: u32,
},
RecordLowerMemory {
record_type_id: u32,
},
Dup,
Swap2,
}
pub(crate) fn to_native<'a, T>(
wit_value: &'a InterfaceValue,
instruction: Instruction,
) -> InstructionResult<T>
where
T: NativeType + TryFrom<&'a InterfaceValue, Error = WasmValueNativeCastError>,
{
T::try_from(wit_value)
.map_err(|error| InstructionError::new(instruction, InstructionErrorKind::ToNative(error)))
}
pub(crate) fn check_function_signature<
'instance,
Instance,
Export,
LocalImport,
Memory,
MemoryView,
>(
instance: &'instance Instance,
local_import: &LocalImport,
values: &[InterfaceValue],
instruction: Instruction,
) -> Result<(), InstructionError>
where
Export: wasm::structures::Export + 'instance,
LocalImport: wasm::structures::LocalImport + 'instance,
Memory: wasm::structures::Memory<MemoryView> + 'instance,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
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, instruction.clone())?;
}
Ok(())
}
pub(crate) fn is_value_compatible_to_type<
'instance,
Instance,
Export,
LocalImport,
Memory,
MemoryView,
>(
instance: &'instance Instance,
interface_type: &InterfaceType,
interface_value: &InterfaceValue,
instruction: Instruction,
) -> Result<(), InstructionError>
where
Export: wasm::structures::Export + 'instance,
LocalImport: wasm::structures::LocalImport + 'instance,
Memory: wasm::structures::Memory<MemoryView> + 'instance,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
match (&interface_type, interface_value) {
(InterfaceType::S8, InterfaceValue::S8(_)) => Ok(()),
(InterfaceType::S16, InterfaceValue::S16(_)) => Ok(()),
(InterfaceType::S32, InterfaceValue::S32(_)) => Ok(()),
(InterfaceType::S64, InterfaceValue::S64(_)) => Ok(()),
(InterfaceType::U8, InterfaceValue::U8(_)) => Ok(()),
(InterfaceType::U16, InterfaceValue::U16(_)) => Ok(()),
(InterfaceType::U32, InterfaceValue::U32(_)) => Ok(()),
(InterfaceType::U64, InterfaceValue::U64(_)) => Ok(()),
(InterfaceType::I32, InterfaceValue::I32(_)) => Ok(()),
(InterfaceType::I64, InterfaceValue::I64(_)) => Ok(()),
(InterfaceType::F32, InterfaceValue::F32(_)) => Ok(()),
(InterfaceType::F64, InterfaceValue::F64(_)) => Ok(()),
(InterfaceType::String, InterfaceValue::String(_)) => Ok(()),
(InterfaceType::Array(ty), InterfaceValue::Array(values)) => {
for value in values {
is_value_compatible_to_type(instance, ty, value, instruction.clone())?
}
Ok(())
}
(InterfaceType::Record(ref record_type_id), InterfaceValue::Record(record_fields)) => {
is_record_fields_compatible_to_type(
instance,
*record_type_id,
record_fields,
instruction,
)?;
Ok(())
}
_ => Err(InstructionError::new(
instruction,
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,
>(
instance: &'instance Instance,
record_type_id: u64,
record_fields: &[InterfaceValue],
instruction: Instruction,
) -> Result<(), InstructionError>
where
Export: wasm::structures::Export + 'instance,
LocalImport: wasm::structures::LocalImport + 'instance,
Memory: wasm::structures::Memory<MemoryView> + 'instance,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
let record_type = instance.wit_record_by_id(record_type_id).ok_or_else(|| {
InstructionError::new(
instruction.clone(),
InstructionErrorKind::RecordTypeByNameIsMissing { record_type_id },
)
})?;
if record_fields.len() != record_type.fields.len() {
return Err(InstructionError::new(
instruction.clone(),
InstructionErrorKind::InvalidValueOnTheStack {
expected_type: InterfaceType::Record(record_type_id),
received_value: InterfaceValue::Record(Vec1::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,
instruction.clone(),
)?;
}
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<InterfaceType>,
pub(crate) outputs: Vec<InterfaceType>,
pub(crate) function: fn(arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>,
}
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) -> &[InterfaceType] {
&self.inputs
}
fn outputs(&self) -> &[InterfaceType] {
&self.outputs
}
fn call(&self, arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()> {
(self.function)(arguments)
}
}
pub(crate) struct LocalImport {
pub(crate) inputs: Vec<InterfaceType>,
pub(crate) outputs: Vec<InterfaceType>,
pub(crate) function: fn(arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>,
}
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) -> &[InterfaceType] {
&self.inputs
}
fn outputs(&self) -> &[InterfaceType] {
&self.outputs
}
fn call(&self, arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()> {
(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> 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![InterfaceType::I32, InterfaceType::I32],
outputs: vec![InterfaceType::I32],
function: |arguments: &[InterfaceValue]| {
let a: i32 = (&arguments[0]).try_into().unwrap();
let b: i32 = (&arguments[1]).try_into().unwrap();
Ok(vec![InterfaceValue::I32(a + b)])
},
},
);
hashmap
},
locals_or_imports: {
let mut hashmap = HashMap::new();
hashmap.insert(
42,
LocalImport {
inputs: vec![InterfaceType::I32, InterfaceType::I32],
outputs: vec![InterfaceType::I32],
function: |arguments: &[InterfaceValue]| {
let a: i32 = (&arguments[0]).try_into().unwrap();
let b: i32 = (&arguments[1]).try_into().unwrap();
Ok(vec![InterfaceValue::I32(a * b)])
},
},
);
hashmap.insert(
43,
LocalImport {
inputs: vec![InterfaceType::I32],
outputs: vec![InterfaceType::I32],
function: |arguments: &[InterfaceValue]| {
let _size: i32 = (&arguments[0]).try_into().unwrap();
Ok(vec![InterfaceValue::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: InterfaceType::I32,
},
RecordFieldType {
name: String::from("field_1"),
ty: InterfaceType::Record(RecordType {
name: String::from("RecordType1"),
fields: vec1![
RecordFieldType {
name: String::from("field_0"),
ty: InterfaceType::String,
},
RecordFieldType {
name: String::from("field1"),
ty: InterfaceType::F32
}
],
}),
},
RecordFieldType {
name: String::from("field_2"),
ty: InterfaceType::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 _)
}
}
}