use crate::compiler::Program;
use crate::host::{self, FuncDef, TryDef};
use fusevm::VM;
use std::path::{Path, PathBuf};
use std::sync::Arc;
const PROG_IMAGE_TAG: &str = "\u{1}node-js-prog-image:";
#[derive(serde::Serialize, serde::Deserialize)]
struct ProgImage {
functions: Vec<FuncDef>,
tries: Vec<TryDef>,
}
pub fn emit_executable(prog: &Program, out: &Path) -> Result<(), String> {
let obj = std::env::temp_dir().join("node_js_aot.o");
emit_object(prog, &obj)?;
let main_c = std::env::temp_dir().join("node_js_aot_main.c");
std::fs::write(
&main_c,
"extern long fusevm_aot_run_embedded(void);\n\
int main(void) { return (int)fusevm_aot_run_embedded(); }\n",
)
.map_err(|e| e.to_string())?;
let lib = staticlib_path()?;
let mut cmd = std::process::Command::new("cc");
cmd.arg(&main_c).arg(&obj).arg(&lib).arg("-o").arg(out);
if cfg!(target_os = "macos") {
cmd.args([
"-framework",
"CoreFoundation",
"-framework",
"Security",
"-liconv",
"-lc++",
]);
} else {
cmd.args(["-lpthread", "-ldl", "-lm", "-lrt"]);
}
let status = cmd.status().map_err(|e| format!("cc: {e}"))?;
if !status.success() {
return Err(format!("link failed (cc exit {:?})", status.code()));
}
Ok(())
}
fn emit_object(prog: &Program, obj: &Path) -> Result<(), String> {
let mut chunk = prog.main.clone();
let image = ProgImage {
functions: prog.functions.iter().map(|(_, f)| f.clone()).collect(),
tries: prog.tries.clone(),
};
let json = serde_json::to_string(&image).map_err(|e| e.to_string())?;
chunk.names.push(format!("{PROG_IMAGE_TAG}{json}"));
fusevm::aot::compile_object(&chunk, obj).map_err(|e| format!("node-js --build: {e}"))
}
fn staticlib_path() -> Result<PathBuf, String> {
if let Ok(p) = std::env::var("NODE_JS_STATICLIB") {
return Ok(PathBuf::from(p));
}
let exe = std::env::current_exe().map_err(|e| e.to_string())?;
let lib = exe.parent().ok_or("no exe dir")?.join("libnodejs.a");
if lib.exists() {
Ok(lib)
} else {
Err(format!(
"libnodejs.a not found next to {}; build the staticlib or set NODE_JS_STATICLIB",
exe.display()
))
}
}
#[no_mangle]
pub unsafe extern "C" fn fusevm_aot_register_builtins(vm: *mut VM) {
let vm = unsafe { &mut *vm };
crate::builtins::install(vm);
vm.set_numeric_hook(Arc::new(crate::builtins::numeric_hook));
let images: Vec<ProgImage> = vm
.chunk
.names
.iter()
.filter_map(|n| n.strip_prefix(PROG_IMAGE_TAG))
.filter_map(|j| serde_json::from_str(j).ok())
.collect();
host::with_host(|h| {
for img in images {
h.load_program(img.functions, img.tries);
}
});
}