When evaluating flags with context in Chicory (Java WASM runtime), the WASM module was crashing with:
com.dylibso.chicory.runtime.TrapException: Trapped on unreachable instruction
This only occurred when providing evaluation context like:
{
"sources": "flags/allFlags.json,rawflags/selector-flags.json",
"email": "ballmer@macrosoft.com",
"injectedmetadata": "set"
}The panic was caused by std::time::SystemTime::now() in the context enrichment function (evaluation.rs:231).
In WASM without WASI support (like Chicory), SystemTime::now() panics instead of returning an error. Since this project uses panic = "abort" (Cargo.toml:55), the panic translates to the WASM unreachable instruction, causing Chicory to throw a TrapException.
The code flow was:
- Call
evaluate()with context - Enter
enrich_context()to add$flagd.timestamp - Call
SystemTime::now()→ PANIC - Panic →
unreachable→ Chicory TrapException
Instead of relying on WASM's unavailable system time, we now use a host function that the WASM runtime must provide.
-
Added host function import (lib.rs:44-58):
#[cfg(target_family = "wasm")] #[link(wasm_import_module = "host")] extern "C" { fn host_get_current_time() -> u64; }
-
Created wrapper function (lib.rs:84-99):
- In WASM: Calls host function with panic catch (defaults to 0)
- In native code: Uses
SystemTime::now()for tests/CLI
-
Updated context enrichment (evaluation.rs:228-230):
let timestamp = crate::get_current_time();
Verified with wasm-objdump:
Import[9]:
- func[0] <host.get_current_time_unix_seconds> <- host.get_current_time_unix_seconds
You must provide the host function when loading the WASM module:
import com.dylibso.chicory.runtime.HostFunction;
import com.dylibso.chicory.wasm.types.Value;
import com.dylibso.chicory.wasm.types.ValueType;
import java.util.List;
// Define the host function
HostFunction getCurrentTime = new HostFunction(
"host", // Module name (must match WASM import)
"get_current_time_unix_seconds", // Function name (must match WASM import)
List.of(), // No parameters
List.of(ValueType.I64), // Returns i64
(Instance instance, Value... args) -> {
// Return current Unix timestamp in seconds
long currentTimeSeconds = System.currentTimeMillis() / 1000;
return new Value[] { Value.i64(currentTimeSeconds) };
}
);
// Add to module when instantiating
Module module = Module.builder(wasmBytes)
.withHostFunction(getCurrentTime)
.build();
Instance instance = module.instantiate();See HOST_FUNCTIONS.md for a complete Java example showing:
- Loading the WASM module with host function
- Updating flag state
- Evaluating flags with context
- Memory management (alloc/dealloc)
-
Rebuild the WASM module:
cargo build --target wasm32-unknown-unknown --no-default-features --release --lib
-
Update your Java code to provide the host function (see above)
-
The evaluation should now work with context:
String context = "{\"email\":\"ballmer@macrosoft.com\"}"; // Should now evaluate successfully without TrapException
-
Verify
$flagd.timestampis available in targeting rules:{ "targeting": { "if": [ {">": [{"var": "$flagd.timestamp"}, 1700000000]}, "variant1", "variant2" ] } }
If the host function is not provided:
- The WASM module catches the panic and defaults
$flagd.timestampto0 - Evaluation continues without errors
- Time-based targeting rules will not work correctly
- HOST_FUNCTIONS.md - Complete implementation guide for all languages
- README.md - Updated with host function requirements
- src/lib.rs - Host function import and wrapper
- src/evaluation.rs - Context enrichment using host function
All time-sensitive operations in WASM should:
- Use host functions instead of
std::time::SystemTime - Wrap in
catch_unwindif panics are possible - Provide sensible defaults when unavailable
This pattern should be used for any other system-dependent functionality in WASM.