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

Component Model & WIT IDL #5

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
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
135 changes: 135 additions & 0 deletions RFCs/component-model-and-wit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# RFC: Component Model & WIT Guest/Host Support

## Overview

- I propose the compatibility of component model and wit for moonbit.
- Using component model in Moonbit's build.
- Moonbit as a wit host.

This might be a too large proposition. Depending on the requirements, it might be better to generate and divide host, guest, and wit idl.

## Why

Moonbit is a language with wonderful expressive power, but the built `.wasm` only has a numerical interface in accordance with the specification of the simple WebAssembly interface.

Especially for the interoperability between JavaScript/TypeScript and Moonbit, I thought it would be nice to have a correspondence with the component model.

## About WebAssembly Component Model & WIT

(This section is unnecessary for those who already have knowledge)

The component model is a specification for the interoperability of wasm binaries, and wit is an IDL for the component model.

```wit
package component:my-wit;
world example {
export add: func(a: s32, b: s32) -> s32;
}
```

Below is an example of implementing Rust's Guest with cargo component and running JS as Host with jco.

```bash
$ cargo component new --reactor my-wit && cd my-wit
$ cargo component build --target wasm32-unknown-unknown --release
# Omitting the implementation of add
$ npx jco transpile target/wasm32-unknown-unknown/release/my_wit.wasm -o out
$ deno eval 'import("./out/my_wit.js").then(({add})=>console.log(add(1,2)))'
3
```

Please refer to the following for details

https://component-model.bytecodealliance.org/language-support/rust.html
https://component-model.bytecodealliance.org/language-support/javascript.html

## Proposal: moonbit as wit guest

This is an implementation for moonbit to be run from other environments.

It is necessary to embed the definition of the component model in the `.wasm` binary generated by moonbit.

https://github.com/WebAssembly/component-model/blob/main/design/mvp/Binary.md#component-definitions
https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md

Wat Example

```
(component
(core module (;0;)
(type (;0;) (func))
(type (;1;) (func (param i32 i32) (result i32)))
(type (;2;) (func (param i32 i32 i32 i32) (result i32)))
```

There are two choices for implementation.

1. `pub fn` or `pub enum` `pub struct` generates component model and wit from moonc
2. (Like cargo component), first define world.wit, generate `trait` code from there, and the user implements to meet the trait

Personally, I prefer 1, but if the expressive power of moonbit and wit do not mesh well, there may be problems. The generation of a wit file is not mandatory, but the component-model written in wasm has low readability and needs to be read out by other tools.

I assume that the component model-compatible `.wasm` will be used as follows.

```js
$ moon build --target wasm-gc
$ npx jco transpile target/wasm-gc/release/build/mylib/mylib.wasm -o out
```

Here is an image of the actual output wit.

```
// lib.mbt
struct Point {
x: Int
y: Int
}
pub fn new_point(x: Int, y: Int) -> Point {
Point::{x, y}
}
```

Generated wit

```wit
package component:mylib;
world example {
record Point {
x: s32,
y: s32,
}
new_point: func(a: s32, b: s32) -> Point;
}
```

The generated type should be a subset of moonbit's expressive power. Depending on the case, it may be necessary to put some constraints.

## Proposal: moonbit as wit host

This is an implementation to load other `.wasm` implemented with the component model.

You need to parse the component-model or WIT IDL and generate glue code for moonbit.

This is the glue code `out/my_wit.js` generated by the previous cargo component + jco.

```js
// Excerpt from out/my_wit.js
function id(arg0) {
var ptr0 = utf8Encode(arg0, realloc0, memory0);
var len0 = utf8EncodedLen;
const ret = exports0.id(ptr0, len0);
var ptr1 = dataView(memory0).getInt32(ret + 0, true);
var len1 = dataView(memory0).getInt32(ret + 4, true);
var result1 = utf8Decoder.decode(new Uint8Array(memory0.buffer, ptr1, len1));
postReturn0(ret);
return result1;
}
// ...
const $init = (async() => {
const module0 = fetchCompile(new URL('./my_wit.core.wasm', import.meta.url));
({ exports: exports0 } = await instantiateCore(await module0));
memory0 = exports0.memory;
realloc0 = exports0.cabi_realloc;
postReturn0 = exports0.cabi_post_id;
})();
```