Skip to main content

Linking

In the Host Functions section we met the Store for the first-time.

A Store is an intermediate-level abstraction that collects Wasm function, global, memory, and table instances as named entities. It simplifies creating instances, especially when there are a lot of interdependencies.

In the simplest case, it allows to register single host functions, globals, memories and tables. For instance, we already saw how to register a console.log() host function to the Store:

import com.dylibso.chicory.wasm.Parser;
import com.dylibso.chicory.runtime.Instance;
import com.dylibso.chicory.runtime.HostFunction;
import com.dylibso.chicory.runtime.Store;
import com.dylibso.chicory.wasm.types.ValueType;

var func = new HostFunction(
"console",
"log",
List.of(ValueType.I32, ValueType.I32),
List.of(),
(Instance instance, long... args) -> { // decompiled is: console_log(13, 0);
var len = (int) args[0];
var offset = (int) args[1];
var message = instance.memory().readString(offset, len);
println(message);
return null;
});

// instantiate the store
var store = new Store();
// registers `console.log` in the store
store.addFunction(func);

However, the store also automatically exposes the exports of a module to the other instances that are registered. In fact, in the Host Functions section, when we created our instance from the logger.wasm module, we also passed a string "logger". This is the name of the instance:

// create a named `instance` with name `logger`
var instance = store.instantiate("logger", Parser.parse(new File("./logger.wasm")));

Because this instance is now named, now any exports in the logger module will be automatically qualified. For instance, the exported function logIt will be visible by other modules as logger.logIt.

Notes

  • The invocation store.instantiate("logger", ...) is in fact equivalent to the lower-level sequence:

    var imports = store.toImportValues();
    var m = Parser.parse(new File("./logger.wasm"));
    var instance = Instance.builder(m).withImportValues(imports).build();
    store.register("logger", instance);

    However, in most cases we recommend to use the shorthand form.

  • Also notice that registering two instances with the same name results in overwriting the functions, globals, memories, tables with matching names. In this case, the new logger2.logIt function overwrote the old logger2.logIt function.

  • The current Store is a mutable object, not meant to be shared (it is not thread-safe).

  • A Store does not resolve interdependencies between modules in itself: if your set of modules have interdependencies, you will have to instantiate and register them in the right order.