use std::path::PathBuf;
use thiserror::Error;
use crate::{
backends::Backend,
event::EventPool,
frontend::{
ao::events::{
EVENT_BREAKPOINT,
EVENT_SYSCALL,
},
LoaderError,
ProcessImage,
ProcessImageBuilder,
},
logger::Logger,
passes::{
Pass,
VerifyerPass,
VerifyerPassError,
},
};
#[derive(Error, Debug)]
pub enum CompilerError<'a> {
#[error("Loader has not been configured correctly: {0}")]
LoaderOptionNotSet(&'static str),
#[error("The frontend had an error: {0}")]
LoaderError(#[from] LoaderError),
#[error("The backend had an error: {0}")]
BackendError(Box<dyn std::error::Error + 'a>),
#[error("Verification failed: {0}")]
VerificationError(#[from] VerifyerPassError),
}
pub struct Loader {
binary: Option<PathBuf>,
library_paths: Vec<PathBuf>,
preloads: Vec<PathBuf>,
ignore_missing_deps: bool,
}
impl Loader {
pub(crate) fn new() -> Self {
Self {
binary: None,
library_paths: Vec::new(),
preloads: Vec::new(),
ignore_missing_deps: false,
}
}
pub fn binary<P: Into<PathBuf>>(mut self, binary: P) -> Self {
self.binary = Some(binary.into());
self
}
pub fn search_path<P: Into<PathBuf>>(mut self, search_path: P) -> Self {
self.library_paths.push(search_path.into());
self
}
pub fn search_paths<I, P>(mut self, search_paths: I) -> Self
where
I: IntoIterator<Item = P>,
P: Into<PathBuf>,
{
for search_path in search_paths {
self.library_paths.push(search_path.into());
}
self
}
pub fn preload<P: Into<PathBuf>>(mut self, library: P) -> Self {
self.preloads.push(library.into());
self
}
pub fn preloads<I, P>(mut self, preloads: I) -> Self
where
I: IntoIterator<Item = P>,
P: Into<PathBuf>,
{
for preload in preloads {
self.preloads.push(preload.into());
}
self
}
pub fn ignore_missing_dependencies(mut self, flag: bool) -> Self {
self.ignore_missing_deps = flag;
self
}
pub fn load(self) -> Result<Compiler, CompilerError<'static>> {
let binary = self.binary.ok_or(CompilerError::LoaderOptionNotSet("binary has not been set"))?;
let mut logger = Logger::spinner();
logger.set_title("Building process image");
let mut event_pool = EventPool::new();
event_pool.add_event(EVENT_SYSCALL);
event_pool.add_event(EVENT_BREAKPOINT);
let image = ProcessImageBuilder::build(binary, &self.library_paths, &self.preloads, self.ignore_missing_deps, &mut event_pool, &logger)?;
let mut compiler = Compiler {
image,
event_pool,
modified: false,
};
drop(logger);
compiler.verify()?;
Ok(compiler)
}
}
#[derive(Debug)]
pub struct Compiler {
pub(crate) image: ProcessImage,
pub(crate) event_pool: EventPool,
modified: bool,
}
impl Compiler {
pub fn loader() -> Loader {
Loader::new()
}
pub fn run_pass<P>(&mut self, pass: &mut P) -> Result<(), P::Error>
where
P: Pass,
{
self.modified = true;
let mut logger = Logger::spinner();
logger.set_title(format!("Running Pass: {}", pass.name()));
logger.set_prefix(pass.name());
let ret = pass.run(&mut self.image, &mut self.event_pool, &logger);
logger.clear_prefix();
ret
}
fn verify(&mut self) -> Result<(), VerifyerPassError> {
let mut verifyer = VerifyerPass::new(false);
self.run_pass(&mut verifyer)?;
self.modified = false;
Ok(())
}
pub fn compile<'a, B: Backend>(mut self, mut backend: B) -> Result<B::Runtime, CompilerError<'a>>
where
<B as Backend>::Error: 'a,
{
if self.modified {
self.verify()?;
}
let mut logger = Logger::spinner();
logger.set_title(format!("Compiling with backend: {}", backend.name()));
logger.set_prefix(backend.name());
let ret = match backend.create_runtime(self.image, self.event_pool, &logger) {
Ok(runtime) => runtime,
Err(err) => return Err(CompilerError::BackendError(Box::new(err))),
};
logger.clear_prefix();
logger.info("Compilation successful");
Ok(ret)
}
pub fn process_image(&self) -> &ProcessImage {
&self.image
}
pub fn event_pool(&self) -> &EventPool {
&self.event_pool
}
}