MoonBridge: generate installable Python packages from MoonBit modules, with typed stubs, GC-safe handles and a stable host ABI.
moon.mod ──moon info──▶ 接口 IR ──typemap──▶ 代码生成 ──▶ wheel
│
MoonBit 运行时(句柄/ABI)
│
wasm 模块 / native 库# 1. 安装 wheel(仓库已附带;也可用 bash scripts/wheel.sh 自行重建)
pip install ./dist/moonbridge-0.1.0-py3-none-any.whl
# 2. import -> install -> 调用 MoonBit 标准库 json 的函数
python - <<'PY'
import moonbridge
moonbridge.install()
from moonbridge import moonbitlang__core__json as mjson
print(mjson.valid('{"a": [1, 2, 3]}')) # True
PY
# 3. 完整闭环:11 条安装后断言(在仓库外运行可排除误导入本地 _gen/ 的可能)
python scripts/wheel_smoke.py
动画里的输出取自干净 venv 的真实执行结果,只有 venv 的绝对路径缩写为 <venv>/...。 完整可复现链路是 bash scripts/e2e.sh,打包细节见 S6 打包。
状态:S0 通过、S1 通过、S2 通过、S3 通过、S4 通过、S5 通过、S6 通过(2026-11-06)。 S0:调用 ABI、内存共享、宿主↔MoonBit 字节流、UTF-8 往返全部跑通,实测见 spikes/01-wasm-python。 S1:.mbti 解析器 + 类型图,在 501 个真实接口文件 / 17,793 条声明上 100% 通过, 见 S1 语料报告。 S2:类型映射 + Python 代码生成,501 个包生成 stub,mypy --strict 0 报错, 见 S2 映射与生成。 S3:跨包句柄与生命周期,真实 json 包端到端跑通,10 万次调用 0 泄漏、guest 内存逐字节不变, 见 S3 运行时。 S4:guest 侧也由生成器产出,对象能被构造、读、写;10 万次 × 6 个用例共 150 万帧, 内存曲线平直、live=0、dropped=0,见 S4 对象模型。 S5:Array / Map / Option / Result / 回调 / 异常字段全部可跨边界——Python 的 list、 dict、lambda 直接传,MoonBit 的 suberror 变成 Python 的 except 类, 见 S5 容器与回调。 S6:打成真正的 wheel——501 KB,508 个条目,requires = [] 的自写 PEP 517 后端, 可复现构建;pip install 之后 import moonbridge; moonbridge.install() 即可用, 见 S6 打包。
开发复盘与「AI 可解释」证据索引:docs/development-retrospective.md — 关键架构决策(wasm 优先、Plan 模型)、AI 工具在开发流程中的角色,以及 「每一个技术选择该翻哪个文件、跑哪条命令才能解释」的对照表。
| 目标语言 | 现有方案 | 形态 |
|---|---|---|
| Node.js | unmbt/napi-mbt | 生成 Node-API 绑定(仅 Node) |
| Java / Android | Nanaloveyuki/ajni | JNI 双向运行时(仅 Java) |
| C | icyaaaww/moonbind | 从 C 头文件生成 MoonBit 绑定(反方向) |
| TypeScript | mizchi/ts | TS ↔ MoonBit 桥接 |
| Python | 无 | — |
| Go / Rust / Swift / PHP | 无 | — |
| 验证项 | 结果 |
|---|---|
| #export_name 导出 | mb_add / mb_fib / 缓冲与字符串函数全部可见 |
| 线性内存导出 | 需 options(link: {"wasm": {"export-memory-name": "memory"}}) |
| 参数表示 | String / Bytes 参数是单个 i32 对象指针 → 必须走句柄协议 |
| 宿主写入 → MoonBit 读 | 写 16 字节,MoonBit 校验和 1626 一致 |
| MoonBit 写 → 宿主读 | 读回 b'AAAAAAAA' |
| UTF-8 往返 | 你好, MoonBit 🚀 → 13 个码位 / 20 字节,往返字节一致 |
| 每次调用开销 | 本机 wasmtime-py 基线:nop 6.9 μs,单参 16.6 μs;MoonBit add 22.7 μs |
.mbti ──lexer──▶ token ──parser──▶ AST ──graph──▶ 类型图
│
依赖顺序 / 互递归组 / 外部类型# 解析若干接口文件,逐文件汇报,任一失败则退出码非 0
moon run --target js src/cli -- path/to/pkg.generated.mbti ...
# 跑真实语料(标准库 + 20 个已发布模块及其依赖)
bash scripts/corpus.sh allAST ──typemap──▶ PyType ──render──▶ .pyi
│ │
降级阶梯(4 级,每级都记账) mypy --strict 校验# 全语料生成 + mypy strict(需要 python + mypy)
bash scripts/gen.sh all
bash scripts/gen.sh core # 仅标准库,离线可跑stubs=501 failed=0 types=1937 funcs=4789 fields=3733 skipped=7
degraded: foreign=4812 unknown=890 result=130 higher-order=699 arity=0 async=81.mbti ──▶ stub(.pyi) 类型检查器读的那一份
└──▶ module(.py) 解释器执行的那一份 ┐
├─ 同一次遍历,签名不可能与函数体不符
世界索引 ──▶ from . import <stem> ┘
│
Python 对象 ──handle id──▶ 句柄表 ──▶ MoonBit 堆 ──▶ wasm guest
◀──批量回收 / close() / with──┘# 生成 + 端到端 + 10 万次调用基准,任一泄漏即退出码非 0
bash scripts/e2e.sh| S2 | S3 | |
|---|---|---|
| 跨包类型降级为 Any | 4812 处 | 605 处(剩下的真的不在语料里) |
| 生成的模块间导入边 | 0 | 566(276 个模块) |
| 10 万次调用的 guest 内存 | — | 64 KiB 不变,句柄表归零 |
┌──────────── Plan ─────────────┐
.mbti ──▶ World ──▶ │ Sym[] 符号/形状/标签 │ ──┬──▶ .py / .pyi 调用方
(S1) (S3) │ HandleSlot[] 每类型一张句柄表 │ └──▶ guest.mbt 应答方
└───────────────────────────────┘| S3 | S4 | |
|---|---|---|
| guest 侧 dispatch | 手写 270 行 | 生成 1161 行(--guest / --guest-out) |
| 句柄表 | 手写 12 张 | 按用到的具体类型生成 |
| 生成类的可调用成员 | 只有方法 | 方法 + __init__ + @property + setter |
| 端到端断言 | 调通 + 释放 | 构造、读、写、句柄实参、dropped=0 |
| 10 万次基准 | 4 个用例 | 6 个用例,150 万帧,内存平直 |
Json.array([Json.number(1.0), Json.string("two")]) # list -> Array[Json]
Json.object_({"a": Json.number(1.0)}) # dict -> Map[String, Json]
mcb.call_with(lambda x: x + 2, 40) # callable -> Int32 id
try: mjson.parse("{oops")
except mjson.ParseError as exc: ... # suberror -> except 类| S4 | S5 | |
|---|---|---|
| wire 形状 | 8 个标量 | 递归树(WArr / WMap / WRes / WCall) |
| guest 符号 | 49 | 54 |
| unmarshalable-param | 15 | 14 |
| 端到端断言 | 29 | 42 |
pip install ./dist/moonbridge-0.1.0-py3-none-any.whl
python -c "import moonbridge; moonbridge.install()"| S5 | S6 | |
|---|---|---|
| 分发形态 | _gen/ 一个目录 | moonbridge-0.1.0-py3-none-any.whl(501 KB / 508 条目) |
| 构建依赖 | — | 0(标准库) |
| 构建可复现 | — | 两次构建 sha256 相同,且与 pip 驱动后端的结果相同 |
| 安装后的初始化 | 传 wasm 路径 | moonbridge.install() |
| 安装后功能断言 | — | 11 项全绿,live=0 |
# MoonBit 侧
moon check --deny-warn
moon test --deny-warn
moon fmt --check
# 语料(core 子集离线可跑;all 需要网络)
bash scripts/corpus.sh core
bash scripts/corpus.sh all
# 生成 Python stub 并跑 mypy strict
bash scripts/gen.sh all
# 生成 + 端到端 + 基准(需要 python + wasmtime)
bash scripts/e2e.sh
BENCH_CALLS=20000 bash scripts/e2e.sh # 快速跑一遍
# 只打包:构建 wheel、装进临时 venv、从仓库外冒烟测试
bash scripts/wheel.sh
bash scripts/wheel.sh --keep-venv # 留着 venv 检查
# Spike 复现(需要 Python 3.10+ 与 wasmtime)
cd spikes/01-wasm-python/module && moon build --target wasm && cd ..
python bridge_test.pyCLI 目前在 js 后端运行(唯一不依赖 C 工具链的后端),文件读取只有一处 extern "js",其余代码与目标无关。
Install
Download zipMoonBridge: generate installable Python packages from MoonBit modules, with typed stubs, GC-safe handles and a stable host ABI.