Skip to content

Commit

Permalink
Introduce the Store + fixes to validation (#495)
Browse files Browse the repository at this point in the history
Closes #486 

Introduces the Store abstraction and adopts it in the test cases,
cleaning up a lot of unnecessary test cases. ~~I am opening the PR in
draft, as it needs rebasing anyway.~~

The change is relatively simple, it introduces the `Store` as described
in #486, with a fluent interface of the type:

```java
var store = new Store()
   .addFunctions(f1, ...)
   .addGlobals(g1, ...)
```

This API is copied over from `HostImports`, although this is not a
subclass; it is still "related" to it; in fact:
 
- it is possible to get the contents of a store ("converting" it, if you
will) to a `HostImports` instance with `store.toHostImports()`;
- the `Store` also accepts an entire `HostImports` instance as a
shorthand for registering all of the items in it with
`store.addHostImports(hostImports)`

It also provides shorthands to register all of the exports in a module
as qualified "HostGlobal"s, "HostFunction"s etc.

```java
store.register("name", instance); // now each export `e` in instance, is stored as the qualified name `name.e`
```

It is also possible to instantiate and register in one move:

```java
var instance = store.instantiate("name", module); 
```

this is equivalent to:

```java
var instance = Instance.builder(module).withHostImports(store.toHostImports()).build()
store.register("name", instance)
```

The Store is deterministic, in that its behavior is known: when clashing
instances (i.e. where their qualified name `n.e` is clashing) are
registered to the store, the latest registration overwrites the oldest.
Internally each type(*) (globals, funcs, etc) is kept in separate
LinkedHashMaps.

Introducing the `Store` enormously simplifies the definition of the spec
tests, because such tests actually expect changes to "stick" across
cases; i.e. they mutate sequentially a shared `store` instance adding
and updating memories, functions, globals, tables.

In fact, introducing the store highlighted a bunch of issues with
validation, that I am fixing contextually: even though we would not let
any invalid instance slip, we would raise `UnlinkableException`s during
the initialization of the instance, instead of throwing an
`InvalidException` at validation time.

Potentially this allows us to avoid the second check at the time of
instantiation, because we could assume that validation has passed.

(*) note: this actually means that clashes might exists if two different
types have the same qualified name 🤔
e.g. `n.e` is a func, but there is also a `n.e` that is a global 🤔

---------

Signed-off-by: Edoardo Vacchi <[email protected]>
Co-authored-by: Andrea Peruffo <[email protected]>
  • Loading branch information
evacchi and andreaTP authored Aug 30, 2024
1 parent 780d3f2 commit 29aa68a
Show file tree
Hide file tree
Showing 43 changed files with 694 additions and 2,160 deletions.
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,57 @@ writeResultFile("hostFunction.result", hostFunctionResult);
```
-->

### Store and Instantiating Multiple Modules

A [Store][spec] is an intermediate-level abstraction that collects Wasm function, global, memory, and table instances
as named entities.

It simplifies creating instances when there are a lot of interdependencies, by collecting all the

In the simplest case, it allows to register single host functions, globals, memories and tables:

```java
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);
```

However, the store also automatically exposes the exports of a module to the other instances that are registered.

```java
// registers the `instance` created earlier (see the previous section) with the name `logger`
store.register("logger", instance);
// now the exported function `logIt` can be imported by other modules as `logger.logIt`
```

There is also a shorthand method to instantiate a module and register the resulting instance:

```java
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();
store.register("logger2", instance);
```

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.

[spec]: https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#store%E2%91%A0

## Development

### Why is this needed?
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit 29aa68a

Please sign in to comment.