1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#![recursion_limit = "256"]
//! Regression tests for the synchronous-builtin fast path on `Op::CallBuiltin`.
//!
//! `execute_call_builtin_sync` dispatches synchronous builtins directly instead
//! of bailing to the async handler (which would re-run `resolve_named_closure`
//! and spin up the async state machine). These tests pin the *semantics* that
//! optimization must preserve:
//! - a bare builtin call still produces the builtin's result,
//! - a user `fn` that shadows a builtin name still wins over the builtin,
//! - builtin calls keep working inside loops / collection callbacks,
//! - builtin argument validation still fires on the fast path.
use harn_vm::value::{VmError, VmValue};
fn eval(source: &str) -> Result<VmValue, String> {
harn_vm::reset_thread_local_state();
let chunk = harn_vm::compile_source(source)?;
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.map_err(|e| e.to_string())?;
rt.block_on(async {
let local = tokio::task::LocalSet::new();
local
.run_until(async {
let mut vm = harn_vm::Vm::new();
harn_vm::register_vm_stdlib(&mut vm);
vm.execute(&chunk)
.await
.map_err(|e: VmError| format!("{e:?}"))
})
.await
})
}
#[test]
fn sync_builtin_call_dispatches_inline() {
// `abs` is a synchronous builtin: the fast path must return its result.
assert!(matches!(
eval("pipeline t(task) { return abs(-5) }"),
Ok(VmValue::Int(5))
));
}
#[test]
fn user_fn_shadowing_a_builtin_name_still_wins() {
// The sync fast path is only reached *after* `resolve_named_closure`
// confirms the name is not a user closure. A module-level `fn` named like a
// builtin must therefore still take precedence over the builtin.
let result = eval(
r"pipeline t(task) {
fn abs(x) { return 999 }
return abs(-5)
}",
);
assert!(
matches!(result, Ok(VmValue::Int(999))),
"user fn must shadow the builtin, got {result:?}"
);
}
#[test]
fn builtin_calls_work_inside_a_loop() {
let result = eval(
r"pipeline t(task) {
var s = 0
for x in [-1, -2, 3] {
s = s + abs(x)
}
return s
}",
);
assert!(
matches!(result, Ok(VmValue::Int(6))),
"expected sum of abs values, got {result:?}"
);
}
#[test]
fn builtin_argument_validation_still_fires_on_fast_path() {
// Calling a numeric builtin with a wrong-typed argument must still error
// (validation runs inside the sync dispatch, not only on the async path).
let result = eval(r#"pipeline t(task) { return abs("not a number") }"#);
assert!(
result.is_err(),
"expected a validation error for abs(string), got {result:?}"
);
}