use typelisp_front::dump::{apply_types, UnitItem, UnitState};
use crate::compile::symbols::CompiledItem;
use crate::{Checker, Heap, Interp};
pub fn read_types(unit: &typelisp_front::dump::UnitRef, label: &str) -> Result<UnitState, String> {
typelisp_front::dump::read_state(unit.types, label)
}
pub fn load_unit(
heap: &mut Heap,
chk: &mut Checker,
interp: &mut Interp,
state: UnitState,
bitcode: &[u8],
) -> Result<(), String> {
load_unit_with(heap, chk, interp, state, Some(bitcode))
}
pub fn load_unit_types_only(
heap: &mut Heap,
chk: &mut Checker,
interp: &mut Interp,
state: UnitState,
) -> Result<(), String> {
load_unit_with(heap, chk, interp, state, None)
}
fn load_unit_with(
heap: &mut Heap,
chk: &mut Checker,
interp: &mut Interp,
state: UnitState,
bitcode: Option<&[u8]>,
) -> Result<(), String> {
let label = state.label.clone();
let globals = state.globals.clone();
let body_abi = state.body_abi;
let body_layout = state.body_layout;
let items: Vec<CompiledItem> = state
.items
.iter()
.map(|i| match i {
UnitItem::Fn(path) => CompiledItem::Fn(path.clone()),
UnitItem::Method(path, name) => CompiledItem::Method(path.clone(), name.clone()),
})
.collect();
apply_types(heap, chk, interp, state)?;
for (name, id) in &globals {
let path = typelisp_front::dump::parse_path(name);
let got = interp
.promote_global(heap, &path)
.map_err(|e| format!("{}: creating storage for global `{}`: {}", label, path, e))?;
if got != *id {
return Err(format!(
"{}: global `{}` was compiled against slot {} but this load put it in slot {} — \
something claimed a compiled-global id before the dump was applied",
label, path, id, got
));
}
}
let Some(bitcode) = bitcode.filter(|b| !b.is_empty()) else {
return Ok(());
};
crate::compile::driver::install_compiled_library(
interp,
crate::compile::CompiledLibrary {
label: &label,
bitcode,
items: &items,
body_abi,
body_layout,
},
)
}
pub fn capture_program_dump(
heap: &mut Heap,
source: &str,
src_root: &std::path::Path,
entry_segs: &[String],
globals: &[(String, usize)],
) -> Result<Vec<u8>, String> {
let prelude = typelisp_front::dump::parse(typelisp_front::prelude::DUMP, "prelude")?;
let prelude_unit = prelude.first().ok_or_else(|| "prelude: the committed dump holds no units".to_string())?;
let prelude_state = typelisp_front::dump::read_state(prelude_unit.types, "prelude")?;
typelisp_front::dump::verify_sources_digest(&prelude_state, typelisp_front::prelude::DUMPED_SOURCES, typelisp_front::prelude::REGEN_SCRIPT)?;
let mut chk = Checker::new();
let mut interp = Interp::new();
load_unit_types_only(heap, &mut chk, &mut interp, prelude_state)?;
let before = chk.signature(heap)?;
typelisp_front::dump::bind_globals(&interp, globals);
let mark = heap.root_count();
let mut forms: Vec<crate::Value> = Vec::new();
let reader = crate::Reader::new();
let mut loader = typelisp_front::project::Loader::new(src_root.to_path_buf());
let mut program = reader.forms_within(
typelisp_front::dump::PROGRAM_LABEL,
source,
typelisp_front::mem::symbols::ns_of(entry_segs),
);
let entry_path = super::aot::entry_point_path(entry_segs);
chk.enter_file_module(entry_segs);
loop {
let next = {
let hook = typelisp_front::read::DriverReadEval::new(&mut chk, &interp);
program.next_form_with(heap, Some(&hook)).map_err(|e| e.to_string())?
};
let Some((v, _)) = next else { break };
loader.load_uses_in(heap, &reader, &mut chk, &mut interp, std::slice::from_ref(&v)).map_err(|e| e.to_string())?;
let tl = chk.check_form(heap, &interp, v).map_err(|e| e.to_string())?;
let _ = chk.take_warnings();
if super::aot::is_trailing_main(heap, tl, &entry_path) {
continue;
}
heap.push_root(tl);
forms.push(tl);
if typelisp_front::dump::already_initialized_global(heap, &interp, tl) {
continue;
}
interp.exec(heap, tl).map_err(|e| e.to_string())?;
}
chk.exit_file_module(entry_segs.len());
let mut dep_forms: Vec<crate::Value> = Vec::new();
for tl in loader.take_pending() {
heap.push_root(tl);
dep_forms.push(tl);
interp.exec(heap, tl).map_err(|e| e.to_string())?;
}
dep_forms.extend(forms);
let forms = dep_forms;
let delta = chk.capture_delta(heap, &before)?;
let state = typelisp_front::dump::capture_types(heap, delta, typelisp_front::dump::PROGRAM_LABEL, None, None, &forms, globals.to_vec())?;
while heap.root_count() > mark {
heap.pop_root();
}
Ok(typelisp_front::dump::write(&[
(prelude_unit.types.to_vec(), Vec::new()),
(typelisp_front::dump::write_state(&state)?, Vec::new()),
]))
}
pub fn dump_image(interp: &Interp, heap: &mut Heap, path: &str) -> Result<(), String> {
let checker = interp
.checker_handle()
.ok_or_else(|| "dump: this environment has no checker — only the CLI and the REPL can dump".to_string())?;
let (delta, forms, globals_before) = interp
.with_recording(|baseline, forms, globals_before| {
let chk = checker.borrow();
Ok::<_, String>((chk.capture_delta(heap, baseline)?, forms.to_vec(), globals_before))
})
.ok_or_else(|| "dump: this session was not recording what it defines".to_string())??;
let mut plan = crate::compile::prelude_bootstrap::PreludePlan::default();
for tl in &forms {
crate::compile::prelude_bootstrap::collect_item(heap, *tl, &mut plan)?;
}
let compiled: Vec<CompiledItem> = plan
.items
.iter()
.filter(|item| is_compiled(interp, item))
.cloned()
.collect();
let bitcode = if compiled.is_empty() { Vec::new() } else { emit_bitcode(interp, heap, &compiled)? };
let mut globals: Vec<(String, usize)> = interp
.compiled_globals
.borrow()
.iter()
.filter(|(_, id)| **id >= globals_before)
.map(|(p, id)| (p.to_string(), *id))
.collect();
globals.sort_by_key(|(_, id)| *id);
let state = typelisp_front::dump::capture_types_with_abi(
heap,
delta,
"session",
None,
None,
&forms,
compiled.iter().map(crate::compile::prelude_bootstrap::unit_item).collect(),
globals,
crate::compile::EMITTED_BODY_ABI,
crate::compile::EMITTED_BODY_ABI,
crate::compile::EMITTED_LAYOUT,
crate::compile::EMITTED_LAYOUT,
)?;
let mut units: Vec<(Vec<u8>, Vec<u8>)> = interp.with_dump_sources(|sources| {
let mut out = Vec::new();
for bytes in sources {
for unit in typelisp_front::dump::parse(bytes, "dump")? {
out.push((unit.types.to_vec(), unit.bitcode.to_vec()));
}
}
Ok::<_, String>(out)
})?;
units.push((typelisp_front::dump::write_state(&state)?, bitcode));
std::fs::write(path, typelisp_front::dump::write(&units))
.map_err(|e| format!("dump: writing \"{}\": {}", path, e))
}
fn is_compiled(interp: &Interp, item: &CompiledItem) -> bool {
let def = match item {
CompiledItem::Fn(path) => interp.root.borrow().get_fn(path),
CompiledItem::Method(type_path, method) => interp.root.borrow().get_method(type_path, method),
};
def.is_some_and(|d| d.compiled.borrow().is_some())
}
fn emit_bitcode(interp: &Interp, heap: &mut Heap, items: &[CompiledItem]) -> Result<Vec<u8>, String> {
use inkwell::AddressSpace;
use std::cell::RefCell;
use std::rc::Rc;
let ctx = crate::compile::llvm_context();
let module = {
let _guard = crate::compile::COMPILE_LOCK.lock().unwrap();
let module = ctx.create_module("session");
let ptr_ty = ctx.ptr_type(AddressSpace::default());
let fn_ty = ctx.i64_type().fn_type(&[ptr_ty.into(), ctx.i32_type().into()], false);
for (name, _) in crate::compile::externs::rt_extern_functions() {
module.add_function(name, fn_ty, None);
}
let lisp_fn_ty = if crate::compile::EMITTED_BODY_ABI == typelisp_abi::BODY_ABI_COROUTINE {
crate::compile::llvm_builtins::coroutine_fn_type()
} else {
fn_ty
};
for item in items {
let sym = item.symbol_name();
if module.get_function(&sym).is_none() {
module.add_function(&sym, lisp_fn_ty, None);
}
}
Rc::new(RefCell::new(module))
};
for item in items {
let node = item.node_name();
crate::compile::driver::add_compiled_function(interp, heap, module.clone(), &node, &item.symbol_name())
.map_err(|e| format!("dump: re-compiling `{}` failed: {}", node, e))?;
}
let _guard = crate::compile::COMPILE_LOCK.lock().unwrap();
let bitcode = {
let m = module.borrow();
m.verify().map_err(|e| format!("dump: the session module failed verification: {}", e)).map(|()| {
m.write_bitcode_to_memory().as_slice().to_vec()
})
};
drop(module);
bitcode
}
pub fn load_image(
heap: &mut Heap,
chk: &mut Checker,
interp: &mut Interp,
bytes: std::borrow::Cow<'static, [u8]>,
label: &str,
) -> Result<(), String> {
crate::compile::install_llvm_backend();
for unit in typelisp_front::dump::parse(&bytes, label)? {
let state = typelisp_front::dump::read_state(unit.types, label)?;
match state.label.as_str() {
"prelude" => typelisp_front::dump::verify_sources_digest(
&state,
typelisp_front::prelude::DUMPED_SOURCES,
typelisp_front::prelude::REGEN_SCRIPT,
)?,
"compiler island" => typelisp_front::dump::verify_digest(
&state,
crate::compiler::SOURCE,
crate::compiler::REGEN_SCRIPT,
)?,
_ => {}
}
load_unit(heap, chk, interp, state, unit.bitcode)?;
}
interp.push_dump_source(bytes);
Ok(())
}