Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructure and improve the docs #679

Merged
merged 22 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/docs/examples/_category_.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
label: Examples
position: 30
54 changes: 54 additions & 0 deletions docs/docs/examples/rust.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
sidebar_position: 1
sidebar_label: Rust
title: Using Rust with Chicory
---
## Compile Rust to Wasm

Compiling a Rust library to Wasm is easy and can be performed using standard `rustc` options:

```bash
rustc --target=wasm32-unknown-unknown --crate-type=cdylib
```

when you need to add support for wasi preview 1(typically when using CLIs) you can use:

```bash
rustc --target=wasm32-wasi --crate-type=bin
```

## Using in Chicory

<!--
```java
//DEPS com.dylibso.chicory:docs-lib:999-SNAPSHOT
//DEPS com.dylibso.chicory:runtime:999-SNAPSHOT

docs.FileOps.copyFromWasmCorpus("count_vowels.rs.wasm", "count_vowels.rs.wasm");

System.setOut(new PrintStream(
new BufferedOutputStream(
new FileOutputStream("docs/examples/rust.md.result"))));
```
-->

```java
import com.dylibso.chicory.wasm.Parser;
import com.dylibso.chicory.runtime.Instance;

var instance = Instance.builder(Parser.parse(new File("count_vowels.rs.wasm"))).build();

var alloc = instance.export("alloc");
var dealloc = instance.export("dealloc");
var countVowels = instance.export("count_vowels");

var memory = instance.memory();
var message = "Hello, World!";
var len = message.getBytes().length;
int ptr = (int) alloc.apply(len)[0];

memory.writeString(ptr, message);

var result = countVowels.apply(ptr, len)[0];
System.out.println(result);
```
2 changes: 2 additions & 0 deletions docs/docs/experimental/_category_.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
label: Experimental
position: 20
152 changes: 152 additions & 0 deletions docs/docs/experimental/aot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
---
sidebar_position: 3
sidebar_label: AOT compilation
title: AOT compilation
---
## Runtime AOT

<!--
```java
//DEPS com.dylibso.chicory:docs-lib:999-SNAPSHOT
//DEPS com.dylibso.chicory:aot-experimental:999-SNAPSHOT

import com.dylibso.chicory.wasm.Parser;
import com.dylibso.chicory.wasm.WasmModule;
import com.dylibso.chicory.runtime.Instance;
import com.dylibso.chicory.runtime.Machine;
import com.dylibso.chicory.runtime.InterpreterMachine;

docs.FileOps.copyFromWasmCorpus("count_vowels.rs.wasm", "your.wasm");
```
-->

The Ahead-of-Time compiler backend is a drop-in replacement for the interpreter, and it passes 100% of the same
spec tests that the interpreter already supports.

You can instantiate a module using the AoT by explicitly providing a `MachineFactory`.
The default `Machine` implementation is the `InterpreterMachine`.

You can opt in to the AoT mode by writing:

```java
import com.dylibso.chicory.experimental.aot.AotMachine;

var module = Parser.parse(new File("your.wasm"));
var instance = Instance.builder(module).withMachineFactory(AotMachine::new).build();
```

after you add the dependency:

```xml
<dependency>
<groupId>com.dylibso.chicory</groupId>
<artifactId>aot-experimental</artifactId>
</dependency>
```

This will translate every module you instantiate into Java bytecode on-the-fly and in-memory.
The resulting code is usually expected to evaluate (much)faster and consume less memory.

