create
* when actually needed (after the class presence check).
*/
private static final class CompilerBridge {
- static com.dylibso.chicory.runtime.Instance.Builder createBuilder(WasmModule module) {
- return com.dylibso.chicory.runtime.Instance.builder(module)
+ static run.endive.runtime.Instance.Builder createBuilder(WasmModule module) {
+ return run.endive.runtime.Instance.builder(module)
.withMachineFactory(
- com.dylibso.chicory.compiler.MachineFactoryCompiler::compile);
+ run.endive.compiler.MachineFactoryCompiler::compile);
}
}
}
diff --git a/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveComponentAdapter.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveComponentAdapter.java
new file mode 100644
index 0000000..80c490e
--- /dev/null
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveComponentAdapter.java
@@ -0,0 +1,203 @@
+package ai.tegmentum.webassembly4j.provider.endive;
+
+import ai.tegmentum.webassembly4j.api.Component;
+import ai.tegmentum.webassembly4j.api.ComponentInstance;
+import ai.tegmentum.webassembly4j.api.LinkingContext;
+import ai.tegmentum.webassembly4j.api.WitHostFunctionDefinition;
+import ai.tegmentum.webassembly4j.api.exception.InstantiationException;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import run.tegmentum.wasmcm.endive.HostCallbackDispatcher;
+import run.tegmentum.wasmcm.endive.HostCallbackException;
+import run.tegmentum.wasmcm.endive.WasmcmRuntimeGuestLoader;
+
+/**
+ * SPI-facing {@link Component} that layers the Component Model on top of the
+ * unmodified Endive core-Wasm engine via the {@code wasmcm_runtime_guest.wasm}
+ * portable Rust runtime.
+ *
+ * The heavy lifting happens in {@link WasmcmRuntimeGuestLoader}: the CM
+ * component bytes have already been parsed through the guest by the time this
+ * adapter is constructed (see {@link EndiveEngineAdapter#loadComponent(byte[])})
+ * — this class just adapts the {@link Component#instantiate(LinkingContext)}
+ * surface onto {@code wasmcm_instantiate_v2} /
+ * {@code wasmcm_instantiate_with_imports_v2} and hands back an
+ * {@link EndiveComponentInstanceAdapter}.
+ *
+ *
Linking context translation
+ *
+ * {@link LinkingContext#witHostFunctions()} entries are grouped by their
+ * WIT-path interface prefix (everything before the {@code '#'}), each group
+ * registered as a host provider via
+ * {@link WasmcmRuntimeGuestLoader#registerHostProvider(String, int)}. A single
+ * {@link HostCallbackDispatcher} per provider routes the guest's
+ * {@code wasmcm_host_call} extern into the caller's Java implementations by
+ * function index.
+ *
+ *
Function-index order matches the order in which the caller added the
+ * {@link WitHostFunctionDefinition}s to the linking context — the WIT interface
+ * is not read back from the component here, so the caller controls the mapping.
+ * This is stable and deterministic; callers building against a specific WIT
+ * world should add their host functions in the WIT interface's declared order.
+ *
+ *
Note on runtime-guest gaps at this milestone
+ *
+ * Registering a host provider only records intent inside the runtime guest;
+ * the Wall 2 chain that routes a {@code canon lower} against a component-level
+ * import through {@code wasmcm_host_call} is a work in progress on the wasm-cm
+ * Rust side (see {@code HostCallbackDispatcher} javadoc for the specific gap).
+ * When a component that imports interfaces the runtime cannot yet route is
+ * instantiated, {@code wasmcm_instantiate_with_imports_v2} surfaces
+ * {@code HOST_ERR_UNLINKABLE} — this adapter re-throws as
+ * {@link InstantiationException} with the guest's diagnostic message, matching
+ * the "no fake success" contract.
+ */
+final class EndiveComponentAdapter implements Component {
+
+ private final WasmcmRuntimeGuestLoader loader;
+ private final long componentHandle;
+ private final boolean ownsLoader;
+ private volatile boolean closed;
+
+ EndiveComponentAdapter(
+ WasmcmRuntimeGuestLoader loader, long componentHandle, boolean ownsLoader) {
+ this.loader = loader;
+ this.componentHandle = componentHandle;
+ this.ownsLoader = ownsLoader;
+ }
+
+ @Override
+ public ComponentInstance instantiate() {
+ return instantiate((LinkingContext) null);
+ }
+
+ @Override
+ public ComponentInstance instantiate(LinkingContext linkingContext) {
+ checkOpen();
+ try {
+ long instanceHandle;
+ if (linkingContext == null) {
+ instanceHandle = loader.instantiate(componentHandle);
+ } else {
+ Map imports = registerImports(linkingContext);
+ if (imports.isEmpty()) {
+ instanceHandle = loader.instantiate(componentHandle);
+ } else {
+ instanceHandle = loader.instantiateWithImports(componentHandle, imports);
+ }
+ }
+ return new EndiveComponentInstanceAdapter(loader, instanceHandle);
+ } catch (IllegalStateException e) {
+ throw new InstantiationException(
+ "Failed to instantiate Component Model component via wasmcm runtime guest: "
+ + e.getMessage(),
+ e);
+ }
+ }
+
+ /**
+ * Bind each unique WIT-path interface prefix declared by the linking
+ * context's {@link LinkingContext#witHostFunctions()} to a provider handle
+ * and route the guest's {@code wasmcm_host_call} extern to the caller's Java
+ * implementations. Function index within a provider is the caller's
+ * insertion order.
+ */
+ private Map registerImports(LinkingContext linkingContext) {
+ List defs = linkingContext.witHostFunctions();
+ // Group by interface prefix (the segment before the final '#').
+ Map> byInterface = new LinkedHashMap<>();
+ for (WitHostFunctionDefinition def : defs) {
+ String iface = interfacePrefix(def.witPath());
+ byInterface.computeIfAbsent(iface, k -> new java.util.ArrayList<>()).add(def);
+ }
+ Map providers = new LinkedHashMap<>();
+ for (Map.Entry> e : byInterface.entrySet()) {
+ String iface = e.getKey();
+ java.util.List funcs = e.getValue();
+ long providerHandle = loader.registerHostProvider(iface, funcs.size());
+ loader.registerHostCallback(providerHandle, new WitDispatcher(funcs));
+ providers.put(iface, providerHandle);
+ }
+ return providers;
+ }
+
+ private static String interfacePrefix(String witPath) {
+ int hash = witPath.indexOf('#');
+ return hash < 0 ? "" : witPath.substring(0, hash);
+ }
+
+ /**
+ * Routes a {@code wasmcm_host_call} for a given interface into the WIT host
+ * function at the guest-supplied {@code funcIdx}. Argument / result codec
+ * matches {@code wasmcm_runtime_guest::codec}.
+ */
+ private static final class WitDispatcher implements HostCallbackDispatcher {
+
+ private final List funcs;
+
+ WitDispatcher(List funcs) {
+ this.funcs = Collections.unmodifiableList(new java.util.ArrayList<>(funcs));
+ }
+
+ @Override
+ public byte[] dispatch(int funcIdx, byte[] argFrame) throws HostCallbackException {
+ if (funcIdx < 0 || funcIdx >= funcs.size()) {
+ throw new HostCallbackException(
+ "no WIT host function registered at index " + funcIdx
+ + " (interface has " + funcs.size() + " slots)");
+ }
+ WitHostFunctionDefinition def = funcs.get(funcIdx);
+ List decoded = WasmcmValueCodec.decodeFrame(argFrame);
+ Object[] argsArr = decoded.toArray(new Object[0]);
+ Object[] results;
+ try {
+ results = def.function().execute(argsArr);
+ } catch (RuntimeException e) {
+ throw new HostCallbackException(
+ "WIT host function '" + def.witPath() + "' threw: " + e.getMessage());
+ }
+ if (results == null) {
+ results = new Object[0];
+ }
+ return WasmcmValueCodec.encodeFrame(results);
+ }
+ }
+
+ @Override
+ public List exportedInterfaces() {
+ // The runtime guest surfaces exports only after instantiation (they are
+ // properties of the instance, not the parsed component handle at this
+ // milestone); return empty at the pre-instantiation Component level so
+ // callers know to instantiate first.
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List importedInterfaces() {
+ return Collections.emptyList();
+ }
+
+ private void checkOpen() {
+ if (closed) {
+ throw new IllegalStateException("EndiveComponentAdapter already closed");
+ }
+ }
+
+ @Override
+ public void close() {
+ if (closed) {
+ return;
+ }
+ closed = true;
+ if (ownsLoader) {
+ // The loader owns an Endive Instance running the runtime guest;
+ // Endive Instances currently do not expose a close() (they release
+ // when GC'd), so the ownership boundary is intentionally soft here.
+ // Left as a no-op — the JVM will collect the companion instance
+ // with the loader.
+ }
+ }
+}
diff --git a/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveComponentInstanceAdapter.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveComponentInstanceAdapter.java
new file mode 100644
index 0000000..71d66e2
--- /dev/null
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveComponentInstanceAdapter.java
@@ -0,0 +1,177 @@
+package ai.tegmentum.webassembly4j.provider.endive;
+
+import ai.tegmentum.webassembly4j.api.ComponentInstance;
+import ai.tegmentum.webassembly4j.api.Function;
+import ai.tegmentum.webassembly4j.api.Global;
+import ai.tegmentum.webassembly4j.api.Memory;
+import ai.tegmentum.webassembly4j.api.Table;
+import ai.tegmentum.webassembly4j.api.exception.ExecutionException;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import run.endive.runtime.ExportFunction;
+import run.tegmentum.wasmcm.endive.WasmcmRuntimeGuestLoader;
+
+/**
+ * SPI-facing {@link ComponentInstance} whose entire concrete implementation is
+ * layered on top of {@code wasmcm_runtime_guest.wasm} running inside an Endive
+ * companion instance. All Component Model semantics — export dispatch, canon
+ * lift/lower, resource tables — live in the guest blob; the adapter only
+ * marshals values across the wire and dispatches guest exports.
+ *
+ * Instances are minted by {@link EndiveComponentAdapter#instantiate()}
+ * / {@link EndiveComponentAdapter#instantiate(ai.tegmentum.webassembly4j.api.LinkingContext)}
+ * and hold the u32 instance handle the guest returned. Multiple instances of
+ * the same {@code Component} share the same {@link WasmcmRuntimeGuestLoader}.
+ */
+final class EndiveComponentInstanceAdapter implements ComponentInstance {
+
+ private final WasmcmRuntimeGuestLoader loader;
+ private final long instanceHandle;
+
+ EndiveComponentInstanceAdapter(WasmcmRuntimeGuestLoader loader, long instanceHandle) {
+ this.loader = loader;
+ this.instanceHandle = instanceHandle;
+ }
+
+ @Override
+ public Object invoke(String functionName, Object... args) {
+ Object[] safeArgs = args == null ? new Object[0] : args;
+ byte[] argFrame;
+ try {
+ argFrame = safeArgs.length == 0
+ ? new byte[0]
+ : WasmcmValueCodec.encodeFrame(safeArgs);
+ } catch (RuntimeException e) {
+ throw new ExecutionException(
+ "Failed to marshal arguments for component export '" + functionName + "'", e);
+ }
+ byte[] resultFrame;
+ try {
+ resultFrame = loader.callExport(instanceHandle, functionName, argFrame);
+ } catch (IllegalStateException e) {
+ throw new ExecutionException(
+ "Component export '" + functionName + "' failed: " + e.getMessage(), e);
+ }
+ List results;
+ try {
+ results = WasmcmValueCodec.decodeFrame(resultFrame);
+ } catch (RuntimeException e) {
+ throw new ExecutionException(
+ "Failed to decode results for component export '" + functionName + "'", e);
+ }
+ if (results.isEmpty()) {
+ return null;
+ }
+ if (results.size() == 1) {
+ return results.get(0);
+ }
+ return results;
+ }
+
+ @Override
+ public boolean hasFunction(String name) {
+ for (String exp : exportedFunctions()) {
+ if (exp.equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public List exportedFunctions() {
+ // The runtime guest exposes the flattened export list of the instantiated
+ // component through wasmcm_instance_export_count / wasmcm_instance_export_name.
+ // These are string names as the component itself surfaces them (function names
+ // for root-level exports; interface-qualified names for interface exports).
+ ExportFunction countFn = loader.guest().exports().function("wasmcm_instance_export_count");
+ ExportFunction nameFn = loader.guest().exports().function("wasmcm_instance_export_name");
+ if (countFn == null || nameFn == null) {
+ return Collections.emptyList();
+ }
+ run.endive.runtime.Memory mem = loader.guest().exports().memory("memory");
+ ExportFunction alloc = loader.guest().exports().function("wasmcm_alloc");
+ ExportFunction free = loader.guest().exports().function("wasmcm_free");
+ long[] countResult = countFn.apply(instanceHandle);
+ int count = (int) (countResult[0] & 0xFFFFFFFFL);
+ if (count == 0) {
+ return Collections.emptyList();
+ }
+ int bufCap = 512;
+ long[] allocResult = alloc.apply((long) bufCap, 1L);
+ int bufPtr = (int) (allocResult[0] & 0xFFFFFFFFL);
+ if (bufPtr == 0) {
+ return Collections.emptyList();
+ }
+ try {
+ java.util.List out = new java.util.ArrayList<>(count);
+ for (int i = 0; i < count; i++) {
+ long[] r = nameFn.apply(instanceHandle, (long) i, (long) bufPtr, (long) bufCap);
+ int n = (int) (r[0] & 0xFFFFFFFFL);
+ if (n == 0) {
+ continue;
+ }
+ byte[] bytes = mem.readBytes(bufPtr, n);
+ out.add(new String(bytes, StandardCharsets.UTF_8));
+ }
+ return Collections.unmodifiableList(out);
+ } finally {
+ free.apply((long) bufPtr, (long) bufCap);
+ }
+ }
+
+ @Override
+ public List exportedInterfaces() {
+ // The runtime guest's flattened export list mingles interface-qualified
+ // function exports (e.g. "wasi:clocks/monotonic-clock@0.2.3#now") with
+ // root-level ones. Extract the interface prefixes to give callers a
+ // provider-agnostic view of "which interfaces does this instance surface".
+ java.util.LinkedHashSet ifaces = new java.util.LinkedHashSet<>();
+ for (String name : exportedFunctions()) {
+ int hash = name.indexOf('#');
+ if (hash > 0) {
+ ifaces.add(name.substring(0, hash));
+ }
+ }
+ return java.util.Collections.unmodifiableList(new java.util.ArrayList<>(ifaces));
+ }
+
+ @Override
+ public boolean exportsInterface(String name) {
+ return exportedInterfaces().contains(name);
+ }
+
+ @Override
+ public Optional function(String name) {
+ // Component instances do not surface core-module exports; the SPI's
+ // ComponentInstance javadoc documents this contract explicitly.
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional memory(String name) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional table(String name) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional global(String name) {
+ return Optional.empty();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Optional unwrap(Class nativeType) {
+ if (nativeType.isInstance(loader)) {
+ return Optional.of((T) loader);
+ }
+ return Optional.empty();
+ }
+}
diff --git a/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveConfigs.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveConfigs.java
new file mode 100644
index 0000000..ac684a2
--- /dev/null
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveConfigs.java
@@ -0,0 +1,42 @@
+package ai.tegmentum.webassembly4j.provider.endive;
+
+import ai.tegmentum.webassembly4j.api.config.WebAssemblyConfig;
+import ai.tegmentum.webassembly4j.api.config.WebAssemblyConfigBuilder;
+import ai.tegmentum.webassembly4j.provider.endive.config.EndiveConfig;
+
+import java.util.function.Consumer;
+
+/**
+ * Convenience entry point for building a {@link WebAssemblyConfig} pre-wired for the
+ * Endive pure-Java engine.
+ *
+ * Examples
+ * {@code
+ * // Just Endive tunings:
+ * WebAssemblyConfig cfg = EndiveConfigs.of(b -> b
+ * .executionMode(EndiveConfig.ExecutionMode.COMPILE));
+ *
+ * // Default interpreter + cross-engine settings:
+ * WebAssemblyConfig cfg = EndiveConfigs.builder(b -> {})
+ * .debug(true)
+ * .build();
+ * }
+ */
+public final class EndiveConfigs {
+
+ public static final String ENGINE_ID = "endive";
+
+ private EndiveConfigs() {}
+
+ public static WebAssemblyConfig of(Consumer configurator) {
+ return builder(configurator).build();
+ }
+
+ public static WebAssemblyConfigBuilder builder(Consumer configurator) {
+ EndiveConfig.Builder cb = EndiveConfig.builder();
+ configurator.accept(cb);
+ return WebAssemblyConfig.builder()
+ .engine(ENGINE_ID)
+ .engineConfig(cb.build());
+ }
+}
diff --git a/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveEngineAdapter.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveEngineAdapter.java
new file mode 100644
index 0000000..827ed96
--- /dev/null
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveEngineAdapter.java
@@ -0,0 +1,107 @@
+package ai.tegmentum.webassembly4j.provider.endive;
+
+import ai.tegmentum.webassembly4j.api.Component;
+import ai.tegmentum.webassembly4j.api.Engine;
+import ai.tegmentum.webassembly4j.api.EngineCapabilities;
+import ai.tegmentum.webassembly4j.api.EngineInfo;
+import ai.tegmentum.webassembly4j.api.Module;
+import ai.tegmentum.webassembly4j.api.config.WebAssemblyConfig;
+import ai.tegmentum.webassembly4j.api.exception.UnsupportedFeatureException;
+import ai.tegmentum.webassembly4j.api.exception.ValidationException;
+import ai.tegmentum.webassembly4j.provider.endive.config.EndiveConfig;
+import run.endive.wasm.Parser;
+import run.endive.wasm.WasmModule;
+import run.tegmentum.wasmcm.endive.WasmcmRuntimeGuestLoader;
+
+import java.util.Optional;
+import java.util.function.Function;
+
+final class EndiveEngineAdapter implements Engine {
+
+ private final WebAssemblyConfig config;
+ private final EndiveConfig.ExecutionMode executionMode;
+
+ private EndiveEngineAdapter(WebAssemblyConfig config, EndiveConfig.ExecutionMode executionMode) {
+ this.config = config;
+ this.executionMode = executionMode;
+ }
+
+ static EndiveEngineAdapter create(WebAssemblyConfig config) {
+ EndiveConfig.ExecutionMode mode = EndiveConfig.ExecutionMode.INTERPRET;
+ if (config != null) {
+ Optional extends ai.tegmentum.webassembly4j.api.config.EngineConfig> ec = config.engineConfig();
+ if (ec.isPresent() && ec.get() instanceof EndiveConfig) {
+ mode = ((EndiveConfig) ec.get()).executionMode();
+ }
+ }
+ return new EndiveEngineAdapter(config, mode);
+ }
+
+ @Override
+ public EngineInfo info() {
+ return new EndiveEngineInfo();
+ }
+
+ @Override
+ public EngineCapabilities capabilities() {
+ return new EndiveEngineCapabilities();
+ }
+
+ @Override
+ public Module loadModule(byte[] bytes) {
+ try {
+ WasmModule wasmModule = Parser.parse(bytes);
+ Function builderFactory =
+ createBuilderFactory(wasmModule);
+ return new EndiveModuleAdapter(wasmModule, builderFactory);
+ } catch (Exception e) {
+ throw new ValidationException("Failed to load WebAssembly module", e);
+ }
+ }
+
+ private Function createBuilderFactory(
+ WasmModule wasmModule) {
+ if (executionMode == EndiveConfig.ExecutionMode.COMPILE) {
+ return CompilerSupport.createCompilingBuilderFactory();
+ }
+ return m -> run.endive.runtime.Instance.builder(m);
+ }
+
+ @Override
+ public Component loadComponent(byte[] bytes) {
+ byte[] guestWasm = WasmcmGuestBlobLocator.locateOrNull();
+ if (guestWasm == null) {
+ throw new UnsupportedFeatureException(
+ "Component Model support requires wasmcm_runtime_guest.wasm; provide it via "
+ + WasmcmGuestBlobLocator.describeLookupOrder());
+ }
+ try {
+ WasmcmRuntimeGuestLoader loader = new WasmcmRuntimeGuestLoader(guestWasm);
+ long componentHandle = loader.parseComponent(bytes);
+ return new EndiveComponentAdapter(loader, componentHandle, true);
+ } catch (IllegalStateException e) {
+ throw new ValidationException(
+ "Failed to parse Component Model binary through wasmcm runtime guest: "
+ + e.getMessage(),
+ e);
+ } catch (RuntimeException e) {
+ throw new ValidationException("Failed to load Component Model binary", e);
+ }
+ }
+
+ @Override
+ public Optional extension(Class extensionType) {
+ return Optional.empty();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Optional unwrap(Class nativeType) {
+ return Optional.empty();
+ }
+
+ @Override
+ public void close() {
+ // No native resources to release
+ }
+}
diff --git a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryEngineCapabilities.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveEngineCapabilities.java
similarity index 71%
rename from chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryEngineCapabilities.java
rename to endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveEngineCapabilities.java
index 97976f0..7242b13 100644
--- a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryEngineCapabilities.java
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveEngineCapabilities.java
@@ -1,8 +1,8 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.EngineCapabilities;
-final class ChicoryEngineCapabilities implements EngineCapabilities {
+final class EndiveEngineCapabilities implements EngineCapabilities {
@Override
public boolean supportsCoreModules() {
@@ -11,7 +11,9 @@ public boolean supportsCoreModules() {
@Override
public boolean supportsComponents() {
- return false;
+ // Component Model is layered in via wasmcm_runtime_guest.wasm; concretely
+ // available whenever the guest blob is resolvable at loadComponent time.
+ return WasmcmGuestBlobLocator.locateOrNull() != null;
}
@Override
diff --git a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryEngineInfo.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveEngineInfo.java
similarity index 72%
rename from chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryEngineInfo.java
rename to endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveEngineInfo.java
index cb2194b..0e519fa 100644
--- a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryEngineInfo.java
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveEngineInfo.java
@@ -1,17 +1,17 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.EngineInfo;
-final class ChicoryEngineInfo implements EngineInfo {
+final class EndiveEngineInfo implements EngineInfo {
@Override
public String engineId() {
- return "chicory";
+ return "endive";
}
@Override
public String providerId() {
- return "chicory";
+ return "endive";
}
@Override
diff --git a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryFunctionAdapter.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveFunctionAdapter.java
similarity index 79%
rename from chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryFunctionAdapter.java
rename to endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveFunctionAdapter.java
index 9f79d42..69ea7d5 100644
--- a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryFunctionAdapter.java
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveFunctionAdapter.java
@@ -1,16 +1,16 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.Function;
import ai.tegmentum.webassembly4j.api.ValueType;
import ai.tegmentum.webassembly4j.api.exception.ExecutionException;
import ai.tegmentum.webassembly4j.api.exception.TrapException;
-import com.dylibso.chicory.runtime.ExportFunction;
-import com.dylibso.chicory.wasm.types.FunctionType;
-import com.dylibso.chicory.wasm.types.ValType;
+import run.endive.runtime.ExportFunction;
+import run.endive.wasm.types.FunctionType;
+import run.endive.wasm.types.ValType;
import java.util.List;
-final class ChicoryFunctionAdapter implements Function {
+final class EndiveFunctionAdapter implements Function {
private static final long[] EMPTY_LONGS = new long[0];
@@ -24,7 +24,7 @@ private enum FastPath { V_V, V_I, I_I, II_I, I_V, J_J, II_V, I_J, D_D, V_J, GENE
private final long[] argScratch;
private final FastPath fastPath;
- ChicoryFunctionAdapter(ExportFunction nativeFunction, FunctionType functionType) {
+ EndiveFunctionAdapter(ExportFunction nativeFunction, FunctionType functionType) {
this.nativeFunction = nativeFunction;
this.functionType = functionType;
this.cachedParamTypes = functionType.params();
@@ -116,14 +116,14 @@ public Object invoke(Object... args) {
argScratch[0] = ((Number) args[0]).longValue();
return nativeFunction.apply(argScratch)[0];
case D_D:
- argScratch[0] = com.dylibso.chicory.wasm.types.Value.doubleToLong(
+ argScratch[0] = run.endive.wasm.types.Value.doubleToLong(
((Number) args[0]).doubleValue());
- return com.dylibso.chicory.wasm.types.Value.longToDouble(
+ return run.endive.wasm.types.Value.longToDouble(
nativeFunction.apply(argScratch)[0]);
default:
return invokeGeneric(args);
}
- } catch (com.dylibso.chicory.runtime.TrapException e) {
+ } catch (run.endive.runtime.TrapException e) {
throw new TrapException(e.getMessage(), e);
} catch (Exception e) {
if (e instanceof ExecutionException) throw (ExecutionException) e;
@@ -164,9 +164,9 @@ private long convertToLong(Object value, ValType type) {
} else if (ValType.I64.equals(type)) {
return num.longValue();
} else if (ValType.F32.equals(type)) {
- return com.dylibso.chicory.wasm.types.Value.floatToLong(num.floatValue());
+ return run.endive.wasm.types.Value.floatToLong(num.floatValue());
} else if (ValType.F64.equals(type)) {
- return com.dylibso.chicory.wasm.types.Value.doubleToLong(num.doubleValue());
+ return run.endive.wasm.types.Value.doubleToLong(num.doubleValue());
} else {
throw new ExecutionException("Unsupported parameter type: " + type);
}
@@ -178,30 +178,30 @@ private Object extractValue(long raw, ValType type) {
} else if (ValType.I64.equals(type)) {
return raw;
} else if (ValType.F32.equals(type)) {
- return com.dylibso.chicory.wasm.types.Value.longToFloat(raw);
+ return run.endive.wasm.types.Value.longToFloat(raw);
} else if (ValType.F64.equals(type)) {
- return com.dylibso.chicory.wasm.types.Value.longToDouble(raw);
+ return run.endive.wasm.types.Value.longToDouble(raw);
} else {
return raw;
}
}
- private static ValueType[] convertTypes(List chicoryTypes) {
- ValueType[] types = new ValueType[chicoryTypes.size()];
- for (int i = 0; i < chicoryTypes.size(); i++) {
- types[i] = convertType(chicoryTypes.get(i));
+ private static ValueType[] convertTypes(List endiveTypes) {
+ ValueType[] types = new ValueType[endiveTypes.size()];
+ for (int i = 0; i < endiveTypes.size(); i++) {
+ types[i] = convertType(endiveTypes.get(i));
}
return types;
}
- private static ValueType convertType(ValType chicoryType) {
- if (chicoryType .equals(ValType.I32)) return ValueType.I32;
- if (chicoryType .equals(ValType.I64)) return ValueType.I64;
- if (chicoryType .equals(ValType.F32)) return ValueType.F32;
- if (chicoryType .equals(ValType.F64)) return ValueType.F64;
- if (chicoryType .equals(ValType.V128)) return ValueType.V128;
- if (chicoryType .equals(ValType.FuncRef)) return ValueType.FUNCREF;
- if (chicoryType .equals(ValType.ExternRef)) return ValueType.EXTERNREF;
- throw new IllegalArgumentException("Unknown type: " + chicoryType);
+ private static ValueType convertType(ValType endiveType) {
+ if (endiveType .equals(ValType.I32)) return ValueType.I32;
+ if (endiveType .equals(ValType.I64)) return ValueType.I64;
+ if (endiveType .equals(ValType.F32)) return ValueType.F32;
+ if (endiveType .equals(ValType.F64)) return ValueType.F64;
+ if (endiveType .equals(ValType.V128)) return ValueType.V128;
+ if (endiveType .equals(ValType.FuncRef)) return ValueType.FUNCREF;
+ if (endiveType .equals(ValType.ExternRef)) return ValueType.EXTERNREF;
+ throw new IllegalArgumentException("Unknown type: " + endiveType);
}
}
diff --git a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryGcExtension.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveGcExtension.java
similarity index 84%
rename from chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryGcExtension.java
rename to endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveGcExtension.java
index 980ae01..27cdce4 100644
--- a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryGcExtension.java
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveGcExtension.java
@@ -1,4 +1,4 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.gc.GcArrayInstance;
import ai.tegmentum.webassembly4j.api.gc.GcArrayType;
@@ -12,23 +12,23 @@
import ai.tegmentum.webassembly4j.api.gc.GcStructInstance;
import ai.tegmentum.webassembly4j.api.gc.GcStructType;
import ai.tegmentum.webassembly4j.api.gc.GcValue;
-import com.dylibso.chicory.runtime.WasmArray;
-import com.dylibso.chicory.runtime.WasmI31Ref;
-import com.dylibso.chicory.runtime.WasmStruct;
-import com.dylibso.chicory.wasm.types.Value;
+import run.endive.runtime.WasmArray;
+import run.endive.runtime.WasmI31Ref;
+import run.endive.runtime.WasmStruct;
+import run.endive.wasm.types.Value;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
/**
- * Chicory implementation of the GC extension.
+ * Endive implementation of the GC extension.
*
- * Chicory GC objects are plain Java objects allocated on the JVM heap.
- * Values are encoded as {@code long} using Chicory's {@link Value} helpers.
+ *
Endive GC objects are plain Java objects allocated on the JVM heap.
+ * Values are encoded as {@code long} using Endive's {@link Value} helpers.
* A synthetic type index starting from 0x10000 is used for programmatically
* created types to avoid collision with module type sections.
*/
-final class ChicoryGcExtension implements GcExtension {
+final class EndiveGcExtension implements GcExtension {
private static final int SYNTHETIC_TYPE_BASE = 0x10000;
@@ -48,7 +48,7 @@ public GcStructInstance createStruct(GcStructType type, GcValue... values) {
int typeIdx = (int) nextTypeIndex.getAndIncrement();
WasmStruct nativeStruct = new WasmStruct(typeIdx, fields);
allocationCount.incrementAndGet();
- return new ChicoryGcStructInstance(nativeStruct, type);
+ return new EndiveGcStructInstance(nativeStruct, type);
}
@Override
@@ -66,7 +66,7 @@ public GcArrayInstance createArray(GcArrayType type, GcValue... elements) {
int typeIdx = (int) nextTypeIndex.getAndIncrement();
WasmArray nativeArray = new WasmArray(typeIdx, encoded);
allocationCount.incrementAndGet();
- return new ChicoryGcArrayInstance(nativeArray, type);
+ return new EndiveGcArrayInstance(nativeArray, type);
}
@Override
@@ -83,14 +83,14 @@ public GcArrayInstance createArray(GcArrayType type, int length) {
int typeIdx = (int) nextTypeIndex.getAndIncrement();
WasmArray nativeArray = new WasmArray(typeIdx, encoded);
allocationCount.incrementAndGet();
- return new ChicoryGcArrayInstance(nativeArray, type);
+ return new EndiveGcArrayInstance(nativeArray, type);
}
@Override
public GcI31Instance createI31(int value) {
WasmI31Ref nativeRef = new WasmI31Ref(value);
allocationCount.incrementAndGet();
- return new ChicoryGcI31Instance(nativeRef);
+ return new EndiveGcI31Instance(nativeRef);
}
@Override
@@ -162,12 +162,12 @@ private static GcValue decodeValue(long raw, GcFieldType fieldType) {
// --- Inner adapter classes ---
- private static final class ChicoryGcStructInstance implements GcStructInstance {
+ private static final class EndiveGcStructInstance implements GcStructInstance {
private final WasmStruct nativeStruct;
private final GcStructType structType;
- ChicoryGcStructInstance(WasmStruct nativeStruct, GcStructType structType) {
+ EndiveGcStructInstance(WasmStruct nativeStruct, GcStructType structType) {
this.nativeStruct = nativeStruct;
this.structType = structType;
}
@@ -214,19 +214,19 @@ public boolean isNull() {
@Override
public boolean refEquals(GcObject other) {
- if (!(other instanceof ChicoryGcStructInstance)) {
+ if (!(other instanceof EndiveGcStructInstance)) {
return false;
}
- return this.nativeStruct == ((ChicoryGcStructInstance) other).nativeStruct;
+ return this.nativeStruct == ((EndiveGcStructInstance) other).nativeStruct;
}
}
- private static final class ChicoryGcArrayInstance implements GcArrayInstance {
+ private static final class EndiveGcArrayInstance implements GcArrayInstance {
private final WasmArray nativeArray;
private final GcArrayType arrayType;
- ChicoryGcArrayInstance(WasmArray nativeArray, GcArrayType arrayType) {
+ EndiveGcArrayInstance(WasmArray nativeArray, GcArrayType arrayType) {
this.nativeArray = nativeArray;
this.arrayType = arrayType;
}
@@ -271,18 +271,18 @@ public boolean isNull() {
@Override
public boolean refEquals(GcObject other) {
- if (!(other instanceof ChicoryGcArrayInstance)) {
+ if (!(other instanceof EndiveGcArrayInstance)) {
return false;
}
- return this.nativeArray == ((ChicoryGcArrayInstance) other).nativeArray;
+ return this.nativeArray == ((EndiveGcArrayInstance) other).nativeArray;
}
}
- private static final class ChicoryGcI31Instance implements GcI31Instance {
+ private static final class EndiveGcI31Instance implements GcI31Instance {
private final WasmI31Ref nativeRef;
- ChicoryGcI31Instance(WasmI31Ref nativeRef) {
+ EndiveGcI31Instance(WasmI31Ref nativeRef) {
this.nativeRef = nativeRef;
}
@@ -313,10 +313,10 @@ public boolean isNull() {
@Override
public boolean refEquals(GcObject other) {
- if (!(other instanceof ChicoryGcI31Instance)) {
+ if (!(other instanceof EndiveGcI31Instance)) {
return false;
}
- return this.nativeRef.value() == ((ChicoryGcI31Instance) other).nativeRef.value();
+ return this.nativeRef.value() == ((EndiveGcI31Instance) other).nativeRef.value();
}
}
}
diff --git a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryGlobalAdapter.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveGlobalAdapter.java
similarity index 70%
rename from chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryGlobalAdapter.java
rename to endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveGlobalAdapter.java
index 18992b5..c26f92b 100644
--- a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryGlobalAdapter.java
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveGlobalAdapter.java
@@ -1,19 +1,19 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.Global;
import ai.tegmentum.webassembly4j.api.ValueType;
-import com.dylibso.chicory.runtime.GlobalInstance;
-import com.dylibso.chicory.wasm.types.MutabilityType;
-import com.dylibso.chicory.wasm.types.ValType;
-import com.dylibso.chicory.wasm.types.Value;
+import run.endive.runtime.GlobalInstance;
+import run.endive.wasm.types.MutabilityType;
+import run.endive.wasm.types.ValType;
+import run.endive.wasm.types.Value;
import java.util.Optional;
-final class ChicoryGlobalAdapter implements Global {
+final class EndiveGlobalAdapter implements Global {
private final GlobalInstance nativeGlobal;
- ChicoryGlobalAdapter(GlobalInstance nativeGlobal) {
+ EndiveGlobalAdapter(GlobalInstance nativeGlobal) {
this.nativeGlobal = nativeGlobal;
}
@@ -70,11 +70,11 @@ public Optional unwrap(Class nativeType) {
return Optional.empty();
}
- private static ValueType convertType(ValType chicoryType) {
- if (chicoryType .equals(ValType.I32)) return ValueType.I32;
- if (chicoryType .equals(ValType.I64)) return ValueType.I64;
- if (chicoryType .equals(ValType.F32)) return ValueType.F32;
- if (chicoryType .equals(ValType.F64)) return ValueType.F64;
- throw new IllegalArgumentException("Unknown type: " + chicoryType);
+ private static ValueType convertType(ValType endiveType) {
+ if (endiveType .equals(ValType.I32)) return ValueType.I32;
+ if (endiveType .equals(ValType.I64)) return ValueType.I64;
+ if (endiveType .equals(ValType.F32)) return ValueType.F32;
+ if (endiveType .equals(ValType.F64)) return ValueType.F64;
+ throw new IllegalArgumentException("Unknown type: " + endiveType);
}
}
diff --git a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryInstanceAdapter.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveInstanceAdapter.java
similarity index 71%
rename from chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryInstanceAdapter.java
rename to endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveInstanceAdapter.java
index c5819ce..1f38a23 100644
--- a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryInstanceAdapter.java
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveInstanceAdapter.java
@@ -1,4 +1,4 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.Function;
import ai.tegmentum.webassembly4j.api.Global;
@@ -11,13 +11,13 @@
import java.util.Map;
import java.util.Optional;
-final class ChicoryInstanceAdapter implements Instance {
+final class EndiveInstanceAdapter implements Instance {
- private final com.dylibso.chicory.runtime.Instance nativeInstance;
+ private final run.endive.runtime.Instance nativeInstance;
private final Map> functionCache = new HashMap<>();
private final Map> memoryCache = new HashMap<>();
- ChicoryInstanceAdapter(com.dylibso.chicory.runtime.Instance nativeInstance) {
+ EndiveInstanceAdapter(run.endive.runtime.Instance nativeInstance) {
this.nativeInstance = nativeInstance;
}
@@ -29,11 +29,11 @@ public Optional function(String name) {
}
Optional result;
try {
- com.dylibso.chicory.runtime.ExportFunction exportFunc =
+ run.endive.runtime.ExportFunction exportFunc =
nativeInstance.exports().function(name);
- com.dylibso.chicory.wasm.types.FunctionType funcType =
+ run.endive.wasm.types.FunctionType funcType =
nativeInstance.exportType(name);
- result = Optional.of(new ChicoryFunctionAdapter(exportFunc, funcType));
+ result = Optional.of(new EndiveFunctionAdapter(exportFunc, funcType));
} catch (Exception e) {
result = Optional.empty();
}
@@ -49,9 +49,9 @@ public Optional memory(String name) {
}
Optional result;
try {
- com.dylibso.chicory.runtime.Memory nativeMemory =
+ run.endive.runtime.Memory nativeMemory =
nativeInstance.exports().memory(name);
- result = Optional.of(new ChicoryMemoryAdapter(nativeMemory));
+ result = Optional.of(new EndiveMemoryAdapter(nativeMemory));
} catch (Exception e) {
result = Optional.empty();
}
@@ -62,9 +62,9 @@ public Optional memory(String name) {
@Override
public Optional table(String name) {
try {
- com.dylibso.chicory.runtime.TableInstance tableInstance =
+ run.endive.runtime.TableInstance tableInstance =
nativeInstance.exports().table(name);
- return Optional.of(new ChicoryTableAdapter(tableInstance));
+ return Optional.of(new EndiveTableAdapter(tableInstance));
} catch (Exception e) {
return Optional.empty();
}
@@ -73,9 +73,9 @@ public Optional table(String name) {
@Override
public Optional global(String name) {
try {
- com.dylibso.chicory.runtime.GlobalInstance globalInstance =
+ run.endive.runtime.GlobalInstance globalInstance =
nativeInstance.exports().global(name);
- return Optional.of(new ChicoryGlobalAdapter(globalInstance));
+ return Optional.of(new EndiveGlobalAdapter(globalInstance));
} catch (Exception e) {
return Optional.empty();
}
@@ -85,7 +85,7 @@ public Optional global(String name) {
@SuppressWarnings("unchecked")
public Optional extension(Class extensionType) {
if (extensionType == GcExtension.class) {
- return Optional.of((T) new ChicoryGcExtension());
+ return Optional.of((T) new EndiveGcExtension());
}
return Optional.empty();
}
diff --git a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryMemoryAdapter.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveMemoryAdapter.java
similarity index 72%
rename from chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryMemoryAdapter.java
rename to endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveMemoryAdapter.java
index 7dd075e..477030e 100644
--- a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryMemoryAdapter.java
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveMemoryAdapter.java
@@ -1,21 +1,21 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.Memory;
import java.nio.ByteBuffer;
import java.util.Optional;
-final class ChicoryMemoryAdapter implements Memory {
+final class EndiveMemoryAdapter implements Memory {
- private final com.dylibso.chicory.runtime.Memory nativeMemory;
+ private final run.endive.runtime.Memory nativeMemory;
- ChicoryMemoryAdapter(com.dylibso.chicory.runtime.Memory nativeMemory) {
+ EndiveMemoryAdapter(run.endive.runtime.Memory nativeMemory) {
this.nativeMemory = nativeMemory;
}
@Override
public long byteSize() {
- return (long) nativeMemory.pages() * com.dylibso.chicory.runtime.Memory.PAGE_SIZE;
+ return (long) nativeMemory.pages() * run.endive.runtime.Memory.PAGE_SIZE;
}
@Override
@@ -36,7 +36,7 @@ public long grow(long pages) {
@Override
public ByteBuffer asByteBuffer() {
throw new UnsupportedOperationException(
- "Chicory does not expose memory as a direct ByteBuffer");
+ "Endive does not expose memory as a direct ByteBuffer");
}
@Override
diff --git a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryModuleAdapter.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveModuleAdapter.java
similarity index 83%
rename from chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryModuleAdapter.java
rename to endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveModuleAdapter.java
index 2053352..d001f20 100644
--- a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryModuleAdapter.java
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveModuleAdapter.java
@@ -1,4 +1,4 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.ExportDescriptor;
import ai.tegmentum.webassembly4j.api.HostFunctionDefinition;
@@ -10,19 +10,19 @@
import ai.tegmentum.webassembly4j.api.WasiContext;
import ai.tegmentum.webassembly4j.api.exception.InstantiationException;
import ai.tegmentum.webassembly4j.api.exception.LinkingException;
-import com.dylibso.chicory.runtime.HostFunction;
-import com.dylibso.chicory.runtime.ImportFunction;
-import com.dylibso.chicory.runtime.ImportValues;
-import com.dylibso.chicory.wasi.WasiOptions;
-import com.dylibso.chicory.wasi.WasiPreview1;
-import com.dylibso.chicory.wasm.WasmModule;
-import com.dylibso.chicory.wasm.types.Export;
-import com.dylibso.chicory.wasm.types.ExternalType;
-import com.dylibso.chicory.wasm.types.FunctionImport;
-import com.dylibso.chicory.wasm.types.FunctionType;
-import com.dylibso.chicory.wasm.types.Import;
-import com.dylibso.chicory.wasm.types.ValType;
-import com.dylibso.chicory.wasm.types.Value;
+import run.endive.runtime.HostFunction;
+import run.endive.runtime.ImportFunction;
+import run.endive.runtime.ImportValues;
+import run.endive.wasi.WasiOptions;
+import run.endive.wasi.WasiPreview1;
+import run.endive.wasm.WasmModule;
+import run.endive.wasm.types.Export;
+import run.endive.wasm.types.ExternalType;
+import run.endive.wasm.types.FunctionImport;
+import run.endive.wasm.types.FunctionType;
+import run.endive.wasm.types.Import;
+import run.endive.wasm.types.ValType;
+import run.endive.wasm.types.Value;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -31,13 +31,13 @@
import java.util.List;
import java.util.Map;
-final class ChicoryModuleAdapter implements Module {
+final class EndiveModuleAdapter implements Module {
private final WasmModule wasmModule;
- private final java.util.function.Function builderFactory;
+ private final java.util.function.Function builderFactory;
- ChicoryModuleAdapter(WasmModule wasmModule,
- java.util.function.Function builderFactory) {
+ EndiveModuleAdapter(WasmModule wasmModule,
+ java.util.function.Function builderFactory) {
this.wasmModule = wasmModule;
this.builderFactory = builderFactory;
}
@@ -45,9 +45,9 @@ final class ChicoryModuleAdapter implements Module {
@Override
public Instance instantiate() {
try {
- com.dylibso.chicory.runtime.Instance nativeInstance =
+ run.endive.runtime.Instance nativeInstance =
builderFactory.apply(wasmModule).build();
- return new ChicoryInstanceAdapter(nativeInstance);
+ return new EndiveInstanceAdapter(nativeInstance);
} catch (Exception e) {
throw new InstantiationException(
"Failed to instantiate WebAssembly module", e);
@@ -90,8 +90,8 @@ public Instance instantiate(LinkingContext linkingContext) {
List hostFunctions = linkingContext.hostFunctions();
for (HostFunctionDefinition def : hostFunctions) {
- List paramTypes = convertToChicoryTypes(def.parameterTypes());
- List returnTypes = convertToChicoryTypes(def.resultTypes());
+ List paramTypes = convertToEndiveTypes(def.parameterTypes());
+ List returnTypes = convertToEndiveTypes(def.resultTypes());
HostFunction hostFunc = new HostFunction(
def.moduleName(), def.functionName(),
@@ -115,25 +115,25 @@ public Instance instantiate(LinkingContext linkingContext) {
importBuilder.withFunctions(functions);
ImportValues imports = importBuilder.build();
- com.dylibso.chicory.runtime.Instance nativeInstance =
+ run.endive.runtime.Instance nativeInstance =
builderFactory.apply(wasmModule)
.withImportValues(imports)
.build();
- return new ChicoryInstanceAdapter(nativeInstance);
+ return new EndiveInstanceAdapter(nativeInstance);
} catch (Exception e) {
throw new LinkingException("Failed to instantiate with linking context", e);
}
}
- private static List convertToChicoryTypes(ValueType[] types) {
+ private static List convertToEndiveTypes(ValueType[] types) {
List result = new ArrayList<>(types.length);
for (ValueType type : types) {
- result.add(convertToChicoryType(type));
+ result.add(convertToEndiveType(type));
}
return result;
}
- private static ValType convertToChicoryType(ValueType type) {
+ private static ValType convertToEndiveType(ValueType type) {
switch (type) {
case I32: return ValType.I32;
case I64: return ValType.I64;
@@ -232,8 +232,8 @@ private ExportDescriptor convertFunctionExport(Export export) {
FunctionType funcType = wasmModule.functionSection()
.getFunctionType(localIndex, wasmModule.typeSection());
return ExportDescriptor.function(export.name(),
- convertChicoryTypes(funcType.params()),
- convertChicoryTypes(funcType.returns()));
+ convertEndiveTypes(funcType.params()),
+ convertEndiveTypes(funcType.returns()));
}
return ExportDescriptor.function(export.name(),
new ValueType[0], new ValueType[0]);
@@ -248,8 +248,8 @@ private ImportDescriptor convertImport(Import imp) {
if (typeIdx < wasmModule.typeSection().typeCount()) {
FunctionType funcType = wasmModule.typeSection().getType(typeIdx);
return ImportDescriptor.function(imp.module(), imp.name(),
- convertChicoryTypes(funcType.params()),
- convertChicoryTypes(funcType.returns()));
+ convertEndiveTypes(funcType.params()),
+ convertEndiveTypes(funcType.returns()));
}
return ImportDescriptor.function(imp.module(), imp.name(),
new ValueType[0], new ValueType[0]);
@@ -264,15 +264,15 @@ private ImportDescriptor convertImport(Import imp) {
}
}
- private static ValueType[] convertChicoryTypes(List types) {
+ private static ValueType[] convertEndiveTypes(List types) {
ValueType[] result = new ValueType[types.size()];
for (int i = 0; i < types.size(); i++) {
- result[i] = convertChicoryType(types.get(i));
+ result[i] = convertEndiveType(types.get(i));
}
return result;
}
- private static ValueType convertChicoryType(ValType type) {
+ private static ValueType convertEndiveType(ValType type) {
if (type .equals(ValType.I32)) return ValueType.I32;
if (type .equals(ValType.I64)) return ValueType.I64;
if (type .equals(ValType.F32)) return ValueType.F32;
diff --git a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryProvider.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveProvider.java
similarity index 76%
rename from chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryProvider.java
rename to endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveProvider.java
index 8178220..eb9939b 100644
--- a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryProvider.java
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveProvider.java
@@ -1,9 +1,9 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.Engine;
import ai.tegmentum.webassembly4j.api.config.EngineConfig;
import ai.tegmentum.webassembly4j.api.config.WebAssemblyConfig;
-import ai.tegmentum.webassembly4j.provider.chicory.config.ChicoryConfig;
+import ai.tegmentum.webassembly4j.provider.endive.config.EndiveConfig;
import ai.tegmentum.webassembly4j.spi.EngineProvider;
import ai.tegmentum.webassembly4j.spi.ProviderAvailability;
import ai.tegmentum.webassembly4j.spi.ProviderDescriptor;
@@ -13,10 +13,10 @@
import java.util.Optional;
import java.util.Set;
-public final class ChicoryProvider implements EngineProvider {
+public final class EndiveProvider implements EngineProvider {
- private static final String ENGINE_ID = "chicory";
- private static final String PROVIDER_ID = "chicory";
+ private static final String ENGINE_ID = "endive";
+ private static final String PROVIDER_ID = "endive";
private static final String VERSION = "1.0.0-SNAPSHOT";
private static final int MIN_JAVA = 11;
private static final int PRIORITY = 50;
@@ -42,17 +42,17 @@ public ProviderDescriptor descriptor() {
public ProviderAvailability availability() {
return new ProviderAvailability() {
@Override public boolean available() { return true; }
- @Override public String message() { return "Chicory pure-Java runtime always available"; }
+ @Override public String message() { return "Endive pure-Java runtime always available"; }
};
}
@Override
public Optional> configType() {
- return Optional.of(ChicoryConfig.class);
+ return Optional.of(EndiveConfig.class);
}
@Override
public Engine create(WebAssemblyConfig config) {
- return ChicoryEngineAdapter.create(config);
+ return EndiveEngineAdapter.create(config);
}
}
diff --git a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryTableAdapter.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveTableAdapter.java
similarity index 70%
rename from chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryTableAdapter.java
rename to endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveTableAdapter.java
index cd0542f..5fbf670 100644
--- a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryTableAdapter.java
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/EndiveTableAdapter.java
@@ -1,15 +1,15 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.Table;
-import com.dylibso.chicory.runtime.TableInstance;
+import run.endive.runtime.TableInstance;
import java.util.Optional;
-final class ChicoryTableAdapter implements Table {
+final class EndiveTableAdapter implements Table {
private final TableInstance nativeTable;
- ChicoryTableAdapter(TableInstance nativeTable) {
+ EndiveTableAdapter(TableInstance nativeTable) {
this.nativeTable = nativeTable;
}
diff --git a/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/WasmcmGuestBlobLocator.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/WasmcmGuestBlobLocator.java
new file mode 100644
index 0000000..7b8fb9b
--- /dev/null
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/WasmcmGuestBlobLocator.java
@@ -0,0 +1,122 @@
+package ai.tegmentum.webassembly4j.provider.endive;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Locates the {@code wasmcm_runtime_guest.wasm} blob — the portable Rust Component
+ * Model runtime cross-compiled to {@code wasm32-unknown-unknown} — that the Endive
+ * provider layers on top of Endive to obtain a real Component Model surface.
+ *
+ * Lookup order:
+ *
+ * System property {@code wasmcm.runtime.guest.wasm} — absolute path to the {@code .wasm}.
+ * Environment variable {@code WASMCM_RUNTIME_GUEST_WASM} — absolute path to the {@code .wasm}.
+ * Environment variable {@code WASMCM_HOME} — the guest is resolved to
+ * {@code $WASMCM_HOME/target/wasm32-unknown-unknown/release/wasmcm_runtime_guest.wasm}.
+ * Classpath resource {@code wasmcm_runtime_guest.wasm} on the thread-context class loader
+ * (with a fallback to this class's loader).
+ * Sibling checkout heuristic {@code ~/git/wasm-cm/target/wasm32-unknown-unknown/release/wasmcm_runtime_guest.wasm}
+ * — convenient for local development when a companion {@code wasm-cm} tree is present.
+ *
+ *
+ * Returns {@code null} when nothing matches; callers surface a clear error to the user
+ * rather than pretending Component Model works with no guest present.
+ */
+final class WasmcmGuestBlobLocator {
+
+ static final String SYSTEM_PROPERTY = "wasmcm.runtime.guest.wasm";
+ static final String ENV_GUEST_WASM = "WASMCM_RUNTIME_GUEST_WASM";
+ static final String ENV_HOME = "WASMCM_HOME";
+ static final String CLASSPATH_RESOURCE = "wasmcm_runtime_guest.wasm";
+ static final String HOME_RELATIVE_GUEST =
+ "target/wasm32-unknown-unknown/release/wasmcm_runtime_guest.wasm";
+
+ private WasmcmGuestBlobLocator() {}
+
+ static byte[] locateOrNull() {
+ String prop = System.getProperty(SYSTEM_PROPERTY);
+ byte[] bytes = readIfRegular(prop);
+ if (bytes != null) {
+ return bytes;
+ }
+
+ String envGuest = System.getenv(ENV_GUEST_WASM);
+ bytes = readIfRegular(envGuest);
+ if (bytes != null) {
+ return bytes;
+ }
+
+ String home = System.getenv(ENV_HOME);
+ if (home != null && !home.isEmpty()) {
+ bytes = readIfRegular(Paths.get(home, HOME_RELATIVE_GUEST).toString());
+ if (bytes != null) {
+ return bytes;
+ }
+ }
+
+ bytes = readClasspath(Thread.currentThread().getContextClassLoader());
+ if (bytes != null) {
+ return bytes;
+ }
+ bytes = readClasspath(WasmcmGuestBlobLocator.class.getClassLoader());
+ if (bytes != null) {
+ return bytes;
+ }
+
+ String userHome = System.getProperty("user.home");
+ if (userHome != null && !userHome.isEmpty()) {
+ Path sibling = Paths.get(userHome, "git", "wasm-cm", HOME_RELATIVE_GUEST);
+ bytes = readIfRegular(sibling.toString());
+ if (bytes != null) {
+ return bytes;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Human-readable summary of the lookup paths, embedded in error messages when
+ * no blob can be found so callers know how to point the provider at one.
+ */
+ static String describeLookupOrder() {
+ return "-D" + SYSTEM_PROPERTY + "="
+ + " | $" + ENV_GUEST_WASM
+ + " | $" + ENV_HOME + "/" + HOME_RELATIVE_GUEST
+ + " | classpath:" + CLASSPATH_RESOURCE
+ + " | ~/git/wasm-cm/" + HOME_RELATIVE_GUEST;
+ }
+
+ private static byte[] readIfRegular(String path) {
+ if (path == null || path.isEmpty()) {
+ return null;
+ }
+ Path p = Paths.get(path);
+ if (!Files.isRegularFile(p)) {
+ return null;
+ }
+ try {
+ return Files.readAllBytes(p);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private static byte[] readClasspath(ClassLoader loader) {
+ if (loader == null) {
+ return null;
+ }
+ try (InputStream is = loader.getResourceAsStream(CLASSPATH_RESOURCE)) {
+ if (is == null) {
+ return null;
+ }
+ return is.readAllBytes();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+}
diff --git a/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/WasmcmValueCodec.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/WasmcmValueCodec.java
new file mode 100644
index 0000000..8aa507c
--- /dev/null
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/WasmcmValueCodec.java
@@ -0,0 +1,585 @@
+package ai.tegmentum.webassembly4j.provider.endive;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Wire codec for the {@code wasmcm_runtime_guest} argument / result frames.
+ *
+ * Mirrors the tags and layout documented on {@code wasmcm_runtime_guest::codec}
+ * (see {@code crates/wasmcm-runtime-guest/src/codec.rs} in the wasm-cm tree):
+ *
+ *
+ * frame := [u32 total_bytes_including_header][u32 count][value * count]
+ * value := [u32 tag][payload...]
+ *
+ *
+ * All integers are little-endian and unaligned.
+ *
+ *
The Java surface exchanges natural Java types with callers of
+ * {@link EndiveComponentInstanceAdapter#invoke}: {@code Boolean, Byte, Short,
+ * Integer, Long, Float, Double, Character, String, byte[], List, Map} — mapped
+ * onto the tag arms below at encode time. Decoding produces the mirror shape.
+ *
+ *
Types the Java-side natural mapping cannot express unambiguously (unsigned
+ * widths, variants, options, results, enums, flags, resources) accept a
+ * pre-boxed {@link Boxed} sentinel that carries the wire tag alongside the
+ * payload.
+ */
+final class WasmcmValueCodec {
+
+ static final int TAG_BOOL = 0;
+ static final int TAG_S8 = 1;
+ static final int TAG_U8 = 2;
+ static final int TAG_S16 = 3;
+ static final int TAG_U16 = 4;
+ static final int TAG_S32 = 5;
+ static final int TAG_U32 = 6;
+ static final int TAG_S64 = 7;
+ static final int TAG_U64 = 8;
+ static final int TAG_F32 = 9;
+ static final int TAG_F64 = 10;
+ static final int TAG_CHAR = 11;
+ static final int TAG_STRING = 12;
+ static final int TAG_LIST = 13;
+ static final int TAG_RECORD = 14;
+ static final int TAG_TUPLE = 15;
+ static final int TAG_VARIANT = 16;
+ static final int TAG_OPTION = 17;
+ static final int TAG_RESULT = 18;
+ static final int TAG_FLAGS = 19;
+ static final int TAG_ENUM = 20;
+ static final int TAG_OWN = 21;
+ static final int TAG_BORROW = 22;
+
+ private WasmcmValueCodec() {}
+
+ /**
+ * Envelope for a payload plus its explicit wire tag — the escape hatch for
+ * WIT types the Java-native shape can't disambiguate on its own (unsigned
+ * widths, variants, options, results, enums, flags, own/borrow handles).
+ *
+ *
The concrete payload shape per tag:
+ *
+ * {@link #TAG_U8}/{@link #TAG_U16}/{@link #TAG_U32}: {@link Integer}
+ * carrying the unsigned bit pattern.
+ * {@link #TAG_U64}: {@link Long} carrying the unsigned bit pattern.
+ * {@link #TAG_S8}: {@link Byte}. {@link #TAG_S16}: {@link Short}.
+ * {@link #TAG_S32}: {@link Integer}. {@link #TAG_S64}: {@link Long}.
+ * {@link #TAG_OPTION}: {@link Boxed} for {@code some(v)} carrying the
+ * inner value, or {@code null} payload for {@code none}.
+ * {@link #TAG_RESULT}: {@code Object[]{isOkBoolean, payloadOrNull}}.
+ * {@link #TAG_ENUM}: {@code Object[]{caseIndexInt, caseNameString}}.
+ * {@link #TAG_VARIANT}: {@code Object[]{caseIndexInt, caseNameString, payloadOrNull}}.
+ * {@link #TAG_FLAGS}: {@link List} of {@link String}.
+ * {@link #TAG_OWN}/{@link #TAG_BORROW}: {@link Integer} handle.
+ *
+ */
+ static final class Boxed {
+ final int tag;
+ final Object payload;
+
+ Boxed(int tag, Object payload) {
+ this.tag = tag;
+ this.payload = payload;
+ }
+
+ int tag() {
+ return tag;
+ }
+
+ Object payload() {
+ return payload;
+ }
+ }
+
+ // -----------------------------------------------------------------
+ // Encode
+ // -----------------------------------------------------------------
+
+ static byte[] encodeFrame(Object[] values) {
+ ByteArrayOutputStream inner = new ByteArrayOutputStream();
+ writeU32(inner, values.length);
+ for (Object v : values) {
+ encodeValue(inner, v);
+ }
+ byte[] body = inner.toByteArray();
+ int total = 4 + body.length;
+ ByteArrayOutputStream out = new ByteArrayOutputStream(total);
+ writeU32(out, total);
+ try {
+ out.write(body);
+ } catch (java.io.IOException e) {
+ throw new IllegalStateException("BAOS never throws", e);
+ }
+ return out.toByteArray();
+ }
+
+ static void encodeValue(ByteArrayOutputStream buf, Object v) {
+ if (v instanceof Boxed) {
+ Boxed b = (Boxed) v;
+ encodeBoxed(buf, b);
+ return;
+ }
+ if (v instanceof Boolean) {
+ writeU32(buf, TAG_BOOL);
+ buf.write(((Boolean) v) ? 1 : 0);
+ return;
+ }
+ if (v instanceof Byte) {
+ writeU32(buf, TAG_S8);
+ buf.write(((Byte) v) & 0xFF);
+ return;
+ }
+ if (v instanceof Short) {
+ writeU32(buf, TAG_S16);
+ writeI16(buf, (Short) v);
+ return;
+ }
+ if (v instanceof Integer) {
+ writeU32(buf, TAG_S32);
+ writeI32(buf, (Integer) v);
+ return;
+ }
+ if (v instanceof Long) {
+ writeU32(buf, TAG_S64);
+ writeI64(buf, (Long) v);
+ return;
+ }
+ if (v instanceof Float) {
+ writeU32(buf, TAG_F32);
+ writeI32(buf, Float.floatToRawIntBits((Float) v));
+ return;
+ }
+ if (v instanceof Double) {
+ writeU32(buf, TAG_F64);
+ writeI64(buf, Double.doubleToRawLongBits((Double) v));
+ return;
+ }
+ if (v instanceof Character) {
+ writeU32(buf, TAG_CHAR);
+ writeU32(buf, (Character) v);
+ return;
+ }
+ if (v instanceof String) {
+ writeU32(buf, TAG_STRING);
+ writeBytes(buf, ((String) v).getBytes(StandardCharsets.UTF_8));
+ return;
+ }
+ if (v instanceof byte[]) {
+ byte[] bytes = (byte[]) v;
+ writeU32(buf, TAG_LIST);
+ writeU32(buf, bytes.length);
+ for (byte b : bytes) {
+ writeU32(buf, TAG_U8);
+ buf.write(b & 0xFF);
+ }
+ return;
+ }
+ if (v instanceof List>) {
+ List> list = (List>) v;
+ writeU32(buf, TAG_LIST);
+ writeU32(buf, list.size());
+ for (Object item : list) {
+ encodeValue(buf, item);
+ }
+ return;
+ }
+ if (v instanceof Map, ?>) {
+ Map, ?> map = (Map, ?>) v;
+ writeU32(buf, TAG_RECORD);
+ writeU32(buf, map.size());
+ for (Map.Entry, ?> e : map.entrySet()) {
+ writeBytes(buf, e.getKey().toString().getBytes(StandardCharsets.UTF_8));
+ encodeValue(buf, e.getValue());
+ }
+ return;
+ }
+ throw new IllegalArgumentException(
+ "cannot encode Java type "
+ + (v == null ? "null" : v.getClass().getName())
+ + " as a wasmcm ComponentValue; wrap in "
+ + Boxed.class.getName()
+ + " with an explicit wire tag");
+ }
+
+ private static void encodeBoxed(ByteArrayOutputStream buf, Boxed b) {
+ switch (b.tag) {
+ case TAG_BOOL:
+ writeU32(buf, TAG_BOOL);
+ buf.write(((Boolean) b.payload) ? 1 : 0);
+ return;
+ case TAG_S8:
+ writeU32(buf, TAG_S8);
+ buf.write(((Number) b.payload).byteValue() & 0xFF);
+ return;
+ case TAG_U8:
+ writeU32(buf, TAG_U8);
+ buf.write(((Number) b.payload).intValue() & 0xFF);
+ return;
+ case TAG_S16:
+ writeU32(buf, TAG_S16);
+ writeI16(buf, ((Number) b.payload).shortValue());
+ return;
+ case TAG_U16:
+ writeU32(buf, TAG_U16);
+ writeI16(buf, (short) (((Number) b.payload).intValue() & 0xFFFF));
+ return;
+ case TAG_S32:
+ writeU32(buf, TAG_S32);
+ writeI32(buf, ((Number) b.payload).intValue());
+ return;
+ case TAG_U32:
+ writeU32(buf, TAG_U32);
+ writeI32(buf, ((Number) b.payload).intValue());
+ return;
+ case TAG_S64:
+ writeU32(buf, TAG_S64);
+ writeI64(buf, ((Number) b.payload).longValue());
+ return;
+ case TAG_U64:
+ writeU32(buf, TAG_U64);
+ writeI64(buf, ((Number) b.payload).longValue());
+ return;
+ case TAG_F32:
+ writeU32(buf, TAG_F32);
+ writeI32(buf, Float.floatToRawIntBits(((Number) b.payload).floatValue()));
+ return;
+ case TAG_F64:
+ writeU32(buf, TAG_F64);
+ writeI64(buf, Double.doubleToRawLongBits(((Number) b.payload).doubleValue()));
+ return;
+ case TAG_CHAR:
+ writeU32(buf, TAG_CHAR);
+ writeU32(buf, (int) ((Character) b.payload).charValue());
+ return;
+ case TAG_STRING:
+ writeU32(buf, TAG_STRING);
+ writeBytes(buf, ((String) b.payload).getBytes(StandardCharsets.UTF_8));
+ return;
+ case TAG_LIST:
+ writeU32(buf, TAG_LIST);
+ {
+ List> list = (List>) b.payload;
+ writeU32(buf, list.size());
+ for (Object item : list) {
+ encodeValue(buf, item);
+ }
+ }
+ return;
+ case TAG_RECORD:
+ writeU32(buf, TAG_RECORD);
+ {
+ @SuppressWarnings("unchecked")
+ Map map = (Map) b.payload;
+ writeU32(buf, map.size());
+ for (Map.Entry e : map.entrySet()) {
+ writeBytes(buf, e.getKey().getBytes(StandardCharsets.UTF_8));
+ encodeValue(buf, e.getValue());
+ }
+ }
+ return;
+ case TAG_TUPLE:
+ writeU32(buf, TAG_TUPLE);
+ {
+ List> list = (List>) b.payload;
+ writeU32(buf, list.size());
+ for (Object item : list) {
+ encodeValue(buf, item);
+ }
+ }
+ return;
+ case TAG_VARIANT:
+ writeU32(buf, TAG_VARIANT);
+ {
+ Object[] parts = (Object[]) b.payload;
+ int caseIdx = ((Number) parts[0]).intValue();
+ String caseName = (String) parts[1];
+ Object innerPayload = parts.length > 2 ? parts[2] : null;
+ writeU32(buf, caseIdx);
+ writeBytes(buf, caseName.getBytes(StandardCharsets.UTF_8));
+ if (innerPayload == null) {
+ buf.write(0);
+ } else {
+ buf.write(1);
+ encodeValue(buf, innerPayload);
+ }
+ }
+ return;
+ case TAG_OPTION:
+ writeU32(buf, TAG_OPTION);
+ if (b.payload == null) {
+ buf.write(0);
+ } else {
+ buf.write(1);
+ encodeValue(buf, b.payload);
+ }
+ return;
+ case TAG_RESULT:
+ writeU32(buf, TAG_RESULT);
+ {
+ Object[] parts = (Object[]) b.payload;
+ boolean isOk = ((Boolean) parts[0]);
+ Object inner = parts.length > 1 ? parts[1] : null;
+ buf.write(isOk ? 1 : 0);
+ if (inner == null) {
+ buf.write(0);
+ } else {
+ buf.write(1);
+ encodeValue(buf, inner);
+ }
+ }
+ return;
+ case TAG_FLAGS:
+ writeU32(buf, TAG_FLAGS);
+ {
+ List> labels = (List>) b.payload;
+ writeU32(buf, labels.size());
+ for (Object label : labels) {
+ writeBytes(buf, ((String) label).getBytes(StandardCharsets.UTF_8));
+ }
+ }
+ return;
+ case TAG_ENUM:
+ writeU32(buf, TAG_ENUM);
+ {
+ Object[] parts = (Object[]) b.payload;
+ int caseIdx = ((Number) parts[0]).intValue();
+ String caseName = (String) parts[1];
+ writeU32(buf, caseIdx);
+ writeBytes(buf, caseName.getBytes(StandardCharsets.UTF_8));
+ }
+ return;
+ case TAG_OWN:
+ writeU32(buf, TAG_OWN);
+ writeU32(buf, ((Number) b.payload).intValue());
+ return;
+ case TAG_BORROW:
+ writeU32(buf, TAG_BORROW);
+ writeU32(buf, ((Number) b.payload).intValue());
+ return;
+ default:
+ throw new IllegalArgumentException("unknown wire tag " + b.tag);
+ }
+ }
+
+ // -----------------------------------------------------------------
+ // Decode
+ // -----------------------------------------------------------------
+
+ static List decodeFrame(byte[] bytes) {
+ if (bytes == null || bytes.length == 0) {
+ return java.util.Collections.emptyList();
+ }
+ Cursor c = new Cursor(bytes, 0);
+ readU32(c); // total_bytes — discard, callers already sized the array
+ int count = readU32(c);
+ List out = new ArrayList<>(count);
+ for (int i = 0; i < count; i++) {
+ out.add(decodeValue(c));
+ }
+ return out;
+ }
+
+ static Object decodeValue(Cursor c) {
+ int tag = readU32(c);
+ switch (tag) {
+ case TAG_BOOL:
+ return c.readU8() != 0;
+ case TAG_S8:
+ return (byte) c.readU8();
+ case TAG_U8:
+ return c.readU8() & 0xFF;
+ case TAG_S16:
+ return (short) readI16(c);
+ case TAG_U16:
+ return readI16(c) & 0xFFFF;
+ case TAG_S32:
+ return readU32(c);
+ case TAG_U32:
+ // Represent unsigned u32 as a Long to keep the natural mapping non-lossy.
+ return ((long) readU32(c)) & 0xFFFFFFFFL;
+ case TAG_S64:
+ return readI64(c);
+ case TAG_U64:
+ // No unsigned long in Java; caller can reinterpret bits.
+ return readI64(c);
+ case TAG_F32:
+ return Float.intBitsToFloat(readU32(c));
+ case TAG_F64:
+ return Double.longBitsToDouble(readI64(c));
+ case TAG_CHAR:
+ return (char) readU32(c);
+ case TAG_STRING:
+ return new String(readBytes(c), StandardCharsets.UTF_8);
+ case TAG_LIST: {
+ int n = readU32(c);
+ List list = new ArrayList<>(n);
+ for (int i = 0; i < n; i++) {
+ list.add(decodeValue(c));
+ }
+ return list;
+ }
+ case TAG_RECORD: {
+ int n = readU32(c);
+ Map record = new LinkedHashMap<>();
+ for (int i = 0; i < n; i++) {
+ String name = new String(readBytes(c), StandardCharsets.UTF_8);
+ Object value = decodeValue(c);
+ record.put(name, value);
+ }
+ return record;
+ }
+ case TAG_TUPLE: {
+ int n = readU32(c);
+ List list = new ArrayList<>(n);
+ for (int i = 0; i < n; i++) {
+ list.add(decodeValue(c));
+ }
+ return new Boxed(TAG_TUPLE, list);
+ }
+ case TAG_VARIANT: {
+ int caseIdx = readU32(c);
+ String caseName = new String(readBytes(c), StandardCharsets.UTF_8);
+ int hasPayload = c.readU8();
+ Object payload = hasPayload == 0 ? null : decodeValue(c);
+ return new Boxed(TAG_VARIANT, new Object[] {caseIdx, caseName, payload});
+ }
+ case TAG_OPTION: {
+ int has = c.readU8();
+ return has == 0 ? new Boxed(TAG_OPTION, null) : new Boxed(TAG_OPTION, decodeValue(c));
+ }
+ case TAG_RESULT: {
+ boolean isOk = c.readU8() != 0;
+ int has = c.readU8();
+ Object payload = has == 0 ? null : decodeValue(c);
+ return new Boxed(TAG_RESULT, new Object[] {isOk, payload});
+ }
+ case TAG_FLAGS: {
+ int n = readU32(c);
+ List labels = new ArrayList<>(n);
+ for (int i = 0; i < n; i++) {
+ labels.add(new String(readBytes(c), StandardCharsets.UTF_8));
+ }
+ return new Boxed(TAG_FLAGS, labels);
+ }
+ case TAG_ENUM: {
+ int caseIdx = readU32(c);
+ String caseName = new String(readBytes(c), StandardCharsets.UTF_8);
+ return new Boxed(TAG_ENUM, new Object[] {caseIdx, caseName});
+ }
+ case TAG_OWN:
+ return new Boxed(TAG_OWN, readU32(c));
+ case TAG_BORROW:
+ return new Boxed(TAG_BORROW, readU32(c));
+ default:
+ throw new IllegalStateException("unknown wire tag " + tag);
+ }
+ }
+
+ // -----------------------------------------------------------------
+ // Low-level LE readers / writers
+ // -----------------------------------------------------------------
+
+ static final class Cursor {
+ final byte[] bytes;
+ int off;
+
+ Cursor(byte[] bytes, int off) {
+ this.bytes = bytes;
+ this.off = off;
+ }
+
+ int readU8() {
+ require(1);
+ return bytes[off++] & 0xFF;
+ }
+
+ void require(int n) {
+ if (off + n > bytes.length) {
+ throw new IllegalStateException(
+ "wire frame truncated: need " + n + " at " + off + " of " + bytes.length);
+ }
+ }
+ }
+
+ private static void writeU32(ByteArrayOutputStream buf, int v) {
+ buf.write(v & 0xFF);
+ buf.write((v >>> 8) & 0xFF);
+ buf.write((v >>> 16) & 0xFF);
+ buf.write((v >>> 24) & 0xFF);
+ }
+
+ private static void writeI16(ByteArrayOutputStream buf, int v) {
+ buf.write(v & 0xFF);
+ buf.write((v >>> 8) & 0xFF);
+ }
+
+ private static void writeI32(ByteArrayOutputStream buf, int v) {
+ writeU32(buf, v);
+ }
+
+ private static void writeI64(ByteArrayOutputStream buf, long v) {
+ buf.write((int) (v & 0xFF));
+ buf.write((int) ((v >>> 8) & 0xFF));
+ buf.write((int) ((v >>> 16) & 0xFF));
+ buf.write((int) ((v >>> 24) & 0xFF));
+ buf.write((int) ((v >>> 32) & 0xFF));
+ buf.write((int) ((v >>> 40) & 0xFF));
+ buf.write((int) ((v >>> 48) & 0xFF));
+ buf.write((int) ((v >>> 56) & 0xFF));
+ }
+
+ private static void writeBytes(ByteArrayOutputStream buf, byte[] bytes) {
+ writeU32(buf, bytes.length);
+ try {
+ buf.write(bytes);
+ } catch (java.io.IOException e) {
+ throw new IllegalStateException("BAOS never throws", e);
+ }
+ }
+
+ private static int readU32(Cursor c) {
+ c.require(4);
+ int v = (c.bytes[c.off] & 0xFF)
+ | ((c.bytes[c.off + 1] & 0xFF) << 8)
+ | ((c.bytes[c.off + 2] & 0xFF) << 16)
+ | ((c.bytes[c.off + 3] & 0xFF) << 24);
+ c.off += 4;
+ return v;
+ }
+
+ private static int readI16(Cursor c) {
+ c.require(2);
+ int v = (c.bytes[c.off] & 0xFF) | ((c.bytes[c.off + 1] & 0xFF) << 8);
+ c.off += 2;
+ return v;
+ }
+
+ private static long readI64(Cursor c) {
+ c.require(8);
+ long v = (long) (c.bytes[c.off] & 0xFF)
+ | ((long) (c.bytes[c.off + 1] & 0xFF) << 8)
+ | ((long) (c.bytes[c.off + 2] & 0xFF) << 16)
+ | ((long) (c.bytes[c.off + 3] & 0xFF) << 24)
+ | ((long) (c.bytes[c.off + 4] & 0xFF) << 32)
+ | ((long) (c.bytes[c.off + 5] & 0xFF) << 40)
+ | ((long) (c.bytes[c.off + 6] & 0xFF) << 48)
+ | ((long) (c.bytes[c.off + 7] & 0xFF) << 56);
+ c.off += 8;
+ return v;
+ }
+
+ private static byte[] readBytes(Cursor c) {
+ int n = readU32(c);
+ c.require(n);
+ byte[] out = new byte[n];
+ System.arraycopy(c.bytes, c.off, out, 0, n);
+ c.off += n;
+ return out;
+ }
+}
diff --git a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/config/ChicoryConfig.java b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/config/EndiveConfig.java
similarity index 71%
rename from chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/config/ChicoryConfig.java
rename to endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/config/EndiveConfig.java
index a834ffc..ae257e3 100644
--- a/chicory4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/chicory/config/ChicoryConfig.java
+++ b/endive4j-provider/src/main/java/ai/tegmentum/webassembly4j/provider/endive/config/EndiveConfig.java
@@ -1,20 +1,20 @@
-package ai.tegmentum.webassembly4j.provider.chicory.config;
+package ai.tegmentum.webassembly4j.provider.endive.config;
import ai.tegmentum.webassembly4j.api.config.EngineConfig;
import java.util.Optional;
/**
- * Configuration for the Chicory WebAssembly engine.
+ * Configuration for the Endive WebAssembly engine.
*
* Controls the execution mode used when instantiating modules:
*
* {@link ExecutionMode#INTERPRET} (default) — pure interpretation, no extra dependencies
* {@link ExecutionMode#COMPILE} — runtime AOT compilation to JVM bytecode via ASM,
- * requires {@code com.dylibso.chicory:compiler} on the classpath
+ * requires {@code run.endive:compiler} on the classpath
*
*/
-public final class ChicoryConfig implements EngineConfig {
+public final class EndiveConfig implements EngineConfig {
/**
* Execution mode for WebAssembly modules.
@@ -22,13 +22,13 @@ public final class ChicoryConfig implements EngineConfig {
public enum ExecutionMode {
/** Pure interpreter — no extra dependencies required. */
INTERPRET,
- /** Runtime compilation to JVM bytecode — requires chicory-compiler dependency. */
+ /** Runtime compilation to JVM bytecode — requires endive-compiler dependency. */
COMPILE
}
private final ExecutionMode executionMode;
- private ChicoryConfig(Builder builder) {
+ private EndiveConfig(Builder builder) {
this.executionMode = builder.executionMode;
}
@@ -50,8 +50,8 @@ public Builder executionMode(ExecutionMode executionMode) {
return this;
}
- public ChicoryConfig build() {
- return new ChicoryConfig(this);
+ public EndiveConfig build() {
+ return new EndiveConfig(this);
}
}
}
diff --git a/endive4j-provider/src/main/resources/META-INF/services/ai.tegmentum.webassembly4j.spi.EngineProvider b/endive4j-provider/src/main/resources/META-INF/services/ai.tegmentum.webassembly4j.spi.EngineProvider
new file mode 100644
index 0000000..5918e2e
--- /dev/null
+++ b/endive4j-provider/src/main/resources/META-INF/services/ai.tegmentum.webassembly4j.spi.EngineProvider
@@ -0,0 +1 @@
+ai.tegmentum.webassembly4j.provider.endive.EndiveProvider
diff --git a/endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveComponentIntegrationTest.java b/endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveComponentIntegrationTest.java
new file mode 100644
index 0000000..11e1e02
--- /dev/null
+++ b/endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveComponentIntegrationTest.java
@@ -0,0 +1,184 @@
+package ai.tegmentum.webassembly4j.provider.endive;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+import ai.tegmentum.webassembly4j.api.Component;
+import ai.tegmentum.webassembly4j.api.ComponentInstance;
+import ai.tegmentum.webassembly4j.api.DefaultLinkingContext;
+import ai.tegmentum.webassembly4j.api.Engine;
+import ai.tegmentum.webassembly4j.api.WitHostFunctionDefinition;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * End-to-end verification of Endive's Component Model surface: parse a real CM
+ * binary, instantiate it under the layered runtime guest, invoke an exported
+ * function and inspect exported names / interfaces.
+ *
+ * The tests are skipped when the {@code wasmcm_runtime_guest.wasm} blob is
+ * not resolvable via {@link WasmcmGuestBlobLocator} — a fresh checkout that has
+ * not built the sibling {@code wasm-cm} tree gets a green {@code mvn test}
+ * rather than a red failure.
+ */
+final class EndiveComponentIntegrationTest {
+
+ @Test
+ void lookupOrderStringNamesBlobExplicitly() {
+ // The UnsupportedFeatureException raised when the guest is missing embeds
+ // this string; verify it stays actionable ("names the blob and every knob").
+ String s = WasmcmGuestBlobLocator.describeLookupOrder();
+ assertTrue(s.contains("wasmcm_runtime_guest.wasm"),
+ "lookup order should name the blob: " + s);
+ assertTrue(s.contains(WasmcmGuestBlobLocator.SYSTEM_PROPERTY),
+ "lookup order should name the system property: " + s);
+ assertTrue(s.contains(WasmcmGuestBlobLocator.ENV_HOME),
+ "lookup order should name WASMCM_HOME: " + s);
+ assertTrue(s.contains("classpath"),
+ "lookup order should mention classpath: " + s);
+ }
+
+ @Test
+ void scalarComponentRoundTripsAddThroughGuest() throws Exception {
+ assumeGuestPresent();
+ byte[] bytes = loadClasspath("scalar-component.wasm");
+ assumeTrue(bytes != null, "scalar-component.wasm missing from test resources");
+
+ try (Engine engine = EndiveEngineAdapter.create(null);
+ Component component = engine.loadComponent(bytes)) {
+ assertNotNull(component, "loadComponent must not return null");
+ ComponentInstance instance = component.instantiate();
+ assertNotNull(instance, "instantiate must not return null");
+
+ // The scalar-component fixture exposes exactly `add: func(u32, u32) -> u32`
+ // at the component root. hasFunction / exportedFunctions rely on the guest's
+ // wasmcm_instance_export_count / wasmcm_instance_export_name.
+ assertTrue(instance.hasFunction("add"),
+ "scalar-component should export 'add', got " + instance.exportedFunctions());
+
+ // U32 args round-trip through Boxed(TAG_U32, int); the fixture returns U32.
+ Object result = instance.invoke(
+ "add",
+ new WasmcmValueCodec.Boxed(WasmcmValueCodec.TAG_U32, 2),
+ new WasmcmValueCodec.Boxed(WasmcmValueCodec.TAG_U32, 3));
+ assertEquals(5L, ((Number) result).longValue(),
+ "add(2, 3) should be 5; got " + result);
+ }
+ }
+
+ /**
+ * Instantiate a real WASI Preview 2 component that imports
+ * {@code wasip2:host/primitives@0.1.0} and {@code wasi:io/poll@0.2.3}.
+ *
+ *
Walls 2, 3, 8 and 9 have landed: the register/dispatch
+ * surface, the runtime-side {@code ImportedInstance::HostCallback}
+ * plumbing, the guest-side {@code HostEngineWasm::create_host_func}
+ * + {@code instantiate}-with-CoreFunc-imports pair, and — most
+ * recently — the variable-length import descriptor codec that
+ * turns {@code HostEngineWasm::module_imports} into a real
+ * enumeration are all wired end-to-end. The runtime's
+ * {@code instantiate_with_ref_imports} builds a full
+ * {@code ext_imports} list from the compiled module's declared
+ * imports and Endive's linker successfully binds every row.
+ *
+ * Exit criterion: {@code component.instantiate(ctx)} returns a
+ * non-null instance and the guest surfaces at least one export.
+ * If a Wall 10 emerges (e.g. an ImportedInstance::Component gap or
+ * a Resource wiring issue), this test will fail with a fresh
+ * diagnostic and the assertions get flipped again per the
+ * milestone discipline.
+ */
+ @Test
+ void wasiClocksComponentInstantiatesCleanly() throws Exception {
+ assumeGuestPresent();
+ byte[] bytes = loadClasspath("wasi_p2_clocks.component.wasm");
+ assumeTrue(bytes != null, "wasi_p2_clocks.component.wasm missing from test resources");
+
+ try (Engine engine = EndiveEngineAdapter.create(null);
+ Component component = engine.loadComponent(bytes)) {
+ assertNotNull(component, "loadComponent should succeed on a WASI P2 component binary");
+
+ DefaultLinkingContext ctx = DefaultLinkingContext.builder()
+ .addWitHostFunction(
+ new WitHostFunctionDefinition(
+ "wasip2:host/primitives@0.1.0#clock-monotonic-now-ns",
+ args -> new Object[] {
+ new WasmcmValueCodec.Boxed(
+ WasmcmValueCodec.TAG_U64, 42L)
+ }))
+ .addWitHostFunction(
+ new WitHostFunctionDefinition(
+ "wasip2:host/primitives@0.1.0#clock-wall-now-ns",
+ args -> new Object[] {
+ new WasmcmValueCodec.Boxed(
+ WasmcmValueCodec.TAG_U64, 43L)
+ }))
+ .addWitHostFunction(
+ new WitHostFunctionDefinition(
+ "wasip2:host/primitives@0.1.0#clock-monotonic-resolution-ns",
+ args -> new Object[] {
+ new WasmcmValueCodec.Boxed(
+ WasmcmValueCodec.TAG_U64, 1L)
+ }))
+ // wasi:io/poll@0.2.3 is a resource-type-only import
+ // (num_funcs=0 in the sibling Rust probe); a
+ // placeholder entry keeps the guest's provider
+ // cross-check happy.
+ .addWitHostFunction(
+ new WitHostFunctionDefinition(
+ "wasi:io/poll@0.2.3#__placeholder",
+ args -> new Object[0]))
+ .build();
+
+ ComponentInstance instance = component.instantiate(ctx);
+ assertNotNull(instance, "instantiate should return a live instance after Wall 9");
+ assertTrue(
+ !instance.exportedFunctions().isEmpty(),
+ "wasi_p2_clocks should surface at least one export; got "
+ + instance.exportedFunctions());
+ }
+ }
+
+ private static void assumeGuestPresent() {
+ byte[] guest = WasmcmGuestBlobLocator.locateOrNull();
+ assumeTrue(
+ guest != null,
+ () ->
+ "wasmcm_runtime_guest.wasm not found; searched: "
+ + WasmcmGuestBlobLocator.describeLookupOrder());
+ }
+
+ private static byte[] loadClasspath(String resource) throws IOException {
+ try (InputStream is = EndiveComponentIntegrationTest.class
+ .getClassLoader()
+ .getResourceAsStream(resource)) {
+ if (is == null) {
+ // Fallback: check the module test resources on disk. Convenience path so
+ // running from an IDE without a fresh test-compile still works.
+ Path fallback = Paths.get(
+ System.getProperty("user.dir"),
+ "src", "test", "resources", resource);
+ if (Files.isRegularFile(fallback)) {
+ return Files.readAllBytes(fallback);
+ }
+ return null;
+ }
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buf = new byte[8192];
+ int n;
+ while ((n = is.read(buf)) > 0) {
+ out.write(buf, 0, n);
+ }
+ return out.toByteArray();
+ }
+ }
+}
diff --git a/endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveConfigsTest.java b/endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveConfigsTest.java
new file mode 100644
index 0000000..fa8377f
--- /dev/null
+++ b/endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveConfigsTest.java
@@ -0,0 +1,38 @@
+package ai.tegmentum.webassembly4j.provider.endive;
+
+import ai.tegmentum.webassembly4j.api.config.WebAssemblyConfig;
+import ai.tegmentum.webassembly4j.provider.endive.config.EndiveConfig;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class EndiveConfigsTest {
+
+ @Test
+ void ofWiresEngineAndEngineConfig() {
+ WebAssemblyConfig cfg = EndiveConfigs.of(b -> b
+ .executionMode(EndiveConfig.ExecutionMode.COMPILE));
+
+ assertEquals("endive", cfg.engineId().orElse(null));
+ assertTrue(cfg.engineConfig().isPresent());
+ EndiveConfig cc = (EndiveConfig) cfg.engineConfig().get();
+ assertEquals(EndiveConfig.ExecutionMode.COMPILE, cc.executionMode());
+ }
+
+ @Test
+ void builderHandsOffToCommonSettings() {
+ WebAssemblyConfig cfg = EndiveConfigs.builder(b -> {})
+ .debug(true)
+ .build();
+
+ assertEquals("endive", cfg.engineId().orElse(null));
+ assertTrue(cfg.commonConfig().debug().orElse(false));
+ EndiveConfig cc = (EndiveConfig) cfg.engineConfig().get();
+ assertEquals(EndiveConfig.ExecutionMode.INTERPRET, cc.executionMode());
+ }
+
+ @Test
+ void engineIdConstantMatchesProviderDescriptor() {
+ assertEquals(new EndiveProvider().descriptor().engineId(), EndiveConfigs.ENGINE_ID);
+ }
+}
diff --git a/chicory4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryGcExtensionTest.java b/endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveGcExtensionTest.java
similarity index 96%
rename from chicory4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryGcExtensionTest.java
rename to endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveGcExtensionTest.java
index c904892..ecd37eb 100644
--- a/chicory4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryGcExtensionTest.java
+++ b/endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveGcExtensionTest.java
@@ -1,4 +1,4 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.gc.GcArrayInstance;
import ai.tegmentum.webassembly4j.api.gc.GcArrayType;
@@ -17,13 +17,13 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-class ChicoryGcExtensionTest {
+class EndiveGcExtensionTest {
- private ChicoryGcExtension gc;
+ private EndiveGcExtension gc;
@BeforeEach
void setUp() {
- gc = new ChicoryGcExtension();
+ gc = new EndiveGcExtension();
}
@Test
diff --git a/chicory4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryIntegrationTest.java b/endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveIntegrationTest.java
similarity index 86%
rename from chicory4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryIntegrationTest.java
rename to endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveIntegrationTest.java
index a0d130a..522066d 100644
--- a/chicory4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryIntegrationTest.java
+++ b/endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveIntegrationTest.java
@@ -1,4 +1,4 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.DefaultLinkingContext;
import ai.tegmentum.webassembly4j.api.Engine;
@@ -9,12 +9,12 @@
import ai.tegmentum.webassembly4j.api.ValueType;
import ai.tegmentum.webassembly4j.api.config.WebAssemblyConfig;
import ai.tegmentum.webassembly4j.api.exception.WebAssemblyException;
-import ai.tegmentum.webassembly4j.provider.chicory.config.ChicoryConfig;
+import ai.tegmentum.webassembly4j.provider.endive.config.EndiveConfig;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
-class ChicoryIntegrationTest {
+class EndiveIntegrationTest {
// (func (export "add") (param i32 i32) (result i32) local.get 0 local.get 1 i32.add)
private static final byte[] ADD_MODULE = new byte[] {
@@ -59,7 +59,7 @@ class ChicoryIntegrationTest {
@Test
void addFunction() {
- try (Engine engine = ChicoryEngineAdapter.create(null);
+ try (Engine engine = EndiveEngineAdapter.create(null);
Module module = engine.loadModule(ADD_MODULE)) {
Instance instance = module.instantiate();
Function add = instance.function("add").orElseThrow();
@@ -70,7 +70,7 @@ void addFunction() {
@Test
void voidFunction() {
- try (Engine engine = ChicoryEngineAdapter.create(null);
+ try (Engine engine = EndiveEngineAdapter.create(null);
Module module = engine.loadModule(VOID_MODULE)) {
Instance instance = module.instantiate();
Function noop = instance.function("noop").orElseThrow();
@@ -81,14 +81,14 @@ void voidFunction() {
@Test
void invalidModuleThrows() {
- try (Engine engine = ChicoryEngineAdapter.create(null)) {
+ try (Engine engine = EndiveEngineAdapter.create(null)) {
assertThrows(WebAssemblyException.class, () -> engine.loadModule(INVALID_MODULE));
}
}
@Test
void nonexistentFunctionReturnsEmpty() {
- try (Engine engine = ChicoryEngineAdapter.create(null);
+ try (Engine engine = EndiveEngineAdapter.create(null);
Module module = engine.loadModule(ADD_MODULE)) {
Instance instance = module.instantiate();
assertFalse(instance.function("nonexistent").isPresent());
@@ -97,7 +97,7 @@ void nonexistentFunctionReturnsEmpty() {
@Test
void memoryReadWrite() {
- try (Engine engine = ChicoryEngineAdapter.create(null);
+ try (Engine engine = EndiveEngineAdapter.create(null);
Module module = engine.loadModule(MEMORY_MODULE)) {
Instance instance = module.instantiate();
Function store = instance.function("store").orElseThrow();
@@ -111,7 +111,7 @@ void memoryReadWrite() {
@Test
void memoryDirectAccess() {
- try (Engine engine = ChicoryEngineAdapter.create(null);
+ try (Engine engine = EndiveEngineAdapter.create(null);
Module module = engine.loadModule(MEMORY_MODULE)) {
Instance instance = module.instantiate();
Memory memory = instance.memory("memory").orElseThrow();
@@ -125,7 +125,7 @@ void memoryDirectAccess() {
@Test
void memoryGrow() {
- try (Engine engine = ChicoryEngineAdapter.create(null);
+ try (Engine engine = EndiveEngineAdapter.create(null);
Module module = engine.loadModule(MEMORY_MODULE)) {
Instance instance = module.instantiate();
Memory memory = instance.memory("memory").orElseThrow();
@@ -140,7 +140,7 @@ void memoryGrow() {
@Test
void hostFunctionLinking() {
- try (Engine engine = ChicoryEngineAdapter.create(null);
+ try (Engine engine = EndiveEngineAdapter.create(null);
Module module = engine.loadModule(IMPORT_MODULE)) {
DefaultLinkingContext ctx = DefaultLinkingContext.builder()
.addHostFunction("env", "add_offset",
@@ -158,12 +158,12 @@ void hostFunctionLinking() {
@Test
void runtimeCompilationMode() {
WebAssemblyConfig config = WebAssemblyConfig.builder()
- .engineConfig(ChicoryConfig.builder()
- .executionMode(ChicoryConfig.ExecutionMode.COMPILE)
+ .engineConfig(EndiveConfig.builder()
+ .executionMode(EndiveConfig.ExecutionMode.COMPILE)
.build())
.build();
- try (Engine engine = ChicoryEngineAdapter.create(config);
+ try (Engine engine = EndiveEngineAdapter.create(config);
Module module = engine.loadModule(ADD_MODULE)) {
Instance instance = module.instantiate();
Function add = instance.function("add").orElseThrow();
@@ -174,12 +174,12 @@ void runtimeCompilationMode() {
@Test
void compilationModeWithHostFunctions() {
WebAssemblyConfig config = WebAssemblyConfig.builder()
- .engineConfig(ChicoryConfig.builder()
- .executionMode(ChicoryConfig.ExecutionMode.COMPILE)
+ .engineConfig(EndiveConfig.builder()
+ .executionMode(EndiveConfig.ExecutionMode.COMPILE)
.build())
.build();
- try (Engine engine = ChicoryEngineAdapter.create(config);
+ try (Engine engine = EndiveEngineAdapter.create(config);
Module module = engine.loadModule(IMPORT_MODULE)) {
DefaultLinkingContext ctx = DefaultLinkingContext.builder()
.addHostFunction("env", "add_offset",
diff --git a/chicory4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryProviderTest.java b/endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveProviderTest.java
similarity index 51%
rename from chicory4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryProviderTest.java
rename to endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveProviderTest.java
index 1ec0ea9..401ea84 100644
--- a/chicory4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/chicory/ChicoryProviderTest.java
+++ b/endive4j-provider/src/test/java/ai/tegmentum/webassembly4j/provider/endive/EndiveProviderTest.java
@@ -1,26 +1,28 @@
-package ai.tegmentum.webassembly4j.provider.chicory;
+package ai.tegmentum.webassembly4j.provider.endive;
import ai.tegmentum.webassembly4j.api.Engine;
import ai.tegmentum.webassembly4j.api.EngineCapabilities;
import ai.tegmentum.webassembly4j.api.EngineInfo;
import ai.tegmentum.webassembly4j.api.config.WebAssemblyConfig;
import ai.tegmentum.webassembly4j.api.exception.UnsupportedFeatureException;
-import ai.tegmentum.webassembly4j.provider.chicory.config.ChicoryConfig;
+import ai.tegmentum.webassembly4j.api.exception.ValidationException;
+import ai.tegmentum.webassembly4j.api.exception.WebAssemblyException;
+import ai.tegmentum.webassembly4j.provider.endive.config.EndiveConfig;
import ai.tegmentum.webassembly4j.spi.ProviderAvailability;
import ai.tegmentum.webassembly4j.spi.ProviderDescriptor;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
-class ChicoryProviderTest {
+class EndiveProviderTest {
- private final ChicoryProvider provider = new ChicoryProvider();
+ private final EndiveProvider provider = new EndiveProvider();
@Test
void descriptorIsCorrect() {
ProviderDescriptor desc = provider.descriptor();
- assertEquals("chicory", desc.engineId());
- assertEquals("chicory", desc.providerId());
+ assertEquals("endive", desc.engineId());
+ assertEquals("endive", desc.providerId());
assertEquals(11, desc.minimumJavaVersion());
assertEquals(50, desc.priority());
assertTrue(desc.tags().contains("pure-java"));
@@ -44,8 +46,8 @@ void createEngine() {
try (Engine engine = provider.create(null)) {
assertNotNull(engine);
EngineInfo info = engine.info();
- assertEquals("chicory", info.engineId());
- assertEquals("chicory", info.providerId());
+ assertEquals("endive", info.engineId());
+ assertEquals("endive", info.providerId());
assertEquals(11, info.minimumJavaVersion());
}
}
@@ -55,7 +57,9 @@ void capabilities() {
try (Engine engine = provider.create(null)) {
EngineCapabilities caps = engine.capabilities();
assertTrue(caps.supportsCoreModules());
- assertFalse(caps.supportsComponents());
+ // supportsComponents() reflects runtime-guest presence; asserting a
+ // hard value would make the test order-dependent on the env
+ // (see WasmcmGuestBlobLocator). Locking in the rest of the surface.
assertTrue(caps.supportsWasi());
assertFalse(caps.supportsFuel());
assertFalse(caps.supportsNativeInterop());
@@ -66,15 +70,28 @@ void capabilities() {
}
@Test
- void loadComponentThrowsUnsupported() {
+ void loadComponentSurfacesFailureCleanly() {
try (Engine engine = provider.create(null)) {
- assertThrows(UnsupportedFeatureException.class,
+ // Two honest outcomes depending on whether the wasmcm_runtime_guest.wasm
+ // blob is resolvable at test time:
+ // - guest present -> the guest rejects the empty payload with a
+ // malformed/invalid diagnostic; the provider
+ // wraps that as ValidationException.
+ // - guest missing -> the provider surfaces UnsupportedFeatureException
+ // naming the blob and how to point at it.
+ // Both are WebAssemblyException subtypes; the test locks in the union.
+ WebAssemblyException ex = assertThrows(
+ WebAssemblyException.class,
() -> engine.loadComponent(new byte[0]));
+ assertTrue(
+ ex instanceof UnsupportedFeatureException || ex instanceof ValidationException,
+ "expected UnsupportedFeatureException or ValidationException, got "
+ + ex.getClass().getName());
}
}
@Test
- void configTypeAdvertisesChicoryConfig() {
- assertEquals(ChicoryConfig.class, provider.configType().orElse(null));
+ void configTypeAdvertisesEndiveConfig() {
+ assertEquals(EndiveConfig.class, provider.configType().orElse(null));
}
}
diff --git a/endive4j-provider/src/test/resources/scalar-component.wasm b/endive4j-provider/src/test/resources/scalar-component.wasm
new file mode 100644
index 0000000..a67fa30
Binary files /dev/null and b/endive4j-provider/src/test/resources/scalar-component.wasm differ
diff --git a/endive4j-provider/src/test/resources/wasi_p2_clocks.component.wasm b/endive4j-provider/src/test/resources/wasi_p2_clocks.component.wasm
new file mode 100644
index 0000000..04caa87
Binary files /dev/null and b/endive4j-provider/src/test/resources/wasi_p2_clocks.component.wasm differ
diff --git a/graalwasm4j-provider/pom.xml b/graalwasm4j-provider/pom.xml
index ae756ac..46b3dbf 100644
--- a/graalwasm4j-provider/pom.xml
+++ b/graalwasm4j-provider/pom.xml
@@ -8,7 +8,7 @@
ai.tegmentum.webassembly4j
webassembly4j-parent
- 1.5.0
+ 2.5.2-SNAPSHOT
graalwasm4j-provider
diff --git a/mvnw b/mvnw
index 8d937f4..1ddd97b 100755
--- a/mvnw
+++ b/mvnw
@@ -19,7 +19,7 @@
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
-# Apache Maven Wrapper startup batch script, version 3.2.0
+# Apache Maven Wrapper startup batch script, version 3.3.4
#
# Required ENV vars:
# ------------------
@@ -33,75 +33,84 @@
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
-if [ -z "$MAVEN_SKIP_RC" ] ; then
+if [ -z "$MAVEN_SKIP_RC" ]; then
- if [ -f /usr/local/etc/mavenrc ] ; then
+ if [ -f /usr/local/etc/mavenrc ]; then
. /usr/local/etc/mavenrc
fi
- if [ -f /etc/mavenrc ] ; then
+ if [ -f /etc/mavenrc ]; then
. /etc/mavenrc
fi
- if [ -f "$HOME/.mavenrc" ] ; then
+ if [ -f "$HOME/.mavenrc" ]; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
-cygwin=false;
-darwin=false;
+cygwin=false
+darwin=false
mingw=false
case "$(uname)" in
- CYGWIN*) cygwin=true ;;
- MINGW*) mingw=true;;
- Darwin*) darwin=true
- # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
- # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
- if [ -z "$JAVA_HOME" ]; then
- if [ -x "/usr/libexec/java_home" ]; then
- JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
- else
- JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
- fi
+CYGWIN*) cygwin=true ;;
+MINGW*) mingw=true ;;
+Darwin*)
+ darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ JAVA_HOME="$(/usr/libexec/java_home)"
+ export JAVA_HOME
+ else
+ JAVA_HOME="/Library/Java/Home"
+ export JAVA_HOME
fi
- ;;
+ fi
+ ;;
esac
-if [ -z "$JAVA_HOME" ] ; then
- if [ -r /etc/gentoo-release ] ; then
+if [ -z "$JAVA_HOME" ]; then
+ if [ -r /etc/gentoo-release ]; then
JAVA_HOME=$(java-config --jre-home)
fi
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
- [ -n "$CLASSPATH" ] &&
- CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+if $cygwin; then
+ [ -n "$JAVA_HOME" ] \
+ && JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] \
+ && CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
- [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
- JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
+if $mingw; then
+ [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \
+ && JAVA_HOME="$(
+ cd "$JAVA_HOME" || (
+ echo "cannot cd into $JAVA_HOME." >&2
+ exit 1
+ )
+ pwd
+ )"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
- if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
+ if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=$(which readlink)
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
- if $darwin ; then
- javaHome="$(dirname "\"$javaExecutable\"")"
- javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
+ if $darwin; then
+ javaHome="$(dirname "$javaExecutable")"
+ javaExecutable="$(cd "$javaHome" && pwd -P)/javac"
else
- javaExecutable="$(readlink -f "\"$javaExecutable\"")"
+ javaExecutable="$(readlink -f "$javaExecutable")"
fi
- javaHome="$(dirname "\"$javaExecutable\"")"
+ javaHome="$(dirname "$javaExecutable")"
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
JAVA_HOME="$javaHome"
export JAVA_HOME
@@ -109,52 +118,60 @@ if [ -z "$JAVA_HOME" ]; then
fi
fi
-if [ -z "$JAVACMD" ] ; then
- if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+if [ -z "$JAVACMD" ]; then
+ if [ -n "$JAVA_HOME" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
- JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
+ JAVACMD="$(
+ \unset -f command 2>/dev/null
+ \command -v java
+ )"
fi
fi
-if [ ! -x "$JAVACMD" ] ; then
+if [ ! -x "$JAVACMD" ]; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
-if [ -z "$JAVA_HOME" ] ; then
- echo "Warning: JAVA_HOME environment variable is not set."
+if [ -z "$JAVA_HOME" ]; then
+ echo "Warning: JAVA_HOME environment variable is not set." >&2
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
- if [ -z "$1" ]
- then
- echo "Path not specified to find_maven_basedir"
+ if [ -z "$1" ]; then
+ echo "Path not specified to find_maven_basedir" >&2
return 1
fi
basedir="$1"
wdir="$1"
- while [ "$wdir" != '/' ] ; do
- if [ -d "$wdir"/.mvn ] ; then
+ while [ "$wdir" != '/' ]; do
+ if [ -d "$wdir"/.mvn ]; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
- wdir=$(cd "$wdir/.." || exit 1; pwd)
+ wdir=$(
+ cd "$wdir/.." || exit 1
+ pwd
+ )
fi
# end of workaround
done
- printf '%s' "$(cd "$basedir" || exit 1; pwd)"
+ printf '%s' "$(
+ cd "$basedir" || exit 1
+ pwd
+ )"
}
# concatenates all lines of a file
@@ -165,7 +182,7 @@ concat_lines() {
# enabled. Otherwise, we may read lines that are delimited with
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
# splitting rules.
- tr -s '\r\n' ' ' < "$1"
+ tr -s '\r\n' ' ' <"$1"
fi
}
@@ -177,75 +194,85 @@ log() {
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
if [ -z "$BASE_DIR" ]; then
- exit 1;
+ exit 1
fi
-MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+export MAVEN_PROJECTBASEDIR
log "$MAVEN_PROJECTBASEDIR"
+trim() {
+ # MWRAPPER-139:
+ # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+ # Needed for removing poorly interpreted newline sequences when running in more
+ # exotic environments such as mingw bash on Windows.
+ printf "%s" "${1}" | tr -d '[:space:]'
+}
+
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
if [ -r "$wrapperJarPath" ]; then
- log "Found $wrapperJarPath"
+ log "Found $wrapperJarPath"
else
- log "Couldn't find $wrapperJarPath, downloading it ..."
+ log "Couldn't find $wrapperJarPath, downloading it ..."
- if [ -n "$MVNW_REPOURL" ]; then
- wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ if [ -n "$MVNW_REPOURL" ]; then
+ wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar"
+ else
+ wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar"
+ fi
+ while IFS="=" read -r key value; do
+ case "$key" in wrapperUrl)
+ wrapperUrl=$(trim "${value-}")
+ break
+ ;;
+ esac
+ done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+ log "Downloading from: $wrapperUrl"
+
+ if $cygwin; then
+ wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+ fi
+
+ if command -v wget >/dev/null; then
+ log "Found wget ... using wget"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget ${QUIET:+"$QUIET"} "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
- wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ wget ${QUIET:+"$QUIET"} --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
- while IFS="=" read -r key value; do
- # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
- safeValue=$(echo "$value" | tr -d '\r')
- case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
- esac
- done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
- log "Downloading from: $wrapperUrl"
-
+ elif command -v curl >/dev/null; then
+ log "Found curl ... using curl"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl ${QUIET:+"$QUIET"} -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ else
+ curl ${QUIET:+"$QUIET"} --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ fi
+ else
+ log "Falling back to using Java to download"
+ javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
+ # For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
- wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+ javaSource=$(cygpath --path --windows "$javaSource")
+ javaClass=$(cygpath --path --windows "$javaClass")
fi
-
- if command -v wget > /dev/null; then
- log "Found wget ... using wget"
- [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
- if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
- wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
- else
- wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
- fi
- elif command -v curl > /dev/null; then
- log "Found curl ... using curl"
- [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
- if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
- curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
- else
- curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
- fi
- else
- log "Falling back to using Java to download"
- javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
- javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
- # For Cygwin, switch paths to Windows format before running javac
- if $cygwin; then
- javaSource=$(cygpath --path --windows "$javaSource")
- javaClass=$(cygpath --path --windows "$javaClass")
- fi
- if [ -e "$javaSource" ]; then
- if [ ! -e "$javaClass" ]; then
- log " - Compiling MavenWrapperDownloader.java ..."
- ("$JAVA_HOME/bin/javac" "$javaSource")
- fi
- if [ -e "$javaClass" ]; then
- log " - Running MavenWrapperDownloader.java ..."
- ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
- fi
- fi
+ if [ -e "$javaSource" ]; then
+ if [ ! -e "$javaClass" ]; then
+ log " - Compiling MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/javac" "$javaSource")
+ fi
+ if [ -e "$javaClass" ]; then
+ log " - Running MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
+ fi
fi
+ fi
fi
##########################################################################################
# End of extension
@@ -254,22 +281,25 @@ fi
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
wrapperSha256Sum=""
while IFS="=" read -r key value; do
- case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
+ case "$key" in wrapperSha256Sum)
+ wrapperSha256Sum=$(trim "${value-}")
+ break
+ ;;
esac
-done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
if [ -n "$wrapperSha256Sum" ]; then
wrapperSha256Result=false
- if command -v sha256sum > /dev/null; then
- if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
+ if command -v sha256sum >/dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c - >/dev/null 2>&1; then
wrapperSha256Result=true
fi
- elif command -v shasum > /dev/null; then
- if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
+ elif command -v shasum >/dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then
wrapperSha256Result=true
fi
else
- echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
- echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+ echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2
exit 1
fi
if [ $wrapperSha256Result = false ]; then
@@ -284,12 +314,12 @@ MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
- [ -n "$JAVA_HOME" ] &&
- JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
- [ -n "$CLASSPATH" ] &&
- CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
- [ -n "$MAVEN_PROJECTBASEDIR" ] &&
- MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+ [ -n "$JAVA_HOME" ] \
+ && JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] \
+ && CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+ [ -n "$MAVEN_PROJECTBASEDIR" ] \
+ && MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
fi
# Provide a "standardized" way to retrieve the CLI args that will
diff --git a/mvnw.cmd b/mvnw.cmd
index f80fbad..c453424 100644
--- a/mvnw.cmd
+++ b/mvnw.cmd
@@ -18,7 +18,7 @@
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
-@REM Apache Maven Wrapper startup batch script, version 3.2.0
+@REM Apache Maven Wrapper startup batch script, version 3.3.4
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@@ -59,22 +59,22 @@ set ERROR_CODE=0
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
-echo.
+echo. >&2
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
-echo.
+echo. >&2
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
-echo.
+echo. >&2
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
-echo.
+echo. >&2
goto error
@REM ==== END VALIDATION ====
@@ -119,7 +119,7 @@ SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
-set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
@@ -133,7 +133,7 @@ if exist %WRAPPER_JAR% (
)
) else (
if not "%MVNW_REPOURL%" == "" (
- SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.4/maven-wrapper-3.3.4.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
@@ -160,11 +160,12 @@ FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapp
)
IF NOT %WRAPPER_SHA_256_SUM%=="" (
powershell -Command "&{"^
+ "Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
- " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
- " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
- " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
+ " Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
+ " Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
+ " Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
" exit 1;"^
"}"^
"}"
diff --git a/pom.xml b/pom.xml
index 7008c50..2d92038 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
ai.tegmentum.webassembly4j
webassembly4j-parent
- 1.5.0
+ 2.5.2-SNAPSHOT
pom
WebAssembly4J Parent
@@ -43,7 +43,7 @@
wasmtime4j-provider
wamr4j-provider
wasm3-provider
- chicory4j-provider
+ endive4j-provider
graalwasm4j-provider
webassembly4j-runtime
webassembly4j-testing
@@ -52,6 +52,9 @@
webassembly4j-benchmarks
webassembly4j-bindgen
webassembly4j-component-builder
+ wasmos-embedder-generated
+ wasmos-runtime-generated
+ wasmos-provider
@@ -62,22 +65,22 @@
UTF-8
- 5.10.4
- 46.0.1-1.1.7
+ 5.14.4
+ 47.0.2-1.5.1
2.4.4-1.0.2
- 1.7.3
- 24.2.1
+ 1.1.0
+ 24.2.2
1.37
- 2.15.2
- 3.2.5
+ 2.22.2
+ 3.5.16
- 3.12.1
- 3.2.5
- 3.3.0
- 3.3.0
- 3.6.3
- 1.6.0
+ 3.16.0
+ 3.6.0
+ 3.5.1
+ 3.4.0
+ 3.12.0
+ 1.8.0
@@ -92,6 +95,16 @@
webassembly4j-spi
${project.version}
+
+ ai.tegmentum.webassembly4j
+ wasmos-embedder-generated
+ ${project.version}
+
+
+ ai.tegmentum.webassembly4j
+ wasmos-runtime-generated
+ ${project.version}
+
ai.tegmentum
wasmtime4j
@@ -123,24 +136,24 @@
${wamr4j.version}
- com.dylibso.chicory
+ run.endive
runtime
- ${chicory.version}
+ ${endive.version}
- com.dylibso.chicory
+ run.endive
wasm
- ${chicory.version}
+ ${endive.version}
- com.dylibso.chicory
+ run.endive
wasi
- ${chicory.version}
+ ${endive.version}
- com.dylibso.chicory
+ run.endive
compiler
- ${chicory.version}
+ ${endive.version}
org.graalvm.polyglot
@@ -304,7 +317,7 @@
org.apache.maven.plugins
maven-gpg-plugin
- 3.1.0
+ 3.2.8
sign-artifacts
diff --git a/wamr4j-provider/pom.xml b/wamr4j-provider/pom.xml
index 50c46c5..0d6f270 100644
--- a/wamr4j-provider/pom.xml
+++ b/wamr4j-provider/pom.xml
@@ -8,7 +8,7 @@
ai.tegmentum.webassembly4j
webassembly4j-parent
- 1.5.0
+ 2.5.2-SNAPSHOT
wamr4j-provider
diff --git a/wasm3-provider/pom.xml b/wasm3-provider/pom.xml
index 8522d4f..3566a5d 100644
--- a/wasm3-provider/pom.xml
+++ b/wasm3-provider/pom.xml
@@ -8,7 +8,7 @@
ai.tegmentum.webassembly4j
webassembly4j-parent
- 1.5.0
+ 2.5.2-SNAPSHOT
wasm3-provider
diff --git a/wasmos-embedder-generated/pom.xml b/wasmos-embedder-generated/pom.xml
new file mode 100644
index 0000000..480ac8a
--- /dev/null
+++ b/wasmos-embedder-generated/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+
+
+ ai.tegmentum.webassembly4j
+ webassembly4j-parent
+ 2.5.2-SNAPSHOT
+
+
+ wasmos-embedder-generated
+ jar
+
+ WebAssembly4J :: wasmos-embedder generated bindings
+
+ Java bindings generated by webassembly4j-bindgen from
+ wasmos:host/embedder@0.1.0 (per ADR-006). The sole content of this
+ module is:
+
+ - The 8 generated source files under
+ ai.tegmentum.wasmos.embedder.generated:
+ * RuntimeInstance, HostProvider — resources whose bodies
+ dispatch through the bindgen-2.0 runtime-provider SPI
+ (see EmbedderRuntime below).
+ * EmbedderCallbacks, ImportSatisfaction, Error, ErrorCode.
+ * EmbedderRuntime — bindgen-2.0 runtime-provider SPI. The
+ embedder implements this once (e.g.
+ wasi-p2-rs's WasmcmEmbedderRuntime) and installs it via
+ EmbedderRuntimeRegistry.install(...).
+ * EmbedderRuntimeRegistry — single-slot registry the
+ generated resource bodies consult.
+ - A local copy of the WitResult carrier at
+ ai.tegmentum.webassembly4j.bindgen.wit.WitResult so the module
+ has no compile-time or runtime dependency on
+ webassembly4j-bindgen (which is a maven-plugin-packaged
+ build-time-only artifact).
+
+ Downstream embedders (wasi-p2-rs, wasm-cm) depend on this jar for
+ the ADR-006 embedder surface and provide a concrete EmbedderRuntime
+ implementation that maps the SPI onto their runtime driver
+ (WasmcmRuntimeGuestLoader for the JVM lane).
+
+ Regenerate the wasmos.embedder.generated files with:
+
+ java -jar webassembly4j-bindgen/target/webassembly4j-bindgen-2.5.1-cli.jar \
+ --wit ~/git/wasm-cm/wit/wasmos-host/embedder.wit \
+ --package ai.tegmentum.wasmos.embedder.generated \
+ --output wasmos-embedder-generated/src/main/java \
+ --no-impl \
+ --runtime-provider EmbedderRuntime
+
+
+
+
+ ai.tegmentum.webassembly4j
+ webassembly4j-api
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 11
+
+
+
+
+
diff --git a/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/EmbedderApi.java b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/EmbedderApi.java
new file mode 100644
index 0000000..d13fb50
--- /dev/null
+++ b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/EmbedderApi.java
@@ -0,0 +1,8 @@
+package ai.tegmentum.wasmos.embedder.generated;
+
+import ai.tegmentum.webassembly4j.bindgen.wit.WitResult;
+import java.util.List;
+
+public interface EmbedderApi {
+ WitResult, Error> dispatchHostCallback(long callbackId, List argsFrame);
+}
diff --git a/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/EmbedderCallbacks.java b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/EmbedderCallbacks.java
new file mode 100644
index 0000000..ff532f4
--- /dev/null
+++ b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/EmbedderCallbacks.java
@@ -0,0 +1,12 @@
+package ai.tegmentum.wasmos.embedder.generated;
+
+import ai.tegmentum.webassembly4j.bindgen.wit.WitResult;
+import java.util.List;
+
+public interface EmbedderCallbacks {
+ WitResult, String> hostCall(int providerHandle, int funcIdx, List args);
+
+ long nowMonotonicNs();
+
+ List readRandom(int len);
+}
diff --git a/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/EmbedderRuntime.java b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/EmbedderRuntime.java
new file mode 100644
index 0000000..0b421ba
--- /dev/null
+++ b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/EmbedderRuntime.java
@@ -0,0 +1,32 @@
+package ai.tegmentum.wasmos.embedder.generated;
+
+import ai.tegmentum.webassembly4j.bindgen.wit.WitResult;
+import java.util.List;
+
+/**
+ * Runtime-provider SPI for the generated bindings.
+ *
+ * The embedder implements this interface once and installs the implementation
+ * via {@link EmbedderRuntimeRegistry#install(EmbedderRuntime)}. Generated resource method bodies dispatch
+ * through {@link EmbedderRuntimeRegistry#runtime()} to reach the installed provider.
+ *
+ *
Emitted by webassembly4j-bindgen 2.0 (runtime-provider dispatch mode).
+ */
+public interface EmbedderRuntime {
+ HostProvider hostProviderCreate(String interfaceName, int numFuncs);
+
+ void hostProviderAttachMemory(long handle, HostProviderMemory mem);
+
+ void hostProviderClose(long handle);
+
+ WitResult runtimeInstanceInstantiate(List componentBytes,
+ List imports);
+
+ WitResult, Error> runtimeInstanceCallExport(long handle, String name, List args);
+
+ String runtimeInstanceIntrospect(long handle);
+
+ WitResult runtimeInstanceVerifyWorld(long handle, String expectedWit);
+
+ void runtimeInstanceClose(long handle);
+}
diff --git a/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/EmbedderRuntimeRegistry.java b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/EmbedderRuntimeRegistry.java
new file mode 100644
index 0000000..a998298
--- /dev/null
+++ b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/EmbedderRuntimeRegistry.java
@@ -0,0 +1,36 @@
+package ai.tegmentum.wasmos.embedder.generated;
+
+import java.util.Objects;
+
+/**
+ * Single-slot registry for the {@link EmbedderRuntime} runtime-provider SPI.
+ *
+ * Call {@link #install(EmbedderRuntime)} once from the embedder's startup path, before
+ * invoking any generated resource method. Generated bodies reach the installed
+ * provider via {@link #runtime()}, which throws {@link IllegalStateException} if
+ * nothing was installed.
+ *
+ *
Emitted by webassembly4j-bindgen 2.0.
+ */
+public final class EmbedderRuntimeRegistry {
+ private static volatile EmbedderRuntime provider;
+
+ private EmbedderRuntimeRegistry() {
+ }
+
+ public static void install(EmbedderRuntime provider) {
+ EmbedderRuntimeRegistry.provider = Objects.requireNonNull(provider, "provider");
+ }
+
+ public static void uninstall() {
+ EmbedderRuntimeRegistry.provider = null;
+ }
+
+ public static EmbedderRuntime runtime() {
+ EmbedderRuntime r = provider;
+ if (r == null) {
+ throw new IllegalStateException("EmbedderRuntime not installed; call EmbedderRuntimeRegistry.install(...) before invoking generated bindings");
+ }
+ return r;
+ }
+}
diff --git a/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/Error.java b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/Error.java
new file mode 100644
index 0000000..09cbbd1
--- /dev/null
+++ b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/Error.java
@@ -0,0 +1,38 @@
+package ai.tegmentum.wasmos.embedder.generated;
+
+public final class Error {
+ private final ErrorCode code;
+
+ private final String message;
+
+ public Error(ErrorCode code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+
+ public ErrorCode code() {
+ return this.code;
+ }
+
+ public String message() {
+ return this.message;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ Error that = (Error) obj;
+ return java.util.Objects.equals(this.code, that.code) && java.util.Objects.equals(this.message, that.message);
+ }
+
+ @Override
+ public int hashCode() {
+ return java.util.Objects.hash(code, message);
+ }
+
+ @Override
+ public String toString() {
+ return "Error[code=" + code + ", message=" + message + "]";
+ }
+}
diff --git a/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/ErrorCode.java b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/ErrorCode.java
new file mode 100644
index 0000000..13f028a
--- /dev/null
+++ b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/ErrorCode.java
@@ -0,0 +1,17 @@
+package ai.tegmentum.wasmos.embedder.generated;
+
+public enum ErrorCode {
+ INVALID_COMPONENT,
+
+ UNSATISFIED_IMPORT,
+
+ MISSING_EXPORT,
+
+ HOST_CALLBACK_FAILED,
+
+ TRAP,
+
+ WIT_MISMATCH,
+
+ OTHER
+}
diff --git a/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/HostProvider.java b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/HostProvider.java
new file mode 100644
index 0000000..789ea52
--- /dev/null
+++ b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/HostProvider.java
@@ -0,0 +1,31 @@
+package ai.tegmentum.wasmos.embedder.generated;
+
+/**
+ * Resource type: host-provider
+ *
+ *
This resource should be closed after use.
+ */
+public class HostProvider implements AutoCloseable {
+ private final long handle;
+
+ public HostProvider(long handle) {
+ this.handle = handle;
+ }
+
+ public long handle() {
+ return this.handle;
+ }
+
+ @Override
+ public void close() {
+ EmbedderRuntimeRegistry.runtime().hostProviderClose(this.handle);
+ }
+
+ public static HostProvider create(String interfaceName, int numFuncs) {
+ return EmbedderRuntimeRegistry.runtime().hostProviderCreate(interfaceName, numFuncs);
+ }
+
+ public void attachMemory(HostProviderMemory mem) {
+ EmbedderRuntimeRegistry.runtime().hostProviderAttachMemory(this.handle, mem);
+ }
+}
diff --git a/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/HostProviderMemory.java b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/HostProviderMemory.java
new file mode 100644
index 0000000..4496d12
--- /dev/null
+++ b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/HostProviderMemory.java
@@ -0,0 +1,45 @@
+package ai.tegmentum.wasmos.embedder.generated;
+
+public final class HostProviderMemory {
+ private final long memory;
+
+ private final long realloc;
+
+ private final StringEncoding stringEncoding;
+
+ public HostProviderMemory(long memory, long realloc, StringEncoding stringEncoding) {
+ this.memory = memory;
+ this.realloc = realloc;
+ this.stringEncoding = stringEncoding;
+ }
+
+ public long memory() {
+ return this.memory;
+ }
+
+ public long realloc() {
+ return this.realloc;
+ }
+
+ public StringEncoding stringEncoding() {
+ return this.stringEncoding;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ HostProviderMemory that = (HostProviderMemory) obj;
+ return this.memory == that.memory && this.realloc == that.realloc && java.util.Objects.equals(this.stringEncoding, that.stringEncoding);
+ }
+
+ @Override
+ public int hashCode() {
+ return java.util.Objects.hash(memory, realloc, stringEncoding);
+ }
+
+ @Override
+ public String toString() {
+ return "HostProviderMemory[memory=" + memory + ", realloc=" + realloc + ", stringEncoding=" + stringEncoding + "]";
+ }
+}
diff --git a/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/ImportSatisfaction.java b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/ImportSatisfaction.java
new file mode 100644
index 0000000..e8cb76e
--- /dev/null
+++ b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/ImportSatisfaction.java
@@ -0,0 +1,38 @@
+package ai.tegmentum.wasmos.embedder.generated;
+
+public final class ImportSatisfaction {
+ private final String interfaceName;
+
+ private final HostProvider provider;
+
+ public ImportSatisfaction(String interfaceName, HostProvider provider) {
+ this.interfaceName = interfaceName;
+ this.provider = provider;
+ }
+
+ public String interfaceName() {
+ return this.interfaceName;
+ }
+
+ public HostProvider provider() {
+ return this.provider;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ ImportSatisfaction that = (ImportSatisfaction) obj;
+ return java.util.Objects.equals(this.interfaceName, that.interfaceName) && java.util.Objects.equals(this.provider, that.provider);
+ }
+
+ @Override
+ public int hashCode() {
+ return java.util.Objects.hash(interfaceName, provider);
+ }
+
+ @Override
+ public String toString() {
+ return "ImportSatisfaction[interfaceName=" + interfaceName + ", provider=" + provider + "]";
+ }
+}
diff --git a/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/RuntimeInstance.java b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/RuntimeInstance.java
new file mode 100644
index 0000000..b65247a
--- /dev/null
+++ b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/RuntimeInstance.java
@@ -0,0 +1,43 @@
+package ai.tegmentum.wasmos.embedder.generated;
+
+import ai.tegmentum.webassembly4j.bindgen.wit.WitResult;
+import java.util.List;
+
+/**
+ * Resource type: runtime-instance
+ *
+ *
This resource should be closed after use.
+ */
+public class RuntimeInstance implements AutoCloseable {
+ private final long handle;
+
+ public RuntimeInstance(long handle) {
+ this.handle = handle;
+ }
+
+ public long handle() {
+ return this.handle;
+ }
+
+ @Override
+ public void close() {
+ EmbedderRuntimeRegistry.runtime().runtimeInstanceClose(this.handle);
+ }
+
+ public static WitResult instantiate(List componentBytes,
+ List imports) {
+ return EmbedderRuntimeRegistry.runtime().runtimeInstanceInstantiate(componentBytes, imports);
+ }
+
+ public WitResult, Error> callExport(String name, List args) {
+ return EmbedderRuntimeRegistry.runtime().runtimeInstanceCallExport(this.handle, name, args);
+ }
+
+ public String introspect() {
+ return EmbedderRuntimeRegistry.runtime().runtimeInstanceIntrospect(this.handle);
+ }
+
+ public WitResult verifyWorld(String expectedWit) {
+ return EmbedderRuntimeRegistry.runtime().runtimeInstanceVerifyWorld(this.handle, expectedWit);
+ }
+}
diff --git a/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/StringEncoding.java b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/StringEncoding.java
new file mode 100644
index 0000000..3109daa
--- /dev/null
+++ b/wasmos-embedder-generated/src/main/java/ai/tegmentum/wasmos/embedder/generated/StringEncoding.java
@@ -0,0 +1,18 @@
+package ai.tegmentum.wasmos.embedder.generated;
+
+public interface StringEncoding {
+ final class Utf8 implements StringEncoding {
+ public Utf8() {
+ }
+ }
+
+ final class Utf16 implements StringEncoding {
+ public Utf16() {
+ }
+ }
+
+ final class Latin1OrUtf16 implements StringEncoding {
+ public Latin1OrUtf16() {
+ }
+ }
+}
diff --git a/wasmos-embedder-generated/src/main/java/ai/tegmentum/webassembly4j/bindgen/wit/WitResult.java b/wasmos-embedder-generated/src/main/java/ai/tegmentum/webassembly4j/bindgen/wit/WitResult.java
new file mode 100644
index 0000000..ba37ff4
--- /dev/null
+++ b/wasmos-embedder-generated/src/main/java/ai/tegmentum/webassembly4j/bindgen/wit/WitResult.java
@@ -0,0 +1,127 @@
+package ai.tegmentum.webassembly4j.bindgen.wit;
+
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+
+/**
+ * Runtime representation of the WIT {@code result} type.
+ *
+ * The bindgen code generator references this class by its fully-qualified
+ * name (see {@link
+ * ai.tegmentum.webassembly4j.bindgen.generator.TypeMappingRegistry}); it must
+ * therefore exist as a real class on the consumer's classpath. The two
+ * factory methods {@link #ok(Object)} and {@link #err(Object)} construct
+ * either side; the resulting instance is immutable.
+ *
+ *
Generated code typically follows this pattern:
+ *
+ *
{@code
+ * WitResult result = parser.parse(language, source);
+ * if (result.isOk()) {
+ * ParseResult tree = result.unwrap();
+ * } else {
+ * String msg = result.unwrapErr();
+ * }
+ * }
+ *
+ * @param the success-payload type ({@link Void} when the WIT signature is
+ * {@code result<_, err>})
+ * @param the error-payload type ({@link Void} when the WIT signature is
+ * {@code result} or {@code result})
+ */
+public final class WitResult {
+
+ private final T ok;
+ private final E err;
+ private final boolean isOk;
+
+ private WitResult(final T ok, final E err, final boolean isOk) {
+ this.ok = ok;
+ this.err = err;
+ this.isOk = isOk;
+ }
+
+ /** Construct a successful result. */
+ public static WitResult ok(final T value) {
+ return new WitResult<>(value, null, true);
+ }
+
+ /** Construct an error result. */
+ public static WitResult err(final E value) {
+ return new WitResult<>(null, value, false);
+ }
+
+ /** True if this is a success result. */
+ public boolean isOk() {
+ return isOk;
+ }
+
+ /** True if this is an error result. */
+ public boolean isErr() {
+ return !isOk;
+ }
+
+ /** Returns the success payload or throws when {@link #isErr()}. */
+ public T unwrap() {
+ if (!isOk) {
+ throw new NoSuchElementException("WitResult is err: " + err);
+ }
+ return ok;
+ }
+
+ /** Returns the error payload or throws when {@link #isOk()}. */
+ public E unwrapErr() {
+ if (isOk) {
+ throw new NoSuchElementException("WitResult is ok: " + ok);
+ }
+ return err;
+ }
+
+ /** Optional view of the success side. */
+ public Optional ok() {
+ return isOk ? Optional.ofNullable(ok) : Optional.empty();
+ }
+
+ /** Optional view of the error side. */
+ public Optional err() {
+ return isOk ? Optional.empty() : Optional.ofNullable(err);
+ }
+
+ /** Map the success payload through {@code f}, leaving an error unchanged. */
+ public WitResult map(final Function super T, ? extends U> f) {
+ Objects.requireNonNull(f, "f");
+ return isOk ? WitResult.ok(f.apply(ok)) : WitResult.err(err);
+ }
+
+ /** Map the error payload through {@code f}, leaving a success unchanged. */
+ public WitResult mapErr(final Function super E, ? extends F> f) {
+ Objects.requireNonNull(f, "f");
+ return isOk ? WitResult.ok(ok) : WitResult.err(f.apply(err));
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof WitResult)) {
+ return false;
+ }
+ final WitResult, ?> other = (WitResult, ?>) o;
+ return isOk == other.isOk
+ && Objects.equals(ok, other.ok)
+ && Objects.equals(err, other.err);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ok, err, isOk);
+ }
+
+ @Override
+ public String toString() {
+ return isOk ? "Ok(" + ok + ")" : "Err(" + err + ")";
+ }
+}
diff --git a/wasmos-provider/.gitignore b/wasmos-provider/.gitignore
new file mode 100644
index 0000000..70ebdef
--- /dev/null
+++ b/wasmos-provider/.gitignore
@@ -0,0 +1,15 @@
+# Cargo build tree — lives outside Maven's target/ (see pom.xml exec-maven-plugin
+# CARGO_TARGET_DIR env var) so `mvn clean` doesn't blow away the Rust cache.
+# Everything under here is regenerated by cargo build; never committed.
+.cargo-target/
+
+# Standard cargo target dir — used if cargo is invoked directly without the
+# CARGO_TARGET_DIR override.
+native/target/
+
+# Native library outputs staged by the maven-antrun step into
+# src/main/resources/natives/{platform}/ — these are regenerated by every
+# build from the cargo output, so tracking them creates the stale-dylib
+# landmine we hit in wasmtime4j (see local-dev profile discussion). The
+# maven build's unpack-dependencies overlay is authoritative.
+src/main/resources/natives/
diff --git a/wasmos-provider/README.md b/wasmos-provider/README.md
new file mode 100644
index 0000000..ebbfeb8
--- /dev/null
+++ b/wasmos-provider/README.md
@@ -0,0 +1,119 @@
+# wasmos-provider
+
+`webassembly4j` provider backed by the Rust
+[`wasmos-runtime`](https://github.com/tegmentum/wasmos) library. Wraps
+wasmos-runtime's `add_all_to_linker` + `caps::wasi_p2` surface via JNI
+so Java consumers can host wasmos-shaped WASM components (typically
+control-plane guests composed with an adapter) through
+webassembly4j's normal `Engine` / `Component` / `ComponentInstance` API.
+
+Selected via `EngineSelection.byId("wasmos")`; provider priority 90.
+
+## What works
+
+- Load a wasmos-composed component from bytes
+- Instantiate with default caps or a `LinkingContext.wasiContext()`
+ ("wasi_p2" cap granted through wasmos-runtime)
+- Honor `ComponentConfig` fields: `maxMemoryBytes`, `epochDeadline`,
+ `fuelLimit`, `maxTableElements`, `maxInstances`, `maxTables`,
+ `maxMemories`
+- Invoke exported functions — sync path (`ComponentInstance.invoke`)
+ and async path (`WasmosAsyncExtension.invokeAsync` returning
+ `CompletableFuture`) with best-effort cancellation
+- Two-way marshalling for primitives, `String`, `List`, `Map`,
+ `Set` (flags), `Optional`, tuples, `byte[]` (list fast path),
+ and the typed carriers `WitResource`, `WitFuture`, `WitStream`,
+ `WitErrorContext`
+- Component serialize / deserialize for hot-reload
+- Nested-interface function lookup (functions inside exported
+ interfaces resolve via `#` syntax)
+
+## What is intentionally not supported
+
+- Java-provided core-wasm host functions (`LinkingContext.hostFunctions`)
+ — wasmos is WIT-first; use `wasmtime4j-provider` if you need to inject
+ Java host functions into a core-wasm module
+- `Engine.loadModule` (core-wasm loading) — wasmos-provider is
+ component-only
+- Host-side reading of `Val::Future` / `Val::Stream` — wasmtime 47's
+ typed reader API doesn't support dynamic-payload polling. Parking,
+ closing, and passing these values back into guests works; awaiting or
+ iterating them host-side throws a documented "wasmtime API gap".
+ When wasmtime upstream ships the dynamic reader, the stub JNI
+ entries (`futureAwait`, `streamRead`) can be filled in without a
+ Java surface change.
+- `wasi:nn`, `wasi:http`, per-endpoint network egress — needs
+ wasmtime-wasi upstream API surface
+
+## Build setup — REQUIRES sibling repository checkouts
+
+`wasmos-runtime` (this module's Rust dep) is declared via a RELATIVE
+path (`../../../wasmos` from `wasmos-provider/native/Cargo.toml`),
+which itself declares path deps on other tegmentum repositories that
+must be co-checked-out at fixed relative paths. **Building this crate
+from a fresh clone of `webassembly4j` alone WILL fail.** You need
+four sibling checkouts:
+
+```
+~/git/webassembly4j (this repo)
+~/git/wasmos (wasmos-provider/native/Cargo.toml points here via ../../../wasmos)
+~/git/wasm-cm (wasmos's sibling path dep, ../wasm-cm/...)
+~/git/wasm-continuity (wasmos's sibling path dep, ../wasm-continuity/...)
+```
+
+Once all four are checked out at the same parent (any parent — the
+paths are all relative), `cd wasmos-provider && mvn install` from the
+webassembly4j root works.
+
+### Why not git-dep?
+
+`wasmos-runtime = { git = "..." }` fails at build time because cargo
+fetches wasmos from GitHub but then cannot resolve wasmos's own
+`../wasm-cm/...` and `../wasm-continuity/...` path deps in the fetched
+tree.
+
+### Why not crates.io?
+
+Neither `wasmos-runtime`, `wasm-cm`, nor `wasm-continuity` publishes
+versioned crates yet. Publishing wasmos-provider to Maven Central is
+blocked on this.
+
+### Paths to unblock external / CI builds
+
+1. wasmos + wasm-cm + wasm-continuity publish versioned crates.
+2. wasmos restructures to git-dep (or workspace-internal) its
+ siblings — no more `path = "..//..."`.
+3. Fork wasmos into a git-dep-clean tree.
+
+The `wasmos-provider/native/Cargo.toml` comment tracks the same
+information for cargo-only readers.
+
+## Layout
+
+```
+wasmos-provider/
+├── pom.xml # exec-maven-plugin runs cargo
+├── native/
+│ ├── Cargo.toml # path dep to ~/git/wasmos
+│ └── src/lib.rs # JNI extern "C" shims
+└── src/main/java/ai/tegmentum/webassembly4j/provider/wasmos/
+ ├── WasmosProvider.java # EngineProvider SPI impl
+ ├── WasmosEngineAdapter.java
+ ├── WasmosComponentAdapter.java
+ ├── WasmosComponentInstanceAdapter.java
+ ├── WasmosMarshalling.java # WIT-shape carriers + JSON codec
+ ├── ext/
+ │ ├── WasmosAsyncExtension.java # invokeAsync + futures/streams
+ │ └── WitErrorContextException.java
+ └── jni/
+ ├── WasmosNative.java # native method decls
+ └── WasmosNativeLoader.java # extracts dylib from jar
+```
+
+## Testing
+
+`mvn test -pl wasmos-provider` runs the Java suite (62 tests) plus
+the Rust unit tests (19). Includes an end-to-end test that loads
+`~/git/wasmos/tests/e2e-fixtures/composed_wasmtime.wasm` — assumeTrue-
+skipped if the fixture path doesn't exist. The wasmos-composed component
+returns 42 from its `run` export.
diff --git a/wasmos-provider/native/Cargo.lock b/wasmos-provider/native/Cargo.lock
new file mode 100644
index 0000000..947d0ad
--- /dev/null
+++ b/wasmos-provider/native/Cargo.lock
@@ -0,0 +1,3932 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "addr2line"
+version = "0.26.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59317f77929f0e679d39364702289274de2f0f0b22cbf50b2b8cff2169a0b27a"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
+
+[[package]]
+name = "ambient-authority"
+version = "0.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
+
+[[package]]
+name = "arbitrary"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
+
+[[package]]
+name = "arrayref"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
+
+[[package]]
+name = "async-trait"
+version = "0.1.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "autocfg"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
+
+[[package]]
+name = "aws-lc-rs"
+version = "1.17.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1"
+dependencies = [
+ "aws-lc-sys",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c"
+dependencies = [
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+ "pkg-config",
+]
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bitflags"
+version = "2.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
+
+[[package]]
+name = "blake3"
+version = "1.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "cc",
+ "cfg-if",
+ "constant_time_eq",
+ "cpufeatures 0.3.0",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
+dependencies = [
+ "hybrid-array",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.20.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
+dependencies = [
+ "allocator-api2",
+]
+
+[[package]]
+name = "bytes"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
+
+[[package]]
+name = "cap-fs-ext"
+version = "4.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d78e5a3368ae89b7cb68186411452b4b9fac8b41be9c19bf3f47c2d2c8e36e6b"
+dependencies = [
+ "cap-primitives",
+ "cap-std",
+ "io-lifetimes 3.0.1",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "cap-primitives"
+version = "4.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdadbd7c002d3a484b35243669abdae85a0ebaded5a61117169dc3400f9a7ff0"
+dependencies = [
+ "ambient-authority",
+ "fs-set-times",
+ "io-extras",
+ "io-lifetimes 3.0.1",
+ "ipnet",
+ "maybe-owned",
+ "rustix",
+ "rustix-linux-procfs",
+ "windows-sys 0.61.2",
+ "winx",
+]
+
+[[package]]
+name = "cap-std"
+version = "4.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7281235d6e96d3544ca18bba9049be92f4190f8d923e3caef1b5f66cfa752608"
+dependencies = [
+ "cap-primitives",
+ "io-extras",
+ "io-lifetimes 3.0.1",
+ "rustix",
+]
+
+[[package]]
+name = "cc"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+dependencies = [
+ "find-msvc-tools",
+ "jobserver",
+ "libc",
+ "shlex",
+]
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
+
+[[package]]
+name = "chacha20"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.3.0",
+ "rand_core 0.10.1",
+]
+
+[[package]]
+name = "chrono"
+version = "0.4.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
+dependencies = [
+ "iana-time-zone",
+ "num-traits",
+ "serde",
+ "windows-link",
+]
+
+[[package]]
+name = "cmake"
+version = "0.1.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "cobs"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
+dependencies = [
+ "thiserror 2.0.19",
+]
+
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "constant_time_eq"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
+
+[[package]]
+name = "core-foundation"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "cpp_demangle"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0667304c32ea56cb4cd6d2d7c0cfe9a2f8041229db8c033af7f8d69492429def"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cranelift-assembler-x64"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f0435c6939d6ef218bf0a3063094f2cc5c46f7a44a88f81cf932da8d3922ff8"
+dependencies = [
+ "cranelift-assembler-x64-meta",
+]
+
+[[package]]
+name = "cranelift-assembler-x64-meta"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aca52511cc21a2c9c927aef493d11c8087bdc5483249a3d93aee41e81b8bd4d1"
+dependencies = [
+ "cranelift-srcgen",
+]
+
+[[package]]
+name = "cranelift-bforest"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6867e99f068454b0c81c50b807af88aa80aaf383105e2be26675d81fc4278466"
+dependencies = [
+ "cranelift-entity",
+ "wasmtime-internal-core",
+]
+
+[[package]]
+name = "cranelift-bitset"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cf66017aed971344230199ad35ca01c955cf824a0e65fed5240452d2a302445"
+dependencies = [
+ "serde",
+ "serde_derive",
+ "wasmtime-internal-core",
+]
+
+[[package]]
+name = "cranelift-codegen"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "933b2e5237665bf9367a34ad2100eed70c94b2796586cb09d9ba11693e061667"
+dependencies = [
+ "bumpalo",
+ "cranelift-assembler-x64",
+ "cranelift-bforest",
+ "cranelift-bitset",
+ "cranelift-codegen-meta",
+ "cranelift-codegen-shared",
+ "cranelift-control",
+ "cranelift-entity",
+ "cranelift-isle",
+ "gimli",
+ "hashbrown 0.17.1",
+ "libm",
+ "log",
+ "postcard",
+ "pulley-interpreter",
+ "regalloc2",
+ "rustc-hash",
+ "serde",
+ "serde_derive",
+ "sha2",
+ "smallvec",
+ "target-lexicon",
+ "wasmtime-internal-core",
+]
+
+[[package]]
+name = "cranelift-codegen-meta"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "322e3ca0603683cc17f0c808559c0fb85ad52b0f20ace0478bdd4ba2fa1e9678"
+dependencies = [
+ "cranelift-assembler-x64-meta",
+ "cranelift-codegen-shared",
+ "cranelift-srcgen",
+ "heck",
+ "pulley-interpreter",
+]
+
+[[package]]
+name = "cranelift-codegen-shared"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "287ab88211d049178dc8dda243e2db176919d8b29db605c0efc7c6d76ebea0f7"
+
+[[package]]
+name = "cranelift-control"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c11438f9becf9a1e1e061bf76a33241b941a07ed01a489e9dad5fa79dc3624e9"
+dependencies = [
+ "arbitrary",
+]
+
+[[package]]
+name = "cranelift-entity"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "584ecabe1462593a9cba9efbf9022e0987421ddb548989b455ab8bdd444b743c"
+dependencies = [
+ "cranelift-bitset",
+ "serde",
+ "serde_derive",
+ "wasmtime-internal-core",
+]
+
+[[package]]
+name = "cranelift-frontend"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34249873145213a12106d5fd8a98a9dc9e90e16525e2337b2907874d1e7e9ee7"
+dependencies = [
+ "cranelift-codegen",
+ "hashbrown 0.17.1",
+ "log",
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cranelift-isle"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6f1c1dc4d6b4bc4f0be12b08cee0e59f7610287827aa62590f8e9ec6f45dff4"
+
+[[package]]
+name = "cranelift-native"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31d9e4f6d9a3777b1ffa818000b4a6d7afd1b8787af71a9c58d94baef9076ced"
+dependencies = [
+ "cranelift-codegen",
+ "libc",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cranelift-srcgen"
+version = "0.134.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4155f8a1afdef96a0272c67804926eee4ef35f9708ab22ad6878d4c9543df596"
+
+[[package]]
+name = "crc-fast"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e75b2483e97a5a7da73ac68a05b629f9c53cff58d8ed1c77866079e18b00dba5"
+dependencies = [
+ "digest 0.10.7",
+ "spin",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
+dependencies = [
+ "hybrid-array",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
+
+[[package]]
+name = "debugid"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
+dependencies = [
+ "uuid",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer 0.10.4",
+ "crypto-common 0.1.7",
+]
+
+[[package]]
+name = "digest"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
+dependencies = [
+ "block-buffer 0.12.1",
+ "crypto-common 0.2.2",
+]
+
+[[package]]
+name = "directories-next"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
+dependencies = [
+ "cfg-if",
+ "dirs-sys-next",
+]
+
+[[package]]
+name = "dirs-sys-next"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
+]
+
+[[package]]
+name = "dunce"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
+[[package]]
+name = "either"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
+
+[[package]]
+name = "embedded-io"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
+
+[[package]]
+name = "embedded-io"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "errno"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foldhash"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "fs-set-times"
+version = "0.20.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94e7099f6313ecacbe1256e8ff9d617b75d1bcb16a6fddef94866d225a01a14a"
+dependencies = [
+ "io-lifetimes 2.0.4",
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
+[[package]]
+name = "futures"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307"
+
+[[package]]
+name = "futures-task"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
+
+[[package]]
+name = "futures-util"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "fxprof-processed-profile"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25234f20a3ec0a962a61770cfe39ecf03cb529a6e474ad8cff025ed497eda557"
+dependencies = [
+ "bitflags",
+ "debugid",
+ "rustc-hash",
+ "serde",
+ "serde_derive",
+ "serde_json",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi 5.3.0",
+ "wasip2",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "r-efi 6.0.0",
+ "rand_core 0.10.1",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gimli"
+version = "0.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf7f043f89559805f8c7cacc432749b2fa0d0a0a9ee46ce47164ed5ba7f126c"
+dependencies = [
+ "fnv",
+ "hashbrown 0.16.1",
+ "indexmap",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "h2"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+
+[[package]]
+name = "hashbrown"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
+dependencies = [
+ "foldhash",
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "http"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0"
+dependencies = [
+ "bytes",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+
+[[package]]
+name = "humantime"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15"
+
+[[package]]
+name = "hybrid-array"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c"
+dependencies = [
+ "typenum",
+]
+
+[[package]]
+name = "hyper"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-rustls"
+version = "0.27.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
+dependencies = [
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+ "webpki-roots",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
+dependencies = [
+ "base64",
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "ipnet",
+ "libc",
+ "percent-encoding",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "utf8_iter",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
+dependencies = [
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
+
+[[package]]
+name = "icu_properties"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
+dependencies = [
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
+
+[[package]]
+name = "icu_provider"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "id-arena"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
+
+[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.17.1",
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "io-extras"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20fd6de4ccfcc187e38bc21cfa543cb5a302cb86a8b114eb7f0bf0dc9f8ac00f"
+dependencies = [
+ "io-lifetimes 3.0.1",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983"
+
+[[package]]
+name = "io-lifetimes"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f0fb0570afe1fed943c5c3d4102d5358592d8625fda6a0007fdbe65a92fba96"
+
+[[package]]
+name = "ipnet"
+version = "2.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
+
+[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+
+[[package]]
+name = "ittapi"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b996fe614c41395cdaedf3cf408a9534851090959d90d54a535f675550b64b1"
+dependencies = [
+ "anyhow",
+ "ittapi-sys",
+ "log",
+]
+
+[[package]]
+name = "ittapi-sys"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52f5385394064fa2c886205dba02598013ce83d3e92d33dbdc0c52fe0e7bf4fc"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys 0.3.1",
+ "log",
+ "thiserror 1.0.69",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
+dependencies = [
+ "cfg-if",
+ "combine",
+ "jni-macros",
+ "jni-sys 0.4.1",
+ "log",
+ "simd_cesu8",
+ "thiserror 2.0.19",
+ "walkdir",
+ "windows-link",
+]
+
+[[package]]
+name = "jni-macros"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "simd_cesu8",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258"
+dependencies = [
+ "jni-sys 0.4.1",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
+dependencies = [
+ "jni-sys-macros",
+]
+
+[[package]]
+name = "jni-sys-macros"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
+dependencies = [
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "jobserver"
+version = "0.1.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
+dependencies = [
+ "getrandom 0.4.3",
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "leb128"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c83bff1d572d6b9aeef67ddfc8448e4a3737909cb28e81f97c791b9018703e52"
+
+[[package]]
+name = "leb128fmt"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
+
+[[package]]
+name = "libc"
+version = "0.2.189"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
+
+[[package]]
+name = "libm"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
+
+[[package]]
+name = "libredox"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
+
+[[package]]
+name = "litemap"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
+
+[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
+
+[[package]]
+name = "lru-slab"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
+
+[[package]]
+name = "mach2"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b"
+
+[[package]]
+name = "maybe-owned"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4"
+
+[[package]]
+name = "md-5"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98"
+dependencies = [
+ "cfg-if",
+ "digest 0.11.3",
+]
+
+[[package]]
+name = "memchr"
+version = "2.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
+
+[[package]]
+name = "memfd"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227"
+dependencies = [
+ "rustix",
+]
+
+[[package]]
+name = "mio"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "nix"
+version = "0.31.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "cfg_aliases",
+ "libc",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "object"
+version = "0.39.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b"
+dependencies = [
+ "crc32fast",
+ "hashbrown 0.17.1",
+ "indexmap",
+ "memchr",
+]
+
+[[package]]
+name = "object_store"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d354792e39fa5f0009e47623cf8b15b099bf9a652fa55c6f817fe28ac84fea50"
+dependencies = [
+ "async-trait",
+ "aws-lc-rs",
+ "base64",
+ "bytes",
+ "chrono",
+ "crc-fast",
+ "form_urlencoded",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body-util",
+ "humantime",
+ "hyper",
+ "itertools 0.15.0",
+ "md-5",
+ "nix",
+ "parking_lot",
+ "percent-encoding",
+ "quick-xml",
+ "rand 0.10.2",
+ "reqwest 0.13.4",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "thiserror 2.0.19",
+ "tokio",
+ "tracing",
+ "url",
+ "walkdir",
+ "wasm-bindgen-futures",
+ "web-time",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "openssl-probe"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-link",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "petgraph"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
+dependencies = [
+ "fixedbitset",
+ "indexmap",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
+
+[[package]]
+name = "postcard"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
+dependencies = [
+ "cobs",
+ "embedded-io 0.4.0",
+ "embedded-io 0.6.1",
+ "serde",
+]
+
+[[package]]
+name = "potential_utf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "pulley-interpreter"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6064e52588328c1275d690baad99f8d4b3fdd870736966f1a63cfcbb20560b6"
+dependencies = [
+ "cranelift-bitset",
+ "log",
+ "pulley-macros",
+ "wasmtime-internal-core",
+]
+
+[[package]]
+name = "pulley-macros"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e832980317bfc7f8c8538ffc9c69b82912f3e33ec70f7c855d521654c70fa62"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.41.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
+dependencies = [
+ "memchr",
+ "serde",
+]
+
+[[package]]
+name = "quinn"
+version = "0.11.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8"
+dependencies = [
+ "bytes",
+ "cfg_aliases",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash",
+ "rustls",
+ "socket2",
+ "thiserror 2.0.19",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560"
+dependencies = [
+ "aws-lc-rs",
+ "bytes",
+ "getrandom 0.4.3",
+ "lru-slab",
+ "rand 0.10.2",
+ "rand_pcg",
+ "ring",
+ "rustc-hash",
+ "rustls",
+ "rustls-pki-types",
+ "slab",
+ "thiserror 2.0.19",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
+[[package]]
+name = "rand"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
+dependencies = [
+ "rand_chacha",
+ "rand_core 0.9.5",
+]
+
+[[package]]
+name = "rand"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
+dependencies = [
+ "chacha20",
+ "getrandom 0.4.3",
+ "rand_core 0.10.1",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.9.5",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
+dependencies = [
+ "getrandom 0.3.4",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
+
+[[package]]
+name = "rand_pcg"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a"
+dependencies = [
+ "rand_core 0.10.1",
+]
+
+[[package]]
+name = "rayon"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
+dependencies = [
+ "getrandom 0.2.17",
+ "libredox",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "regalloc2"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "757712e8e61590d6d4f5d563483755538b5aa13467837a3b41cd9832509a7f85"
+dependencies = [
+ "allocator-api2",
+ "bumpalo",
+ "hashbrown 0.17.1",
+ "log",
+ "rustc-hash",
+ "serde",
+ "smallvec",
+]
+
+[[package]]
+name = "reqwest"
+version = "0.12.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
+dependencies = [
+ "base64",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-rustls",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls",
+ "rustls-pki-types",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tokio-rustls",
+ "tokio-util",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams 0.4.2",
+ "web-sys",
+ "webpki-roots",
+]
+
+[[package]]
+name = "reqwest"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
+dependencies = [
+ "base64",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-rustls",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "percent-encoding",
+ "pin-project-lite",
+ "quinn",
+ "rustls",
+ "rustls-pki-types",
+ "rustls-platform-verifier",
+ "sync_wrapper",
+ "tokio",
+ "tokio-rustls",
+ "tokio-util",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams 0.5.0",
+ "web-sys",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.17",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb"
+
+[[package]]
+name = "rustc-hash"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustix-linux-procfs"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fc84bf7e9aa16c4f2c758f27412dc9841341e16aa682d9c7ac308fe3ee12056"
+dependencies = [
+ "once_cell",
+ "rustix",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06"
+dependencies = [
+ "aws-lc-rs",
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d"
+dependencies = [
+ "openssl-probe",
+ "rustls-pki-types",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
+dependencies = [
+ "web-time",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-platform-verifier"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
+dependencies = [
+ "core-foundation",
+ "core-foundation-sys",
+ "jni 0.22.4",
+ "log",
+ "once_cell",
+ "rustls",
+ "rustls-native-certs",
+ "rustls-platform-verifier-android",
+ "rustls-webpki",
+ "security-framework",
+ "security-framework-sys",
+ "webpki-root-certs",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustls-platform-verifier-android"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
+dependencies = [
+ "aws-lc-rs",
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
+
+[[package]]
+name = "ryu"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "security-framework"
+version = "3.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
+dependencies = [
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.151"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha1"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.2.17",
+ "digest 0.10.7",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.2.17",
+ "digest 0.10.7",
+]
+
+[[package]]
+name = "shlex"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
+
+[[package]]
+name = "simd_cesu8"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520"
+dependencies = [
+ "rustc_version",
+ "simdutf8",
+]
+
+[[package]]
+name = "simdutf8"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
+
+[[package]]
+name = "slab"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+
+[[package]]
+name = "smallvec"
+version = "1.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "socket2"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "spin"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3"
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "2.0.119"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.13.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
+
+[[package]]
+name = "tempfile"
+version = "3.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
+dependencies = [
+ "fastrand",
+ "getrandom 0.4.3",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
+dependencies = [
+ "thiserror-impl 2.0.19",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
+dependencies = [
+ "bytes",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "libc",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "toml"
+version = "0.9.12+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
+dependencies = [
+ "indexmap",
+ "serde_core",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_parser",
+ "toml_writer",
+ "winnow 0.7.15",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.7.5+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.1.3+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56"
+dependencies = [
+ "winnow 1.0.4",
+]
+
+[[package]]
+name = "toml_writer"
+version = "1.1.2+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
+
+[[package]]
+name = "tower"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "url",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "tungstenite"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8"
+dependencies = [
+ "bytes",
+ "data-encoding",
+ "http",
+ "httparse",
+ "log",
+ "rand 0.9.5",
+ "sha1",
+ "thiserror 2.0.19",
+]
+
+[[package]]
+name = "typenum"
+version = "1.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "unicode-width"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "url"
+version = "2.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "uuid"
+version = "1.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
+dependencies = [
+ "getrandom 0.4.3",
+ "js-sys",
+ "serde_core",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasip2"
+version = "1.0.4+wasi-0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "wasm-compose"
+version = "0.252.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d59b710751a35d54732a63851cdfacbfb2266b7160ce174faf269d3f4e84e31b"
+dependencies = [
+ "anyhow",
+ "heck",
+ "indexmap",
+ "log",
+ "petgraph",
+ "smallvec",
+ "wasm-encoder 0.252.0",
+ "wasmparser 0.252.0",
+ "wat",
+]
+
+[[package]]
+name = "wasm-continuity"
+version = "0.1.0"
+dependencies = [
+ "blake3",
+ "serde",
+ "serde_json",
+ "thiserror 2.0.19",
+ "uuid",
+]
+
+[[package]]
+name = "wasm-continuity-driver"
+version = "0.1.0"
+dependencies = [
+ "serde",
+ "serde_json",
+ "thiserror 2.0.19",
+ "wasm-continuity",
+ "wasm-continuity-fencing",
+]
+
+[[package]]
+name = "wasm-continuity-fencing"
+version = "0.1.0"
+dependencies = [
+ "thiserror 2.0.19",
+ "wasm-continuity",
+]
+
+[[package]]
+name = "wasm-continuity-kv"
+version = "0.1.0"
+dependencies = [
+ "serde",
+ "serde_json",
+ "thiserror 2.0.19",
+ "wasm-continuity",
+ "wasm-continuity-kv-backend",
+]
+
+[[package]]
+name = "wasm-continuity-kv-backend"
+version = "0.1.0"
+dependencies = [
+ "serde",
+ "thiserror 2.0.19",
+]
+
+[[package]]
+name = "wasm-encoder"
+version = "0.252.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8185ae345fa5687c054626ff9a50e7089797a343d9904d1dc9820eb4c4d3196f"
+dependencies = [
+ "leb128fmt",
+ "wasmparser 0.252.0",
+]
+
+[[package]]
+name = "wasm-encoder"
+version = "0.254.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09480d646178e5fdd12bb06e812d0af9a3a191dbc9cd697fdc86687beade7393"
+dependencies = [
+ "leb128fmt",
+ "wasmparser 0.254.0",
+]
+
+[[package]]
+name = "wasm-streams"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
+dependencies = [
+ "futures-util",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-streams"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb"
+dependencies = [
+ "futures-util",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "wasmcm-cm-host-rust"
+version = "0.0.1"
+dependencies = [
+ "anyhow",
+ "wasmcm-host-rust",
+ "wasmtime",
+]
+
+[[package]]
+name = "wasmcm-host-rust"
+version = "0.0.1"
+dependencies = [
+ "wasmtime",
+]
+
+[[package]]
+name = "wasmos-bootstrap-log"
+version = "0.1.0"
+
+[[package]]
+name = "wasmos-capability"
+version = "0.1.0"
+
+[[package]]
+name = "wasmos-graph"
+version = "0.1.0"
+dependencies = [
+ "wasmos-capability",
+ "wasmos-machine",
+]
+
+[[package]]
+name = "wasmos-machine"
+version = "0.1.0"
+dependencies = [
+ "wasmos-bootstrap-log",
+]
+
+[[package]]
+name = "wasmos-provider-native"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "jni 0.21.1",
+ "serde",
+ "serde_json",
+ "tokio",
+ "wasmos-runtime",
+ "wasmtime",
+ "wasmtime-wasi",
+]
+
+[[package]]
+name = "wasmos-resolver"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "serde",
+ "serde_json",
+ "wasmos-capability",
+ "wasmos-graph",
+ "wasmos-machine",
+]
+
+[[package]]
+name = "wasmos-runtime"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bytes",
+ "futures",
+ "futures-core",
+ "futures-util",
+ "log",
+ "object_store",
+ "reqwest 0.12.28",
+ "serde_json",
+ "tokio",
+ "tungstenite",
+ "uuid",
+ "wasm-continuity",
+ "wasm-continuity-driver",
+ "wasm-continuity-fencing",
+ "wasm-continuity-kv",
+ "wasm-continuity-kv-backend",
+ "wasmcm-cm-host-rust",
+ "wasmcm-host-rust",
+ "wasmos-resolver",
+ "wasmparser 0.253.0",
+ "wasmtime",
+ "wasmtime-wasi",
+ "wat",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.252.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3eb099dcadcde5be9eef55e3a337128efd4e44b4c93122487e4d2e4e1c6627c"
+dependencies = [
+ "bitflags",
+ "hashbrown 0.17.1",
+ "indexmap",
+ "semver",
+ "serde",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.253.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19db11f87d2486580e1e8b6f494c54df7e0566b87d0b599db843c24019667339"
+dependencies = [
+ "bitflags",
+ "hashbrown 0.17.1",
+ "indexmap",
+ "semver",
+ "serde",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.254.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5769a29f799fbab136aaf65b4fe5384cd7d93fe6fc9ba0dcb6c8382a1f16e27"
+dependencies = [
+ "bitflags",
+ "indexmap",
+ "semver",
+]
+
+[[package]]
+name = "wasmprinter"
+version = "0.252.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7142797de29b35ab8dbf15c00f55fda75d409da4c423a8ab8bd6b667a785824b"
+dependencies = [
+ "anyhow",
+ "termcolor",
+ "wasmparser 0.252.0",
+]
+
+[[package]]
+name = "wasmtime"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06d9f263ccb212017dae1d109b9997f218a7675db1e9a7bedf07ae1643322bf3"
+dependencies = [
+ "addr2line",
+ "async-trait",
+ "bitflags",
+ "bumpalo",
+ "bytes",
+ "cc",
+ "cfg-if",
+ "encoding_rs",
+ "futures",
+ "fxprof-processed-profile",
+ "gimli",
+ "ittapi",
+ "libc",
+ "log",
+ "mach2",
+ "memfd",
+ "object",
+ "once_cell",
+ "postcard",
+ "pulley-interpreter",
+ "rayon",
+ "rustix",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "smallvec",
+ "target-lexicon",
+ "tempfile",
+ "wasm-compose",
+ "wasm-encoder 0.252.0",
+ "wasmparser 0.252.0",
+ "wasmtime-environ",
+ "wasmtime-internal-cache",
+ "wasmtime-internal-component-macro",
+ "wasmtime-internal-component-util",
+ "wasmtime-internal-core",
+ "wasmtime-internal-cranelift",
+ "wasmtime-internal-fiber",
+ "wasmtime-internal-jit-debug",
+ "wasmtime-internal-jit-icache-coherence",
+ "wasmtime-internal-unwinder",
+ "wasmtime-internal-versioned-export-macros",
+ "wat",
+ "windows-sys 0.61.2",
+ "wit-parser",
+]
+
+[[package]]
+name = "wasmtime-environ"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9915e3d90c36a16e0fec8be746ee87e21cf0d4a017ecf09c49b7d1cc6f31b2b6"
+dependencies = [
+ "anyhow",
+ "cpp_demangle",
+ "cranelift-bforest",
+ "cranelift-bitset",
+ "cranelift-entity",
+ "gimli",
+ "hashbrown 0.17.1",
+ "indexmap",
+ "log",
+ "object",
+ "postcard",
+ "rustc-demangle",
+ "semver",
+ "serde",
+ "serde_derive",
+ "sha2",
+ "smallvec",
+ "target-lexicon",
+ "wasm-encoder 0.252.0",
+ "wasmparser 0.252.0",
+ "wasmprinter",
+ "wasmtime-internal-component-util",
+ "wasmtime-internal-core",
+]
+
+[[package]]
+name = "wasmtime-internal-cache"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b7c25436e1602aad2de80bfb3f586dcdfb6fe426d5b17cba37fabf9a1714d32"
+dependencies = [
+ "base64",
+ "directories-next",
+ "log",
+ "postcard",
+ "rustix",
+ "serde",
+ "serde_derive",
+ "sha2",
+ "toml",
+ "wasmtime-environ",
+ "windows-sys 0.61.2",
+ "zstd",
+]
+
+[[package]]
+name = "wasmtime-internal-component-macro"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b627ea698db36fb9110aa77868467f1d23791a76a88a433cb6a43e1242073b77"
+dependencies = [
+ "anyhow",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "wasmtime-internal-component-util",
+ "wasmtime-internal-wit-bindgen",
+ "wit-parser",
+]
+
+[[package]]
+name = "wasmtime-internal-component-util"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f45bf943cff1d2f1976bac6a241ca4ecaf29a6efd5f1ad89e7722439fa5c392f"
+
+[[package]]
+name = "wasmtime-internal-core"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea33e1f9685be82f90223bebcf2ffefb6b596ea3d82a3c42384527b9c6f5b447"
+dependencies = [
+ "anyhow",
+ "hashbrown 0.17.1",
+ "libm",
+ "serde",
+]
+
+[[package]]
+name = "wasmtime-internal-cranelift"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b1ce104719932a48dbdfec0cc292c96494b0bfe764946e45fbfd3340e3a68f9"
+dependencies = [
+ "cfg-if",
+ "cranelift-codegen",
+ "cranelift-control",
+ "cranelift-entity",
+ "cranelift-frontend",
+ "cranelift-native",
+ "gimli",
+ "itertools 0.14.0",
+ "log",
+ "object",
+ "pulley-interpreter",
+ "smallvec",
+ "target-lexicon",
+ "thiserror 2.0.19",
+ "wasmparser 0.252.0",
+ "wasmtime-environ",
+ "wasmtime-internal-core",
+ "wasmtime-internal-unwinder",
+ "wasmtime-internal-versioned-export-macros",
+]
+
+[[package]]
+name = "wasmtime-internal-fiber"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "976e4b71b9c0da8a9a7bb8d5fa0abedc4da879b397111fd91ece73be524fe621"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "libc",
+ "rustix",
+ "wasmtime-environ",
+ "wasmtime-internal-versioned-export-macros",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "wasmtime-internal-jit-debug"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1f60ecba3497cfc26284903c2e79023fd5ea991b95b0e8dafe994fd9fca4c4e"
+dependencies = [
+ "cc",
+ "object",
+ "rustix",
+ "wasmtime-internal-versioned-export-macros",
+]
+
+[[package]]
+name = "wasmtime-internal-jit-icache-coherence"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fae6decb025ae6d12f3391b36deb62a6838d45b18864f796d84bced722ceac0d"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasmtime-internal-core",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "wasmtime-internal-unwinder"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0982122d9470d3d72ed7aa9ed91e69538ae44c1494cd22d78fec7846b248a940"
+dependencies = [
+ "cfg-if",
+ "cranelift-codegen",
+ "log",
+ "object",
+ "wasmtime-environ",
+]
+
+[[package]]
+name = "wasmtime-internal-versioned-export-macros"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "363cf88a80e6ed13311d0d34d9bcd02a2e07d98113b994ff41ed386a206f498e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "wasmtime-internal-wit-bindgen"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ff41bb1656cbeb7befc42b8a7078b30a5f8393a0c8bca36a6cec94360c1a16"
+dependencies = [
+ "anyhow",
+ "bitflags",
+ "heck",
+ "indexmap",
+ "wit-parser",
+]
+
+[[package]]
+name = "wasmtime-wasi"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed1750b8e5e5d5b1ee3c0eb602d2c49d6a37a36e9996eaf0708b48ac92b62b06"
+dependencies = [
+ "async-trait",
+ "bitflags",
+ "bytes",
+ "cap-fs-ext",
+ "cap-std",
+ "cfg-if",
+ "futures",
+ "io-lifetimes 3.0.1",
+ "rand 0.10.2",
+ "rustix",
+ "thiserror 2.0.19",
+ "tokio",
+ "tracing",
+ "url",
+ "wasmtime",
+ "wasmtime-wasi-io",
+ "wiggle",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "wasmtime-wasi-io"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54777d3afeaf3f32cc444df661d9cb4fd57067971e183c2b1f5251f33b939e68"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures",
+ "tracing",
+ "wasmtime",
+]
+
+[[package]]
+name = "wast"
+version = "35.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68"
+dependencies = [
+ "leb128",
+]
+
+[[package]]
+name = "wast"
+version = "254.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ed4dfc8f6b9fc38b231065e2cdfbf7359af5ab945990abf09658dcc63c3e32"
+dependencies = [
+ "bumpalo",
+ "leb128fmt",
+ "memchr",
+ "unicode-width",
+ "wasm-encoder 0.254.0",
+]
+
+[[package]]
+name = "wat"
+version = "1.254.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7127f7f9b8f127c879991cecd35f494e4628bae1b0874c681414d8d8831e952c"
+dependencies = [
+ "wast 254.0.0",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki-root-certs"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
+dependencies = [
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "wiggle"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a23c2dc694dfafb35beb542e172d7c256488912cd84902828f8661723ad0eb7"
+dependencies = [
+ "bitflags",
+ "thiserror 2.0.19",
+ "tracing",
+ "wasmtime",
+ "wasmtime-environ",
+ "wiggle-macro",
+]
+
+[[package]]
+name = "wiggle-generate"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2fe48e5e455e827339d8e65de602cc7839d241bd7063be1fc235e35581788ad"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "wasmtime-environ",
+ "witx",
+]
+
+[[package]]
+name = "wiggle-macro"
+version = "47.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb520c20bed78e5a311d4c9ad883ac3b6d070617d07a19fbe5dc0b408c501b4b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "wiggle-generate",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
+[[package]]
+name = "winnow"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
+
+[[package]]
+name = "winnow"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
+
+[[package]]
+name = "winx"
+version = "0.36.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d"
+dependencies = [
+ "bitflags",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.57.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
+
+[[package]]
+name = "wit-parser"
+version = "0.252.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4266bea110371c620ccf3201c5023676046bc4556e5c7cfb5d500bda5ebc162d"
+dependencies = [
+ "anyhow",
+ "hashbrown 0.17.1",
+ "id-arena",
+ "indexmap",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-ident",
+ "wasmparser 0.252.0",
+]
+
+[[package]]
+name = "witx"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b"
+dependencies = [
+ "anyhow",
+ "log",
+ "thiserror 1.0.69",
+ "wast 35.0.2",
+]
+
+[[package]]
+name = "writeable"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
+
+[[package]]
+name = "yoke"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5"
+dependencies = [
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "synstructure",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+ "synstructure",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
+
+[[package]]
+name = "zerotrie"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "zmij"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
+
+[[package]]
+name = "zstd"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "7.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
+dependencies = [
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.16+zstd.1.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
diff --git a/wasmos-provider/native/Cargo.toml b/wasmos-provider/native/Cargo.toml
new file mode 100644
index 0000000..2b8890d
--- /dev/null
+++ b/wasmos-provider/native/Cargo.toml
@@ -0,0 +1,71 @@
+[package]
+name = "wasmos-provider-native"
+version = "0.1.0"
+edition = "2021"
+description = "JNI wrapper crate for wasmos-runtime, backing the webassembly4j wasmos-provider"
+license = "Apache-2.0"
+
+[lib]
+# webassembly4j's Java layer loads the resulting dylib via System.load() at
+# ServiceLoader-discovery time; a cdylib gives us the plain shared object with
+# the right #[no_mangle] extern "C" symbols on macOS / Linux / Windows.
+name = "wasmos_provider"
+crate-type = ["cdylib"]
+
+[dependencies]
+# wasmos-runtime dep — RELATIVE path (../../../wasmos) resolves against
+# a co-checked-out sibling tegmentum working tree. Same tree also needs
+# ~/git/wasm-cm and ~/git/wasm-continuity because wasmos-runtime's own
+# Cargo.toml declares path deps to those siblings.
+#
+# Standard developer layout expected:
+#
+# ~/git/webassembly4j (this repo)
+# ~/git/wasmos (../../../wasmos from wasmos-provider/native/)
+# ~/git/wasm-cm (wasmos's sibling path dep)
+# ~/git/wasm-continuity (wasmos's sibling path dep)
+#
+# A git-dep pinned to tegmentum/wasmos main fails to resolve at build
+# time — cargo fetches wasmos but the sibling `../wasm-cm/...` and
+# `../wasm-continuity/...` paths don't exist in the fetched tree. Same
+# problem would hit crates.io publication.
+#
+# Unblocking external / CI builds needs one of:
+# 1. wasmos + wasm-cm + wasm-continuity publish versioned crates.
+# 2. wasmos restructures to git-dep (or workspace-internal) its
+# siblings — no more `path = "..//..."`.
+# 3. Fork wasmos into a git-dep-clean tree we control.
+#
+# See wasmos-provider/README.md for the full build-setup story.
+wasmos-runtime = { path = "../../../wasmos" }
+
+# Must match wasmos-runtime's wasmtime pin (47.0.2). Using anything else
+# would compile-fail against wasmos-runtime's public wasmtime types
+# (Engine / Component / Linker / Store).
+wasmtime = { version = "47.0.2", features = ["component-model-bytes"] }
+wasmtime-wasi = "47.0.2"
+
+# instantiate_async / call_async require a Tokio runtime; we drive one per
+# call inside the JNI shim rather than expecting the JVM caller to.
+tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
+
+# JNI bindings. Version tracks wasmtime4j-native's pin (0.21 is Java 8+
+# compatible and matches wasmtime4j-native/Cargo.toml at the time of this
+# scaffold).
+jni = "0.21"
+
+# Better panic diagnostics when the wrapper crashes on a JVM caller.
+anyhow = "1.0"
+
+# JSON marshalling for two-way Val <-> Java-object conversion across the
+# JNI boundary. Modelled on wasmtime4j-native's concurrent_call_json module —
+# the "trade compile speed for invoke correctness now, swap in binary later"
+# strategy. serde + serde_json are already deep transitive deps via wasmtime
+# so the compile-cost delta is zero.
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+
+[profile.release]
+opt-level = 3
+lto = false
+codegen-units = 16
diff --git a/wasmos-provider/native/src/lib.rs b/wasmos-provider/native/src/lib.rs
new file mode 100644
index 0000000..2aad418
--- /dev/null
+++ b/wasmos-provider/native/src/lib.rs
@@ -0,0 +1,2552 @@
+//! JNI wrapper around `wasmos-runtime` — the Rust crate at
+//! `~/git/wasmos` that layers wasmos-shaped WIT-typed host bindings
+//! (host:wasmtime, host:bootstrap, host:caps, host:grants,
+//! wasmos:host/*) on top of an internal wasmtime linkage.
+//!
+//! # Surface
+//!
+//! Engine / component / instance lifecycle mirrors the wasmtime4j
+//! bindings 1:1 as opaque `long` handles. The invoke path has two
+//! flavours:
+//!
+//! * `instanceInvokeReturningI32` — nullary fast path for the demo
+//! (`guest-demo-portable`'s `run() -> i32`). Skips JSON round-trip.
+//! * `instanceInvokeJson` — general path: Java hands over a JSON blob
+//! of typed `JsonVal` argument values, Rust converts to
+//! `wasmtime::component::Val`, calls the function, converts results
+//! back into JSON. The JSON strategy is the one wasmtime4j-native
+//! uses for its concurrent-call path — cheap-and-cheerful, easy to
+//! evolve. A binary marshaller can replace it later without touching
+//! the Java-side API.
+//! * `instanceInvokeJsonReturningBytes` — same as `instanceInvokeJson`
+//! but returns the first `list` result as a raw jbyteArray,
+//! dodging the base64 detour on the JSON path.
+//!
+//! Instantiation has two flavours:
+//!
+//! * `componentInstantiate` — default WASI ctx, no limits, no epoch.
+//! Kept for the MVP demo path.
+//! * `componentInstantiateWithConfig` — accepts optional WASI settings
+//! (args / env / preopens / stdio inheritance / http toggle) plus
+//! optional `maxMemoryBytes` and `epochDeadline`. Handles the
+//! `LinkingContext.wasiContext()` + `ComponentConfig` plumbing.
+//!
+//! # Concurrency
+//!
+//! wasmos-runtime's `add_all_to_linker` uses async wasmtime imports
+//! (see the "Async linker" doc comment on `caps::wasi_p2` in wasmos
+//! ~/git/wasmos/src/lib.rs), so instantiation and invocation must
+//! run inside a Tokio runtime. A multi-thread runtime is created
+//! once per engine and reused for every `block_on` on that engine's
+//! components / instances.
+//!
+//! # Error handling
+//!
+//! Every entry point catches `anyhow::Error`s and throws a Java
+//! `WebAssemblyException`. Rust panics in this crate would be a bug
+//! (we take input from Java-side validated shims), not a normal
+//! path — we do NOT `catch_unwind` on the JNI boundary because
+//! `&mut JNIEnv` isn't `UnwindSafe`. A panic would abort the JVM;
+//! any code path that could plausibly panic should return a Result
+//! instead.
+
+#![allow(non_snake_case)]
+#![allow(clippy::missing_safety_doc)]
+
+use std::collections::HashMap;
+use std::sync::Mutex;
+
+use jni::objects::{JByteArray, JClass, JObjectArray, JString};
+use jni::sys::{jboolean, jbyteArray, jint, jlong, jstring};
+use jni::JNIEnv;
+
+use wasmtime::component::{Component, FutureAny, Linker, ResourceAny, StreamAny, Val};
+use wasmtime::{AsContextMut, Config, Engine, Store};
+use wasmos_runtime::{caps, HostState};
+use wasmtime_wasi::{DirPerms, FilePerms, WasiCtxBuilder};
+
+use serde::{Deserialize, Serialize};
+
+// -----------------------------------------------------------------------------
+// Handle wrappers
+// -----------------------------------------------------------------------------
+// Each Java `long` handle is a `Box::into_raw(Box::new(_))` pointer. We hand
+// the JVM the pointer as an opaque `jlong`; ownership returns to Rust via the
+// matching `*Close` entry point which reconstructs the Box and drops it.
+
+/// Owned engine + persistent Tokio runtime. The runtime is created eagerly at
+/// engine creation time so subsequent instantiate / invoke calls can `block_on`
+/// with no per-call setup cost — and, more importantly, so async wasi:p2
+/// timers/clocks see a live runtime for their whole lifetime.
+///
+/// The engine has `epoch_interruption(true)` set so
+/// `componentInstantiateWithConfig` can wire an epoch deadline without needing
+/// a second engine — the flag is a no-op for callers that don't set a deadline.
+struct EngineHandle {
+ engine: Engine,
+ runtime: tokio::runtime::Runtime,
+}
+
+/// Owned component + a back-pointer to the engine (needed at instantiate
+/// time to build a fresh Linker + Store).
+struct ComponentHandle {
+ engine_ptr: *mut EngineHandle,
+ component: Component,
+}
+
+/// Owned component instance + its Store. The store lives here so
+/// subsequent invoke calls have the store to thread through wasmtime's
+/// call_async. Also carries the engine back-pointer so invoke can
+/// grab the same Tokio runtime that instantiated it.
+///
+/// `resources` is a per-instance registry that parks `ResourceAny` values
+/// returned from guest calls. Java receives an opaque `WitResource{ tableId,
+/// typeName, owned }` — the wasm ResourceAny stays in Rust; only its slot id
+/// crosses the JNI boundary. On the way back in, we look up the parked value
+/// and hand it back to wasmtime. `ResourceAny` is `Copy`, so lookups are
+/// cheap; no reference-counting needed.
+struct InstanceHandle {
+ engine_ptr: *mut EngineHandle,
+ store: Mutex>,
+ instance: wasmtime::component::Instance,
+ resources: Mutex,
+ /// Per-instance registry for `FutureAny` values returned by guest calls.
+ /// Slot ids are handed to Java as `long`s (see `JsonVal::Future.table_id`);
+ /// monotonic, never reused (same policy as `ResourceRegistry`). See
+ /// `FutureRegistry` docs for lifecycle details, and the module-level
+ /// "wasmtime Val::Future API gap" note for what awaiting looks like
+ /// today (spoiler: type-erased polling isn't in the public API).
+ futures: Mutex,
+ /// Per-instance registry for `StreamAny` values returned by guest calls.
+ /// Mirrors `FutureRegistry` exactly — `StreamAny`'s public API is
+ /// structurally identical to `FutureAny`'s in wasmtime 47 (Clone, Debug,
+ /// `try_into_stream_reader::` requires compile-time `T`, and `close(store)`
+ /// for disposal). Park + close + pass-in work; host-side dynamic reading
+ /// is the same API gap as futures. See `StreamRegistry` for docs.
+ streams: Mutex,
+ /// Per-instance registry for `ErrorContextAny` values returned by guest
+ /// calls or received on `result<_, error-context>` err branches. Uses the
+ /// same parking-lot shape as the other registries so pass-back into a
+ /// guest is possible even though wasmtime 47 exposes no `close`/`drop` on
+ /// `ErrorContextAny` (see the FIXME on
+ /// `wasmtime::component::ErrorContextAny`). The parked value is only ever
+ /// released when the instance handle is dropped, or when Java explicitly
+ /// evicts it via `errorContextClose`.
+ error_contexts: Mutex,
+}
+
+/// Per-instance parking lot for `ResourceAny` values. Slot ids are handed to
+/// Java as `long`s (see `JsonVal::Resource.table_id`). Ids are monotonically
+/// increasing; we never reuse a freed slot so the "stale handle after
+/// take-owned" case surfaces as a clean "unknown resource id" error rather
+/// than an aliased hit on a newly parked resource.
+///
+/// Ownership semantics — driven by the `owned` bit on the way IN from Java:
+/// * `owned=true` → the Java caller is transferring the resource to the
+/// guest (matches WIT `own`). We remove the entry; subsequent uses of
+/// that WitResource fail.
+/// * `owned=false` → the Java caller is lending the resource for the length
+/// of this call (matches WIT `borrow`). The entry stays.
+///
+/// On the way OUT from a guest return, we park with the ResourceAny's own
+/// `owned()` flag reported back to Java verbatim.
+#[derive(Default)]
+struct ResourceRegistry {
+ next_id: u64,
+ entries: HashMap,
+}
+
+impl ResourceRegistry {
+ /// Park a `ResourceAny` and hand out a fresh slot id. Ids monotonically
+ /// increase; freed slots are never reused (see struct docs).
+ fn park(&mut self, any: ResourceAny) -> u64 {
+ let id = self.next_id;
+ self.next_id = self
+ .next_id
+ .checked_add(1)
+ .expect("wasmos-provider: resource id counter overflow");
+ self.entries.insert(id, any);
+ id
+ }
+
+ /// Fetch (borrow-shaped) — returns a copy but keeps the entry so the same
+ /// WitResource can be lent multiple times.
+ fn peek(&self, id: u64) -> Option {
+ self.entries.get(&id).copied()
+ }
+
+ /// Fetch and remove (own-shaped) — the entry is consumed; subsequent
+ /// lookups against this id will miss.
+ fn take(&mut self, id: u64) -> Option {
+ self.entries.remove(&id)
+ }
+}
+
+/// Per-instance parking lot for `FutureAny` values. Mirrors `ResourceRegistry`
+/// exactly — monotonic u64 ids, no reuse, take-vs-peek semantics driven by
+/// the Java caller's intent. See the module-level docs for how this interacts
+/// with the wasmtime 47 `FutureAny` public API (short version: return-direction
+/// parking works; pass-in direction works; dynamic *awaiting* a `FutureAny`
+/// with a runtime-known payload type is a wasmtime public-API gap — the
+/// `FutureAny::try_into_future_reader::` path requires `T` at compile time
+/// and `FutureConsumer::Item` is an associated type, so there's no
+/// type-erased await surface today).
+#[derive(Default)]
+struct FutureRegistry {
+ next_id: u64,
+ entries: HashMap,
+}
+
+impl FutureRegistry {
+ fn park(&mut self, any: FutureAny) -> u64 {
+ let id = self.next_id;
+ self.next_id = self
+ .next_id
+ .checked_add(1)
+ .expect("wasmos-provider: future id counter overflow");
+ self.entries.insert(id, any);
+ id
+ }
+
+ /// Borrow-shaped fetch. `FutureAny: Clone` (wasmtime derives it) so we
+ /// can hand a copy to a guest call while keeping the entry parked, which
+ /// matches the "read end" semantics — the same `FutureAny` may in
+ /// principle be lowered into more than one call before being closed.
+ ///
+ /// Currently unused by production code (there's no borrow-shaped Future
+ /// arm in `to_val` — the JsonVal::Future decode always takes; see the
+ /// enum docs for why); kept for parity with `ResourceRegistry::peek` and
+ /// exercised by the test suite. Suppress dead-code warning accordingly.
+ #[allow(dead_code)]
+ fn peek(&self, id: u64) -> Option {
+ self.entries.get(&id).cloned()
+ }
+
+ /// Own-shaped fetch. Removes the entry; subsequent lookups miss.
+ fn take(&mut self, id: u64) -> Option {
+ self.entries.remove(&id)
+ }
+}
+
+/// Per-instance parking lot for `StreamAny` values. Mirrors `FutureRegistry`
+/// exactly — monotonic u64 ids, no reuse, take-vs-peek semantics driven by
+/// the Java caller's intent. See the module-level docs for how this interacts
+/// with the wasmtime 47 `StreamAny` public API (short version: same story as
+/// `FutureAny` — park and close and pass-in are wired; dynamic host-side
+/// *reading* of a `StreamAny` with a runtime-known payload type is a wasmtime
+/// public-API gap because `StreamAny::try_into_stream_reader::` requires
+/// `T` at compile time).
+#[derive(Default)]
+struct StreamRegistry {
+ next_id: u64,
+ entries: HashMap,
+}
+
+impl StreamRegistry {
+ fn park(&mut self, any: StreamAny) -> u64 {
+ let id = self.next_id;
+ self.next_id = self
+ .next_id
+ .checked_add(1)
+ .expect("wasmos-provider: stream id counter overflow");
+ self.entries.insert(id, any);
+ id
+ }
+
+ /// Borrow-shaped fetch. `StreamAny: Clone`; see `FutureRegistry::peek`
+ /// docs for symmetry rationale. Currently unused by production code
+ /// (there's no borrow-shaped Stream arm in `to_val`), kept for parity
+ /// with `FutureRegistry` and exercised by tests.
+ #[allow(dead_code)]
+ fn peek(&self, id: u64) -> Option {
+ self.entries.get(&id).cloned()
+ }
+
+ /// Own-shaped fetch. Removes the entry; subsequent lookups miss.
+ fn take(&mut self, id: u64) -> Option {
+ self.entries.remove(&id)
+ }
+}
+
+/// Per-instance parking lot for `error-context` handles. Mirrors the shape
+/// of the other registries but differs on lifecycle: wasmtime 47's
+/// `ErrorContextAny` is a placeholder (see `FIXME(#11161)` on the wasmtime
+/// side) with `pub(crate) u32` internals and no publicly-defined `close` or
+/// `drop`. It is also not publicly re-exported at the `wasmtime::component`
+/// module boundary — its `pub` visibility only reaches as far as being usable
+/// through the `Val::ErrorContext(...)` enum variant. To sidestep the naming
+/// problem we park the whole `Val::ErrorContext(any)` value; `Val: Clone`
+/// preserves the parking-lot copy semantics without needing to name the
+/// inner type.
+///
+/// Eviction is Java-driven only (`errorContextClose`) or automatic on
+/// instance handle drop. On pass-in, the parked `Val` is cloned back
+/// verbatim, so wasmtime sees the original ErrorContextAny handle.
+#[derive(Default)]
+struct ErrorContextRegistry {
+ next_id: u64,
+ entries: HashMap,
+}
+
+impl ErrorContextRegistry {
+ /// Park a `Val::ErrorContext(any)` value. The debug caller-passed value
+ /// MUST be an `ErrorContext`-shaped `Val`; other variants would still
+ /// park but round-trip incoherently on pass-in — the type is asserted
+ /// upstream in `from_val`.
+ fn park(&mut self, ec: Val) -> u64 {
+ debug_assert!(
+ matches!(&ec, Val::ErrorContext(_)),
+ "ErrorContextRegistry::park requires a Val::ErrorContext, got {ec:?}"
+ );
+ let id = self.next_id;
+ self.next_id = self
+ .next_id
+ .checked_add(1)
+ .expect("wasmos-provider: error-context id counter overflow");
+ self.entries.insert(id, ec);
+ id
+ }
+
+ #[allow(dead_code)]
+ fn peek(&self, id: u64) -> Option {
+ self.entries.get(&id).cloned()
+ }
+
+ fn take(&mut self, id: u64) -> Option {
+ self.entries.remove(&id)
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Error helpers
+// -----------------------------------------------------------------------------
+
+const EXCEPTION_CLASS: &str = "ai/tegmentum/webassembly4j/api/exception/WebAssemblyException";
+
+/// Throw a `WebAssemblyException` with the given message. Ignores JNI errors
+/// (the JVM will already be in an exception-pending state on any failure here).
+fn throw(env: &mut JNIEnv, msg: &str) {
+ let _ = env.throw_new(EXCEPTION_CLASS, msg);
+}
+
+/// Ergonomic helper — translate an `anyhow::Result` into a JNI return
+/// value: on `Ok`, return the value; on `Err`, throw
+/// `WebAssemblyException` and return the caller-supplied sentinel.
+fn or_throw(env: &mut JNIEnv, result: anyhow::Result, sentinel: T) -> T {
+ match result {
+ Ok(v) => v,
+ Err(e) => {
+ throw(env, &format!("wasmos-provider: {:#}", e));
+ sentinel
+ }
+ }
+}
+
+/// Read a jstring into a `String`, translating any JNI error into `anyhow`.
+fn jstring_to_string(env: &mut JNIEnv, s: &JString) -> anyhow::Result {
+ env.get_string(s)
+ .map(|jni_str| jni_str.into())
+ .map_err(|e| anyhow::anyhow!("get_string: {e}"))
+}
+
+/// Read a JObjectArray of jstrings into `Vec`. Skips null entries
+/// (returns them as empty strings — callers pass tightly-packed arrays).
+fn jstringarray_to_vec(env: &mut JNIEnv, arr: &JObjectArray) -> anyhow::Result> {
+ let len = env
+ .get_array_length(arr)
+ .map_err(|e| anyhow::anyhow!("get_array_length: {e}"))?;
+ let mut out = Vec::with_capacity(len as usize);
+ for i in 0..len {
+ let elem = env
+ .get_object_array_element(arr, i)
+ .map_err(|e| anyhow::anyhow!("get_object_array_element[{i}]: {e}"))?;
+ if elem.is_null() {
+ out.push(String::new());
+ continue;
+ }
+ let js: JString = elem.into();
+ out.push(jstring_to_string(env, &js)?);
+ }
+ Ok(out)
+}
+
+// -----------------------------------------------------------------------------
+// Exported-function lookup helpers
+// -----------------------------------------------------------------------------
+
+/// Resolve a callable `Func` from an `Instance` by name, supporting both
+/// top-level exports and nested-in-interface exports.
+///
+/// * If `name` contains a `#`, split on the FIRST `#` — the left half is
+/// the exported interface name (e.g. `"tegmentum:counter/counter-api@0.1.0"`)
+/// and the right half is the item name within that interface (e.g.
+/// `"[constructor]counter"` or `"[method]counter.get"`).
+/// Wasmtime's `Instance::get_func` doesn't descend into exported instances
+/// on its own; we chain two `get_export_index` calls to reach the func.
+/// * Otherwise, look up `name` directly at the top level — the existing
+/// plain-function path.
+///
+/// Returns `None` if `name` resolves to something that isn't a lifted
+/// component function (e.g. a nested instance, a type export, or unknown).
+fn resolve_func(
+ instance: &wasmtime::component::Instance,
+ store: &mut Store,
+ name: &str,
+) -> Option {
+ if let Some(hash_at) = name.find('#') {
+ let iface_name = &name[..hash_at];
+ let item_name = &name[hash_at + 1..];
+ let iface_idx = instance.get_export_index(&mut *store, None, iface_name)?;
+ let func_idx = instance.get_export_index(&mut *store, Some(&iface_idx), item_name)?;
+ instance.get_func(&mut *store, &func_idx)
+ } else {
+ instance.get_func(&mut *store, name)
+ }
+}
+
+// -----------------------------------------------------------------------------
+// JSON Val marshalling
+// -----------------------------------------------------------------------------
+// Design mirrors wasmtime4j-native's `concurrent_call_json::JsonVal`. Keeps
+// tag names short (`t` / `v`) to trim payload size — the Java side has a
+// matching Jackson-free hand-rolled parser/writer so JVM adds no runtime dep.
+//
+// `Bytes` is a fast-path variant that base64-encodes a `list` payload —
+// avoids the O(n) JsonVal-per-byte round-trip for large binary blobs. On the
+// return path we detect `Val::List` whose elements are all `Val::U8` and
+// emit the `Bytes` variant automatically.
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(tag = "t", content = "v")]
+enum JsonVal {
+ Bool(bool),
+ S8(i8),
+ S16(i16),
+ S32(i32),
+ S64(i64),
+ U8(u8),
+ U16(u16),
+ U32(u32),
+ U64(u64),
+ F32(f32),
+ F64(f64),
+ Char(char),
+ String(String),
+ /// `list` fast path — base64-encoded bytes. Reduces both wire size
+ /// and per-element boxing on both sides.
+ Bytes(String),
+ List(Vec),
+ Option(Option>),
+ /// Result-shaped value. `is_ok` picks the branch; the corresponding side
+ /// carries the payload (or `None` for `result<_,_>` with a unit branch).
+ Result {
+ is_ok: bool,
+ #[serde(skip_serializing_if = "Option::is_none", default)]
+ ok: Option>,
+ #[serde(skip_serializing_if = "Option::is_none", default)]
+ err: Option>,
+ },
+ Record(Vec<(String, JsonVal)>),
+ Tuple(Vec),
+ Variant {
+ discriminant: String,
+ #[serde(skip_serializing_if = "Option::is_none", default)]
+ value: Option>,
+ },
+ Enum(String),
+ Flags(Vec),
+ /// Distinct from `Record`: keys can be arbitrary `Val`s, not just
+ /// field-name strings. Wire form is a flat array of `[key_json,
+ /// value_json]` two-tuples — identical layout to what wasmtime's
+ /// `Val::Map(Vec<(Val, Val)>)` implies. The Java side uses a
+ /// `WitMap` explicit wrapper so callers opt in intentionally rather
+ /// than have every `Map,?>` guessed one way or another.
+ Map(Vec<(JsonVal, JsonVal)>),
+ /// Component-model `own` / `borrow` handle. The `ResourceAny` lives
+ /// Rust-side in `InstanceHandle::resources`; Java only ever sees
+ /// `table_id` (opaque slot number), `type_name` (opaque
+ /// `ResourceType`-derived identity, useful only for equality), and
+ /// `owned` (drives take-vs-peek on the way back in — see
+ /// `ResourceRegistry` docs). This is the wasmos-provider's `Val::Resource`
+ /// story; no other JsonVal shape crosses a "handle" over the wire.
+ Resource {
+ table_id: u64,
+ type_name: String,
+ owned: bool,
+ },
+ /// Component-model `future` handle. Same parking-lot pattern as
+ /// `Resource`: the `FutureAny` lives Rust-side in
+ /// `InstanceHandle::futures`; Java only ever sees `table_id` (opaque
+ /// slot number) and `type_name` (opaque Debug rendering of the
+ /// `FutureAny`, useful only for equality-adjacent introspection).
+ ///
+ /// There is no `owned` bit here — wasmtime 47's `FutureAny: Clone` and
+ /// the read/write ends are already separated at the type level (this is
+ /// the read end); "borrow" as a distinct concept doesn't apply. The
+ /// Java carrier consumes the slot on pass-in by default (parity with
+ /// how a future handle typically flows exactly once through a guest
+ /// import); explicit disposal is via `futureClose`.
+ ///
+ /// See the `FutureRegistry` docs for the wasmtime API gap that
+ /// currently blocks a dynamic host-side *await* on this handle.
+ Future {
+ table_id: u64,
+ type_name: String,
+ },
+ /// Component-model `stream` handle. Same parking-lot pattern as
+ /// `Future`: the `StreamAny` lives Rust-side in
+ /// `InstanceHandle::streams`; Java only ever sees `table_id` (opaque
+ /// slot number) and `type_name` (opaque Debug rendering of the
+ /// `StreamAny`, useful only for equality-adjacent introspection).
+ ///
+ /// Same lifecycle contract as `Future`: the decode arm always takes
+ /// (a stream handle's read end flows through a guest import exactly
+ /// once). Explicit disposal is via `streamClose`. Host-side dynamic
+ /// reading of the stream's items has the identical wasmtime API gap
+ /// as `Future` — `StreamAny::try_into_stream_reader::` is
+ /// compile-time typed.
+ Stream {
+ table_id: u64,
+ type_name: String,
+ },
+ /// Component-model `error-context` handle. Parked in
+ /// `InstanceHandle::error_contexts` and surfaced to Java as an opaque
+ /// slot id plus the wasmtime-derived numeric `rep` (parsed out of
+ /// `ErrorContextAny`'s Debug rendering — the underlying `u32` is
+ /// `pub(crate)` so Debug is the only public window). The `rep` value
+ /// is useful for equality-adjacent identification; it has no other
+ /// user-facing meaning today because wasmtime 47's `ErrorContextAny`
+ /// is a placeholder (see wasmtime's `FIXME(#11161)`).
+ ///
+ /// There is no wasmtime-defined dispose on `ErrorContextAny`, so
+ /// `errorContextClose` on the Java side is a pure Rust-side registry
+ /// eviction — it drops the parked handle so the slot id becomes
+ /// unknown for later pass-in attempts.
+ ErrorContext {
+ table_id: u64,
+ rep: u32,
+ },
+}
+
+/// Base64 alphabet — hand-rolled to avoid pulling in the `base64` crate for a
+/// couple of `encode` / `decode` calls. Standard alphabet with `=` padding.
+const B64_ALPHABET: &[u8; 64] =
+ b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+fn b64_encode(bytes: &[u8]) -> String {
+ let mut out = String::with_capacity((bytes.len() + 2) / 3 * 4);
+ let mut i = 0;
+ while i + 3 <= bytes.len() {
+ let b0 = bytes[i];
+ let b1 = bytes[i + 1];
+ let b2 = bytes[i + 2];
+ out.push(B64_ALPHABET[(b0 >> 2) as usize] as char);
+ out.push(B64_ALPHABET[(((b0 & 0x03) << 4) | (b1 >> 4)) as usize] as char);
+ out.push(B64_ALPHABET[(((b1 & 0x0f) << 2) | (b2 >> 6)) as usize] as char);
+ out.push(B64_ALPHABET[(b2 & 0x3f) as usize] as char);
+ i += 3;
+ }
+ let rem = bytes.len() - i;
+ if rem == 1 {
+ let b0 = bytes[i];
+ out.push(B64_ALPHABET[(b0 >> 2) as usize] as char);
+ out.push(B64_ALPHABET[((b0 & 0x03) << 4) as usize] as char);
+ out.push('=');
+ out.push('=');
+ } else if rem == 2 {
+ let b0 = bytes[i];
+ let b1 = bytes[i + 1];
+ out.push(B64_ALPHABET[(b0 >> 2) as usize] as char);
+ out.push(B64_ALPHABET[(((b0 & 0x03) << 4) | (b1 >> 4)) as usize] as char);
+ out.push(B64_ALPHABET[((b1 & 0x0f) << 2) as usize] as char);
+ out.push('=');
+ }
+ out
+}
+
+fn b64_decode(s: &str) -> anyhow::Result> {
+ // Build a reverse-lookup table lazily. Cheap; done once per decode.
+ let mut rev = [255u8; 256];
+ for (i, &c) in B64_ALPHABET.iter().enumerate() {
+ rev[c as usize] = i as u8;
+ }
+ let bytes = s.as_bytes();
+ if bytes.len() % 4 != 0 {
+ anyhow::bail!("base64: length not a multiple of 4");
+ }
+ let mut out = Vec::with_capacity(bytes.len() / 4 * 3);
+ let mut i = 0;
+ while i < bytes.len() {
+ let c0 = bytes[i];
+ let c1 = bytes[i + 1];
+ let c2 = bytes[i + 2];
+ let c3 = bytes[i + 3];
+ let v0 = rev[c0 as usize];
+ let v1 = rev[c1 as usize];
+ if v0 == 255 || v1 == 255 {
+ anyhow::bail!("base64: invalid char at offset {i}");
+ }
+ out.push((v0 << 2) | (v1 >> 4));
+ if c2 != b'=' {
+ let v2 = rev[c2 as usize];
+ if v2 == 255 {
+ anyhow::bail!("base64: invalid char at offset {}", i + 2);
+ }
+ out.push(((v1 & 0x0f) << 4) | (v2 >> 2));
+ if c3 != b'=' {
+ let v3 = rev[c3 as usize];
+ if v3 == 255 {
+ anyhow::bail!("base64: invalid char at offset {}", i + 3);
+ }
+ out.push(((v2 & 0x03) << 6) | v3);
+ }
+ }
+ i += 4;
+ }
+ Ok(out)
+}
+
+impl JsonVal {
+ /// Decode a JsonVal into a wasmtime `Val`, using `resources`,
+ /// `futures`, `streams`, and `error_contexts` to resolve any parked
+ /// slot ids back into `ResourceAny` / `FutureAny` / `StreamAny` /
+ /// `ErrorContextAny` handles. All registries are passed as `&mut`
+ /// because the pass-in arms consume (take) entries — Resource by way
+ /// of the caller-supplied `owned` bit, Future/Stream unconditionally
+ /// (a future/stream read end typically flows through a guest import
+ /// exactly once), ErrorContext unconditionally (Clone would let us
+ /// peek but no known WIT shape wants to hold the same handle twice).
+ fn to_val(
+ &self,
+ resources: &mut ResourceRegistry,
+ futures: &mut FutureRegistry,
+ streams: &mut StreamRegistry,
+ error_contexts: &mut ErrorContextRegistry,
+ ) -> anyhow::Result {
+ Ok(match self {
+ JsonVal::Bool(v) => Val::Bool(*v),
+ JsonVal::S8(v) => Val::S8(*v),
+ JsonVal::S16(v) => Val::S16(*v),
+ JsonVal::S32(v) => Val::S32(*v),
+ JsonVal::S64(v) => Val::S64(*v),
+ JsonVal::U8(v) => Val::U8(*v),
+ JsonVal::U16(v) => Val::U16(*v),
+ JsonVal::U32(v) => Val::U32(*v),
+ JsonVal::U64(v) => Val::U64(*v),
+ JsonVal::F32(v) => Val::Float32(*v),
+ JsonVal::F64(v) => Val::Float64(*v),
+ JsonVal::Char(v) => Val::Char(*v),
+ JsonVal::String(v) => Val::String(v.clone()),
+ JsonVal::Bytes(b64) => {
+ let bytes = b64_decode(b64)?;
+ Val::List(bytes.into_iter().map(Val::U8).collect())
+ }
+ JsonVal::List(items) => Val::List(
+ items
+ .iter()
+ .map(|v| v.to_val(resources, futures, streams, error_contexts))
+ .collect::>>()?,
+ ),
+ JsonVal::Option(opt) => Val::Option(
+ opt.as_ref()
+ .map(|v| v.to_val(resources, futures, streams, error_contexts).map(Box::new))
+ .transpose()?,
+ ),
+ JsonVal::Result { is_ok, ok, err } => {
+ if *is_ok {
+ Val::Result(Ok(ok
+ .as_ref()
+ .map(|v| v.to_val(resources, futures, streams, error_contexts).map(Box::new))
+ .transpose()?))
+ } else {
+ Val::Result(Err(err
+ .as_ref()
+ .map(|v| v.to_val(resources, futures, streams, error_contexts).map(Box::new))
+ .transpose()?))
+ }
+ }
+ JsonVal::Record(fields) => Val::Record(
+ fields
+ .iter()
+ .map(|(name, jv)| jv.to_val(resources, futures, streams, error_contexts).map(|v| (name.clone(), v)))
+ .collect::>>()?,
+ ),
+ JsonVal::Tuple(items) => Val::Tuple(
+ items
+ .iter()
+ .map(|v| v.to_val(resources, futures, streams, error_contexts))
+ .collect::>>()?,
+ ),
+ JsonVal::Variant { discriminant, value } => Val::Variant(
+ discriminant.clone(),
+ value
+ .as_ref()
+ .map(|v| v.to_val(resources, futures, streams, error_contexts).map(Box::new))
+ .transpose()?,
+ ),
+ JsonVal::Enum(v) => Val::Enum(v.clone()),
+ JsonVal::Flags(v) => Val::Flags(v.clone()),
+ JsonVal::Map(pairs) => Val::Map(
+ pairs
+ .iter()
+ .map(|(k, v)| Ok((k.to_val(resources, futures, streams, error_contexts)?,
+ v.to_val(resources, futures, streams, error_contexts)?)))
+ .collect::>>()?,
+ ),
+ JsonVal::Resource { table_id, owned, .. } => {
+ // Java-side `owned` bit drives the take-vs-peek choice — the
+ // WIT signature the guest expects is what the caller is
+ // targeting, and it dictates whether we transfer ownership
+ // (own) or lend a copy (borrow). `type_name` is not
+ // consulted here; it's Java-side introspection only.
+ let any = if *owned {
+ resources.take(*table_id).ok_or_else(|| {
+ anyhow::anyhow!(
+ "wasmos-provider: unknown resource id {} (already consumed \
+ as own or never parked)",
+ table_id
+ )
+ })?
+ } else {
+ resources.peek(*table_id).ok_or_else(|| {
+ anyhow::anyhow!(
+ "wasmos-provider: unknown resource id {} (never parked or \
+ already consumed by prior own transfer)",
+ table_id
+ )
+ })?
+ };
+ Val::Resource(any)
+ }
+ JsonVal::Future { table_id, .. } => {
+ // Futures unconditionally take: a `future` handle
+ // typically flows through a guest import exactly once, and
+ // the read end (which is what we're holding) is single-shot
+ // by nature. Peek-style repeated pass-in isn't a shape the
+ // component model surfaces, so we don't try to model it.
+ // Callers that need to keep the handle after passing it in
+ // shouldn't; there's no "borrow>" in the wire
+ // model.
+ let any = futures.take(*table_id).ok_or_else(|| {
+ anyhow::anyhow!(
+ "wasmos-provider: unknown future id {} (already consumed or \
+ never parked)",
+ table_id
+ )
+ })?;
+ Val::Future(any)
+ }
+ JsonVal::Stream { table_id, .. } => {
+ // Streams unconditionally take, same rationale as Future:
+ // the read end of a `stream` is single-shot at the
+ // wire-model level.
+ let any = streams.take(*table_id).ok_or_else(|| {
+ anyhow::anyhow!(
+ "wasmos-provider: unknown stream id {} (already consumed or \
+ never parked)",
+ table_id
+ )
+ })?;
+ Val::Stream(any)
+ }
+ JsonVal::ErrorContext { table_id, .. } => {
+ // ErrorContext unconditionally takes on pass-in. There is no
+ // borrow shape for `error-context` in the WIT wire model —
+ // an error-context handle is terminal by convention. If a
+ // caller needs to re-park after peeking they can round-trip
+ // via `from_val` on the returned Val, but pass-in through
+ // JSON always consumes to keep parity with Future/Stream.
+ //
+ // The registry stores the whole `Val::ErrorContext(any)`
+ // (see ErrorContextRegistry docs for why); pass-in hands
+ // that Val straight back to wasmtime.
+ error_contexts.take(*table_id).ok_or_else(|| {
+ anyhow::anyhow!(
+ "wasmos-provider: unknown error-context id {} (already consumed \
+ or never parked)",
+ table_id
+ )
+ })?
+ }
+ })
+ }
+
+ /// Encode a wasmtime `Val` into a JsonVal, parking any `Resource(any)` /
+ /// `Future(any)` / `Stream(any)` / `ErrorContext(any)` values in their
+ /// respective registries and emitting a slot id in the JSON.
+ fn from_val(
+ val: &Val,
+ resources: &mut ResourceRegistry,
+ futures: &mut FutureRegistry,
+ streams: &mut StreamRegistry,
+ error_contexts: &mut ErrorContextRegistry,
+ ) -> anyhow::Result {
+ Ok(match val {
+ Val::Bool(v) => JsonVal::Bool(*v),
+ Val::S8(v) => JsonVal::S8(*v),
+ Val::S16(v) => JsonVal::S16(*v),
+ Val::S32(v) => JsonVal::S32(*v),
+ Val::S64(v) => JsonVal::S64(*v),
+ Val::U8(v) => JsonVal::U8(*v),
+ Val::U16(v) => JsonVal::U16(*v),
+ Val::U32(v) => JsonVal::U32(*v),
+ Val::U64(v) => JsonVal::U64(*v),
+ Val::Float32(v) => JsonVal::F32(*v),
+ Val::Float64(v) => JsonVal::F64(*v),
+ Val::Char(v) => JsonVal::Char(*v),
+ Val::String(v) => JsonVal::String(v.clone()),
+ Val::List(items) => {
+ // Fast-path: if all items are U8, encode as `Bytes(base64)`.
+ if !items.is_empty() && items.iter().all(|v| matches!(v, Val::U8(_))) {
+ let raw: Vec = items
+ .iter()
+ .map(|v| match v {
+ Val::U8(b) => *b,
+ _ => unreachable!("guarded by matches! above"),
+ })
+ .collect();
+ JsonVal::Bytes(b64_encode(&raw))
+ } else {
+ JsonVal::List(
+ items
+ .iter()
+ .map(|v| JsonVal::from_val(v, resources, futures, streams, error_contexts))
+ .collect::>>()?,
+ )
+ }
+ }
+ Val::Option(opt) => JsonVal::Option(
+ opt.as_ref()
+ .map(|v| JsonVal::from_val(v, resources, futures, streams, error_contexts).map(Box::new))
+ .transpose()?,
+ ),
+ Val::Result(res) => match res {
+ Ok(ok_val) => JsonVal::Result {
+ is_ok: true,
+ ok: ok_val
+ .as_ref()
+ .map(|v| JsonVal::from_val(v, resources, futures, streams, error_contexts).map(Box::new))
+ .transpose()?,
+ err: None,
+ },
+ Err(err_val) => JsonVal::Result {
+ is_ok: false,
+ ok: None,
+ err: err_val
+ .as_ref()
+ .map(|v| JsonVal::from_val(v, resources, futures, streams, error_contexts).map(Box::new))
+ .transpose()?,
+ },
+ },
+ Val::Record(fields) => JsonVal::Record(
+ fields
+ .iter()
+ .map(|(name, v)| {
+ JsonVal::from_val(v, resources, futures, streams, error_contexts).map(|jv| (name.clone(), jv))
+ })
+ .collect::>>()?,
+ ),
+ Val::Tuple(items) => JsonVal::Tuple(
+ items
+ .iter()
+ .map(|v| JsonVal::from_val(v, resources, futures, streams, error_contexts))
+ .collect::>>()?,
+ ),
+ Val::Variant(discriminant, payload) => JsonVal::Variant {
+ discriminant: discriminant.clone(),
+ value: payload
+ .as_ref()
+ .map(|v| JsonVal::from_val(v, resources, futures, streams, error_contexts).map(Box::new))
+ .transpose()?,
+ },
+ Val::Enum(discriminant) => JsonVal::Enum(discriminant.clone()),
+ Val::Flags(v) => JsonVal::Flags(v.clone()),
+ Val::Map(pairs) => JsonVal::Map(
+ pairs
+ .iter()
+ .map(|(k, v)| {
+ Ok((
+ JsonVal::from_val(k, resources, futures, streams, error_contexts)?,
+ JsonVal::from_val(v, resources, futures, streams, error_contexts)?,
+ ))
+ })
+ .collect::>>()?,
+ ),
+ Val::Resource(any) => {
+ // Park the ResourceAny; hand Java a stable u64 slot id and
+ // the ResourceType's Debug rendering as opaque type identity.
+ // The Debug format for a guest ResourceType includes the
+ // component's internal resource index; that's stable within
+ // an instance's lifetime, which is exactly the window a
+ // WitResource is valid for.
+ let owned = any.owned();
+ let type_name = format!("{:?}", any.ty());
+ let table_id = resources.park(*any);
+ JsonVal::Resource {
+ table_id,
+ type_name,
+ owned,
+ }
+ }
+ Val::Future(any) => {
+ // Park the FutureAny; hand Java an opaque u64 slot id + the
+ // FutureAny's Debug rendering as a type-identity hint (the
+ // payload type is buried in a private `PayloadType`
+ // field, so Debug is the widest window wasmtime 47's public
+ // API gives us). See the module-level "wasmtime API gap"
+ // note on why Java cannot yet host-side *await* this handle.
+ let type_name = format!("{:?}", any);
+ // `FutureAny: Clone` — safe to clone into the registry
+ // rather than consuming the borrow. Java has to explicitly
+ // dispose via `futureClose`; the registry keeps the parked
+ // copy alive until then (or until the instance handle
+ // itself is dropped, which drops the whole registry).
+ let table_id = futures.park(any.clone());
+ JsonVal::Future {
+ table_id,
+ type_name,
+ }
+ }
+ Val::Stream(any) => {
+ // Same pattern as Val::Future. `StreamAny: Clone` in
+ // wasmtime 47, so we clone into the registry and keep the
+ // borrow intact. Debug format is the only public window on
+ // the payload type — same "opaque hint" role as Future.
+ let type_name = format!("{:?}", any);
+ let table_id = streams.park(any.clone());
+ JsonVal::Stream {
+ table_id,
+ type_name,
+ }
+ }
+ Val::ErrorContext(any) => {
+ // Park the ErrorContext-shaped Val. Its Debug form is
+ // `ErrorContextAny(N)` where N is the pub(crate) u32 rep;
+ // parse the number out so Java gets a plain numeric hint
+ // instead of the debug string wrapper. If the format ever
+ // changes (unlikely — it's a plain tuple struct) fall back
+ // to 0. The rep is only meant for equality-adjacent
+ // introspection; wasmtime's `FIXME(#11161)` documents that
+ // the type has no other operations at 47.0.2.
+ //
+ // We store the whole `Val` in the registry (not the
+ // `ErrorContextAny`) because `ErrorContextAny` isn't
+ // re-exported at the `wasmtime::component` module boundary
+ // and can't be named as a HashMap value type from outside
+ // the wasmtime crate. Cloning the enclosing Val preserves
+ // the underlying handle.
+ let dbg = format!("{:?}", any);
+ let rep = dbg
+ .strip_prefix("ErrorContextAny(")
+ .and_then(|s| s.strip_suffix(')'))
+ .and_then(|s| s.parse::().ok())
+ .unwrap_or(0);
+ let table_id = error_contexts.park(val.clone());
+ JsonVal::ErrorContext { table_id, rep }
+ }
+ })
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Engine lifecycle
+// -----------------------------------------------------------------------------
+
+/// Create a wasmos-friendly wasmtime engine. `wasm_component_model = true` is
+/// mandatory to load composed components; `epoch_interruption = true` is
+/// mandatory so per-instance epoch deadlines are cheap to opt into via
+/// `componentInstantiateWithConfig` (the flag is a no-op for callers who
+/// don't set a deadline).
+#[no_mangle]
+pub extern "system" fn Java_ai_tegmentum_webassembly4j_provider_wasmos_jni_WasmosNative_engineCreate(
+ mut env: JNIEnv,
+ _class: JClass,
+) -> jlong {
+ let result: anyhow::Result = (|| {
+ let mut config = Config::new();
+ // NB: wasmtime 47 removed `Config::async_support` — async is now
+ // available on any engine that turned on `component-model` (which
+ // it does by default). Explicit call intentionally omitted; leaving
+ // this comment as a breadcrumb for any future wasmtime downgrade.
+ config.wasm_component_model(true);
+ config.epoch_interruption(true);
+ // Enable fuel bookkeeping globally so per-instance fuel limits are
+ // cheap to opt into via `componentInstantiateWithConfig`. No-op
+ // performance hit for callers that never set fuel — wasmtime only
+ // decrements on backedges when the store has a nonzero fuel budget.
+ config.consume_fuel(true);
+ let engine = Engine::new(&config)?;
+ let runtime = tokio::runtime::Builder::new_multi_thread()
+ .enable_all()
+ .build()?;
+ let handle = Box::new(EngineHandle { engine, runtime });
+ Ok(Box::into_raw(handle) as jlong)
+ })();
+ or_throw(&mut env, result, 0)
+}
+
+/// Drop the engine and its Tokio runtime. Safe to call once with a
+/// nonzero handle produced by `engineCreate`; no-op on zero.
+///
+/// # Safety
+/// The handle MUST originate from `engineCreate`. Passing anything else is UB.
+#[no_mangle]
+pub unsafe extern "system" fn Java_ai_tegmentum_webassembly4j_provider_wasmos_jni_WasmosNative_engineClose(
+ _env: JNIEnv,
+ _class: JClass,
+ handle: jlong,
+) {
+ if handle == 0 {
+ return;
+ }
+ let _ = Box::from_raw(handle as *mut EngineHandle);
+}
+
+/// Increment the engine's epoch counter — call from a Java timer thread when
+/// an instance was instantiated with `epochDeadline`. Callers that never set
+/// a deadline can ignore this.
+#[no_mangle]
+pub extern "system" fn Java_ai_tegmentum_webassembly4j_provider_wasmos_jni_WasmosNative_engineIncrementEpoch(
+ mut env: JNIEnv,
+ _class: JClass,
+ handle: jlong,
+) {
+ if handle == 0 {
+ throw(&mut env, "wasmos-provider: engine handle is null");
+ return;
+ }
+ let engine = unsafe { &(*(handle as *mut EngineHandle)).engine };
+ engine.increment_epoch();
+}
+
+// -----------------------------------------------------------------------------
+// Component lifecycle
+// -----------------------------------------------------------------------------
+
+/// Compile a Component from raw bytes. Returns a handle usable by
+/// `componentInstantiate`.
+#[no_mangle]
+pub extern "system" fn Java_ai_tegmentum_webassembly4j_provider_wasmos_jni_WasmosNative_componentLoad(
+ mut env: JNIEnv,
+ _class: JClass,
+ engine_handle: jlong,
+ bytes: JByteArray,
+) -> jlong {
+ // Convert the byte array up-front so the fallible-body closure below
+ // doesn't need to hold `&mut env`.
+ let raw_result: anyhow::Result> = env
+ .convert_byte_array(&bytes)
+ .map_err(|e| anyhow::anyhow!("convert_byte_array: {e}"));
+ let raw = match raw_result {
+ Ok(v) => v,
+ Err(e) => {
+ throw(&mut env, &format!("wasmos-provider: {:#}", e));
+ return 0;
+ }
+ };
+
+ let result: anyhow::Result = (|| {
+ if engine_handle == 0 {
+ anyhow::bail!("engine handle is null");
+ }
+ let engine_ptr = engine_handle as *mut EngineHandle;
+ // SAFETY: We never move the Box; we only borrow through the raw pointer.
+ // Java is responsible for keeping the engine alive as long as any component
+ // handle derived from it is live (documented on WasmosEngineAdapter).
+ let engine = unsafe { &(*engine_ptr).engine };
+ let component = Component::from_binary(engine, &raw)?;
+ let handle = Box::new(ComponentHandle {
+ engine_ptr,
+ component,
+ });
+ Ok(Box::into_raw(handle) as jlong)
+ })();
+ or_throw(&mut env, result, 0)
+}
+
+/// Drop a component handle. See `engineClose` safety note.
+#[no_mangle]
+pub unsafe extern "system" fn Java_ai_tegmentum_webassembly4j_provider_wasmos_jni_WasmosNative_componentClose(
+ _env: JNIEnv,
+ _class: JClass,
+ handle: jlong,
+) {
+ if handle == 0 {
+ return;
+ }
+ let _ = Box::from_raw(handle as *mut ComponentHandle);
+}
+
+/// Serialize a compiled Component into its cached AOT byte representation.
+/// The returned blob can later be handed to `componentDeserialize` on an
+/// engine with a compatible wasmtime config to skip the compile step —
+/// the intended use case is warm restarts / hot-reload where the same
+/// component gets loaded repeatedly across process launches.
+///
+/// Byte compatibility is tied to the wasmtime version, engine config, and
+/// target ISA; a deserialize on a different setup will fail cleanly rather
+/// than silently produce wrong code.
+#[no_mangle]
+pub extern "system" fn Java_ai_tegmentum_webassembly4j_provider_wasmos_jni_WasmosNative_componentSerialize(
+ mut env: JNIEnv,
+ _class: JClass,
+ component_handle: jlong,
+) -> jbyteArray {
+ let result: anyhow::Result> = (|| {
+ if component_handle == 0 {
+ anyhow::bail!("component handle is null");
+ }
+ let ch = component_handle as *mut ComponentHandle;
+ let component = unsafe { &(*ch).component };
+ component
+ .serialize()
+ .map_err(|e| anyhow::anyhow!("Component::serialize: {e}"))
+ })();
+ match result {
+ Ok(bytes) => match env.byte_array_from_slice(&bytes) {
+ Ok(a) => a.into_raw(),
+ Err(e) => {
+ throw(&mut env, &format!("wasmos-provider: byte_array_from_slice: {e}"));
+ std::ptr::null_mut()
+ }
+ },
+ Err(e) => {
+ throw(&mut env, &format!("wasmos-provider: {:#}", e));
+ std::ptr::null_mut()
+ }
+ }
+}
+
+/// Deserialize a component previously produced by `componentSerialize`.
+/// Returns a fresh ComponentHandle bound to the caller's engine.
+///
+/// # Safety
+///
+/// `Component::deserialize` is `unsafe` in wasmtime because it trusts the
+/// input bytes to be a well-formed AOT image that the current engine
+/// produced (a hostile / corrupted image can cause UB when its
+/// pre-computed function pointers are called). The trust boundary belongs
+/// to the JVM caller: they're responsible for only feeding this bytes
+/// they got from a prior `componentSerialize` call on this or a
+/// compatible engine. We don't try to validate here — the wasmtime API
+/// puts the safety contract in the caller's hands and we relay it.
+#[no_mangle]
+pub extern "system" fn Java_ai_tegmentum_webassembly4j_provider_wasmos_jni_WasmosNative_componentDeserialize(
+ mut env: JNIEnv,
+ _class: JClass,
+ engine_handle: jlong,
+ bytes: JByteArray,
+) -> jlong {
+ let raw_result: anyhow::Result> = env
+ .convert_byte_array(&bytes)
+ .map_err(|e| anyhow::anyhow!("convert_byte_array: {e}"));
+ let raw = match raw_result {
+ Ok(v) => v,
+ Err(e) => {
+ throw(&mut env, &format!("wasmos-provider: {:#}", e));
+ return 0;
+ }
+ };
+
+ let result: anyhow::Result = (|| {
+ if engine_handle == 0 {
+ anyhow::bail!("engine handle is null");
+ }
+ let engine_ptr = engine_handle as *mut EngineHandle;
+ let engine = unsafe { &(*engine_ptr).engine };
+ // SAFETY: The JVM caller warranted the input bytes came from a
+ // prior componentSerialize call against a compatible engine. Any
+ // failure will surface as an anyhow error caught below.
+ let component = unsafe {
+ Component::deserialize(engine, &raw)
+ .map_err(|e| anyhow::anyhow!("Component::deserialize: {e}"))?
+ };
+ let handle = Box::new(ComponentHandle {
+ engine_ptr,
+ component,
+ });
+ Ok(Box::into_raw(handle) as jlong)
+ })();
+ or_throw(&mut env, result, 0)
+}
+
+// -----------------------------------------------------------------------------
+// Instance lifecycle + invoke
+// -----------------------------------------------------------------------------
+
+/// Instantiate a component with the full wasmos-runtime host surface
+/// (`add_all_to_linker`) plus `caps::wasi_p2` for the composed
+/// adapter-wasmtime's wasi imports.
+///
+/// A fresh `Store` is created per instantiation with a default
+/// `HostState::new()` — empty preview-2 WasiCtx, no memory limits, no epoch
+/// deadline. For a configured WASI ctx / memory-limits / epoch-deadline path,
+/// use `componentInstantiateWithConfig`.
+#[no_mangle]
+pub extern "system" fn Java_ai_tegmentum_webassembly4j_provider_wasmos_jni_WasmosNative_componentInstantiate(
+ mut env: JNIEnv,
+ _class: JClass,
+ component_handle: jlong,
+) -> jlong {
+ let result: anyhow::Result = (|| {
+ instantiate_with(component_handle, HostState::new(), LimitsBag::none())
+ })();
+ or_throw(&mut env, result, 0)
+}
+
+/// Same as `componentInstantiate` but with an optional configured WASI ctx
+/// (built from the argsArr / envKeysArr / envValsArr / preopen arrays) plus
+/// a `LimitsBag` struct of outer-store limits. Any `-1` numeric arg means
+/// "unset".
+///
+/// Bit-flags in `flags`: 0x01=inheritStdin, 0x02=inheritStdout,
+/// 0x04=inheritStderr, 0x08=hasWasiContext (if 0, all wasi args are ignored
+/// and default HostState::new() is used).
+///
+/// Limit args (all `-1` = unset):
+/// * `max_memory_bytes` — per-linear-memory upper bound in bytes
+/// * `epoch_deadline` — trap when epoch counter passes this value
+/// * `fuel_limit` — set the store's initial fuel budget (requires
+/// `Config::consume_fuel(true)`, which the engine enables globally)
+/// * `max_table_elements` — per-table element cap
+/// * `max_instances` — max component-model instances per store
+/// * `max_tables` — max tables per store
+/// * `max_memories` — max linear memories per store
+#[no_mangle]
+pub extern "system" fn Java_ai_tegmentum_webassembly4j_provider_wasmos_jni_WasmosNative_componentInstantiateWithConfig(
+ mut env: JNIEnv,
+ _class: JClass,
+ component_handle: jlong,
+ flags: jint,
+ args_arr: JObjectArray,
+ env_keys_arr: JObjectArray,
+ env_vals_arr: JObjectArray,
+ preopen_hosts_arr: JObjectArray,
+ preopen_guests_arr: JObjectArray,
+ preopen_writable_arr: JByteArray,
+ max_memory_bytes: jlong,
+ epoch_deadline: jlong,
+ fuel_limit: jlong,
+ max_table_elements: jlong,
+ max_instances: jlong,
+ max_tables: jlong,
+ max_memories: jlong,
+) -> jlong {
+ let has_wasi = (flags & 0x08) != 0;
+
+ // Pull all jni-scoped inputs up front so the fallible body doesn't hold
+ // multiple simultaneous borrows of &mut env.
+ let host_state_result: anyhow::Result = (|| {
+ if !has_wasi {
+ return Ok(HostState::new());
+ }
+ let inherit_stdin = (flags & 0x01) != 0;
+ let inherit_stdout = (flags & 0x02) != 0;
+ let inherit_stderr = (flags & 0x04) != 0;
+ let args = if args_arr.is_null() {
+ Vec::new()
+ } else {
+ jstringarray_to_vec(&mut env, &args_arr)?
+ };
+ let env_keys = if env_keys_arr.is_null() {
+ Vec::new()
+ } else {
+ jstringarray_to_vec(&mut env, &env_keys_arr)?
+ };
+ let env_vals = if env_vals_arr.is_null() {
+ Vec::new()
+ } else {
+ jstringarray_to_vec(&mut env, &env_vals_arr)?
+ };
+ if env_keys.len() != env_vals.len() {
+ anyhow::bail!(
+ "envKeys / envVals length mismatch: {} vs {}",
+ env_keys.len(),
+ env_vals.len()
+ );
+ }
+ let preopen_hosts = if preopen_hosts_arr.is_null() {
+ Vec::new()
+ } else {
+ jstringarray_to_vec(&mut env, &preopen_hosts_arr)?
+ };
+ let preopen_guests = if preopen_guests_arr.is_null() {
+ Vec::new()
+ } else {
+ jstringarray_to_vec(&mut env, &preopen_guests_arr)?
+ };
+ let preopen_writable: Vec = if preopen_writable_arr.is_null() {
+ Vec::new()
+ } else {
+ env.convert_byte_array(&preopen_writable_arr)
+ .map_err(|e| anyhow::anyhow!("convert_byte_array(preopen_writable): {e}"))?
+ };
+ if preopen_hosts.len() != preopen_guests.len()
+ || preopen_hosts.len() != preopen_writable.len()
+ {
+ anyhow::bail!(
+ "preopen array length mismatch: hosts={} guests={} writable={}",
+ preopen_hosts.len(),
+ preopen_guests.len(),
+ preopen_writable.len()
+ );
+ }
+
+ let mut b = WasiCtxBuilder::new();
+ if inherit_stdin {
+ b.inherit_stdin();
+ }
+ if inherit_stdout {
+ b.inherit_stdout();
+ }
+ if inherit_stderr {
+ b.inherit_stderr();
+ }
+ for arg in &args {
+ b.arg(arg);
+ }
+ for (k, v) in env_keys.iter().zip(env_vals.iter()) {
+ b.env(k, v);
+ }
+ for i in 0..preopen_hosts.len() {
+ let host_path = &preopen_hosts[i];
+ let guest_path = &preopen_guests[i];
+ let writable = preopen_writable[i] != 0;
+ let (dp, fp) = if writable {
+ (DirPerms::all(), FilePerms::all())
+ } else {
+ (DirPerms::READ, FilePerms::READ)
+ };
+ b.preopened_dir(host_path, guest_path, dp, fp)
+ .map_err(|e| anyhow::anyhow!("preopened_dir({host_path} -> {guest_path}): {e}"))?;
+ }
+ Ok(HostState::with_wasi(b.build()))
+ })();
+ let host_state = match host_state_result {
+ Ok(v) => v,
+ Err(e) => {
+ throw(&mut env, &format!("wasmos-provider: {:#}", e));
+ return 0;
+ }
+ };
+
+ let limits = LimitsBag {
+ max_memory_bytes: neg_to_none(max_memory_bytes).map(|v| v as usize),
+ epoch_deadline: neg_to_none(epoch_deadline).map(|v| v as u64),
+ fuel_limit: neg_to_none(fuel_limit).map(|v| v as u64),
+ max_table_elements: neg_to_none(max_table_elements).map(|v| v as usize),
+ max_instances: neg_to_none(max_instances).map(|v| v as usize),
+ max_tables: neg_to_none(max_tables).map(|v| v as usize),
+ max_memories: neg_to_none(max_memories).map(|v| v as usize),
+ };
+
+ let result: anyhow::Result = (|| {
+ instantiate_with(component_handle, host_state, limits)
+ })();
+ or_throw(&mut env, result, 0)
+}
+
+/// `-1` (or any negative) → None; otherwise Some(v). Centralised so the
+/// "-1 means unset" convention is threaded through one place, not every
+/// call site.
+fn neg_to_none(v: jlong) -> Option {
+ if v < 0 {
+ None
+ } else {
+ Some(v)
+ }
+}
+
+/// Outer-store limits bundle. Every field is optional — unset means "use
+/// wasmtime's default" for that dimension. Fields map 1:1 onto
+/// `StoreLimitsBuilder` setters (except `fuel_limit`, which goes through
+/// `Store::set_fuel`, and `epoch_deadline`, which goes through
+/// `Store::set_epoch_deadline` + `epoch_deadline_trap`).
+struct LimitsBag {
+ max_memory_bytes: Option,
+ epoch_deadline: Option,
+ fuel_limit: Option,
+ max_table_elements: Option,
+ max_instances: Option,
+ max_tables: Option,
+ max_memories: Option,
+}
+
+impl LimitsBag {
+ fn none() -> Self {
+ Self {
+ max_memory_bytes: None,
+ epoch_deadline: None,
+ fuel_limit: None,
+ max_table_elements: None,
+ max_instances: None,
+ max_tables: None,
+ max_memories: None,
+ }
+ }
+
+ /// True if any of the StoreLimits fields (memory / tables / memories /
+ /// instances / table-elements) is set — used to decide whether to
+ /// install `Store::limiter`. `fuel_limit` and `epoch_deadline` go
+ /// through separate Store APIs so they don't count.
+ fn wants_limiter(&self) -> bool {
+ self.max_memory_bytes.is_some()
+ || self.max_table_elements.is_some()
+ || self.max_instances.is_some()
+ || self.max_tables.is_some()
+ || self.max_memories.is_some()
+ }
+}
+
+/// Shared instantiate path. `host_state` supplies any WASI context;
+/// `limits` bundles the optional outer-store limits (memory / fuel /
+/// epoch / instances / tables / memories / table-elements).
+fn instantiate_with(
+ component_handle: jlong,
+ mut host_state: HostState,
+ limits: LimitsBag,
+) -> anyhow::Result {
+ if component_handle == 0 {
+ anyhow::bail!("component handle is null");
+ }
+ let ch = component_handle as *mut ComponentHandle;
+ let engine_ptr = unsafe { (*ch).engine_ptr };
+ let engine = unsafe { &(*engine_ptr).engine };
+ let runtime = unsafe { &(*engine_ptr).runtime };
+ let component_ref = unsafe { &(*ch).component };
+
+ // Configure outer_limits BEFORE building the store — the limiter closure
+ // borrows the field mutably. Only build a fresh StoreLimits when at
+ // least one dimension was requested; otherwise leave HostState's
+ // default (wide-open, matches pre-limits behaviour).
+ if limits.wants_limiter() {
+ let mut b = wasmtime::StoreLimitsBuilder::new();
+ if let Some(v) = limits.max_memory_bytes {
+ b = b.memory_size(v);
+ }
+ if let Some(v) = limits.max_table_elements {
+ b = b.table_elements(v);
+ }
+ if let Some(v) = limits.max_instances {
+ b = b.instances(v);
+ }
+ if let Some(v) = limits.max_tables {
+ b = b.tables(v);
+ }
+ if let Some(v) = limits.max_memories {
+ b = b.memories(v);
+ }
+ host_state.outer_limits = b.build();
+ }
+
+ let mut linker: Linker = Linker::new(engine);
+ wasmos_runtime::add_all_to_linker(&mut linker, |s| s)
+ .map_err(|e| anyhow::anyhow!("add_all_to_linker: {e}"))?;
+ caps::wasi_p2(&mut linker).map_err(|e| anyhow::anyhow!("caps::wasi_p2: {e}"))?;
+
+ let mut store = Store::new(engine, host_state);
+ if limits.wants_limiter() {
+ store.limiter(|state| &mut state.outer_limits);
+ }
+ // The engine has epoch_interruption(true) globally so per-instance
+ // deadlines are cheap to opt into. Without this, the store's default
+ // deadline of 0 traps immediately on the first backedge check — even
+ // for callers that never asked for epoch interruption. Setting to
+ // u64::MAX effectively disables the trap for unopted-in callers.
+ if let Some(deadline) = limits.epoch_deadline {
+ // Trap when the deadline is exceeded — the simplest sane default;
+ // yield-based deadlines can be added later behind a separate flag.
+ store.epoch_deadline_trap();
+ store.set_epoch_deadline(deadline);
+ } else {
+ store.set_epoch_deadline(u64::MAX);
+ }
+ // Fuel: engine has `consume_fuel(true)` globally so `set_fuel` is a
+ // cheap opt-in per store. Wasmtime starts fuel-enabled stores at 0
+ // fuel — which would trap the very first backedge for callers who
+ // never asked for a fuel budget. Setting to u64::MAX by default
+ // makes the "no fuel limit" case work identically to a fuel-disabled
+ // engine from the caller's perspective; the small per-backedge
+ // instrumentation cost is the price for lazy opt-in.
+ let fuel = limits.fuel_limit.unwrap_or(u64::MAX);
+ store
+ .set_fuel(fuel)
+ .map_err(|e| anyhow::anyhow!("set_fuel({fuel}): {e}"))?;
+
+ let instance =
+ runtime.block_on(async { linker.instantiate_async(&mut store, component_ref).await })?;
+
+ let handle = Box::new(InstanceHandle {
+ engine_ptr,
+ store: Mutex::new(store),
+ instance,
+ resources: Mutex::new(ResourceRegistry::default()),
+ futures: Mutex::new(FutureRegistry::default()),
+ streams: Mutex::new(StreamRegistry::default()),
+ error_contexts: Mutex::new(ErrorContextRegistry::default()),
+ });
+ Ok(Box::into_raw(handle) as jlong)
+}
+
+/// Drop an instance handle (which drops its Store).
+#[no_mangle]
+pub unsafe extern "system" fn Java_ai_tegmentum_webassembly4j_provider_wasmos_jni_WasmosNative_instanceClose(
+ _env: JNIEnv,
+ _class: JClass,
+ handle: jlong,
+) {
+ if handle == 0 {
+ return;
+ }
+ let _ = Box::from_raw(handle as *mut InstanceHandle);
+}
+
+// -----------------------------------------------------------------------------
+// Future handle lifecycle
+// -----------------------------------------------------------------------------
+// See the `FutureRegistry` docs for the parking-lot design; these two entry
+// points are the Java-visible surface for disposing of a parked `FutureAny`
+// and (attempting to) await its value. Await is a wasmtime-47 public-API gap
+// (see below); close is fully functional.
+
+/// Evict a parked `FutureAny` from the instance's future registry and call
+/// `FutureAny::close(store)` on it so the write end sees the read end as
+/// dropped. Idempotent-adjacent: passing an unknown id is an error (matches
+/// the Resource contract), passing a null instance handle throws.
+///
+/// Safe to call on a WitFuture that was already consumed by a guest pass-in
+/// only in the sense that the entry will have been removed — the error path
+/// will surface as "unknown future id". Java-side callers should keep a
+/// close call in a `finally` block when they own a WitFuture but never pass
+/// it back to a guest.
+#[no_mangle]
+pub extern "system" fn Java_ai_tegmentum_webassembly4j_provider_wasmos_jni_WasmosNative_futureClose(
+ mut env: JNIEnv,
+ _class: JClass,
+ instance_handle: jlong,
+ future_id: jlong,
+) {
+ let result: anyhow::Result<()> = (|| {
+ if instance_handle == 0 {
+ anyhow::bail!("instance handle is null");
+ }
+ let ih = instance_handle as *mut InstanceHandle;
+ let store_mutex = unsafe { &(*ih).store };
+ let futures_mutex = unsafe { &(*ih).futures };
+ let mut store = store_mutex.lock().unwrap();
+ let mut futures = futures_mutex.lock().unwrap();
+ let mut any = futures.take(future_id as u64).ok_or_else(|| {
+ anyhow::anyhow!(
+ "wasmos-provider: unknown future id {} (already closed, transferred \
+ to guest, or never parked)",
+ future_id
+ )
+ })?;
+ // FutureAny::close signals the writer that the read end is gone.
+ // Swallow a "future already closed" error only implicitly (via the
+ // take path above); a live registry hit that fails to close is a
+ // real error worth surfacing.
+ any.close(store.as_context_mut())
+ .map_err(|e| anyhow::anyhow!("FutureAny::close: {e}"))?;
+ Ok(())
+ })();
+ or_throw(&mut env, result, ());
+}
+
+/// Attempt to await a parked `FutureAny` and hand back the resolved value
+/// as a JSON blob (same schema as `instanceInvokeJson` returns).
+///
+/// # wasmtime 47 API gap
+///
+/// This method currently ALWAYS throws `WebAssemblyException` with a clear
+/// "wasmtime API gap" message. The `FutureAny` public API in wasmtime 47
+/// only exposes:
+///
+/// * `try_into_future_reader::