use std::collections::HashMap;
use std::sync::Arc;
use relon_codegen_cranelift::{AotEvaluator, CapabilityVtable, HostFnPtr, SandboxConfig};
use relon_codegen_llvm::LlvmAotEvaluator;
use relon_eval_api::{Evaluator, RuntimeError, Value};
use relon_ir::ir::{Func, IrType, Module as IrModule, Op, TaggedOp, TrapKind, NO_CAPABILITY_BIT};
use relon_parser::TokenRange;
fn tagged(op: Op) -> TaggedOp {
TaggedOp {
op,
range: TokenRange::default(),
}
}
fn build_legacy_trap_ir(kind: TrapKind) -> IrModule {
IrModule {
imports: vec![],
funcs: vec![Func {
name: "run_main".to_string(),
params: vec![IrType::I64],
ret: IrType::I64,
body: vec![
tagged(Op::Trap { kind }),
tagged(Op::ConstI64(0)),
tagged(Op::Return),
],
range: TokenRange::default(),
}],
entry_func_index: Some(0),
closure_table: vec![],
}
}
fn build_legacy_checkcap_ir(cap_bit: u32) -> IrModule {
IrModule {
imports: vec![],
funcs: vec![Func {
name: "run_main".to_string(),
params: vec![IrType::I64],
ret: IrType::I64,
body: vec![
tagged(Op::CheckCap { cap_bit }),
tagged(Op::LocalGet(0)),
tagged(Op::Return),
],
range: TokenRange::default(),
}],
entry_func_index: Some(0),
closure_table: vec![],
}
}
#[test]
fn llvm_trap_emits_llvm_trap() {
let ir = build_legacy_trap_ir(TrapKind::IndexOutOfBounds);
let ev = LlvmAotEvaluator::from_ir_direct(ir, vec!["x".into()]).expect("llvm compile");
let dump = ev.emit_ir_dump();
assert!(
dump.contains("llvm.trap"),
"Op::Trap IR dump missing llvm.trap:\n{dump}"
);
}
#[test]
fn llvm_checkcap_rejected_on_legacy_entry() {
let ir = build_legacy_checkcap_ir(2);
let msg = match LlvmAotEvaluator::from_ir_direct(ir, vec!["x".into()]) {
Ok(_) => panic!("CheckCap on legacy entry must fail to build"),
Err(e) => e.to_string(),
};
assert!(
msg.contains("CheckCap") && msg.contains("buffer-protocol"),
"unexpected error for legacy CheckCap: {msg}"
);
}
unsafe extern "C" fn now_stub(_arg: i64) -> i64 {
1_700_000_000
}
fn build_cranelift_checkcap_ir(cap_bit: u32) -> IrModule {
IrModule {
imports: vec![],
funcs: vec![Func {
name: "run_main".to_string(),
params: vec![IrType::I64],
ret: IrType::I64,
body: vec![
tagged(Op::CheckCap { cap_bit }),
tagged(Op::LocalGet(0)),
tagged(Op::Return),
],
range: TokenRange::default(),
}],
entry_func_index: Some(0),
closure_table: vec![],
}
}
#[test]
fn cranelift_checkcap_denies_when_bit_ungranted() {
let ir = build_cranelift_checkcap_ir(2);
let ev = AotEvaluator::from_ir_direct(ir, SandboxConfig::default(), vec!["x".into()])
.expect("cranelift compile");
let mut args = HashMap::new();
args.insert("x".to_string(), Value::Int(99));
let err = ev.run_main(args).expect_err("ungranted bit must deny");
assert!(
matches!(err, RuntimeError::CapabilityDenied { .. }),
"expected CapabilityDenied, got {err:?}"
);
}
#[test]
fn cranelift_checkcap_grants_when_bit_registered() {
let ir = build_cranelift_checkcap_ir(2);
let mut ev = AotEvaluator::from_ir_direct(ir, SandboxConfig::default(), vec!["x".into()])
.expect("cranelift compile");
let mut vt = CapabilityVtable::with_capacity(64);
let fn_ptr: HostFnPtr = now_stub;
vt.register(2, fn_ptr);
ev.install_capabilities_mut(Arc::new(vt));
let mut args = HashMap::new();
args.insert("x".to_string(), Value::Int(99));
let result = ev.run_main(args).expect("granted bit must run the body");
assert_eq!(result, Value::Int(99));
}
#[test]
fn cranelift_checkcap_no_capability_bit_elides_gate() {
let ir = build_cranelift_checkcap_ir(NO_CAPABILITY_BIT);
let ev = AotEvaluator::from_ir_direct(ir, SandboxConfig::default(), vec!["x".into()])
.expect("cranelift compile");
let mut args = HashMap::new();
args.insert("x".to_string(), Value::Int(99));
let result = ev.run_main(args).expect("sentinel must elide the gate");
assert_eq!(result, Value::Int(99));
}