thalir-core 0.1.0

Core IR types and builders for ThalIR smart contract analysis
Documentation
use crate::block::BlockId;
use crate::builder::{FunctionCursor, IRBuilder, IRContext};
use crate::types::Type;

#[test]
fn test_builder_creation() {
    let builder = IRBuilder::new();

    let stats = builder.stats();
    assert_eq!(stats.contracts, 0);
    assert_eq!(stats.functions, 0);
    assert_eq!(stats.blocks, 0);
    assert_eq!(stats.instructions, 0);
    assert_eq!(stats.values, 0);
}

#[test]
fn test_context_tracking() {
    let mut builder = IRBuilder::new();
    let context = builder.context();

    assert!(context.current_contract().is_none());
    assert!(context.current_function().is_none());
    assert!(context.current_block().is_none());

    let mut contract = builder.contract("TestContract");

    let mut func = contract.function("testFunc");

    let entry = func.entry_block();
}

#[test]
fn test_registry_operations() {
    let mut builder = IRBuilder::new();
    let mut contract = builder.contract("RegistryTest");

    let mut func = contract.function("test");
    func.returns(Type::Bool);

    let mut entry = func.entry_block();
    let true_val = entry.constant_bool(true);
    entry.return_value(true_val);

    func.build().unwrap();
    contract.build().unwrap();

    let registry = builder.registry();

    let contract = registry.get_contract("RegistryTest").unwrap();
    assert_eq!(contract.name, "RegistryTest");

    let function = registry
        .get_function_in_contract("RegistryTest", "test")
        .unwrap();
    assert_eq!(function.signature.name, "test");

    let qualified_func = registry.get_function("RegistryTest::test").unwrap();
    assert_eq!(qualified_func.signature.name, "test");
}

#[test]
fn test_cursor_navigation() {
    let mut builder = IRBuilder::new();
    let mut contract = builder.contract("CursorTest");
    let mut func = contract.function("test");

    let block1_id = func.create_block_id();
    let block2_id = func.create_block_id();
    let entry_id = BlockId(0);

    {
        let entry = func.entry_block();
    }

    let mut blocks = std::collections::HashMap::new();
    blocks.insert(entry_id, crate::block::BasicBlock::new(entry_id));
    blocks.insert(block1_id, crate::block::BasicBlock::new(block1_id));
    blocks.insert(block2_id, crate::block::BasicBlock::new(block2_id));

    let mut context = IRContext::new();
    let mut cursor = FunctionCursor::new(&mut context, &mut blocks);

    cursor.goto_block_start(entry_id);
    assert_eq!(cursor.current_block(), Some(entry_id));

    cursor.goto_block_end(block1_id);
    assert_eq!(cursor.current_block(), Some(block1_id));

    let new_block_id = cursor.create_block("new_block".to_string());
    assert_eq!(cursor.current_block(), Some(new_block_id));
}

#[test]
fn test_ssa_tracking() {
    let mut context = IRContext::new();
    let ssa = context.ssa();

    let var1 = ssa.get_or_create_var("x");
    let var2 = ssa.get_or_create_var("y");
    let var1_again = ssa.get_or_create_var("x");

    assert_eq!(var1, var1_again);
    assert_ne!(var1, var2);

    let temp1 = ssa.new_temp();
    let temp2 = ssa.new_temp();

    assert_ne!(temp1, temp2);
}

#[test]
fn test_builder_validation() {
    let mut builder = IRBuilder::new();

    let mut contract = builder.contract("ValidContract");
    let mut func = contract.function("valid");
    let mut entry = func.entry_block();
    entry.return_void().unwrap();
    func.build().unwrap();
    contract.build().unwrap();

    assert!(builder.validate().is_ok());

    let stats = builder.stats();
    assert_eq!(stats.contracts, 1);
    assert_eq!(stats.functions, 1);
    assert!(stats.blocks > 0);
}

#[test]
fn test_builder_clear() {
    let mut builder = IRBuilder::new();

    let mut contract = builder.contract("TempContract");
    let mut func = contract.function("temp");
    let entry = func.entry_block();
    func.build().unwrap();
    contract.build().unwrap();

    let stats = builder.stats();
    assert!(stats.contracts > 0);

    builder.clear();

    let stats = builder.stats();
    assert_eq!(stats.contracts, 0);
    assert_eq!(stats.functions, 0);
    assert_eq!(stats.blocks, 0);
}

#[test]
fn test_multiple_contracts() {
    let mut builder = IRBuilder::new();

    let mut contract1 = builder.contract("Contract1");
    let mut func1 = contract1.function("func1");
    func1.entry_block().return_void().unwrap();
    func1.build().unwrap();
    contract1.build().unwrap();

    let mut contract2 = builder.contract("Contract2");
    let mut func2 = contract2.function("func2");
    func2.entry_block().return_void().unwrap();
    func2.build().unwrap();
    contract2.build().unwrap();

    let registry = builder.registry();
    assert!(registry.get_contract("Contract1").is_some());
    assert!(registry.get_contract("Contract2").is_some());

    assert!(registry.get_function("Contract1::func1").is_some());
    assert!(registry.get_function("Contract2::func2").is_some());
    assert!(registry.get_function("Contract1::func2").is_none());
}