Add object layout, object parsing, instruction layout

* Object layout and parsing are done in the vm::obj module
* Add MemCursor, a wrapper around the std::io::Cursor type for walking
  through VM memory
* Add vm::tick module for containing the Vm::tick() method
  implementation, since it's pretty big
* Instructions are now variable-sized, and are read lazily,
  one-at-a-time directly from memory.
* Add VM runtime error structure
* Probably some other stuff I forgot

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-02-03 17:56:32 -05:00
parent 47ee61ca0d
commit 214f0b8aed
12 changed files with 554 additions and 502 deletions

42
src/vm/obj/error.rs Normal file
View File

@@ -0,0 +1,42 @@
use snafu::Snafu;
use std::{
fmt::Debug,
io,
};
#[derive(Debug, Snafu)]
pub enum ParseError {
#[snafu(display("IO error: {}", source))]
Io { source: io::Error },
#[snafu(display("wrong magic number"))]
WrongMagic,
#[snafu(display("unknown section kind: 0x{:02x}", kind))]
UnknownSectionKind { kind: u8 },
#[snafu(display("invalid UTF-8 string: {}", source))]
InvalidUtf8String { source: std::string::FromUtf8Error },
}
macro_rules! into_parse_error {
(
$($type:ty : $variant:ident),* $(,)?
) => {
$(
impl From<$type> for ParseError {
fn from(other: $type) -> Self {
ParseError::$variant { source: other }
}
}
)*
}
}
into_parse_error! {
io::Error: Io,
std::string::FromUtf8Error: InvalidUtf8String,
}
pub type Result<T> = std::result::Result<T, ParseError>;

View File

