You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor(java): dynamically match wasm-bindgen host functions by prefix (#96)
## Summary
- **WasmRuntime.java** now inspects the WASM module's import section at
startup and registers host function handlers by prefix pattern matching,
instead of hardcoding 9 function names with wasm-bindgen hash suffixes
- **HOST_FUNCTIONS.md** rewritten to document all 9 WASM imports across
3 modules, with dynamic matching examples for Java, Go, and JavaScript
## Motivation
wasm-bindgen generates function names with hash suffixes (e.g.,
`__wbg_getTime_ad1e9878a735af08`) that change whenever Rust dependencies
or wasm-bindgen versions update. This has broken CI in multiple PRs
(#64). By matching imports by prefix (`__wbg_getTime_*`) instead of
exact name, the Java integration survives dependency changes without
code updates.
## How it works
1. Load the WASM module via `CompiledEvaluator.load()`
2. Iterate `importSection().stream()` to discover all `FunctionImport`s
3. Match each import by module + name prefix to the appropriate handler
4. Register handlers dynamically in the Chicory `Store`
## Test plan
- [x] All 30 Java tests pass (`./mvnw test`)
- [x] WASM binary imports verified via `wasm-objdump`
- [x] No Rust code changes required
Closes#74
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The flagd-evaluator WASM module requires the host environment to provide certain functions. These are imported by the WASM module and must be implemented by the runtime (e.g., Java/Chicory, JavaScript, Go, etc.).
3
+
The flagd-evaluator WASM module requires the host environment to provide certain functions. These are imported by the WASM module and must be implemented by the runtime (e.g., Java/Chicory, Go/wazero, JavaScript).
|`__wbindgen_externref_xform__`|~2 (fixed names) | Names are stable but may appear/disappear |
12
14
13
-
Provides the current Unix timestamp (seconds since epoch: 1970-01-01 00:00:00 UTC) to the WASM module. This is used for context enrichment to populate the `$flagd.timestamp` property, which can be used in targeting rules.
15
+
**Important:** The `__wbindgen_placeholder__` function names include a hash suffix (e.g., `__wbg_getTime_ad1e9878a735af08`) that changes whenever Rust dependencies or wasm-bindgen versions change. Host implementations should **match by prefix**, not by exact name. See the [Dynamic Matching](#dynamic-matching-recommended) section.
14
16
15
-
### Why a Host Function?
17
+
##Stable Host Functions
16
18
17
-
The WASM sandbox cannot access system time directly without WASI support. Since Chicory and other pure WASM runtimes don't provide WASI, the host must supply the current time.
Provides the current Unix timestamp (seconds since epoch) for `$flagd.timestamp` context enrichment. The WASM sandbox cannot access system time without WASI, so the host must supply it.
24
24
25
-
## Implementation Examples
25
+
**Return value:** Unix timestamp in seconds (e.g., `1735689600` for 2025-01-01 00:00:00 UTC).
26
26
27
-
### Java (Chicory)
27
+
**If not provided:** The module defaults `$flagd.timestamp` to `0`. Time-based targeting won't work, but evaluation continues without errors.
28
28
29
-
```java
30
-
importcom.dylibso.chicory.runtime.HostFunction;
31
-
importcom.dylibso.chicory.wasm.types.Value;
32
-
importcom.dylibso.chicory.wasm.types.ValueType;
33
-
34
-
// Create the host function
35
-
HostFunction getCurrentTime =newHostFunction(
36
-
"host", // Module name
37
-
"get_current_time_unix_seconds", // Function name
38
-
List.of(), // No parameters
39
-
List.of(ValueType.I64), // Returns i64
40
-
(Instance instance, Value... args) -> {
41
-
long currentTimeSeconds =System.currentTimeMillis() /1000;
These imports come from Rust dependencies (chrono, getrandom) using wasm-bindgen. Their names contain hashes that change across builds. Match by prefix.
int configPtr = alloc.apply(Value.i32(configBytes.length))[0].asInt();
115
-
instance.memory().write(configPtr, configBytes);
39
+
The first argument is an externref index (can be ignored). The second argument is a pointer to a 32-byte buffer in WASM memory. Fill the buffer with random bytes.
**Purpose:** Set externref table entry to null. No-op.
82
+
83
+
## Dynamic Matching (Recommended)
84
+
85
+
Instead of hardcoding exact function names, inspect the WASM module's import section at startup and match by prefix. This way, hash changes from Rust dependency updates don't require host code changes.
0 commit comments