Skip to content

Commit

Permalink
doc: add explanation for inline funcitons
Browse files Browse the repository at this point in the history
  • Loading branch information
peter-jerry-ye committed Jun 3, 2024
1 parent c476f0a commit c32e94b
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 47 deletions.
77 changes: 54 additions & 23 deletions ffi-and-wasm-host.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,31 @@ This will be a type that represents a reference to a foreign object, a `CanvasRe
You can declare a foreign function like this:

```rust
fn get_pi() -> Double = "math" "PI"
fn cos(d : Double) -> Double = "Math" "cos"
```

It's similar to a normal function definition except that the function body is replaced with two strings.

These two strings are used to identify the specific function from a Wasm import object, the first string is the module name, and the second string is the function name.
For Wasm(GC) backend, these two strings are used to identify the specific function from a Wasm import object, the first string is the module name, and the second string is the function name. For JS backend, these two strings are used to call a static function in the global namespace. The example above becomes similar to `const cos = (d) => Math.cos(d)`.

You can also declare inline functions where the function body is replaced with one string.

For WasmGC backend, you may declare it as a Wasm function without name (which will be generated afterwards):
```rust
extern "wasm" fn abs(d : Double) -> Double =
#|(func (param f64) (result f64))
```

and for JS backend, you may declare it as a lambda expression:
```javascript
extern "js" fn abs(d : Double) -> Double =
#|(d) => Math.abs(d)
```

After declaration, you can use foreign functions like regular functions.

For multi-backend project, you may implement backend specific code in the files that ends with `.wasm.mbt` `.wasm-gc.mbt` and `.js.mbt`.

You may also declare a foreign function that will be invoked upon a foreign object by using the foreign reference type like this:

```rust
Expand All @@ -38,6 +54,42 @@ fn begin_path(self: Canvas_ctx) = "canvas" "begin_path"

and apply it to a previously owned reference normally such as `context.begin_path()`.

### Exported functions

Functions that are not methods nor polymorphic functions can be exported if they are public and if the link configuration appears in the `moon.pkg.json` of the package:

```json
{
"link": {
"wasm": {
"exports": [
"add",
"fib:test"
]
},
"wasm-gc": {
"exports": [
"add",
"fib:test"
]
},
"js": {
"exports": [
"add",
"fib:test"
],
"format": "esm"
}
}
}
```

Each backend has a separate definition. For JS backend, a `format` option is used to specify whether the generated JavaScript file should be released as an ES Module (`esm`), a CommonJS module (`cjs`), or an immediately invoked function expression (`iife`).

The example above will export function `add` and `fib`, and the function `fib` will be exported with the name of `test`.

For Wasm(GC) backend, the `_start` function should always be called to initialize all the global instances defined in MoonBit program.

### Use compiled Wasm

