mod clang;
mod error;
mod function;
mod interface;
mod parameter;
mod sal;
mod ty;
mod walk;
use std::{collections::HashMap, path::PathBuf};
use sigmd::Architecture;
pub use self::{error::Error, function::ScoredFunction, interface::ScoredInterface};
pub type CustomKindFn = Box<dyn Fn(&str) -> Option<u8> + Send + Sync + 'static>;
#[derive(Default)]
pub struct BuildContext {
pub sizeofs: HashMap<String, u64>,
pub custom_type_fn: Option<CustomKindFn>,
}
impl BuildContext {
pub fn parse_custom_type(&self, name: impl AsRef<str>) -> Option<u8> {
match &self.custom_type_fn {
Some(custom_type_fn) => custom_type_fn(name.as_ref()),
None => None,
}
}
}
#[derive(Debug)]
pub struct ScoredMetadata {
pub functions: Vec<ScoredFunction>,
pub interfaces: Vec<ScoredInterface>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Score(pub u32);
pub struct Compiler {
index: clang::Index,
}
#[bon::bon]
impl Compiler {
pub fn new() -> Result<Self, Error> {
Ok(Self {
index: clang::Index::new()?,
})
}
#[builder(finish_fn = compile)]
pub fn translation_unit(
&self,
path: impl Into<PathBuf>,
architecture: Architecture,
include: impl IntoIterator<Item = impl Into<PathBuf>>,
inject: impl IntoIterator<Item = impl Into<PathBuf>>,
#[builder(with = |f: impl Fn(&str) -> Option<u8> + Send + Sync + 'static| Box::new(f) as _)]
custom_type_fn: Option<CustomKindFn>,
) -> Result<ScoredMetadata, Error> {
let target = match architecture {
Architecture::X86 => "--target=i686-pc-windows-msvc",
Architecture::X64 => "--target=x86_64-pc-windows-msvc",
};
let mut parser = self.index.parser(path);
#[rustfmt::skip]
parser.args([
target,
"-x", "c++",
"-fms-extensions",
"-fms-compatibility",
"-nostdinc++",
"-nostdsysteminc",
"-nobuiltininc",
]);
for item in include {
let item = item.into();
let include_path = match item.to_str() {
Some(include_path) => include_path,
None => {
tracing::warn!(
path = %item.display(),
"path is not valid UTF-8; skipping"
);
continue;
}
};
parser.args(["-isystem", include_path]);
}
for item in inject {
let item = item.into();
let include_path = match item.to_str() {
Some(include_path) => include_path,
None => {
tracing::warn!(
path = %item.display(),
"path is not valid UTF-8; skipping"
);
continue;
}
};
parser.args(["-include", include_path]);
}
let tu = parser.parse()?;
let mut ctx = BuildContext {
sizeofs: HashMap::new(),
custom_type_fn,
};
walk::collect_sizeofs(tu.get_entity(), &mut ctx);
let mut scored = ScoredMetadata {
functions: Vec::new(),
interfaces: Vec::new(),
};
walk::visit(tu.get_entity(), &ctx, &mut scored);
Ok(scored)
}
}