@@ -1,234 +1,3 @@
use crate::vm::inst::Inst;
use byteorder::{ReadBytesExt, LE};
use snafu::{ensure, Snafu};
use std::{
convert::{TryFrom, TryInto},
fmt::Debug,
io::{self, Cursor, Read},
};
pub const MAGIC: u64 = 0xDEAD_BEA7_BA5E_BA11;
#[derive(Debug)]
pub struct Object {
header: Header,
sections: Vec<Box<dyn Section>>,
}
#[derive(Debug, Clone, Copy)]
pub struct Header {
version: u16,
sections: u16,
}
macro_rules! section_kind {
(
pub enum $enum_name:ident {
$($name:ident = $value:expr),* $(,)?
}
) => {
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum $enum_name {
$($name = $value),*
}
impl TryFrom<u8> for SectionKind {
type Error = ParseError;
fn try_from(other: u8) -> std::result::Result<Self, Self::Error> {
match other {
$(
$value => Ok($enum_name::$name),
)*
_ => Err(ParseError::UnknownSectionKind { kind: other }),
}
}
}
};
}
section_kind! {
pub enum SectionKind {
Data = 0x00,
Code = 0x10,
Meta = 0xFF,
}
}
pub trait Section: Debug {
fn header(&self) -> SectionHeader;
}
#[derive(Debug, Clone, Copy)]
pub struct SectionHeader {
kind: SectionKind,
checksum: u32,
len: u64,
}
#[derive(Debug, Clone)]
pub struct DataSection {
header: SectionHeader,
load_location: u64,
contents: Vec<u8>,
}
impl Section for DataSection {
fn header(&self) -> SectionHeader {
self.header
}
}
#[derive(Debug, Clone)]
pub struct CodeSection {
header: SectionHeader,
load_location: u64,
contents: Vec<Inst>,
}
impl Section for CodeSection {
fn header(&self) -> SectionHeader {
self.header
}
}
#[derive(Debug, Clone)]
pub struct MetaSection {
header: SectionHeader,
entry_count: u64,
entries: Vec<(String, Vec<u8>)>,
}
impl Section for MetaSection {
fn header(&self) -> SectionHeader {
self.header
}
}
#[derive(Debug, Clone)]
pub struct ObjectParser {
bytes: Cursor<Vec<u8>>,
}
impl ObjectParser {
pub fn parse(&mut self) -> Result<Object> {
let header = self.parse_header()?;
let sections = self.parse_sections(header)?;
Ok(Object { header, sections })
}
fn parse_header(&mut self) -> Result<Header> {
let magic = self.bytes.read_u64::<LE>()?;
ensure!(magic == MAGIC, WrongMagic);
let version = self.bytes.read_u16::<LE>()?;
let sections = self.bytes.read_u16::<LE>()?;
Ok(Header { version, sections })
}
fn parse_sections(&mut self, header: Header) -> Result<Vec<Box<dyn Section>>> {
(0..header.sections)
.map(|_| self.parse_section())
.collect()
}
fn parse_section(&mut self) -> Result<Box<dyn Section>> {
let header = self.parse_section_header()?;
let section: Box<dyn Section> = match header.kind {
SectionKind::Data => self.parse_data_section(header).map(Box::new)?,
SectionKind::Code => self.parse_code_section(header).map(Box::new)?,
SectionKind::Meta => self.parse_meta_section(header).map(Box::new)?,
};
Ok(section)
}
fn parse_section_header(&mut self) -> Result<SectionHeader> {
let kind: SectionKind = self.bytes.read_u8()?.try_into()?;
let checksum = self.bytes.read_u32::<LE>()?;
let len = self.bytes.read_u64::<LE>()?;
Ok(SectionHeader {
kind,
checksum,
len,
})
}
fn parse_data_section(&mut self, header: SectionHeader) -> Result<DataSection> {
let load_location = self.bytes.read_u64::<LE>()?;
let contents = self.take_bytes(header.len)?;
Ok(DataSection {
header,
load_location,
contents,
})
}
fn parse_code_section(&mut self, _header: SectionHeader) -> Result<CodeSection> {
let _load_location = self.bytes.read_u64::<LE>()?;
todo!("instruction parsing")
}
fn parse_meta_section(&mut self, header: SectionHeader) -> Result<MetaSection> {
let entry_count = self.bytes.read_u64::<LE>()?;
let mut entries = Vec::with_capacity(entry_count as usize);
for offset in 0 .. entry_count {
let key_bytes = self.parse_sized_data()?;
let key = String::from_utf8(key_bytes)?;
let value = self.parse_sized_data()?;
entries.push((key, value));
}
Ok(MetaSection {
header,
entry_count,
entries,
})
}
fn parse_sized_data(&mut self) -> Result<Vec<u8>> {
let size = self.bytes.read_u64::<LE>()?;
self.take_bytes(size)
}
fn take_bytes(&mut self, count: u64) -> Result<Vec<u8>> {
let mut contents = vec!(0u8; count as usize);
self.bytes.read_exact(&mut contents)?;
Ok(contents)
}
}
#[derive(Debug, Snafu)]
pub enum ParseError {
#[snafu(display("IO error: {}", source))]
Io { source: io::Error },
#[snafu(display("wrong magic number"))]
WrongMagic,
#[snafu(display("unknown section kind: 0x{:02x}", kind))]
UnknownSectionKind { kind: u8 },
#[snafu(display("invalid UTF-8 string: {}", source))]
InvalidUtf8String { source: std::string::FromUtf8Error },
}
macro_rules! into_parse_error {
(
$($type:ty : $variant:ident),* $(,)?
) => {
$(
impl From<$type> for ParseError {
fn from(other: $type) -> Self {
ParseError::$variant { source: other }
}
}
)*
}
}
into_parse_error! {
io::Error: Io,
std::string::FromUtf8Error: InvalidUtf8String,
}
pub type Result<T> = std::result::Result<T, ParseError>;
pub mod obj;
pub mod parser;
pub mod error;

107
src/vm/obj/obj.rs Normal file
View File

