1
//! Shared helpers for the categorized `codegen::*` integration tests.
2
//!
3
//! Every codegen test follows the same pattern: compile a script via
4
//! the public `Compiler`, validate the emitted wasm with `wasmparser`,
5
//! and use real production patterns (host-fn calls inside a `let*`) to
6
//! introduce runtime-typed locals. The legacy approach of injecting
7
//! `Symbol::new(...).with_value(Expr::WasmRuntime(_))` into the symbol
8
//! table is gone — it bypassed the binder-promotion contract that
9
//! `compile_for_stack(WasmRuntime)` now refuses.
10

            
11
#![allow(dead_code)]
12

            
13
use nomiscript::{Compiler, Reader, SymbolTable};
14
use wasmparser::{Validator, WasmFeatures};
15

            
16
/// Validate emitted wasm under the GC + function-references feature
17
/// set the compiler targets. Catches missing-operand bugs, malformed
18
/// type sections, and ref.cast errors that the legacy "is the magic
19
/// header correct" check missed.
20
418
pub fn validate_wasm(wasm: &[u8]) {
21
418
    let features = WasmFeatures::default()
22
418
        | WasmFeatures::GC
23
418
        | WasmFeatures::REFERENCE_TYPES
24
418
        | WasmFeatures::FUNCTION_REFERENCES
25
418
        | WasmFeatures::EXCEPTIONS;
26
418
    Validator::new_with_features(features)
27
418
        .validate_all(wasm)
28
418
        .expect("wasm validation failed");
29
418
}
30

            
31
/// Compile a script in the wasm-builtins symbol table and assert the
32
/// result validates. Returns the bytes for callers that want to inspect
33
/// them further.
34
418
pub fn compile_and_validate(src: &str) -> Vec<u8> {
35
418
    let program = Reader::parse(src).unwrap_or_else(|e| panic!("parse {src:?}: {e}"));
36
418
    let mut symbols = SymbolTable::with_builtins_for_wasm();
37
418
    let mut compiler = Compiler::new();
38
418
    let wasm = compiler
39
418
        .compile(&program, &mut symbols)
40
418
        .unwrap_or_else(|e| panic!("compile {src:?}: {e}"));
41
418
    validate_wasm(&wasm);
42
418
    wasm
43
418
}
44

            
45
/// Compile a script and expect a compile error. Returns the error
46
/// message for callers that want to assert on its contents.
47
110
pub fn compile_expect_error(src: &str) -> String {
48
110
    let program = Reader::parse(src).unwrap_or_else(|e| panic!("parse {src:?}: {e}"));
49
110
    let mut symbols = SymbolTable::with_builtins_for_wasm();
50
110
    let mut compiler = Compiler::new();
51
110
    match compiler.compile(&program, &mut symbols) {
52
        Ok(_) => panic!("expected compile error for {src:?}"),
53
110
        Err(e) => e.to_string(),
54
    }
55
110
}
56

            
57
/// Wraps a script body in a `let*` that introduces a runtime-typed
58
/// `X` of `WasmType::Ratio` via the entity accessor host fn. Use this
59
/// when a test wants to exercise the runtime-arithmetic / runtime-
60
/// control-flow codegen path: any `X` inside the body resolves to a
61
/// real `WasmLocal(idx, Ratio)` and the wasm has a producer for it.
62
78
pub fn wrap_with_runtime_ratio(body: &str) -> String {
63
78
    format!("(let* ((X (transaction-post-date 0))) {body})")
64
78
}
65

            
66
/// Same as [`wrap_with_runtime_ratio`] but binds `IDX` to a runtime
67
/// `WasmType::I32`. Uses `(entity-count)` as the producer. The name
68
/// `IDX` matches the entity-accessor host fn convention (`(entity-type
69
/// IDX)` etc.) so test bodies read naturally.
70
33
pub fn wrap_with_runtime_i32(body: &str) -> String {
71
33
    format!("(let* ((IDX (entity-count))) {body})")
72
33
}
73

            
74
/// Combines [`wrap_with_runtime_ratio`] for a value `X` and
75
/// [`wrap_with_runtime_i32`] for a value `IDX`. Useful when a test
76
/// needs both a runtime ratio and a runtime entity index in scope.
77
pub fn wrap_with_runtime_ratio_and_idx(body: &str) -> String {
78
    format!("(let* ((X (transaction-post-date 0)) (IDX (entity-count))) {body})")
79
}