To use the compiled Wasm, you need to initialize the Wasm module with the host functions so as to meet the needs of the foreign functions, and then use the exported functions provided by the Wasm module.
Expand All @@ -58,27 +110,6 @@ WebAssembly.instantiateStreaming(fetch("xxx.wasm"), {

Check out the documentation such as [MDN](https://developer.mozilla.org/en-US/docs/WebAssembly) or the manual of runtime that you're using to embed the Wasm.

#### Use exported functions

Functions that are not methods nor polymorphic functions can be exported if they are public and if the link configuration appears in the `moon.pkg.json` of the package:

```json
{
"link": {
"wasm": {
"exports": [
"add",
"fib:test"
]
}
}
}
```

The example above will export function `add` and `fib` if compiled with the default wasm backend, and the function `fib` will be exported with the name of `test`.

The `_start` function should always be called to initialize all the global instances defined in MoonBit program.

## Example: Smiling face

Let's walk through a full example to draw a smiling face using Canvas API in MoonBit. Suppose you created a new project with `moon new draw`
Expand Down
79 changes: 55 additions & 24 deletions zh-docs/ffi-and-wasm-host.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,44 +21,40 @@ type Canvas_ctx
你可以像这样定义一个外部函数:

```rust
fn get_pi() -> Double = "math" "PI"
fn cos(d : Double) -> Double = "Math" "cos"
```

它和正常的函数定义十分相像,除了函数体被替换为两个字符串。

这两个字符串是用来在Wasm导入的对象中识别特定的函数:第一个字符串是模块名称,第二个字符串是函数名称。

在声明之后,你可以像普通的函数那样使用外部函数。
对于Wasm(GC)后端,这两个字符串是用来在Wasm导入的对象中识别特定的函数:第一个字符串是模块名称,第二个字符串是函数名称。对于JS后端,这两个字符串被用于访问全局命名空间中的一个静态函数。上述例子会编译为类似`const cos = (d) => Math.cos(d)`

你也可以定义一个使用外部引用类型的外部函数,就像这样:
你也可以定义内联函数,函数体是一个字符串。

对于WasmGC后端,你可以以一个不含名称的Wasm函数定义它(名称将会在之后自动生成):
```rust
fn begin_path(self: Canvas_ctx) = "canvas" "begin_path"
extern "wasm" fn abs(d : Double) -> Double =
#|(func (param f64) (result f64))
```

之后可以将它应用到拥有的外部对象的引用上,如:`context.begin_path()`。

### 使用编译的Wasm
而对于JS后端,你可以定义一个箭头函数表达式:
```javascript
extern "js" fn abs(d : Double) -> Double =
#|(d) => Math.abs(d)
```

使用编译后的Wasm,你需要首先在宿主环境中初始化Wasm模块。这一步需要满足Wasm模块对外部函数的依赖。之后可以使用Wasm模块提供的函数
在声明之后,你可以像普通的函数那样使用外部函数

#### 提供宿主函数
对于多后端项目,你可以在以`.wasm.mbt` `.wasm-gc.mbt` `.js.mbt`结尾的文件中定义后端相关代码。

使用编译后的Wasm,你需要在Wasm导入对象中提供**所有**声明过的外部函数。

例如,在JavaScript中使用包含上述代码片段编译的Wasm
你也可以定义一个使用外部引用类型的外部函数,就像这样:

```js
WebAssembly.instantiateStreaming(fetch("xxx.wasm"), {
math: {
get_pi: () => Math.PI,
},
});
```rust
fn begin_path(self: Canvas_ctx) = "canvas" "begin_path"
```

具体信息可以查阅嵌入Wasm的宿主环境的文档,例如[MDN](https://developer.mozilla.org/en-US/docs/WebAssembly)
之后可以将它应用到拥有的外部对象的引用上,如:`context.begin_path()`

#### 使用导出的函数
### 导出函数

公开函数(非方法、非多态)可以被导出,需要在对应包的`moon.pkg.json`中添加链接设置:

Expand All @@ -70,14 +66,49 @@ WebAssembly.instantiateStreaming(fetch("xxx.wasm"), {
"add",
"fib:test"
]
},
"wasm-gc": {
"exports": [
"add",
"fib:test"
]
},
"js": {
"exports": [
"add",
"fib:test"
],
"format": "esm"
}
}
}
```

上面的例子中,`add``fib`函数将会在编译默认的wasm后端时被导出,并且`fib`函数将被以`test`为名导出。
每一个后端都有一个单独的定义。对JS后端,还有一个额外的`format`选项,可以用来指定生成的JS文件,是ES Module(`esm`),还是CommonJS module(`cjs`),还是立即调用函数表达式(`iife`)。

上面的例子中,`add``fib`函数将会在编译时被导出,并且`fib`函数将被以`test`为名导出。

对于Wasm(GC)后端,`_start`函数总是应当被使用,以初始化月兔程序中定义的全局实例。

### 使用编译的Wasm

使用编译后的Wasm,你需要首先在宿主环境中初始化Wasm模块。这一步需要满足Wasm模块对外部函数的依赖。之后可以使用Wasm模块提供的函数。

`_start`函数总是应当被使用,以初始化月兔程序中定义的全局实例。
#### 提供宿主函数

使用编译后的Wasm,你需要在Wasm导入对象中提供**所有**声明过的外部函数。

例如,在JavaScript中使用包含上述代码片段编译的Wasm:

```js
WebAssembly.instantiateStreaming(fetch("xxx.wasm"), {
Math: {
cos: (d) => Math.cos(d),
},
});
```

具体信息可以查阅嵌入Wasm的宿主环境的文档,例如[MDN](https://developer.mozilla.org/en-US/docs/WebAssembly)

## 例子:笑脸

Expand Down

0 comments on commit c32e94b

Please sign in to comment.