#![cfg(all(feature = "compile", feature = "verify"))]
use keleusma::bytecode::{Module, Op, StructField};
use keleusma::compiler::compile;
use keleusma::lexer::tokenize;
use keleusma::parser::parse;
use keleusma::verify::verify;
fn compile_module(src: &str) -> Module {
compile(&parse(&tokenize(src).expect("lex")).expect("parse")).expect("compile")
}
fn rejected_by_typed_pass(m: &Module) -> bool {
match verify(m) {
Ok(()) => false,
Err(e) => e.message.contains("typed operand-stack"),
}
}
#[test]
fn b1_b2_flat_field_offset_overrun_rejected() {
let src = "struct P { x: Word, y: Word }\n\
fn main() -> Word { let p = P { x: 1, y: 2 }; p.x }";
let mut m = compile_module(src);
assert!(verify(&m).is_ok(), "baseline program must verify");
let mut mutated = false;
for chunk in &mut m.chunks {
for op in &mut chunk.ops {
if let Op::GetField(StructField::Flat { offset, .. }) = op {
*offset = 4096; mutated = true;
}
}
}
assert!(mutated, "expected a flat struct-field access to mutate");
assert!(
rejected_by_typed_pass(&m),
"an out-of-bounds flat field offset must be rejected by the typed pass"
);
}
#[cfg(not(any(feature = "narrow-word-8", feature = "narrow-address-8")))]
#[test]
fn c2_flat_text_field_offset_overrun_rejected() {
let src = "struct W { s: Text, n: Word }\n\
fn main() -> Text { let w = W { s: \"hi\", n: 5 }; w.s }";
let mut m = compile_module(src);
assert!(verify(&m).is_ok(), "baseline program must verify");
let mut mutated = false;
for chunk in &mut m.chunks {
for op in &mut chunk.ops {
if let Op::GetField(StructField::Flat { offset, .. }) = op {
*offset = 40000; mutated = true;
}
}
}
assert!(mutated, "expected a flat Text field access to mutate");
assert!(
rejected_by_typed_pass(&m),
"an out-of-bounds flat Text field offset must be rejected by the typed pass"
);
}
#[test]
fn b6_shared_slot_offset_overrun_rejected() {
let src = "data state { a: Word, b: Word }\n\
fn main() -> Word { state.a }";
let mut m = compile_module(src);
assert!(verify(&m).is_ok(), "baseline program must verify");
let buffer = m.shared_data_bytes;
let layout = m.data_layout.as_mut().expect("a shared data layout");
assert!(
!layout.shared_layout.is_empty(),
"expected a shared-slot layout entry"
);
layout.shared_layout[0].offset = (buffer + 8) as u16;
assert!(
rejected_by_typed_pass(&m),
"a shared-slot offset past the buffer must be rejected by the typed pass"
);
}
#[test]
fn b8_enum_min_payload_mutation_rejected() {
let src = "enum E { A(Word), B }\n\
fn main() -> Word { match E::A(7) { E::A(v) => v, E::B => 0 } }";
let mut m = compile_module(src);
assert!(verify(&m).is_ok(), "baseline program must verify");
assert!(!m.enum_layouts.is_empty(), "expected an enum-layout entry");
m.enum_layouts[0].min_payload += 8;
assert!(
rejected_by_typed_pass(&m),
"a mutated enum min_payload must be rejected by the typed pass"
);
}
#[test]
fn finding3_loop_stack_growth_rejected() {
let src = "fn main() -> Word { for i in 0..4 { i } 0 }";
let mut m = compile_module(src);
assert!(verify(&m).is_ok(), "baseline program must verify");
let mut mutated = false;
'chunks: for chunk in &mut m.chunks {
let mut depth = 0i32;
for op in &mut chunk.ops {
match op {
Op::Loop(_) => depth += 1,
Op::EndLoop(_) => depth -= 1,
Op::PopN(n) if depth > 0 && *n > 0 => {
*n = 0;
mutated = true;
break 'chunks;
}
_ => {}
}
}
}
assert!(mutated, "expected a PopN inside a loop body to neutralize");
assert!(
rejected_by_typed_pass(&m),
"a non-neutral loop body must be rejected by the typed pass"
);
}