Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(interface-types) Implement string and memory instructions #1284

Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## **[Unreleased]**

- [#1284](https://github.com/wasmerio/wasmer/pull/1284) Implement string and memory instructions in `wasmer-interface-types`
- [#1272](https://github.com/wasmerio/wasmer/pull/1272) Fix off-by-one error bug when accessing memory with a `WasmPtr` that contains the last valid byte of memory. Also changes the behavior of `WasmPtr<T, Array>` with a length of 0 and `WasmPtr<T>` where `std::mem::size_of::<T>()` is 0 to always return `None`

## 0.15.0 - 2020-03-04
Expand Down
6 changes: 3 additions & 3 deletions lib/interface-types/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ pub struct Export<'input> {

/// Represents an adapter.
#[derive(PartialEq, Debug)]
pub struct Adapter<'input> {
pub struct Adapter {
/// The adapter function type.
pub function_type: u32,

/// The instructions.
pub instructions: Vec<Instruction<'input>>,
pub instructions: Vec<Instruction>,
}

/// Represents an implementation.
Expand Down Expand Up @@ -137,7 +137,7 @@ pub struct Interfaces<'input> {
pub imports: Vec<Import<'input>>,

/// All the adapters.
pub adapters: Vec<Adapter<'input>>,
pub adapters: Vec<Adapter>,

/// All the exported functions.
pub exports: Vec<Export<'input>>,
Expand Down
18 changes: 8 additions & 10 deletions lib/interface-types/src/decoders/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,14 +174,14 @@ fn instruction<'input, E: ParseError<&'input [u8]>>(
)
}

0x03 => (input, Instruction::ReadUtf8),
0x03 => (input, Instruction::MemoryToString),

0x04 => {
consume!((input, argument_0) = string(input)?);
consume!((input, argument_0) = uleb(input)?);
(
input,
Instruction::WriteUtf8 {
allocator_name: argument_0,
Instruction::StringToMemory {
allocator_index: argument_0 as u32,
},
)
}
Expand Down Expand Up @@ -630,8 +630,8 @@ mod tests {
0x2b, // list of 43 items
0x00, 0x01, // ArgumentGet { index: 1 }
0x01, 0x01, // CallCore { function_index: 1 }
0x03, // ReadUtf8
0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" }
0x03, // MemoryToString
0x04, 0x01, // StringToMemory { allocator_index: 1 }
0x07, // I32ToS8
0x08, // I32ToS8X
0x09, // I32ToU8
Expand Down Expand Up @@ -678,10 +678,8 @@ mod tests {
vec![
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 1 },
Instruction::ReadUtf8,
Instruction::WriteUtf8 {
allocator_name: "abc",
},
Instruction::MemoryToString,
Instruction::StringToMemory { allocator_index: 1 },
Instruction::I32ToS8,
Instruction::I32ToS8X,
Instruction::I32ToU8,
Expand Down
34 changes: 17 additions & 17 deletions lib/interface-types/src/decoders/wat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ mod keyword {
// Instructions.
custom_keyword!(argument_get = "arg.get");
custom_keyword!(call_core = "call-core");
custom_keyword!(read_utf8 = "read-utf8");
custom_keyword!(write_utf8 = "write-utf8");
custom_keyword!(memory_to_string = "memory-to-string");
custom_keyword!(string_to_memory = "string-to-memory");
custom_keyword!(i32_to_s8 = "i32-to-s8");
custom_keyword!(i32_to_s8x = "i32-to-s8x");
custom_keyword!(i32_to_u8 = "i32-to-u8");
Expand Down Expand Up @@ -137,7 +137,7 @@ impl Parse<'_> for InterfaceType {
}
}

impl<'a> Parse<'a> for Instruction<'a> {
impl<'a> Parse<'a> for Instruction {
#[allow(clippy::cognitive_complexity)]
fn parse(parser: Parser<'a>) -> Result<Self> {
let mut lookahead = parser.lookahead1();
Expand All @@ -154,15 +154,15 @@ impl<'a> Parse<'a> for Instruction<'a> {
Ok(Instruction::CallCore {
function_index: parser.parse::<u64>()? as usize,
})
} else if lookahead.peek::<keyword::read_utf8>() {
parser.parse::<keyword::read_utf8>()?;
} else if lookahead.peek::<keyword::memory_to_string>() {
parser.parse::<keyword::memory_to_string>()?;

Ok(Instruction::ReadUtf8)
} else if lookahead.peek::<keyword::write_utf8>() {
parser.parse::<keyword::write_utf8>()?;
Ok(Instruction::MemoryToString)
} else if lookahead.peek::<keyword::string_to_memory>() {
parser.parse::<keyword::string_to_memory>()?;

Ok(Instruction::WriteUtf8 {
allocator_name: parser.parse()?,
Ok(Instruction::StringToMemory {
allocator_index: parser.parse()?,
})
} else if lookahead.peek::<keyword::i32_to_s8>() {
parser.parse::<keyword::i32_to_s8>()?;
Expand Down Expand Up @@ -392,7 +392,7 @@ impl Parse<'_> for FunctionType {
enum Interface<'a> {
Type(Type),
Import(Import<'a>),
Adapter(Adapter<'a>),
Adapter(Adapter),
Export(Export<'a>),
Implementation(Implementation),
}
Expand Down Expand Up @@ -520,7 +520,7 @@ impl<'a> Parse<'a> for Implementation {
}
}

impl<'a> Parse<'a> for Adapter<'a> {
impl<'a> Parse<'a> for Adapter {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<keyword::func>()?;

Expand Down Expand Up @@ -667,8 +667,8 @@ mod tests {
let inputs = vec![
"arg.get 7",
"call-core 7",
"read-utf8",
r#"write-utf8 "foo""#,
"memory-to-string",
"string-to-memory 42",
"i32-to-s8",
"i32-to-s8x",
"i32-to-u8",
Expand Down Expand Up @@ -712,9 +712,9 @@ mod tests {
let outputs = vec![
Instruction::ArgumentGet { index: 7 },
Instruction::CallCore { function_index: 7 },
Instruction::ReadUtf8,
Instruction::WriteUtf8 {
allocator_name: "foo",
Instruction::MemoryToString,
Instruction::StringToMemory {
allocator_index: 42,
},
Instruction::I32ToS8,
Instruction::I32ToS8X,
Expand Down
20 changes: 9 additions & 11 deletions lib/interface-types/src/encoders/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ where
/// Encode an `Adapter` into bytes.
///
/// Decoder is in `decoders::binary::adapters`.
impl<W> ToBytes<W> for Adapter<'_>
impl<W> ToBytes<W> for Adapter
where
W: Write,
{
Expand Down Expand Up @@ -244,7 +244,7 @@ where
/// Encode an `Instruction` into bytes.
///
/// Decoder is `decoders::binary::instruction`.
impl<W> ToBytes<W> for Instruction<'_>
impl<W> ToBytes<W> for Instruction
where
W: Write,
{
Expand All @@ -260,11 +260,11 @@ where
(*function_index as u64).to_bytes(writer)?;
}

Instruction::ReadUtf8 => 0x03_u8.to_bytes(writer)?,
Instruction::MemoryToString => 0x03_u8.to_bytes(writer)?,

Instruction::WriteUtf8 { allocator_name } => {
Instruction::StringToMemory { allocator_index } => {
0x04_u8.to_bytes(writer)?;
allocator_name.to_bytes(writer)?;
(*allocator_index as u64).to_bytes(writer)?;
}

Instruction::I32ToS8 => 0x07_u8.to_bytes(writer)?,
Expand Down Expand Up @@ -550,10 +550,8 @@ mod tests {
vec![
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 1 },
Instruction::ReadUtf8,
Instruction::WriteUtf8 {
allocator_name: "abc",
},
Instruction::MemoryToString,
Instruction::StringToMemory { allocator_index: 1 },
Instruction::I32ToS8,
Instruction::I32ToS8X,
Instruction::I32ToU8,
Expand Down Expand Up @@ -598,8 +596,8 @@ mod tests {
0x2b, // list of 43 items
0x00, 0x01, // ArgumentGet { index: 1 }
0x01, 0x01, // CallCore { function_index: 1 }
0x03, // ReadUtf8
0x04, 0x03, 0x61, 0x62, 0x63, // WriteUtf8 { allocator_name: "abc" }
0x03, // MemoryToString
0x04, 0x01, // StringToMemory { allocator_index: 1 }
0x07, // I32ToS8
0x08, // I32ToS8X
0x09, // I32ToU8
Expand Down
20 changes: 10 additions & 10 deletions lib/interface-types/src/encoders/wat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@ impl ToString for &InterfaceType {
}

/// Encode an `Instruction` into a string.
impl<'input> ToString for &Instruction<'input> {
impl ToString for &Instruction {
fn to_string(&self) -> String {
match self {
Instruction::ArgumentGet { index } => format!("arg.get {}", index),
Instruction::CallCore { function_index } => format!("call-core {}", function_index),
Instruction::ReadUtf8 => "read-utf8".into(),
Instruction::WriteUtf8 { allocator_name } => {
format!(r#"write-utf8 "{}""#, allocator_name)
Instruction::MemoryToString => "memory-to-string".into(),
Instruction::StringToMemory { allocator_index } => {
format!(r#"string-to-memory {}"#, allocator_index)
}
Instruction::I32ToS8 => "i32-to-s8".into(),
Instruction::I32ToS8X => "i32-to-s8x".into(),
Expand Down Expand Up @@ -194,7 +194,7 @@ impl<'input> ToString for &Import<'input> {
}

/// Encode an `Adapter` into a string.
impl<'input> ToString for &Adapter<'input> {
impl ToString for &Adapter {
fn to_string(&self) -> String {
format!(
r#"(@interface func (type {function_type}){instructions})"#,
Expand Down Expand Up @@ -361,9 +361,9 @@ mod tests {
let inputs: Vec<String> = vec![
(&Instruction::ArgumentGet { index: 7 }).to_string(),
(&Instruction::CallCore { function_index: 7 }).to_string(),
(&Instruction::ReadUtf8).to_string(),
(&Instruction::WriteUtf8 {
allocator_name: "foo",
(&Instruction::MemoryToString).to_string(),
(&Instruction::StringToMemory {
allocator_index: 42,
})
.to_string(),
(&Instruction::I32ToS8).to_string(),
Expand Down Expand Up @@ -409,8 +409,8 @@ mod tests {
let outputs = vec![
"arg.get 7",
"call-core 7",
"read-utf8",
r#"write-utf8 "foo""#,
"memory-to-string",
"string-to-memory 42",
"i32-to-s8",
"i32-to-s8x",
"i32-to-u8",
Expand Down
14 changes: 7 additions & 7 deletions lib/interface-types/src/interpreter/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/// Represents all the possible WIT instructions.
#[derive(PartialEq, Debug)]
pub enum Instruction<'input> {
pub enum Instruction {
/// The `arg.get` instruction.
ArgumentGet {
/// The argument index.
Expand All @@ -15,13 +15,13 @@ pub enum Instruction<'input> {
function_index: usize,
},

/// The `read-utf8` instruction.
ReadUtf8,
/// The `memory-to-string` instruction.
MemoryToString,

/// The `write-utf8` instruction.
WriteUtf8 {
/// The allocator function name.
allocator_name: &'input str,
/// The `string-to-memory` instruction.
StringToMemory {
/// The allocator function index.
allocator_index: u32,
},

/// The `i32-to-s8,` instruction.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::interpreter::wasm::values::InterfaceValue;
use std::{cell::Cell, convert::TryFrom};

executable_instruction!(
read_utf8(instruction_name: String) -> _ {
memory_to_string(instruction_name: String) -> _ {
move |runtime| -> _ {
match runtime.stack.pop(2) {
Some(inputs) => match runtime.wasm_instance.memory(0) {
Expand Down Expand Up @@ -55,11 +55,11 @@ executable_instruction!(
#[cfg(test)]
mod tests {
test_executable_instruction!(
test_read_utf8 =
test_memory_to_string =
instructions: [
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 0 },
Instruction::ReadUtf8,
Instruction::MemoryToString,
],
invocation_inputs: [
InterfaceValue::I32(13),
Expand All @@ -75,11 +75,11 @@ mod tests {
);

test_executable_instruction!(
test_read_utf8__read_out_of_memory =
test_memory_to_string__read_out_of_memory =
instructions: [
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 0 },
Instruction::ReadUtf8,
Instruction::MemoryToString,
],
invocation_inputs: [
InterfaceValue::I32(13),
Expand All @@ -91,15 +91,15 @@ mod tests {
memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
..Default::default()
},
error: r#"`read-utf8` failed because it has to read out of the memory bounds (index 13 > memory length 6)."#,
error: r#"`memory-to-string` failed because it has to read out of the memory bounds (index 13 > memory length 6)."#,
);

test_executable_instruction!(
test_read_utf8__invalid_encoding =
test_memory_to_string__invalid_encoding =
instructions: [
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 0 },
Instruction::ReadUtf8,
Instruction::MemoryToString,
],
invocation_inputs: [
InterfaceValue::I32(4),
Expand All @@ -111,21 +111,21 @@ mod tests {
memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::<Vec<Cell<u8>>>()),
..Default::default()
},
error: r#"`read-utf8` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#,
error: r#"`memory-to-string` failed because the read string isn't UTF-8 valid (invalid utf-8 sequence of 1 bytes from index 1)."#,
);

test_executable_instruction!(
test_read_utf8__stack_is_too_small =
test_memory_to_string__stack_is_too_small =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ReadUtf8,
// ^^^^^^^^ `read-utf8` expects 2 values on the stack, only one is present.
Instruction::MemoryToString,
// ^^^^^^^^^^^^^^ `memory-to-string` expects 2 values on the stack, only one is present.
],
invocation_inputs: [
InterfaceValue::I32(13),
InterfaceValue::I32(0),
],
instance: Instance::new(),
error: r#"`read-utf8` failed because there is not enough data on the stack (needs 2)."#,
error: r#"`memory-to-string` failed because there is not enough data on the stack (needs 2)."#,
);
}
Loading