use std::io::BufRead;
use hax_frontend_exporter::{
ThirBody,
id_table::{Table, WithTable},
};
use hax_types::engine_api::{
EngineOptions,
protocol::{FromEngine, ToEngine},
};
use serde::Deserialize;
#[derive(Debug, Clone, ::schemars::JsonSchema, ::serde::Deserialize, ::serde::Serialize)]
pub struct Query {
pub hax_version: String,
pub impl_infos: Vec<(
hax_frontend_exporter::DefId,
hax_frontend_exporter::ImplInfos,
)>,
pub kind: QueryKind,
}
#[derive(Debug, Clone, ::schemars::JsonSchema, ::serde::Deserialize, ::serde::Serialize)]
pub enum QueryKind {
ImportThir {
input: Vec<hax_frontend_exporter::Item<ThirBody>>,
apply_phases: bool,
translation_options: hax_types::cli_options::TranslationOptions,
},
}
#[derive(Debug, Clone, ::schemars::JsonSchema, ::serde::Deserialize, ::serde::Serialize)]
pub enum Response {
ImportThir {
output: Vec<crate::ast::Item>,
},
}
#[derive(::serde::Deserialize, ::serde::Serialize)]
#[serde(untagged)]
pub enum ExtendedToEngine {
ToEngine(ToEngine),
Query(Box<WithTable<EngineOptions>>),
}
#[derive(Debug, Clone, ::schemars::JsonSchema, ::serde::Deserialize, ::serde::Serialize)]
#[serde(untagged)]
pub enum ExtendedFromEngine {
FromEngine(FromEngine),
Response(Response),
}
impl Query {
pub fn execute(&self, table: Table) -> Option<Response> {
use std::io::Write;
use std::process::Command;
macro_rules! send {
($where: expr, $value:expr) => {
serde_json::to_writer(&mut $where, $value).unwrap();
$where.write_all(b"\n").unwrap();
$where.flush().unwrap();
};
}
let mut engine_subprocess =
Command::new(std::env::var("HAX_ENGINE_BINARY").unwrap_or("hax-engine".into()))
.arg("driver_rust_engine")
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn()
.unwrap();
let mut stdin = std::io::BufWriter::new(
engine_subprocess
.stdin
.as_mut()
.expect("Could not write on stdin"),
);
WithTable::run(table, self, |with_table| {
send!(stdin, with_table);
});
let mut response = None;
let stdout = std::io::BufReader::new(engine_subprocess.stdout.take().unwrap());
for slice in stdout.split(b'\n') {
let msg = (|| {
let slice = slice.ok()?;
let mut de = serde_json::Deserializer::from_slice(&slice);
de.disable_recursion_limit();
let de = serde_stacker::Deserializer::new(&mut de);
let msg = ExtendedFromEngine::deserialize(de);
msg.ok()
})()
.expect(
"Hax engine sent an invalid json value. \
This might be caused by debug messages on stdout, \
which is reserved for JSON communication with cargo-hax",
);
match msg {
ExtendedFromEngine::Response(res) => response = Some(res),
ExtendedFromEngine::FromEngine(FromEngine::Exit) => break,
ExtendedFromEngine::FromEngine(from_engine) => {
crate::hax_io::write(&from_engine);
if from_engine.requires_response() {
let ExtendedToEngine::ToEngine(response) = crate::hax_io::read() else {
panic!(
"The frontend sent an incorrect message: expected `ExtendedToEngine::ToEngine` since we sent a `ExtendedFromEngine::FromEngine`."
)
};
send!(stdin, &response);
}
}
}
}
drop(stdin);
let exit_status = engine_subprocess.wait().unwrap();
if !exit_status.success() {
panic!("ocaml engine crashed");
}
response
}
}