extern crate core;
use std::collections::HashMap;
use std::fmt::Debug;
use std::ops::Index;
use crate::opcodes::Opcode;
use crate::types::{
ConstantDef, FunPtr, Function, Native, ObjField, RefFloat, RefFun, RefGlobal, RefInt,
RefString, RefType, Type, TypeObj,
};
pub mod analysis;
pub mod fmt;
pub mod opcodes;
mod read;
pub mod types;
mod write;
pub type Str = flexstr::SharedStr;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Malformed bytecode: {0}")]
MalformedBytecode(String),
#[error("Unsupported bytecode version {version} (expected {min} <= version <= {max})")]
UnsupportedVersion { version: u8, min: u8, max: u8 },
#[error("Value '{value}' is too big to be serialized (|expected| < {limit})")]
ValueOutOfBounds { value: i32, limit: u32 },
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
Utf8Error(#[from] core::str::Utf8Error),
}
#[derive(Debug)]
pub struct Bytecode {
pub version: u8,
pub entrypoint: RefFun,
pub ints: Vec<i32>,
pub floats: Vec<f64>,
pub strings: Vec<Str>,
pub bytes: Option<(Vec<u8>, Vec<usize>)>,
pub debug_files: Option<Vec<Str>>,
pub types: Vec<Type>,
pub globals: Vec<RefType>,
pub natives: Vec<Native>,
pub functions: Vec<Function>,
pub constants: Option<Vec<ConstantDef>>,
findexes: Vec<RefFunKnown>,
fnames: HashMap<Str, usize>,
pub globals_initializers: HashMap<RefGlobal, usize>,
}
impl Bytecode {
pub fn entrypoint(&self) -> &Function {
self.get(self.entrypoint).as_fn().unwrap()
}
pub fn main(&self) -> &Function {
&self.functions[*self.fnames.get("main").unwrap()]
}
pub fn function_by_name(&self, name: &str) -> Option<&Function> {
self.fnames.get(name).map(|&i| &self.functions[i])
}
pub fn findex_max(&self) -> usize {
self.findexes.len()
}
pub fn functions<'a>(&'a self) -> impl Iterator<Item = FunPtr<'a>> + 'a {
(0..self.findex_max()).map(RefFun).map(|r| self.get(r))
}
}
impl Default for Bytecode {
fn default() -> Self {
Self {
version: 5,
entrypoint: Default::default(),
ints: vec![],
floats: vec![],
strings: vec![],
bytes: None,
debug_files: None,
types: vec![],
globals: vec![],
natives: vec![],
functions: vec![],
constants: None,
findexes: vec![],
fnames: Default::default(),
globals_initializers: Default::default(),
}
}
}
#[derive(Debug, Copy, Clone)]
enum RefFunKnown {
Fun(usize),
Native(usize),
}
pub trait Resolve<I> {
type Output<'a>
where
Self: 'a;
fn get(&self, index: I) -> Self::Output<'_>;
}
impl Resolve<RefInt> for Bytecode {
type Output<'a> = i32;
fn get(&self, index: RefInt) -> Self::Output<'_> {
self.ints[index.0]
}
}
impl Resolve<RefFloat> for Bytecode {
type Output<'a> = f64;
fn get(&self, index: RefFloat) -> Self::Output<'_> {
self.floats[index.0]
}
}
impl Resolve<RefString> for Bytecode {
type Output<'a> = Str;
fn get(&self, index: RefString) -> Self::Output<'_> {
if index.0 > 0 {
self.strings[index.0].clone()
} else {
Str::from_static("<none>")
}
}
}
impl Resolve<RefType> for Bytecode {
type Output<'a> = &'a Type;
fn get(&self, index: RefType) -> Self::Output<'_> {
&self.types[index.0]
}
}
impl Resolve<RefFun> for Bytecode {
type Output<'a> = FunPtr<'a>;
fn get(&self, index: RefFun) -> Self::Output<'_> {
match self.findexes[index.0] {
RefFunKnown::Fun(fun) => FunPtr::Fun(&self.functions[fun]),
RefFunKnown::Native(n) => FunPtr::Native(&self.natives[n]),
}
}
}
impl Index<RefInt> for Bytecode {
type Output = i32;
fn index(&self, index: RefInt) -> &Self::Output {
self.ints.index(index.0)
}
}
impl Index<RefFloat> for Bytecode {
type Output = f64;
fn index(&self, index: RefFloat) -> &Self::Output {
self.floats.index(index.0)
}
}
impl Index<RefString> for Bytecode {
type Output = Str;
fn index(&self, index: RefString) -> &Self::Output {
self.strings.index(index.0)
}
}
impl Index<RefType> for Bytecode {
type Output = Type;
fn index(&self, index: RefType) -> &Self::Output {
self.types.index(index.0)
}
}