# WASM 扩展

## 功能

WASM 扩展使用 WASM/WASI 模块作为后端入口，适合用 Rust 编写高性能逻辑、复杂状态管理或需要更清晰 ABI 边界的扩展。它的 `manifest.kind` 为 `wasm`，`manifest.abi` 为 `bts-wasi-1`。

BT 提供 `bt-extension-sdk` Rust SDK，帮助扩展作者处理 BtValueBinary 编解码、WASM 线性内存分配释放、调用 ID 分发和扩展对象句柄。

## 语法

WASM 扩展通常使用 `module.wasm` 作为入口：

```json
{
    "kind": "wasm",
    "abi": "bts-wasi-1",
    "entry": "module.wasm"
}
```

WASM 模块必须导出：

| 导出 | 说明 |
| ------ | ------ |
| `memory` | WASM 线性内存。 |
| `bts_alloc(len) -> ptr` | 给宿主写入参数申请内存。 |
| `bts_call(id, args_ptr, args_len) -> packed_ptr_len` | 根据调用 ID 执行业务逻辑并返回结果缓冲区。 |
| `bts_free(ptr, len)` | 释放宿主传回的参数或返回值内存。 |

模块可以额外导出 `bts_set_module_id(module_id)`。宿主实例化模块后会调用它，SDK 会用该模块 ID 创建扩展对象句柄。

## 参数

WASM 扩展收到的参数来自 BtValueBinary 编码后的数组。入口函数参数按 bindings 顺序传入；对象方法参数会在最前面额外收到接收者 `ExtObject` 句柄。

## 返回值

WASM handler 通过 SDK 返回 `BtValue`。返回原始类型时必须和 bindings 的 `returns` 匹配；返回对象类型时必须返回当前模块创建的同类型 `ExtObject`。

## Rust SDK 流程

创建 WASM 扩展：

```text
bt ext new calc_sdk --kind wasm
cd calc_sdk
rustup target add wasm32-wasip1
cargo build --target wasm32-wasip1 --release
copy target\wasm32-wasip1\release\calc_sdk.wasm module.wasm
bt ext build . -o calc_sdk.bts
```

脚手架中的核心结构类似：

```rust
bt_extension! {
    1 => entry_create,
    2 => entry_add,
    3 => entry_value,
    4 => entry_close,
}
```

这里的数字必须和 `bindings.json` 里的函数或方法 `id` 一致。

## 对象句柄

WASM 扩展不能把 Rust 对象直接交给 BT 脚本。正确做法是：

1. 扩展内部用 `ObjectStore` 保存真实状态。
2. 返回 `ExtObject` 句柄给 BT。
3. 脚本调用对象方法时，宿主把这个句柄作为第一个参数传给 WASM handler。
4. handler 根据对象 ID 找到扩展内部状态，再执行方法。

脚本侧仍然是普通链式调用：

```bt
value = calc(3).add(7).value()

// 输出：10
print value
```

## BtValueBinary

`bts-wasi-1` 使用 BtValueBinary 在宿主和 WASM 之间传输值。它会保留 `empty` 与 `null` 的区别，并支持普通值、Bytes、数组、对象和扩展对象句柄。

不支持通过 ABI 传输函数、类实例、正则、标准库对象、任务、定时器、迭代器或循环引用数组/对象。

## 注意事项

- WASM 模块包含 start section 会被拒绝。
- WASM Runner 会延迟实例化模块，并在线程本地缓存实例。
- WASM 返回对象类型时，必须返回同模块、同类型的扩展对象句柄。
- 长期持有状态的 WASM 对象应提供 `close()` 或 `dispose()`，并在 handler 中释放扩展内部状态。
- `bt ext new --kind wasm` 生成的是 Rust SDK 项目骨架；仍需要先编译 WASM 并复制为 `module.wasm` 后再打包。
