use crate::check::core;
use crate::compile::symbols::CompiledItem;
use crate::{Checker, Heap, Interp, Reader, Value};
pub fn build_island_artifact() -> Result<Vec<u8>, String> {
let mut heap = Heap::with_capacity(1 << 18);
let mut chk = Checker::new();
let mut interp = Interp::new();
crate::prelude::load_interpreted(&mut heap, &mut chk, &mut interp);
let before = chk.signature(&heap)?;
let reader = Reader::new();
let forms = reader
.read_all(&mut heap, crate::compiler::SOURCE)
.map_err(|e| format!("island read failed: {}", e))?;
let forms_digest = hash_read_forms(&heap, &forms)?;
let mut items: Vec<CompiledItem> = Vec::new();
let mut checked: Vec<Value> = Vec::new();
for v in forms {
let tl = chk.check_form(&mut heap, &interp, v).map_err(|e| format!("island check failed: {}", e))?;
for w in chk.take_warnings() {
eprintln!("{}", w);
}
collect_island_items(&heap, tl, &mut items)?;
heap.push_root(tl);
checked.push(tl);
interp.exec(&mut heap, tl).map_err(|e| format!("island exec failed: {}", e))?;
}
let delta = chk.capture_delta(&heap, &before)?;
if std::env::var_os("TYPELISP_BOOTSTRAP_INTERPRETED").is_some() {
eprintln!("island bootstrap: driving the INTERPRETED island (TYPELISP_BOOTSTRAP_INTERPRETED is set)");
} else {
crate::compile::driver::install_compiled_library(&interp, crate::compile::CompiledLibrary {
label: "compiler island",
bitcode: typelisp_front::dump::parse(crate::compiler::ISLAND_DUMP, "compiler island")?
.first()
.ok_or_else(|| "island: the committed dump holds no units".to_string())?
.bitcode,
body_abi: crate::compiler::ISLAND_DUMP_BODY_ABI,
body_layout: crate::compiler::ISLAND_DUMP_BODY_LAYOUT,
items: &items,
})
.map_err(|e| format!("island bootstrap install of the committed .bc failed: {}", e))?;
}
let module = crate::compile::driver::fresh_module_with_declarations("compiler_island", &items);
for item in &items {
let node_name = item.node_name();
crate::compile::driver::add_compiled_function(&interp, &mut heap, module.clone(), &node_name, &item.symbol_name())
.map_err(|e| format!("island compile of `{}` failed: {}", node_name, e))?;
}
let bitcode = {
let _guard = crate::compile::COMPILE_LOCK.lock().unwrap();
let bitcode = {
let m = module.borrow();
crate::compile::verify_module_naming_functions(&m, "island module").map(|()| {
m.write_bitcode_to_memory().as_slice().to_vec()
})
};
drop(module);
bitcode?
};
let state = typelisp_front::dump::capture_types_with_abi(
&heap,
delta,
"compiler island",
Some(typelisp_front::dump::source_digest(crate::compiler::SOURCE)),
Some(forms_digest),
&checked,
items.iter().map(crate::compile::prelude_bootstrap::unit_item).collect(),
Vec::new(),
crate::compile::EMITTED_BODY_ABI,
crate::compiler::SOURCE_EMITS_ABI,
crate::compile::EMITTED_LAYOUT,
crate::compiler::SOURCE_EMITS_LAYOUT,
)?;
let types = typelisp_front::dump::write_state(&state)?;
Ok(typelisp_front::dump::write(&[(types, bitcode)]))
}
pub fn island_source_hash(source: &str) -> Result<u64, String> {
let mut heap = Heap::with_capacity(1 << 18);
let reader = Reader::new();
let forms = reader
.read_all(&mut heap, source)
.map_err(|e| format!("island source hash: read failed: {}", e))?;
hash_read_forms(&heap, &forms)
}
pub fn hash_read_forms(heap: &Heap, forms: &[Value]) -> Result<u64, String> {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
forms.len().hash(&mut hasher);
for form in forms {
hash_form(heap, *form, &mut hasher)?;
}
Ok(hasher.finish())
}
fn hash_form(
heap: &Heap,
root: Value,
hasher: &mut std::collections::hash_map::DefaultHasher,
) -> Result<(), String> {
use std::hash::Hash;
let mut stack = vec![root];
while let Some(v) = stack.pop() {
match v {
Value::Empty => 0u8.hash(hasher),
Value::Int(n) => {
1u8.hash(hasher);
n.hash(hasher);
}
Value::Char(c) => {
2u8.hash(hasher);
c.hash(hasher);
}
Value::Bool(b) => {
3u8.hash(hasher);
b.hash(hasher);
}
Value::Symbol(id) => {
4u8.hash(hasher);
heap.symbol_name(id).hash(hasher);
}
Value::Str(id) => {
5u8.hash(hasher);
heap.string(id).hash(hasher);
}
Value::Path(id) => {
6u8.hash(hasher);
let segments = heap.path_segments(id);
segments.len().hash(hasher);
for seg in segments.iter() {
heap.symbol_name(*seg).hash(hasher);
}
}
Value::Cons(_) => {
7u8.hash(hasher);
let car = heap.car(v).map_err(|e| format!("island source hash: {}", e))?;
let cdr = heap.cdr(v).map_err(|e| format!("island source hash: {}", e))?;
stack.push(cdr);
stack.push(car);
}
Value::Boxed(id) if heap.is_f64(id) => {
8u8.hash(hasher);
heap.f64_value(id).to_bits().hash(hasher);
}
Value::Boxed(id) if heap.is_f32(id) => {
14u8.hash(hasher);
heap.f32_value(id).to_bits().hash(hasher);
}
Value::Boxed(id) if heap.is_bignum(id) => {
9u8.hash(hasher);
heap.bignum_value(id).hash(hasher);
}
Value::Boxed(id) if heap.is_ratio(id) => {
10u8.hash(hasher);
heap.ratio_value(id).hash(hasher);
}
Value::Boxed(_) => {
return Err("island source hash: the reader produced a boxed value this hash does not cover".to_string())
}
}
}
Ok(())
}
fn collect_island_items(heap: &Heap, tl: Value, out: &mut Vec<CompiledItem>) -> Result<(), String> {
let tag = core::op(heap, tl).map(str::to_string).unwrap_or_default();
match tag.as_str() {
"module" => {
for item in core::fields(heap, tl).map_err(|e| e.to_string())?.into_iter().skip(1) {
collect_island_items(heap, item, out)?;
}
}
"defun" => {
let path = core::path_field(heap, tl, 0)
.ok_or_else(|| "island: defun without a name".to_string())?;
out.push(CompiledItem::Fn(path));
}
"defmethod" => {
let type_path = core::path_field(heap, tl, 0)
.ok_or_else(|| "island: defmethod without a type".to_string())?;
let method = match core::field(heap, tl, 1) {
Some(Value::Symbol(id)) => heap.symbol_name(id).to_string(),
_ => return Err("island: defmethod without a name".to_string()),
};
out.push(CompiledItem::Method(type_path, method));
}
"defsignature" => {}
other => {
return Err(format!(
"island: the SOURCE grew a top-level `{}`, which this bootstrap does not know how to emit",
other
))
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::{collect_island_items, island_source_hash};
use crate::{Checker, Heap, Interp, Reader};
#[test]
fn a_generic_call_yields_both_the_caller_and_its_specialization() {
let mut heap = Heap::with_capacity(1 << 16);
let mut chk = Checker::new();
let mut interp = Interp::new();
crate::prelude::load_interpreted(&mut heap, &mut chk, &mut interp);
let reader = Reader::new();
let forms = reader
.read_all(&mut heap, "(defun island-probe ((e Option<Sexpr>)) Sexpr (unwrap (sexpr-car e)))")
.expect("read failed");
let mut items = Vec::new();
for v in forms {
let tl = chk.check_form(&mut heap, &interp, v).expect("check failed");
collect_island_items(&heap, tl, &mut items).expect("collection failed");
}
let names: Vec<String> = items.iter().map(|i| i.node_name()).collect();
assert!(names.iter().any(|n| n == "island-probe"), "the caller itself is missing: {:?}", names);
assert!(
names.len() > 1,
"the specialization `unwrap` was instantiated at must travel with its caller: {:?}",
names
);
}
#[test]
fn an_unknown_top_level_shape_is_refused_rather_than_skipped() {
let mut heap = Heap::with_capacity(1 << 16);
let mut chk = Checker::new();
let mut interp = Interp::new();
crate::prelude::load_interpreted(&mut heap, &mut chk, &mut interp);
let reader = Reader::new();
let forms = reader.read_all(&mut heap, "(defvar (island-probe-global i32) 1)").expect("read failed");
let tl = chk.check_form(&mut heap, &interp, forms[0]).expect("check failed");
let mut items = Vec::new();
let err = collect_island_items(&heap, tl, &mut items).expect_err("a defvar must be refused");
assert!(err.contains("defvar"), "the message should name the shape: {}", err);
}
fn hash(src: &str) -> u64 {
island_source_hash(src).expect("hashing failed")
}
#[test]
fn comments_and_layout_do_not_change_the_island_hash() {
let bare = "(defun f ((n i64)) i64 (+ n 1))";
let commented = r#"
;; A comment nobody should have to regenerate a 1 MB artifact to fix.
(defun f ((n i64)) i64
;; ...including one in the middle,
(+ n 1)) ;; and one at the end.
"#;
assert_eq!(hash(bare), hash(commented));
}
#[test]
fn a_changed_form_changes_the_island_hash() {
assert_ne!(hash("(defun f () i64 (+ 1 1))"), hash("(defun f () i64 (+ 1 2))"));
}
#[test]
fn a_semicolon_inside_a_string_is_not_a_comment() {
assert_ne!(hash(r#"(defun f () string (g ";" 1))"#), hash(r#"(defun f () string (g ";" 2))"#));
}
#[test]
fn source_positions_are_not_part_of_the_island_hash() {
assert_eq!(hash("(f 1)\n(g 2)"), hash("\n\n\n(f 1)\n\n\n\n(g 2)\n\n"));
}
#[test]
fn the_hash_follows_names_not_intern_ids() {
assert_ne!(hash("(alpha beta)"), hash("(beta alpha)"));
assert_eq!(hash("(alpha beta)"), hash("(alpha beta)"));
}
#[test]
fn a_symbol_and_a_string_with_the_same_text_differ() {
assert_ne!(hash("(f abc)"), hash(r#"(f "abc")"#));
}
}