你可以在 MoonBit 中使用外部函数,通过 FFI 进行调用。

FFI

声明外部函数

你可以像这样声明一个外部函数:

  1. func get_pi() -> Float = "math" "get_pi"

它与普通函数定义类似,只是函数体被两个字符串替换。

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

声明之后,你可以像常规函数一样使用外部函数。

使用已编译的 Wasm

要使用已编译的Wasm,你必须在Wasm导入对象中提供所有声明的外部函数。

例如,要在上述代码片段中使用已编译的 Wasm,你需要将上面代码中的 my_wasm_func 添加到 Wasm 导入对象中,如下所示:

  1. WebAssembly.instantiateStreaming(
  2. fetch("xxx.wasm"),
  3. {
  4. math: {
  5. get_pi: () => Math.PI
  6. }
  7. })

完整示例

让我们通过一个完整的示例来演示如何在 MoonBit 中使用 Convas API 绘制一个简单的笑脸。

draw.mbt

  1. type Canvas_ctx
  2. func begin_path(self: Canvas_ctx) = "canvas" "begin_path"
  3. func arc(self: Canvas_ctx, x: Int, y: Int, radius: Int, start_angle: Float, end_angle: Float, counterclockwise: Bool) = "canvas" "arc"
  4. func move_to(self: Canvas_ctx, x: Int, y: Int) = "canvas" "move_to"
  5. func stroke(self: Canvas_ctx) = "canvas" "stroke"
  6. func get_pi() -> Float = "canvas" "get_pi"
  7. let pi: Float = get_pi()
  8. pub func draw(self: Canvas_ctx) {
  9. self.begin_path();
  10. self.arc(75, 75, 50, 0.0, pi * 2.0, true); // Outer circle
  11. self.move_to(110, 75);
  12. self.arc(75, 75, 35, 0.0, pi, false); // Mouth (clockwise)
  13. self.move_to(65, 65);
  14. self.arc(60, 65, 5, 0.0, pi * 2.0, true); // Left eye
  15. self.move_to(95, 65);
  16. self.arc(90, 65, 5, 0.0, pi * 2.0, true); // Right eye
  17. self.stroke();
  18. }

使用 moonc 编译该文件,以获取 draw.mbt.wasm。

  1. moonc compile draw.mbt
  2. wat2wasm draw.mbt.wat

在 Javascript 中可以这样调用它

  1. <html lang="en">
  2. <body>
  3. <canvas id="canvas" width="150" height="150"></canvas>
  4. </body>
  5. <script>
  6. const spectest = {
  7. canvas: {
  8. stroke_rect: (ctx, x, y, width, height) => ctx.strokeRect(x, y, width, height),
  9. begin_path: (ctx) => ctx.beginPath(),
  10. arc: (ctx, x, y, radius, startAngle, endAngle, counterclockwise) => ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise),
  11. move_to: (ctx, x, y) => ctx.moveTo(x, y),
  12. stroke: (ctx) => ctx.stroke(),
  13. get_pi: () => Math.PI,
  14. },
  15. spectest: {
  16. print_i32: (x) => console.log(String(x)),
  17. print_f64: (x) => console.log(String(x)),
  18. print_char: (x) => console.log(String.fromCharCode(x)),
  19. },
  20. };
  21. const canvas = document.getElementById("canvas");
  22. if (canvas.getContext) {
  23. const ctx = canvas.getContext("2d");
  24. WebAssembly.instantiateStreaming(fetch("draw.wasm"), spectest).then(
  25. (obj) => {
  26. obj.instance.exports._start()
  27. obj.instance.exports["Canvas_ctx::draw"](ctx);
  28. }
  29. )
  30. }
  31. </script>
  32. </html>

确保draw.mbt.wasmindex.html 在同一个文件夹中,然后在此文件夹中启动一个http服务器。例如,使用 Python

python3 -m http.server 8080

在浏览器中访问 http://localhost:8080 ,应该会看到一个像这样的笑脸: 外部函数接口(Foreign Function Interface,简称FFI) - 图1