Skip to main content

WASI Preview 1

The WebAssembly System Interface is a suite of host functions that a Wasm module can import to provide system-level capabilities, such as:

  • stdin / stdout / stderr
  • environment variables
  • command line arguments
  • system clock
  • random number generation
  • basic reading and writing of files (through use of a virtual file system)

All such capabilities are virtualized; i.e., the guest will not have direct access to the corresponding host resources, but they will be mediated by the WASI layer, which can be configured to limit their surface. Add the dependency to your build:

<dependency>
<groupId>com.dylibso.chicory</groupId>
<artifactId>wasi</artifactId>
<version>latest-release</version>
</dependency>

How to use

As a host who is running Wasm modules, WASI is just a collection of imports that you need to provide to a wasi-compiled module when instantiating it. You'll also need to configure some options for how these functions behave and what the module can and cannot do.

Remember that you have full control over those functions, you can use just part of provided implementation or swap in specific implementations to better control what is being executed.

WasiPreview1 Instantiation

In order to instantiate a WASI module you need an instance of WasiPreview1.

For instance, download the following example from the link or with curl:

curl https://raw.githubusercontent.com/dylibso/chicory/main/wasm-corpus/src/main/resources/compiled/hello-wasi.wat.wasm > hello-wasi.wasm
import com.dylibso.chicory.log.SystemLogger;
import com.dylibso.chicory.wasi.WasiOptions;
import com.dylibso.chicory.wasi.WasiPreview1;
import com.dylibso.chicory.wasm.Parser;
import com.dylibso.chicory.runtime.Store;

import java.io.File;

var logger = new SystemLogger();
// let's just use the default options for now
var options = WasiOptions.builder().build();
// create our instance of wasip1
var wasi = new WasiPreview1(logger, WasiOptions.builder().build());
// create the module and connect the host functions
var store = new Store().addFunction(wasi.toHostFunctions());
// instantiate and execute the main entry point
store.instantiate("hello-wasi", Parser.parse(new File("hello-wasi.wasm")));

Note: Notice that we don't explicitly execute the module. The module will run when you instantiate it. This is part of the WASI spec. A WASI module will implicitly call _start. To learn more read this blog post.

stdin, stdout, and stderr

To start with, you want to orchestrate stdin, stdout, and stderr of the module. Often, this is the way you communicate with basic WASI-enabled modules by way of the command pattern. In order to make it easy to manipulate these streams, we expose stdin as an InputStream and stdout/stderr as an OutputStream.

Download from the link or with curl:

curl https://raw.githubusercontent.com/dylibso/chicory/main/wasm-corpus/src/main/resources/compiled/greet-wasi.rs.wasm > greet-wasi.wasm
// Let's create a fake stdin stream with the bytes "Chicory"
var fakeStdin = new ByteArrayInputStream("Chicory".getBytes());
// We will create two output streams to capture stdout and stderr
var fakeStdout = new ByteArrayOutputStream();
var fakeStderr = new ByteArrayOutputStream();
// now pass those to our wasi options builder
var wasiOpts = WasiOptions.builder().withStdout(fakeStdout).withStderr(fakeStderr).withStdin(fakeStdin).build();

var wasi = new WasiPreview1(logger, wasiOpts);

// greet-wasi is a rust program that greets the string passed in stdin
var store = new Store().addFunction(wasi.toHostFunctions());
// instantiating will execute the module if it's a WASI command-pattern module
store.instantiate("hello-wasi", Parser.parse(new File("greet-wasi.wasm")));


// check that we output the greeting
assert(fakeStdout.toString().equals("Hello, Chicory!"));
// there should be no bytes in stderr!
assert(fakeStderr.toString().equals(""));

Notice that it is always possible to connect standard output, standard input and standard error to the system's real streams. For instance, in the case of stdout, you would write:

var wasi = WasiOptions.builder().withStdout(System.out).withStderr(System.err).withStdin(System.in).build();

a convenient shorthand for doing the same is:

var wasi = WasiOptions.builder().inheritSystem().build()

arguments

Especially when using CLIs, it's useful to provide command line arguments to the Wasm Module.

You can do that by using:

var wasi = WasiOptions.builder().withArguments(List.of("executable-name", "--more", "--options")).build();

environment variables

To expose environment variables to your WASI module you can list them in the options:

var wasi = WasiOptions.builder().withEnvironment("ENV_ONE_KEY", "my-one-key-value").withEnvironment("ENV_TWO_KEY", "my-two-key-value").build();

disk

We provide limited support for operations on the disk, we only test on a Virtual FileSystem and we encourage you to use the same. We use Google's Jimfs.

Example code to use the disk looks like:

import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;

try (FileSystem fs = Jimfs.newFileSystem(Configuration.unix().toBuilder().setAttributeViews("unix").build())) {
Path source = Path.of("my-source");
Path target = fs.getPath("my-source");
com.dylibso.chicory.wasi.Files.copyDirectory(source, target);

var wasi = WasiOptions.builder().withDirectory(target.toString(), target).build();

// ...
}

Supported Features

If your module calls a wasi function that we don't support, or uses a feature that we don't support, we will throw a WasmRuntimeException.

For the most up-to-date info, and to see what specific functions we support, see the WasiPreview1.java and the following table:

WASI FunctionSupportedNotes
args_get
args_sizes_get
clock_res_get🟡See clock_time_get.
clock_time_get🟡Clock IDs process_cputime_id and thread_cputime_id are not supported.
environ_get
environ_sizes_get
fd_advise
fd_allocate
fd_close
fd_datasync
fd_fdstat_get
fd_fdstat_set_flags
fd_fdstat_set_rights
fd_filestat_get
fd_filestat_set_size
fd_filestat_set_times
fd_pread
fd_prestat_dir_name
fd_prestat_get
fd_pwrite🟡Not supported for files opened in append mode.
fd_read
fd_readdir
fd_renumber
fd_seek
fd_sync
fd_tell
fd_write
path_create_directory
path_filestat_get
path_filestat_set_times
path_link
path_open
path_readlink
path_remove_directory
path_rename
path_symlink
path_unlink_file
poll_oneoff
proc_exit
proc_raise💀This function is no longer part of WASI.
random_get
sched_yield
sock_accept
sock_recv
sock_send
sock_shutdown