@@ -0,0 +1,107 @@
use crate::vm::obj::error::ParseError;
use std::{
convert::TryFrom,
fmt::Debug,
};
pub const MAGIC: u64 = 0xDEAD_BEA7_BA5E_BA11;
#[derive(Debug)]
pub struct Object {
pub header: Header,
pub sections: Vec<Box<dyn Section>>,
}
#[derive(Debug, Clone, Copy)]
pub struct Header {
pub version: u16,
pub sections: u16,
}
macro_rules! section_kind {
(
pub enum $enum_name:ident {
$($name:ident = $value:expr),* $(,)?
}
) => {
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum $enum_name {
$($name = $value),*
}
impl TryFrom<u8> for SectionKind {
type Error = ParseError;
fn try_from(other: u8) -> std::result::Result<Self, Self::Error> {
match other {
$(
$value => Ok($enum_name::$name),
)*
_ => Err(ParseError::UnknownSectionKind { kind: other }),
}
}
}
};
}
section_kind! {
pub enum SectionKind {
Data = 0x00,
Code = 0x10,
Meta = 0xFF,
}
}
pub trait Section: Debug {
fn header(&self) -> SectionHeader;
}
#[derive(Debug, Clone, Copy)]
pub struct SectionHeader {
pub kind: SectionKind,
pub checksum: u32,
pub len: u64,
}
#[derive(Debug, Clone)]
pub struct DataSection {
pub header: SectionHeader,
pub load_location: u64,
pub contents: Vec<u8>,
}
impl Section for DataSection {
fn header(&self) -> SectionHeader {
self.header
}
}
#[derive(Debug, Clone)]
pub struct CodeSection {
pub header: SectionHeader,
pub load_location: u64,
pub contents: Vec<u8>,
}
impl Section for CodeSection {
fn header(&self) -> SectionHeader {
self.header
}
}
#[derive(Debug, Clone)]
pub struct MetaSection {
pub header: SectionHeader,
pub entry_count: u64,
pub entries: Vec<(String, Vec<u8>)>,
}
impl Section for MetaSection {
fn header(&self) -> SectionHeader {
self.header
}
}

108
src/vm/obj/parser.rs Normal file
View File

@@ -0,0 +1,108 @@
use byteorder::{ReadBytesExt, LE};
use crate::vm::obj::{
obj::*,
error::*,
};
use std::{
convert::TryInto,
fmt::Debug,
io::{Cursor, Read},
};
#[derive(Debug, Clone)]
pub struct ObjectParser {
bytes: Cursor<Vec<u8>>,
}
impl ObjectParser {
pub fn parse(&mut self) -> Result<Object> {
let header = self.parse_header()?;
let sections = self.parse_sections(header)?;
Ok(Object { header, sections })
}
fn parse_header(&mut self) -> Result<Header> {
let magic = self.bytes.read_u64::<LE>()?;
if magic != magic {
return Err(ParseError::WrongMagic);
}
let version = self.bytes.read_u16::<LE>()?;
let sections = self.bytes.read_u16::<LE>()?;
Ok(Header { version, sections })
}
fn parse_sections(&mut self, header: Header) -> Result<Vec<Box<dyn Section>>> {
(0..header.sections)
.map(|_| self.parse_section())
.collect()
}
fn parse_section(&mut self) -> Result<Box<dyn Section>> {
let header = self.parse_section_header()?;
let section: Box<dyn Section> = match header.kind {
SectionKind::Data => self.parse_data_section(header).map(Box::new)?,
SectionKind::Code => self.parse_code_section(header).map(Box::new)?,
SectionKind::Meta => self.parse_meta_section(header).map(Box::new)?,
};
Ok(section)
}
fn parse_section_header(&mut self) -> Result<SectionHeader> {
let kind: SectionKind = self.bytes.read_u8()?.try_into()?;
let checksum = self.bytes.read_u32::<LE>()?;
let len = self.bytes.read_u64::<LE>()?;
Ok(SectionHeader {
kind,
checksum,
len,
})
}
fn parse_data_section(&mut self, header: SectionHeader) -> Result<DataSection> {
let load_location = self.bytes.read_u64::<LE>()?;
let contents = self.take_bytes(header.len)?;
Ok(DataSection {
header,
load_location,
contents,
})
}
fn parse_code_section(&mut self, header: SectionHeader) -> Result<CodeSection> {
let load_location = self.bytes.read_u64::<LE>()?;
let contents = self.take_bytes(header.len)?;
Ok(CodeSection {
header,
load_location,
contents,
})
}
fn parse_meta_section(&mut self, header: SectionHeader) -> Result<MetaSection> {
let entry_count = self.bytes.read_u64::<LE>()?;
let mut entries = Vec::with_capacity(entry_count as usize);
for _ in 0 .. entry_count {
let key_bytes = self.parse_sized_data()?;
let key = String::from_utf8(key_bytes)?;
let value = self.parse_sized_data()?;
entries.push((key, value));
}
Ok(MetaSection {
header,
entry_count,
entries,
})
}
fn parse_sized_data(&mut self) -> Result<Vec<u8>> {
let size = self.bytes.read_u64::<LE>()?;
self.take_bytes(size)
}
fn take_bytes(&mut self, count: u64) -> Result<Vec<u8>> {
let mut contents = vec!(0u8; count as usize);
self.bytes.read_exact(&mut contents)?;
Ok(contents)
}
}