Skip to content

Commit

Permalink
Rename HostImports to ExternalValues (#527)
Browse files Browse the repository at this point in the history
a.k.a. **The Big Rename™️**. This follows an offline discussion with
@andreaTP. The idea would be to finalize naming before the future 1.0
release.

The class `HostImports` and the related dependencies `HostFunction`,
`HostTable`, `HostGlobal`, `HostMemory` are currently being used with
two different purposes:

1. allowing a **host**, that is, the application that embeds the Wasm
runtime, to define such values programmatically using the **host**
language (especially true for functions)
2. allowing **the runtime** to link together two different module
instances, connecting their "import" requirements to the "exports" of
other instances.

However, name "Host<something>" makes most sense for the first case,
while it feels slightly off and ambiguous in the second case: are those
Host-defined values, or are those just values belonging to another
instance?

The spec in this regard is not ambiguous, and generally refers to
[**External
Types**](https://webassembly.github.io/spec/core/syntax/types.html#external-types)
and [**External
Values**](https://webassembly.github.io/spec/core/exec/runtime.html#syntax-externval).
Specifically:

- **External Types** classify **imports** and **external values**.
Notice [we are already using the term
`ExternalType`](https://github.com/dylibso/chicory/blob/dc6959dbf20544517374d93e1f7c255d316ecdaa/wasm/src/main/java/com/dylibso/chicory/wasm/types/ExternalType.java#L13)
under package `com.dylibso.chicory.wasm.types`.
- I think **External Values** identifies more correctly what we are
currently calling **HostImports**; i.e. instances of the respective
types (instances of functions, of globals, of memories or of tables),
capturing the fact that these values are just "externally defined", not
necessarily by **the host**, but possibly by an instance of another
module.

Thus, with this change, I propose the following renames:

- `FromHost` -> `ExternalValue`
- `FromHost.FromHostType` -> `ExternalValue.Type` notice that we could
also reuse `com.dylibso.chicory.wasm.types. ExternalType`, but this
would require exposing to the end users package
`com.dylibso.chicory.wasm.types` from package
`com.dylibso.chicory.runtime`
- `HostImports` -> `ExternalValues`
- `HostGlobal` -> `ExternalGlobal`
- `HostTable` -> `ExternalTable`
- `HostMemory` -> `ExternalMemory`
- `HostFunction` still exists, as a *subtype* of `ExternalFunction`:
this allows to keep a distinction that might be useful in the future; in
fact, the constructor to `ExternalFunction` could be even package-local.

Notice that, I have not re-defined `Host{Global,Table,Memory}` as
subtypes of `External{Global,Table,Memory}`, because I feel like there
is no meaningful distinction:
- the implementation for the "host" version does not conceptually
differs from the "wasm" version
- on the other hand, `HostFunction`s implement logic using the host
language, while `ExternalFunction`s are otherwise just the exported
image of an instance of a `Function` defined in a Wasm module. So, even
though their interface might be the same, they are different concepts.

Even if we decided to drop the concrete type `HostFunction` altogether,
it might still make sense to expose a factory
`ExternalFunction.createHostFunction() for an `ExternalFunction` because
the term "host function" is generally known to refer to a host-defined
function.

It might be now feel slightly more awkward to import host functions
directly through an `Instance.Builder`; e.g.:

```
var wasi = new WasiPreview1(logger, wasiOpts);
var hostFunctions = new ExternalValues(wasi.toHostFunctions());
var inst = Instance.builder(Parser.parse(new File("greet-wasi.wasm")))
              .withExternalValues(hostFunctions).build();
```

but consider that, on the other hand, the `Instance.Builder` interface
is a lower-level interface, so it might be acceptable for it to expose
"lower-level" naming; in the future, I would expect users to interact
with the runtime using higher level interfaces, such as the `Store`. In
this case, users would write:

```
var wasi = new WasiPreview1(logger, wasiOpts);
var store = new Store();
store.addFunctions(wasi.toHostFunctions());
var inst = store.instantiate("main", Parser.parse(new File("greet-wasi.wasm")));
```

### Notes

Will follow-up with another, conceptually unrelated rename, but with the
same goal of improved clarity; i.e.:

- `fieldName()` -> `symbolName()`

reason being `fieldName()` to me sounds too much like "name of a
property", that is, it makes me think "Global" but we also use it for
"Function"s. "Symbol" is more generic and should avoid ambiguity.

Signed-off-by: Edoardo Vacchi <[email protected]>

---------

Signed-off-by: Edoardo Vacchi <[email protected]>
  • Loading branch information
evacchi authored Sep 23, 2024
1 parent f25dcde commit 0072147
Show file tree
Hide file tree
Showing 32 changed files with 560 additions and 545 deletions.
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,15 +251,15 @@ public void println(String value) {
import com.dylibso.chicory.runtime.HostFunction;
import com.dylibso.chicory.wasm.types.ValueType;
var func = new HostFunction(
"console",
"log",
(Instance instance, Value... args) -> { // decompiled is: console_log(13, 0);
var len = args[0].asInt();
var offset = args[1].asInt();
var message = instance.memory().readString(offset, len);
println(message);
return null;
},
"console",
"log",
List.of(ValueType.I32, ValueType.I32),
List.of());
```
Expand All @@ -277,9 +277,9 @@ Note that the HostFunction needs 3 things:
Now we just need to pass this host function in during our instantiation phase:

```java
import com.dylibso.chicory.runtime.HostImports;
var imports = new HostImports(new HostFunction[] {func});
var instance = Instance.builder(Parser.parse(new File("./logger.wasm"))).withHostImports(imports).build();
import com.dylibso.chicory.runtime.ExternalValues;
var hostFunctions = new ExternalValues(new HostFunction[] {func});
var instance = Instance.builder(Parser.parse(new File("./logger.wasm"))).withExternalValues(hostFunctions).build();
var logIt = instance.export("logIt");
logIt.apply();
// should print "Hello, World!" 10 times
Expand All @@ -306,7 +306,7 @@ import com.dylibso.chicory.runtime.Store;
// instantiate the store
var store = new Store();
// registers `console.log` in the store (see the previous section for the definition of `func`)
store.addHostFunction(func);
store.addFunction(func);
```

However, the store also automatically exposes the exports of a module to the other instances that are registered.
Expand All @@ -326,8 +326,9 @@ var logger2 = store.instantiate("logger2", Parser.parse(new File("./logger.wasm"
This is equivalent to:

```java
var hostImports = store.toHostImports();
var instance = Instance.builder(m).withHostImports(hostImports).build();
var external = store.toExternalValues();
var m = Parser.parse(new File("./logger.wasm"));
var instance = Instance.builder(m).withExternalValues(external).build();
store.register("logger2", instance);
```

Expand Down
54 changes: 28 additions & 26 deletions aot-tests/src/test/java/com/dylibso/chicory/testing/Spectest.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.dylibso.chicory.testing;

import com.dylibso.chicory.runtime.ExternalGlobal;
import com.dylibso.chicory.runtime.ExternalMemory;
import com.dylibso.chicory.runtime.ExternalTable;
import com.dylibso.chicory.runtime.ExternalValues;
import com.dylibso.chicory.runtime.GlobalInstance;
import com.dylibso.chicory.runtime.HostFunction;
import com.dylibso.chicory.runtime.HostGlobal;
import com.dylibso.chicory.runtime.HostImports;
import com.dylibso.chicory.runtime.HostMemory;
import com.dylibso.chicory.runtime.HostTable;
import com.dylibso.chicory.runtime.Instance;
import com.dylibso.chicory.runtime.Memory;
import com.dylibso.chicory.runtime.TableInstance;
Expand All @@ -23,52 +23,54 @@ public final class Spectest {

private Spectest() {}

public static HostImports toHostImports() {
return new HostImports(
public static ExternalValues toExternalValues() {
return new ExternalValues(
new HostFunction[] {
new HostFunction(noop, "spectest", "print", List.of(), List.of()),
new HostFunction("spectest", "print", noop, List.of(), List.of()),
new HostFunction(
noop, "spectest", "print_i32", List.of(ValueType.I32), List.of()),
"spectest", "print_i32", noop, List.of(ValueType.I32), List.of()),
new HostFunction(
noop, "spectest", "print_i32_1", List.of(ValueType.I32), List.of()),
"spectest", "print_i32_1", noop, List.of(ValueType.I32), List.of()),
new HostFunction(
noop, "spectest", "print_i32_2", List.of(ValueType.I32), List.of()),
"spectest", "print_i32_2", noop, List.of(ValueType.I32), List.of()),
new HostFunction(
noop, "spectest", "print_f32", List.of(ValueType.F32), List.of()),
"spectest", "print_f32", noop, List.of(ValueType.F32), List.of()),
new HostFunction(
noop,
"spectest",
"print_i32_f32",
noop,
List.of(ValueType.I32, ValueType.F32),
List.of()),
new HostFunction(
noop, "spectest", "print_i64", List.of(ValueType.I64), List.of()),
"spectest", "print_i64", noop, List.of(ValueType.I64), List.of()),
new HostFunction(
noop, "spectest", "print_i64_1", List.of(ValueType.I64), List.of()),
"spectest", "print_i64_1", noop, List.of(ValueType.I64), List.of()),
new HostFunction(
noop, "spectest", "print_i64_2", List.of(ValueType.I64), List.of()),
"spectest", "print_i64_2", noop, List.of(ValueType.I64), List.of()),
new HostFunction(
noop, "spectest", "print_f64", List.of(ValueType.F64), List.of()),
"spectest", "print_f64", noop, List.of(ValueType.F64), List.of()),
new HostFunction(
noop,
"spectest",
"print_f64_f64",
noop,
List.of(ValueType.F64, ValueType.F64),
List.of())
},
new HostGlobal[] {
new HostGlobal("spectest", "global_i32", new GlobalInstance(Value.i32(666))),
new HostGlobal("spectest", "global_i64", new GlobalInstance(Value.i64(666))),
new HostGlobal(
new ExternalGlobal[] {
new ExternalGlobal(
"spectest", "global_i32", new GlobalInstance(Value.i32(666))),
new ExternalGlobal(
"spectest", "global_i64", new GlobalInstance(Value.i64(666))),
new ExternalGlobal(
"spectest", "global_f32", new GlobalInstance(Value.fromFloat(666.6f))),
new HostGlobal(
new ExternalGlobal(
"spectest", "global_f64", new GlobalInstance(Value.fromDouble(666.6))),
},
new HostMemory[] {
new HostMemory("spectest", "memory", new Memory(new MemoryLimits(1, 2)))
new ExternalMemory[] {
new ExternalMemory("spectest", "memory", new Memory(new MemoryLimits(1, 2)))
},
new HostTable[] {
new HostTable(
new ExternalTable[] {
new ExternalTable(
"spectest",
"table",
new TableInstance(new Table(ValueType.FuncRef, new Limits(10, 20))))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.dylibso.chicory.testing;

import com.dylibso.chicory.aot.AotMachine;
import com.dylibso.chicory.runtime.HostImports;
import com.dylibso.chicory.runtime.ExternalValues;
import com.dylibso.chicory.runtime.Instance;
import com.dylibso.chicory.runtime.Store;
import com.dylibso.chicory.wabt.Wat2Wasm;
Expand Down Expand Up @@ -62,9 +62,9 @@ public TestModule(Module module) {
}

public Instance instantiate(Store s) {
HostImports hostImports = s.toHostImports();
ExternalValues externalValues = s.toExternalValues();
return Instance.builder(module)
.withHostImports(hostImports)
.withExternalValues(externalValues)
.withMachineFactory(AotMachine::new)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import static java.nio.charset.StandardCharsets.UTF_8;

import com.dylibso.chicory.aot.AotMachine;
import com.dylibso.chicory.runtime.ExternalValues;
import com.dylibso.chicory.runtime.HostFunction;
import com.dylibso.chicory.runtime.HostImports;
import com.dylibso.chicory.runtime.Instance;
import com.dylibso.chicory.wasm.Parser;
import com.dylibso.chicory.wasm.types.ValueType;
Expand Down Expand Up @@ -38,9 +38,9 @@ public void verifyHelloWasi() {
verifyGeneratedBytecode(
"hello-wasi.wat.wasm",
new HostFunction(
(instance, args) -> null,
"wasi_snapshot_preview1",
"fd_write",
(instance, args) -> null,
List.of(ValueType.I32, ValueType.I32, ValueType.I32, ValueType.I32),
List.of(ValueType.I32)));
}
Expand Down Expand Up @@ -89,9 +89,9 @@ public void verifyStart() {
verifyGeneratedBytecode(
"start.wat.wasm",
new HostFunction(
(instance, args) -> null,
"env",
"gotit",
(instance, args) -> null,
List.of(ValueType.I32),
List.of()));
}
Expand All @@ -107,7 +107,7 @@ private static void verifyGeneratedBytecode(String name, HostFunction... hostFun
Parser.parse(
ClassLoader.getSystemClassLoader()
.getResourceAsStream("compiled/" + name)))
.withHostImports(new HostImports(hostFunctions))
.withExternalValues(new ExternalValues(hostFunctions))
.withMachineFactory(AotMachine::new)
.withStart(false)
.build();
Expand Down
8 changes: 4 additions & 4 deletions cli/src/main/java/com/dylibso/chicory/cli/Cli.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.dylibso.chicory.cli;

import com.dylibso.chicory.log.SystemLogger;
import com.dylibso.chicory.runtime.HostImports;
import com.dylibso.chicory.runtime.ExternalValues;
import com.dylibso.chicory.runtime.Instance;
import com.dylibso.chicory.wasi.WasiOptions;
import com.dylibso.chicory.wasi.WasiPreview1;
Expand Down Expand Up @@ -60,17 +60,17 @@ public void run() {
var module = Parser.parse(file);
var imports =
wasi
? new HostImports(
? new ExternalValues(
new WasiPreview1(
logger,
WasiOptions.builder().inheritSystem().build())
.toHostFunctions())
: new HostImports();
: new ExternalValues();
var instance =
Instance.builder(module)
.withInitialize(true)
.withStart(false)
.withHostImports(imports)
.withExternalValues(imports)
.build();

if (functionName != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,9 @@ private Expression processMethod(
var function =
new ObjectCreationExpr()
.setType("HostFunction")
.addArgument(handle)
.addArgument(new StringLiteralExpr(moduleName))
.addArgument(new StringLiteralExpr(name))
.addArgument(handle)
.addArgument(new MethodCallExpr(new NameExpr("List"), "of", paramTypes))
.addArgument(new MethodCallExpr(new NameExpr("List"), "of", returnType));
// TODO: update javaparser and replace with multiline formatting
Expand Down
12 changes: 3 additions & 9 deletions function-processor/src/test/resources/BasicMathGenerated.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,26 @@ private BasicMath_ModuleFactory() {}
public static HostFunction[] toHostFunctions(BasicMath functions) {
return new HostFunction[] {
new HostFunction(
(Instance instance, Value... args) -> {
"math", "add", (Instance instance, Value... args) -> {
long result = functions.add(args[0].asInt(), args[1].asInt());
return new Value[] { Value.i64(result) };
},
"math",
"add",
List.of(ValueType.I32, ValueType.I32),
List.of(ValueType.I64)
),
new HostFunction(
(Instance instance, Value... args) -> {
"math", "square", (Instance instance, Value... args) -> {
double result = functions.pow2(args[0].asFloat());
return new Value[] { Value.fromDouble(result) };
},
"math",
"square",
List.of(ValueType.F32),
List.of(ValueType.F64)
),
new HostFunction(
(Instance instance, Value... args) -> {
"math", "floor_div", (Instance instance, Value... args) -> {
int result = functions.floorDiv(args[0].asInt(), args[1].asInt())
return new Value[] { Value.i32(result) };
},
"math",
"floor_div",
List.of(ValueType.I32, ValueType.I32),
List.of(ValueType.I32)
),
Expand Down
8 changes: 2 additions & 6 deletions function-processor/src/test/resources/NestedGenerated.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,18 @@ private Nested_ModuleFactory() {}
public static HostFunction[] toHostFunctions(Nested functions) {
return new HostFunction[] {
new HostFunction(
(Instance instance, Value... args) -> {
"nested", "print", (Instance instance, Value... args) -> {
functions.print(instance.memory(), args[0].asInt(), args[1].asInt());
return null;
},
"nested",
"print",
List.of(ValueType.I32, ValueType.I32),
List.of()
),
new HostFunction(
(Instance instance, Value... args) -> {
"nested", "exit", (Instance instance, Value... args) -> {
functions.exit();
return null;
},
"nested",
"exit",
List.of(),
List.of()
),
Expand Down
8 changes: 2 additions & 6 deletions function-processor/src/test/resources/NoPackageGenerated.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,18 @@ private Nested_ModuleFactory() {}
public static HostFunction[] toHostFunctions(NoPackage functions) {
return new HostFunction[] {
new HostFunction(
(Instance instance, Value... args) -> {
"nopackage", "print", (Instance instance, Value... args) -> {
functions.print(instance.memory(), args[0].asInt(), args[1].asInt());
return null;
},
"nopackage",
"print",
List.of(ValueType.I32, ValueType.I32),
List.of()
),
new HostFunction(
(Instance instance, Value... args) -> {
"nopackage", "exit", (Instance instance, Value... args) -> {
functions.exit();
return null;
},
"nopackage",
"exit",
List.of(),
List.of()
),
Expand Down
16 changes: 4 additions & 12 deletions function-processor/src/test/resources/SimpleGenerated.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,43 +15,35 @@ private Simple_ModuleFactory() {}
public static HostFunction[] toHostFunctions(Simple functions) {
return new HostFunction[] {
new HostFunction(
(Instance instance, Value... args) -> {
"simple", "print", (Instance instance, Value... args) -> {
functions.print(
instance.memory().readString(args[0].asInt(), args[1].asInt()));
return null;
},
"simple",
"print",
List.of(ValueType.I32, ValueType.I32),
List.of()
),
new HostFunction(
(Instance instance, Value... args) -> {
"simple", "printx", (Instance instance, Value... args) -> {
functions.printx(instance.memory().readCString(args[0].asInt()));
return null;
},
"simple",
"printx",
List.of(ValueType.I32),
List.of()
),
new HostFunction(
(Instance instance, Value... args) -> {
"simple", "random_get", (Instance instance, Value... args) -> {
functions.randomGet(instance.memory(), args[0].asInt(), args[1].asInt());
return null;
},
"simple",
"random_get",
List.of(ValueType.I32, ValueType.I32),
List.of()
),
new HostFunction(
(Instance instance, Value... args) -> {
"simple", "exit", (Instance instance, Value... args) -> {
functions.exit();
return null;
},
"simple",
"exit",
List.of(),
List.of()
),
Expand Down
Loading

0 comments on commit 0072147

Please sign in to comment.