Please note that compiling and executing AoT modules at runtime requires:
- an external dependency on [ASM](https://asm.ow2.io/)
- the usage of runtime reflection

This is usually fine when running on a standard JVM, but it involve some additional configuration when using tools like `native-image`.

## Pre-compiled AOT

You can use the AOT compiler at build-time, by leveraging a Maven plug-in to overcome the usage of reflection and external dependencies of the "Runtime AOT".

This mode of execution reduces startup time and will remove the need for distributing
the original Wasm binary.

Key advantages are:

- improved startup time because the translation occurs only once, when you are packaging your application
- distribute Wasm modules as self-contained jars, making it a convenient way to distribute software that was not originally meant to run on the Java platform
- same performance properties as the in-memory compiler (in fact, the compilation backend is the same)

Example configuration of the Maven plug-in:

```xml
<build>
<plugins>
<plugin>
<groupId>com.dylibso.chicory</groupId>
<artifactId>aot-maven-plugin-experimental</artifactId>
<executions>
<execution>
<id>aot-gen</id>
<goals>
<goal>wasm-aot-gen</goal>
</goals>
<configuration>
<!-- Translate the Wasm binary `add` into bytecode -->
<wasmFile>src/main/resources/add.wasm</wasmFile>
<!-- Generate classes under the following prefix -->
<name>org.acme.wasm.Add</name>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
```

In the codebase you can use the generated module by configuring appropriately the `MachineFactory`:

<!--
```java
// mocking up the generated code
class AddModule {

public static WasmModule load() {
return Parser.parse(new File("your.wasm"));
}

public static Machine create(Instance instance) {
return new InterpreterMachine(instance);
}
}
```
-->

```java
// load the bundled module
var module = AddModule.load();

// instantiate the module with the pre-compiled code
var instance = Instance.builder(module).withMachineFactory(AddModule::create).build();
```

#### IDE shortcomings

In some IDEs the sources generated under the standard folder `target/generated-sources` are not automatically recognized.
To overcome this limitation you can use an additional Maven Plugin for a smoother IDE experience:

```xml
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>addSource</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/chicory-aot</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
```

<!--
```java
docs.FileOps.writeResult("docs/experimental", "aot.md.result", "empty");
```
-->
29 changes: 29 additions & 0 deletions docs/docs/experimental/cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
sidebar_position: 4
sidebar_label: CLI
title: CLI
---
# Install and use the CLI

The experimental Chicory CLI is available for download on Maven at the link:

```
https://repo1.maven.org/maven2/com/dylibso/chicory/cli/<version>/cli-<version>.sh
```

you can download the latest version and use it locally by typing:

```bash
export VERSION=$(curl -sS https://api.github.com/repos/dylibso/chicory/tags --header "Accept: application/json" | jq -r '.[0].name')
curl -L -o chicory https://repo1.maven.org/maven2/com/dylibso/chicory/cli-experimental/${VERSION}/cli-experimental-${VERSION}.sh
chmod a+x chicory
./chicory
```

<!--
```java
//DEPS com.dylibso.chicory:docs-lib:999-SNAPSHOT

docs.FileOps.writeResult("docs/experimental", "cli.md.result", "empty");
```
-->
123 changes: 123 additions & 0 deletions docs/docs/experimental/host-modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
---
sidebar_position: 2
sidebar_label: Annotations
title: Annotations
---
# Host Modules

Instead of writing host functions by hand, you can write a class containing annotated methods
and let the Chicory annotation processor generate the host functions for you. This is especially
useful when you have many host functions.

<!--
```java
//DEPS com.dylibso.chicory:docs-lib:999-SNAPSHOT
//DEPS com.dylibso.chicory:runtime:999-SNAPSHOT
```
-->

```java
@HostModule("demo")
public final class Demo {

public Demo() {};

@WasmExport
public long add(int a, int b) {
return a + b;
}

@WasmExport // the Wasm name is random_get
public void randomGet(Memory memory, int ptr, int len) {
byte[] data = new byte[len];
random.nextBytes(data);
memory.write(ptr, data);
}

public HostFunction[] toHostFunctions() {
return Demo_ModuleFactory.toHostFunctions(this);
}
}
```

The `@HostModule` annotation marks the class as a host module and specifies the module name for
all the host functions. The `@WasmExport` annotation marks a method as host function and optionally
specifies the name of the function. If the name is not specified, then the Java method name is
converted from camel case to snake case, as is a common convention in Wasm.

The `Demo_ModuleFactory` class in `toHostFunctions()` is generated by the annotation processor.

Host functions must be instance methods of the class. Static methods are not supported.
This is because host functions will typically interact with instance state in the host class.

To use the host module, you need to instantiate the host module and fetch the host functions:

<!--
```java
import com.dylibso.chicory.runtime.HostFunction;

// bug in JShell: https://github.com/jbangdev/jbang/issues/1854
public class Demo {
public Demo() {};

public HostFunction[] toHostFunctions() {
return new HostFunction[0];
}
}
```
-->

```java
import com.dylibso.chicory.runtime.ImportValues;

var demo = new Demo();
var imports = ImportValues.builder().addFunction(demo.toHostFunctions()).build();
```

### Type conversions

The following conversions are supported:

| Java Type | Wasm Type |
|-------------------|------------|
| `int` | `i32` |
| `long` | `i64` |
| `float` | `f32` |
| `double` | `f64` |

### Enabling the Annotation Processor

In order to use host modules, you need to import the relevant annotations, e.g. in Maven:

```xml
<dependency>
<groupId>com.dylibso.chicory</groupId>
<artifactId>host-module-annotations-experimental</artifactId>
<version>latest-release</version>
</dependency>
```

and configure the Java compiler to include the Chicory `host-module-processor-experimental` as an annotation processor.
Exactly how this is done depends on the build system you are using, for instance, with Maven:

```xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.dylibso.chicory</groupId>
<artifactId>host-module-processor-experimental</artifactId>
<version>latest-release</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
```

<!--
```java
docs.FileOps.writeResult("docs/experimental", "host-modules.md.result", "empty");
```
-->
24 changes: 24 additions & 0 deletions docs/docs/experimental/why.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
sidebar_position: 1
sidebar_label: Why
title: Why
---
# Why?

Chicory is a young project and we acknowledge that we are exploring and spearheading in many aspects the usage of Web Assembly on the JVM.

Since there is(always) some degree of experimentation going on, and we want to have feedback by the community and early users, we decide to publish also `experimental` modules; everyone is welcome to try things out and report back the experience.

Please note that if "something works" for you, its very unlikely that it will be removed completely, in most cases, expect slight public API changes or module renames to happen.

The goal of having `experimental` modules is because they are not stabilized, we are not 100% confident in the design and we want to be able to perform breaking changes without respecting SemVer.

This includes renaming artifactIDs, classes, methods, and reworking their usage according to user feedback and development progress.

<!--
```java
//DEPS com.dylibso.chicory:docs-lib:999-SNAPSHOT

docs.FileOps.writeResult("docs/experimental", "why.md.result", "empty");
```
-->
Loading
Loading