Skip to main content

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}