jlens_core/stack.rs
1//! Stack headroom checks, so that deep recursion fails with `D1011` instead of overflowing the
2//! stack.
3//!
4//! On native targets the evaluator grows the stack on demand ([`stacker`]). WebAssembly has a
5//! fixed stack, and an overflow there traps and leaves the module instance unusable, so the
6//! recursive parts of jlens stop while some headroom is left.
7//!
8//! Crates that walk syntax trees recursively (such as `jlens-schema`) use the same helpers.
9
10/// Stack bytes that must remain free when entering a recursive step.
11#[cfg(target_arch = "wasm32")]
12const RED_ZONE: usize = 128 * 1024;
13
14/// Whether the stack is close to exhaustion. Only meaningful on WebAssembly; native targets
15/// grow the stack instead and always return `false`.
16#[inline]
17pub fn exhausted() -> bool {
18 #[cfg(target_arch = "wasm32")]
19 {
20 // rustc links wasm32 modules with `--stack-first`: the stack occupies the start of
21 // linear memory and grows down towards address 0, so the address of a local is the
22 // space left.
23 let marker = 0u8;
24 (std::hint::black_box(&marker) as *const u8 as usize) < RED_ZONE
25 }
26 #[cfg(not(target_arch = "wasm32"))]
27 {
28 false
29 }
30}
31
32/// Runs `f`, first growing the stack if little of it is left (native targets only).
33#[inline]
34pub fn grow<R>(f: impl FnOnce() -> R) -> R {
35 #[cfg(not(target_arch = "wasm32"))]
36 {
37 stacker::maybe_grow(128 * 1024, 4 * 1024 * 1024, f)
38 }
39 #[cfg(target_arch = "wasm32")]
40 {
41 f()
42 }
43}