mirror of
https://github.com/fluencelabs/interface-types
synced 2024-12-04 15:20:20 +00:00
Merge branch 'master' into feat-interface-types-instructions-string-and-memory
This commit is contained in:
commit
47b63bc9c6
65
README.md
65
README.md
@ -30,3 +30,68 @@ more](https://github.com/wasmerio/wasmer).
|
||||
|
||||
This crate is an implementation of [the living WebAssembly Interface
|
||||
Types standard](https://github.com/WebAssembly/interface-types).
|
||||
|
||||
## Encoders and decoders
|
||||
|
||||
The `wasmer-interface-types` crate comes with an encoder and a decoder
|
||||
for the WAT format, and the binary format, for the WebAssembly
|
||||
Interface Types. An encoder writes an AST into another format, like
|
||||
WAT or binary. A decoder reads an AST from another format, like WAT or
|
||||
binary.
|
||||
|
||||
## Instructions
|
||||
|
||||
Very basically, WebAssembly Interface Types defines a set of
|
||||
instructions, used by adapters to transform the data between
|
||||
WebAssembly core and the outside world ([learn
|
||||
mode](https://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/Explainer.md)).
|
||||
|
||||
Here is the instructions that are implemented:
|
||||
|
||||
| Instruction | WAT encoder | Binary encoder | WAT decoder | Binary decoder | Interpreter |
|
||||
|-|-|-|-|-|-|
|
||||
| `arg.get` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `call-core` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `memory-to-string` | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| `string-to-memory` | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| `call-adapter` | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| `defer-call-core` | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| `i32-to-s8` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i32-to-s8x` | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| `i32-to-u8` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i32-to-s16` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i32-to-s16x` | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| `i32-to-u16` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i32-to-s32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i32-to-u32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i32-to-s64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i32-to-u64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i64-to-s8` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i64-to-s8x` | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| `i64-to-u8` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i64-to-s16` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i64-to-s16x` | ✅ | ✅ | ✅ | ✅ | ❌ |
|
||||
| `i64-to-u16` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i64-to-s32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i64-to-s32x` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i64-to-u32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i64-to-s64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `i64-to-u64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `s8-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `u8-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `s16-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `u16-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `s32-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `u32-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `s64-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `s64-to-i32x` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `u64-to-i32` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `u64-to-i32x` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `s8-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `u8-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `s16-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `u16-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `s32-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `u32-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `s64-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| `u64-to-i64` | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
|
@ -51,12 +51,16 @@ pub enum InterfaceType {
|
||||
}
|
||||
|
||||
/// Represents a type signature.
|
||||
///
|
||||
/// ```wasm,ignore
|
||||
/// (@interface type (param i32 i32) (result string))
|
||||
/// ```
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Type {
|
||||
/// Types for the parameters.
|
||||
/// Types for the parameters (`(param …)`).
|
||||
pub inputs: Vec<InterfaceType>,
|
||||
|
||||
/// Types for the results.
|
||||
/// Types for the results (`(result …)`).
|
||||
pub outputs: Vec<InterfaceType>,
|
||||
}
|
||||
|
||||
|
@ -168,22 +168,12 @@ fn instruction<'input, E: ParseError<&'input [u8]>>(
|
||||
consume!((input, argument_0) = uleb(input)?);
|
||||
(
|
||||
input,
|
||||
Instruction::Call {
|
||||
Instruction::CallCore {
|
||||
function_index: argument_0 as usize,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
0x02 => {
|
||||
consume!((input, argument_0) = string(input)?);
|
||||
(
|
||||
input,
|
||||
Instruction::CallExport {
|
||||
export_name: argument_0,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
0x03 => (input, Instruction::MemoryToString),
|
||||
|
||||
0x04 => {
|
||||
@ -637,10 +627,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_instructions() {
|
||||
let input = &[
|
||||
0x2c, // list of 44 items
|
||||
0x2b, // list of 43 items
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
0x01, 0x01, // Call { function_index: 1 }
|
||||
0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" }
|
||||
0x01, 0x01, // CallCore { function_index: 1 }
|
||||
0x03, // MemoryToString
|
||||
0x04, 0x01, // StringToMemory { allocator_index: 1 }
|
||||
0x07, // I32ToS8
|
||||
@ -688,8 +677,7 @@ mod tests {
|
||||
&[0x0a][..],
|
||||
vec![
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::Call { function_index: 1 },
|
||||
Instruction::CallExport { export_name: "abc" },
|
||||
Instruction::CallCore { function_index: 1 },
|
||||
Instruction::MemoryToString,
|
||||
Instruction::StringToMemory { allocator_index: 1 },
|
||||
Instruction::I32ToS8,
|
||||
|
@ -27,8 +27,7 @@ mod keyword {
|
||||
|
||||
// Instructions.
|
||||
custom_keyword!(argument_get = "arg.get");
|
||||
custom_keyword!(call);
|
||||
custom_keyword!(call_export = "call-export");
|
||||
custom_keyword!(call_core = "call-core");
|
||||
custom_keyword!(memory_to_string = "memory-to-string");
|
||||
custom_keyword!(string_to_memory = "string-to-memory");
|
||||
custom_keyword!(i32_to_s8 = "i32-to-s8");
|
||||
@ -149,18 +148,12 @@ impl<'a> Parse<'a> for Instruction<'a> {
|
||||
Ok(Instruction::ArgumentGet {
|
||||
index: parser.parse()?,
|
||||
})
|
||||
} else if lookahead.peek::<keyword::call>() {
|
||||
parser.parse::<keyword::call>()?;
|
||||
} else if lookahead.peek::<keyword::call_core>() {
|
||||
parser.parse::<keyword::call_core>()?;
|
||||
|
||||
Ok(Instruction::Call {
|
||||
Ok(Instruction::CallCore {
|
||||
function_index: parser.parse::<u64>()? as usize,
|
||||
})
|
||||
} else if lookahead.peek::<keyword::call_export>() {
|
||||
parser.parse::<keyword::call_export>()?;
|
||||
|
||||
Ok(Instruction::CallExport {
|
||||
export_name: parser.parse()?,
|
||||
})
|
||||
} else if lookahead.peek::<keyword::memory_to_string>() {
|
||||
parser.parse::<keyword::memory_to_string>()?;
|
||||
|
||||
@ -673,8 +666,7 @@ mod tests {
|
||||
fn test_instructions() {
|
||||
let inputs = vec![
|
||||
"arg.get 7",
|
||||
"call 7",
|
||||
r#"call-export "foo""#,
|
||||
"call-core 7",
|
||||
"memory-to-string",
|
||||
"string-to-memory 42",
|
||||
"i32-to-s8",
|
||||
@ -719,8 +711,7 @@ mod tests {
|
||||
];
|
||||
let outputs = vec![
|
||||
Instruction::ArgumentGet { index: 7 },
|
||||
Instruction::Call { function_index: 7 },
|
||||
Instruction::CallExport { export_name: "foo" },
|
||||
Instruction::CallCore { function_index: 7 },
|
||||
Instruction::MemoryToString,
|
||||
Instruction::StringToMemory {
|
||||
allocator_index: 42,
|
||||
|
@ -255,16 +255,11 @@ where
|
||||
(*index as u64).to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::Call { function_index } => {
|
||||
Instruction::CallCore { function_index } => {
|
||||
0x01_u8.to_bytes(writer)?;
|
||||
(*function_index as u64).to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::CallExport { export_name } => {
|
||||
0x02_u8.to_bytes(writer)?;
|
||||
export_name.to_bytes(writer)?;
|
||||
}
|
||||
|
||||
Instruction::MemoryToString => 0x03_u8.to_bytes(writer)?,
|
||||
|
||||
Instruction::StringToMemory { allocator_index } => {
|
||||
@ -554,8 +549,7 @@ mod tests {
|
||||
assert_to_bytes!(
|
||||
vec![
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::Call { function_index: 1 },
|
||||
Instruction::CallExport { export_name: "abc" },
|
||||
Instruction::CallCore { function_index: 1 },
|
||||
Instruction::MemoryToString,
|
||||
Instruction::StringToMemory { allocator_index: 1 },
|
||||
Instruction::I32ToS8,
|
||||
@ -599,10 +593,9 @@ mod tests {
|
||||
Instruction::U64ToI64,
|
||||
],
|
||||
&[
|
||||
0x2c, // list of 44 items
|
||||
0x2b, // list of 43 items
|
||||
0x00, 0x01, // ArgumentGet { index: 1 }
|
||||
0x01, 0x01, // Call { function_index: 1 }
|
||||
0x02, 0x03, 0x61, 0x62, 0x63, // CallExport { export_name: "abc" }
|
||||
0x01, 0x01, // CallCore { function_index: 1 }
|
||||
0x03, // MemoryToString
|
||||
0x04, 0x01, // StringToMemory { allocator_index: 1 }
|
||||
0x07, // I32ToS8
|
||||
|
@ -84,8 +84,7 @@ impl<'input> ToString for &Instruction<'input> {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Instruction::ArgumentGet { index } => format!("arg.get {}", index),
|
||||
Instruction::Call { function_index } => format!("call {}", function_index),
|
||||
Instruction::CallExport { export_name } => format!(r#"call-export "{}""#, export_name),
|
||||
Instruction::CallCore { function_index } => format!("call-core {}", function_index),
|
||||
Instruction::MemoryToString => "memory-to-string".into(),
|
||||
Instruction::StringToMemory { allocator_index } => {
|
||||
format!(r#"string-to-memory {}"#, allocator_index)
|
||||
@ -361,8 +360,7 @@ mod tests {
|
||||
fn test_instructions() {
|
||||
let inputs: Vec<String> = vec![
|
||||
(&Instruction::ArgumentGet { index: 7 }).to_string(),
|
||||
(&Instruction::Call { function_index: 7 }).to_string(),
|
||||
(&Instruction::CallExport { export_name: "foo" }).to_string(),
|
||||
(&Instruction::CallCore { function_index: 7 }).to_string(),
|
||||
(&Instruction::MemoryToString).to_string(),
|
||||
(&Instruction::StringToMemory {
|
||||
allocator_index: 42,
|
||||
@ -410,8 +408,7 @@ mod tests {
|
||||
];
|
||||
let outputs = vec![
|
||||
"arg.get 7",
|
||||
"call 7",
|
||||
r#"call-export "foo""#,
|
||||
"call-core 7",
|
||||
"memory-to-string",
|
||||
"string-to-memory 42",
|
||||
"i32-to-s8",
|
||||
|
@ -9,18 +9,12 @@ pub enum Instruction<'input> {
|
||||
index: u32,
|
||||
},
|
||||
|
||||
/// The `call` instruction.
|
||||
Call {
|
||||
/// The `call-core` instruction.
|
||||
CallCore {
|
||||
/// The function index.
|
||||
function_index: usize,
|
||||
},
|
||||
|
||||
/// The `call-export` instruction.
|
||||
CallExport {
|
||||
/// The exported function name.
|
||||
export_name: &'input str,
|
||||
},
|
||||
|
||||
/// The `memory-to-string` instruction.
|
||||
MemoryToString,
|
||||
|
||||
|
@ -4,7 +4,7 @@ use crate::interpreter::wasm::{
|
||||
};
|
||||
|
||||
executable_instruction!(
|
||||
call(function_index: usize, instruction_name: String) -> _ {
|
||||
call_core(function_index: usize, instruction_name: String) -> _ {
|
||||
move |runtime| -> _ {
|
||||
let instance = &mut runtime.wasm_instance;
|
||||
let index = FunctionIndex::new(function_index);
|
||||
@ -65,11 +65,11 @@ executable_instruction!(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
test_executable_instruction!(
|
||||
test_call =
|
||||
test_call_core =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::Call { function_index: 42 },
|
||||
Instruction::CallCore { function_index: 42 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
@ -80,39 +80,39 @@ mod tests {
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call__invalid_local_import_index =
|
||||
test_call_core__invalid_local_import_index =
|
||||
instructions: [
|
||||
Instruction::Call { function_index: 42 },
|
||||
Instruction::CallCore { function_index: 42 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Default::default(),
|
||||
error: r#"`call 42` cannot call the local or imported function `42` because it doesn't exist."#,
|
||||
error: r#"`call-core 42` cannot call the local or imported function `42` because it doesn't exist."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call__stack_is_too_small =
|
||||
test_call_core__stack_is_too_small =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::Call { function_index: 42 },
|
||||
// ^^ `42` expects 2 values on the stack, only one is present
|
||||
Instruction::CallCore { function_index: 42 },
|
||||
// ^^ `42` expects 2 values on the stack, only one is present
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance::new(),
|
||||
error: r#"`call 42` cannot call the local or imported function `42` because there is not enough data on the stack for the arguments (needs 2)."#,
|
||||
error: r#"`call-core 42` cannot call the local or imported function `42` because there is not enough data on the stack for the arguments (needs 2)."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call__invalid_types_in_the_stack =
|
||||
test_call_core__invalid_types_in_the_stack =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::Call { function_index: 42 },
|
||||
Instruction::CallCore { function_index: 42 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
@ -120,15 +120,15 @@ mod tests {
|
||||
// ^^^ mismatch with `42` signature
|
||||
],
|
||||
instance: Instance::new(),
|
||||
error: r#"`call 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#,
|
||||
error: r#"`call-core 42` cannot call the local or imported function `42` because the value types on the stack mismatch the function signature (expects [I32, I32])."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call__failure_when_calling =
|
||||
test_call_core__failure_when_calling =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::Call { function_index: 42 },
|
||||
Instruction::CallCore { function_index: 42 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
@ -151,15 +151,15 @@ mod tests {
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
error: r#"`call 42` failed when calling the local or imported function `42`."#,
|
||||
error: r#"`call-core 42` failed when calling the local or imported function `42`."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call__void =
|
||||
test_call_core__void =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::Call { function_index: 42 },
|
||||
Instruction::CallCore { function_index: 42 },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
@ -1,177 +0,0 @@
|
||||
use crate::interpreter::wasm::values::InterfaceType;
|
||||
|
||||
executable_instruction!(
|
||||
call_export(export_name: String, instruction_name: String) -> _ {
|
||||
move |runtime| -> _ {
|
||||
let instance = &mut runtime.wasm_instance;
|
||||
|
||||
match instance.export(&export_name) {
|
||||
Some(export) => {
|
||||
let inputs_cardinality = export.inputs_cardinality();
|
||||
|
||||
match runtime.stack.pop(inputs_cardinality) {
|
||||
Some(inputs) => {
|
||||
let input_types = inputs
|
||||
.iter()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<InterfaceType>>();
|
||||
|
||||
if input_types != export.inputs() {
|
||||
return Err(format!(
|
||||
"`{}` cannot call the exported function `{}` because the value types on the stack mismatch the function signature (expects {:?}).",
|
||||
instruction_name,
|
||||
export_name,
|
||||
export.inputs(),
|
||||
))
|
||||
}
|
||||
|
||||
match export.call(&inputs) {
|
||||
Ok(outputs) => {
|
||||
for output in outputs.iter() {
|
||||
runtime.stack.push(output.clone());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(format!(
|
||||
"`{}` failed when calling the exported function `{}`.",
|
||||
instruction_name,
|
||||
export_name
|
||||
))
|
||||
}
|
||||
}
|
||||
None => Err(format!(
|
||||
"`{}` cannot call the exported function `{}` because there is not enough data on the stack for the arguments (needs {}).",
|
||||
instruction_name,
|
||||
export_name,
|
||||
inputs_cardinality,
|
||||
))
|
||||
}
|
||||
}
|
||||
None => Err(format!(
|
||||
"`{}` cannot call the exported function `{}` because it doesn't exist.",
|
||||
instruction_name,
|
||||
export_name,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
test_executable_instruction!(
|
||||
test_call_export =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::CallExport { export_name: "sum" },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance::new(),
|
||||
stack: [InterfaceValue::I32(7)],
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call_export__invalid_export_name =
|
||||
instructions: [Instruction::CallExport { export_name: "bar" }],
|
||||
invocation_inputs: [],
|
||||
instance: Instance::new(),
|
||||
error: r#"`call-export "bar"` cannot call the exported function `bar` because it doesn't exist."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call_export__stack_is_too_small =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::CallExport { export_name: "sum" },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance::new(),
|
||||
error: r#"`call-export "sum"` cannot call the exported function `sum` because there is not enough data on the stack for the arguments (needs 2)."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call_export__invalid_types_in_the_stack =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::CallExport { export_name: "sum" },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I64(4),
|
||||
// ^^^ mismatch with `sum` signature
|
||||
],
|
||||
instance: Instance::new(),
|
||||
error: r#"`call-export "sum"` cannot call the exported function `sum` because the value types on the stack mismatch the function signature (expects [I32, I32])."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call_export__failure_when_calling =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::CallExport { export_name: "sum" },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance {
|
||||
exports: {
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert(
|
||||
"sum".into(),
|
||||
Export {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
function: |_| Err(()),
|
||||
// ^^^^^^^ function fails
|
||||
},
|
||||
);
|
||||
|
||||
hashmap
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
error: r#"`call-export "sum"` failed when calling the exported function `sum`."#,
|
||||
);
|
||||
|
||||
test_executable_instruction!(
|
||||
test_call_export__void =
|
||||
instructions: [
|
||||
Instruction::ArgumentGet { index: 1 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::CallExport { export_name: "sum" },
|
||||
],
|
||||
invocation_inputs: [
|
||||
InterfaceValue::I32(3),
|
||||
InterfaceValue::I32(4),
|
||||
],
|
||||
instance: Instance {
|
||||
exports: {
|
||||
let mut hashmap = HashMap::new();
|
||||
hashmap.insert(
|
||||
"sum".into(),
|
||||
Export {
|
||||
inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
outputs: vec![InterfaceType::I32],
|
||||
function: |_| Ok(vec![]),
|
||||
// ^^^^^^^^^^ void function
|
||||
},
|
||||
);
|
||||
|
||||
hashmap
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
stack: [],
|
||||
);
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
mod argument_get;
|
||||
mod call;
|
||||
mod call_export;
|
||||
mod call_core;
|
||||
mod lowering_lifting;
|
||||
mod memory_to_string;
|
||||
mod string_to_memory;
|
||||
|
||||
pub(crate) use argument_get::argument_get;
|
||||
pub(crate) use call::call;
|
||||
pub(crate) use call_export::call_export;
|
||||
pub(crate) use call_core::call_core;
|
||||
pub(crate) use lowering_lifting::*;
|
||||
pub(crate) use memory_to_string::memory_to_string;
|
||||
pub(crate) use string_to_memory::string_to_memory;
|
||||
|
@ -71,7 +71,7 @@ pub(crate) type ExecutableInstruction<Instance, Export, LocalImport, Memory, Mem
|
||||
/// let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> = (&vec![
|
||||
/// Instruction::ArgumentGet { index: 1 },
|
||||
/// Instruction::ArgumentGet { index: 0 },
|
||||
/// Instruction::CallExport { export_name: "sum" },
|
||||
/// Instruction::CallCore { function_index: 42 },
|
||||
/// ])
|
||||
/// .try_into()
|
||||
/// .unwrap();
|
||||
@ -81,12 +81,12 @@ pub(crate) type ExecutableInstruction<Instance, Export, LocalImport, Memory, Mem
|
||||
///
|
||||
/// // 3. Creates a WebAssembly instance.
|
||||
/// let mut instance = Instance {
|
||||
/// // 3.1. Defines one exported function: `fn sum(a: i32, b: i32) -> i32 { a + b }`.
|
||||
/// exports: {
|
||||
/// // 3.1. Defines one function: `fn sum(a: i32, b: i32) -> i32 { a + b }`.
|
||||
/// locals_or_imports: {
|
||||
/// let mut hashmap = HashMap::new();
|
||||
/// hashmap.insert(
|
||||
/// "sum".into(),
|
||||
/// Export {
|
||||
/// 42,
|
||||
/// LocalImport {
|
||||
/// // Defines the argument types of the function.
|
||||
/// inputs: vec![InterfaceType::I32, InterfaceType::I32],
|
||||
///
|
||||
@ -196,11 +196,8 @@ where
|
||||
Instruction::ArgumentGet { index } => {
|
||||
instructions::argument_get(*index, instruction_name)
|
||||
}
|
||||
Instruction::Call { function_index } => {
|
||||
instructions::call(*function_index, instruction_name)
|
||||
}
|
||||
Instruction::CallExport { export_name } => {
|
||||
instructions::call_export((*export_name).to_owned(), instruction_name)
|
||||
Instruction::CallCore { function_index } => {
|
||||
instructions::call_core(*function_index, instruction_name)
|
||||
}
|
||||
Instruction::MemoryToString => instructions::memory_to_string(instruction_name),
|
||||
Instruction::StringToMemory { allocator_index } => {
|
||||
@ -253,24 +250,3 @@ where
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{wasm::structures::EmptyMemoryView, Instruction, Interpreter};
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[test]
|
||||
fn test_interpreter_from_instructions() {
|
||||
let instructions = vec![
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::ArgumentGet { index: 0 },
|
||||
Instruction::CallExport { export_name: "foo" },
|
||||
Instruction::MemoryToString,
|
||||
Instruction::Call { function_index: 7 },
|
||||
];
|
||||
let interpreter: Interpreter<(), (), (), (), EmptyMemoryView> =
|
||||
(&instructions).try_into().unwrap();
|
||||
|
||||
assert_eq!(interpreter.executable_instructions.len(), 5);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user