use crate::{ast::*, interpreter::Instruction, types::*};
use std::io::{self, Write};
pub trait ToBytes<W>
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()>;
}
impl<W> ToBytes<W> for u8
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(&[*self])
}
}
impl<W> ToBytes<W> for u64
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
let mut value = *self;
loop {
if value < 0x80 {
writer.write_all(&[value as u8])?;
break;
}
writer.write_all(&[((value & 0x7f) | 0x80) as u8])?;
value >>= 7;
}
Ok(())
}
}
impl<W> ToBytes<W> for &str
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(&[self.len() as u8])?;
writer.write_all(self.as_bytes())?;
Ok(())
}
}
impl<W> ToBytes<W> for String
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(&[self.len() as u8])?;
writer.write_all(self.as_bytes())?;
Ok(())
}
}
impl<W, I> ToBytes<W> for Vec<I>
where
W: Write,
I: ToBytes<W>,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
(self.len() as u64).to_bytes(writer)?;
for item in self {
item.to_bytes(writer)?;
}
Ok(())
}
}
impl<W> ToBytes<W> for InterfaceType
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
match self {
InterfaceType::S8 => 0x00_u8.to_bytes(writer),
InterfaceType::S16 => 0x01_u8.to_bytes(writer),
InterfaceType::S32 => 0x02_u8.to_bytes(writer),
InterfaceType::S64 => 0x03_u8.to_bytes(writer),
InterfaceType::U8 => 0x04_u8.to_bytes(writer),
InterfaceType::U16 => 0x05_u8.to_bytes(writer),
InterfaceType::U32 => 0x06_u8.to_bytes(writer),
InterfaceType::U64 => 0x07_u8.to_bytes(writer),
InterfaceType::F32 => 0x08_u8.to_bytes(writer),
InterfaceType::F64 => 0x09_u8.to_bytes(writer),
InterfaceType::String => 0x0a_u8.to_bytes(writer),
InterfaceType::ByteArray => 0x36_u8.to_bytes(writer),
InterfaceType::Anyref => 0x0b_u8.to_bytes(writer),
InterfaceType::I32 => 0x0c_u8.to_bytes(writer),
InterfaceType::I64 => 0x0d_u8.to_bytes(writer),
InterfaceType::Record(record_type) => {
0x0e_u8.to_bytes(writer)?;
record_type.to_bytes(writer)
}
}
}
}
impl<W> ToBytes<W> for RecordType
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
self.fields.to_bytes(writer)
}
}
impl<W> ToBytes<W> for TypeKind
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
match self {
TypeKind::Function => 0x00_u8.to_bytes(writer),
TypeKind::Record => 0x01_u8.to_bytes(writer),
}
}
}
impl<W> ToBytes<W> for InterfaceKind
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
match self {
Self::Type => 0x00_u8.to_bytes(writer),
Self::Import => 0x01_u8.to_bytes(writer),
Self::Adapter => 0x02_u8.to_bytes(writer),
Self::Export => 0x03_u8.to_bytes(writer),
Self::Implementation => 0x04_u8.to_bytes(writer),
}
}
}
impl<W> ToBytes<W> for Type
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
match self {
Type::Function {
name,
arg_types,
arg_names,
output_types,
} => {
TypeKind::Function.to_bytes(writer)?;
name.to_bytes(writer)?;
arg_types.to_bytes(writer)?;
arg_names.to_bytes(writer)?;
output_types.to_bytes(writer)?;
}
Type::Record(record_type) => {
TypeKind::Record.to_bytes(writer)?;
record_type.to_bytes(writer)?;
}
}
Ok(())
}
}
impl<W> ToBytes<W> for Import<'_>
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
self.namespace.to_bytes(writer)?;
self.name.to_bytes(writer)?;
(self.function_type as u64).to_bytes(writer)?;
Ok(())
}
}
impl<W> ToBytes<W> for Adapter
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
(self.function_type as u64).to_bytes(writer)?;
self.instructions.to_bytes(writer)?;
Ok(())
}
}
impl<W> ToBytes<W> for Export<'_>
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
self.name.to_bytes(writer)?;
(self.function_type as u64).to_bytes(writer)?;
Ok(())
}
}
impl<W> ToBytes<W> for Implementation
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
(self.core_function_type as u64).to_bytes(writer)?;
(self.adapter_function_type as u64).to_bytes(writer)?;
Ok(())
}
}
impl<W> ToBytes<W> for Interfaces<'_>
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
if !self.types.is_empty() {
InterfaceKind::Type.to_bytes(writer)?;
self.types.to_bytes(writer)?;
}
if !self.imports.is_empty() {
InterfaceKind::Import.to_bytes(writer)?;
self.imports.to_bytes(writer)?;
}
if !self.adapters.is_empty() {
InterfaceKind::Adapter.to_bytes(writer)?;
self.adapters.to_bytes(writer)?;
}
if !self.exports.is_empty() {
InterfaceKind::Export.to_bytes(writer)?;
self.exports.to_bytes(writer)?;
}
if !self.implementations.is_empty() {
InterfaceKind::Implementation.to_bytes(writer)?;
self.implementations.to_bytes(writer)?;
}
Ok(())
}
}
impl<W> ToBytes<W> for Instruction
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
match self {
Instruction::ArgumentGet { index } => {
0x00_u8.to_bytes(writer)?;
(*index as u64).to_bytes(writer)?;
}
Instruction::CallCore { function_index } => {
0x01_u8.to_bytes(writer)?;
(*function_index as u64).to_bytes(writer)?;
}
Instruction::S8FromI32 => 0x02_u8.to_bytes(writer)?,
Instruction::S8FromI64 => 0x03_u8.to_bytes(writer)?,
Instruction::S16FromI32 => 0x04_u8.to_bytes(writer)?,
Instruction::S16FromI64 => 0x05_u8.to_bytes(writer)?,
Instruction::S32FromI32 => 0x06_u8.to_bytes(writer)?,
Instruction::S32FromI64 => 0x07_u8.to_bytes(writer)?,
Instruction::S64FromI32 => 0x08_u8.to_bytes(writer)?,
Instruction::S64FromI64 => 0x09_u8.to_bytes(writer)?,
Instruction::I32FromS8 => 0x0a_u8.to_bytes(writer)?,
Instruction::I32FromS16 => 0x0b_u8.to_bytes(writer)?,
Instruction::I32FromS32 => 0x0c_u8.to_bytes(writer)?,
Instruction::I32FromS64 => 0x0d_u8.to_bytes(writer)?,
Instruction::I64FromS8 => 0x0e_u8.to_bytes(writer)?,
Instruction::I64FromS16 => 0x0f_u8.to_bytes(writer)?,
Instruction::I64FromS32 => 0x10_u8.to_bytes(writer)?,
Instruction::I64FromS64 => 0x11_u8.to_bytes(writer)?,
Instruction::U8FromI32 => 0x12_u8.to_bytes(writer)?,
Instruction::U8FromI64 => 0x13_u8.to_bytes(writer)?,
Instruction::U16FromI32 => 0x14_u8.to_bytes(writer)?,
Instruction::U16FromI64 => 0x15_u8.to_bytes(writer)?,
Instruction::U32FromI32 => 0x16_u8.to_bytes(writer)?,
Instruction::U32FromI64 => 0x17_u8.to_bytes(writer)?,
Instruction::U64FromI32 => 0x18_u8.to_bytes(writer)?,
Instruction::U64FromI64 => 0x19_u8.to_bytes(writer)?,
Instruction::I32FromU8 => 0x1a_u8.to_bytes(writer)?,
Instruction::I32FromU16 => 0x1b_u8.to_bytes(writer)?,
Instruction::I32FromU32 => 0x1c_u8.to_bytes(writer)?,
Instruction::I32FromU64 => 0x1d_u8.to_bytes(writer)?,
Instruction::I64FromU8 => 0x1e_u8.to_bytes(writer)?,
Instruction::I64FromU16 => 0x1f_u8.to_bytes(writer)?,
Instruction::I64FromU32 => 0x20_u8.to_bytes(writer)?,
Instruction::I64FromU64 => 0x21_u8.to_bytes(writer)?,
Instruction::StringLiftMemory => 0x22_u8.to_bytes(writer)?,
Instruction::StringLowerMemory => 0x23_u8.to_bytes(writer)?,
Instruction::StringSize => 0x24_u8.to_bytes(writer)?,
Instruction::ByteArrayLiftMemory => 0x37_u8.to_bytes(writer)?,
Instruction::ByteArrayLowerMemory => 0x38_u8.to_bytes(writer)?,
Instruction::ByteArraySize => 0x39_u8.to_bytes(writer)?,
Instruction::RecordLift { type_index } => {
0x25_u8.to_bytes(writer)?;
(*type_index as u64).to_bytes(writer)?
}
Instruction::RecordLower { type_index } => {
0x26_u8.to_bytes(writer)?;
(*type_index as u64).to_bytes(writer)?
}
Instruction::RecordLiftMemory { type_index } => {
0x3A_u8.to_bytes(writer)?;
(*type_index as u64).to_bytes(writer)?
}
Instruction::RecordLowerMemory { type_index } => {
0x3B_u8.to_bytes(writer)?;
(*type_index as u64).to_bytes(writer)?
}
Instruction::Dup => 0x34_u8.to_bytes(writer)?,
Instruction::Swap2 => 0x35_u8.to_bytes(writer)?,
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! assert_to_bytes {
($expr:expr, $expected_output:expr) => {{
let mut output = vec![];
$expr.to_bytes(&mut output).expect(concat!(
"Unable to encode the expression `",
stringify!($expr),
"` to bytes."
));
assert_eq!(output.as_slice(), &$expected_output[..]);
}};
}
#[test]
fn test_u8() {
assert_to_bytes!(0x01_u8, &[0x01]);
}
#[test]
fn test_uleb_1_byte() {
assert_to_bytes!(0x01_u64, &[0x01]);
}
#[test]
fn test_uleb_3_bytes() {
assert_to_bytes!(0x7ffc_u64, &[0xfc, 0xff, 0x01]);
}
#[test]
fn test_uleb_from_dward_standard() {
assert_to_bytes!(2u64, &[2u8]);
assert_to_bytes!(127u64, &[127u8]);
assert_to_bytes!(128u64, &[0x80, 1u8]);
assert_to_bytes!(129u64, &[1u8 | 0x80, 1]);
assert_to_bytes!(130u64, &[2u8 | 0x80, 1]);
assert_to_bytes!(12857u64, &[57u8 | 0x80, 100]);
}
#[test]
fn test_empty_str() {
assert_to_bytes!("", &[0x00]);
}
#[test]
fn test_str() {
assert_to_bytes!("abc", &[0x03, 0x61, 0x62, 0x63]);
}
#[test]
fn test_empty_vec() {
assert_to_bytes!(Vec::<u8>::new(), &[0x00]);
}
#[test]
fn test_vec() {
assert_to_bytes!(
vec!["a", "b", "c"],
&[
0x03,
0x01,
0x61,
0x01,
0x62,
0x01,
0x63,
]
);
}
#[test]
fn test_interface_type() {
assert_to_bytes!(InterfaceType::S8, &[0x00]);
assert_to_bytes!(InterfaceType::S16, &[0x01]);
assert_to_bytes!(InterfaceType::S32, &[0x02]);
assert_to_bytes!(InterfaceType::S64, &[0x03]);
assert_to_bytes!(InterfaceType::U8, &[0x04]);
assert_to_bytes!(InterfaceType::U16, &[0x05]);
assert_to_bytes!(InterfaceType::U32, &[0x06]);
assert_to_bytes!(InterfaceType::U64, &[0x07]);
assert_to_bytes!(InterfaceType::F32, &[0x08]);
assert_to_bytes!(InterfaceType::F64, &[0x09]);
assert_to_bytes!(InterfaceType::String, &[0x0a]);
assert_to_bytes!(InterfaceType::Anyref, &[0x0b]);
assert_to_bytes!(InterfaceType::I32, &[0x0c]);
assert_to_bytes!(InterfaceType::I64, &[0x0d]);
assert_to_bytes!(
InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::String]
}),
&[0x0e, 0x01, 0x0a]
);
}
#[test]
fn test_record_type() {
assert_to_bytes!(
RecordType {
fields: vec1![InterfaceType::String]
},
&[
0x01,
0x0a,
]
);
assert_to_bytes!(
RecordType {
fields: vec1![InterfaceType::String, InterfaceType::I32]
},
&[
0x02,
0x0a,
0x0c,
]
);
assert_to_bytes!(
RecordType {
fields: vec1![
InterfaceType::String,
InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::I32, InterfaceType::I32],
}),
InterfaceType::F64,
],
},
&[
0x03,
0x0a,
0x0e,
0x02,
0x0c,
0x0c,
0x09,
]
);
}
#[test]
fn test_interface_kind() {
assert_to_bytes!(InterfaceKind::Type, &[0x00]);
assert_to_bytes!(InterfaceKind::Import, &[0x01]);
assert_to_bytes!(InterfaceKind::Adapter, &[0x02]);
assert_to_bytes!(InterfaceKind::Export, &[0x03]);
assert_to_bytes!(InterfaceKind::Implementation, &[0x04]);
}
#[test]
fn test_export() {
assert_to_bytes!(
Export {
name: "abc",
function_type: 0,
},
&[
0x03,
0x61,
0x62,
0x63,
0x00,
]
);
}
#[test]
fn test_type_function() {
assert_to_bytes!(
Type::Function {
inputs: vec![InterfaceType::I32, InterfaceType::I64],
outputs: vec![InterfaceType::S32],
},
&[
0x00,
0x02,
0x0c,
0x0d,
0x01,
0x02,
]
);
}
#[test]
fn test_type_record() {
assert_to_bytes!(
Type::Record(RecordType {
fields: vec1![InterfaceType::I32, InterfaceType::I64],
}),
&[
0x01,
0x02,
0x0c,
0x0d,
]
);
}
#[test]
fn test_import() {
assert_to_bytes!(
Import {
namespace: "a",
name: "b",
function_type: 0,
},
&[
0x01,
0x61,
0x01,
0x62,
0x00,
]
);
}
#[test]
fn test_adapter() {
assert_to_bytes!(
Adapter {
function_type: 0,
instructions: vec![Instruction::ArgumentGet { index: 1 }],
},
&[
0x00,
0x01,
0x00, 0x01,
]
);
}
#[test]
fn test_interfaces() {
assert_to_bytes!(
Interfaces {
types: vec![Type::Function {
inputs: vec![InterfaceType::S8],
outputs: vec![InterfaceType::S16],
}],
imports: vec![Import {
namespace: "ab",
name: "c",
function_type: 0,
}],
adapters: vec![Adapter {
function_type: 0,
instructions: vec![Instruction::ArgumentGet { index: 1 }],
}],
exports: vec![Export {
name: "ab",
function_type: 1,
}],
implementations: vec![Implementation {
core_function_type: 2,
adapter_function_type: 3,
}],
},
&[
0x00,
0x01,
0x00,
0x01,
0x00,
0x01,
0x01,
0x01,
0x01,
0x02,
0x61, 0x62,
0x01,
0x63,
0x00,
0x02,
0x01,
0x00,
0x01,
0x00, 0x01,
0x03,
0x01,
0x02,
0x61, 0x62,
0x01,
0x04,
0x01,
0x02,
0x03,
]
);
}
#[test]
fn test_instructions() {
assert_to_bytes!(
vec![
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 1 },
Instruction::S8FromI32,
Instruction::S8FromI64,
Instruction::S16FromI32,
Instruction::S16FromI64,
Instruction::S32FromI32,
Instruction::S32FromI64,
Instruction::S64FromI32,
Instruction::S64FromI64,
Instruction::I32FromS8,
Instruction::I32FromS16,
Instruction::I32FromS32,
Instruction::I32FromS64,
Instruction::I64FromS8,
Instruction::I64FromS16,
Instruction::I64FromS32,
Instruction::I64FromS64,
Instruction::U8FromI32,
Instruction::U8FromI64,
Instruction::U16FromI32,
Instruction::U16FromI64,
Instruction::U32FromI32,
Instruction::U32FromI64,
Instruction::U64FromI32,
Instruction::U64FromI64,
Instruction::I32FromU8,
Instruction::I32FromU16,
Instruction::I32FromU32,
Instruction::I32FromU64,
Instruction::I64FromU8,
Instruction::I64FromU16,
Instruction::I64FromU32,
Instruction::I64FromU64,
Instruction::StringLiftMemory,
Instruction::StringLowerMemory,
Instruction::StringSize,
Instruction::RecordLift { type_index: 1 },
Instruction::RecordLower { type_index: 1 },
],
&[
0x27,
0x00, 0x01,
0x01, 0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0a,
0x0b,
0x0c,
0x0d,
0x0e,
0x0f,
0x10,
0x11,
0x12,
0x13,
0x14,
0x15,
0x16,
0x17,
0x18,
0x19,
0x1a,
0x1b,
0x1c,
0x1d,
0x1e,
0x1f,
0x20,
0x21,
0x22,
0x23,
0x24,
0x025, 0x01,
0x026, 0x01,
]
);
}
}