Skip to content

编译到 JavaScript / Wasm:六大技术路线系统比较与统一中层 SSA IR 架构设计 #16

@htoooth

Description

@htoooth

编译到 JavaScript / Wasm:六大技术路线系统比较与统一中层 SSA IR 架构设计

深度研究报告 · 2025年4月 · 覆盖语言:ReScript、Kotlin/JS、Scala.js、Fable、MoonBit、Haxe、Nim、GopherJS、ClojureScript、Gleam


一、问题框架:为什么需要系统性比较

"编译到 JavaScript" 这个标签掩盖了底层极其异质的技术决策空间。当一个语言决定将 JS 作为编译目标时,它实际上在五个维度上做出了相互耦合的设计选择:

  1. 语义保留策略:源语言语义中有多少必须在 JS 运行时保留,有多少可以在编译期擦除
  2. 互操作模型:与 JS 生态的边界是清晰类型化的接口,还是逃逸舱口式的动态逃逸
  3. 中间表示层次:是直接 AST→JS,还是经过一个或多个 IR 降级阶段
  4. 优化空间:dead code elimination、inlining、constant folding 等优化在哪一层发生
  5. Wasm 双轨:JS 目标与 Wasm 目标是同一个中层 IR 的两个后端,还是两条完全独立的管道
    这五个维度的选择,决定了生成代码的可读性、体积、性能以及 debug 体验。本报告将十种语言归纳为六条技术路线,逐一解剖其架构,最后提出一个面向 JS/Wasm 双目标的统一中层 SSA IR 设计方案。

二、六大技术路线

路线 A:ML系 / OCaml血统(ReScript · Fable · MoonBit)

这三个语言有一个共同的血统节点:它们的创始人都深度参与过 OCaml 编译器生态。

ReScript

ReScript 12(2025年底发布)的编译管道如下:

.res 源文件
  → 解析(ReScript 自有 Parser,纯 OCaml 实现)
  → 类型检查(Hindley-Milner,nominal 类型系统,sound)
  → 内部 AST 清理(v12 移除 OCaml 遗留节点)
  → Lambda IR(类 OCaml Lambda 层,做常量折叠、模式匹配编译)
  → JS 代码生成(直接从 Lambda 到 ES module)

关键设计选择:

  • 文件级并行编译:每个 .res 文件独立编译,产出 .cmi(接口)、.cmt(类型树)、.cmj(优化元数据)、.mjs(JS 输出)四个独立制品。没有全程序分析阶段,无需链接器。
  • 依赖图由 Rewatch 驱动:v12 的新构建系统彻底取代了原本基于 Ninja 的包装器,采用精确哈希追踪依赖变更,支持 monorepo 包边界并行。
  • 类型完全擦除:生成的 JS 不携带任何运行时类型信息。option<t> 在 JS 层面直接是 null | t,无包装。
  • 无运行时库依赖@rescript/runtime 是一个极轻量的辅助包,核心逻辑全部在编译期内联。
    ReScript 不通过中间 SSA,而是在 Lambda IR 层做有限优化后直接生成 JS。这是速度的来源,也是整程序优化的天花板。

Fable(F# → JS / Python / Rust / Dart)

Fable 的架构依托 FSharp Compiler Services(FCS):

.fs 源文件
  → FCS 前端(类型检查、FIR/TAST 生成)
  → Fable AST(跨目标的中间表示,携带 F# 语义注解)
  → 目标特定后端(JS / Python / Rust / Dart / Erlang)
  → 目标语言代码

Fable 的核心创新是 Fable AST:这是一个比 FCS 的 TAST 更低层但比目标语言更高层的表示,在这一层做 F# 特有的语义转换——currying 展开、Union Type 编译策略(tagged object vs. plain value)、Option<T> 擦除。

Fable 特别强调 可读性:生成的 JS 是符合 ES2015 规范的惯用代码,开发者可以 debug,可以用任何 bundler 处理。代价是某些 F# 语义(如 structural equality)需要运行时辅助函数,引入少量运行时开销。

MoonBit

MoonBit(2024年底开源 Wasm 后端)是这三者中唯一一个以 Wasm 为第一公民设计的语言:

.mbt 源文件
  → MoonBit 类型检查器(容错型,IDE/编译器共享代码库)
  → MoonBit IR(内部 SSA-like IR,类型完全已知)
  → 目标后端:
      wasm-gc   → WasmGC 二进制(默认,最优化)
      js        → JavaScript(主要用于工具链和小型脚本)
      native    → 原生二进制(通过 C 中间层)

MoonBit 的设计哲学是:WasmGC 的类型系统(struct、array、GC-managed heap objects)与 MoonBit 的值类型系统高度同构,因此可以做到几乎零开销的语义映射。生成的 Wasm 二进制极小——这是 Hongbo Zhang(前 OCaml 核心贡献者、ReScript 创始人)的核心设计目标。


路线 B:JVM系 / 多后端 IR(Kotlin/JS · Scala.js)

这两个语言都来自 JVM 生态,但采用了截然不同的 IR 策略。

Kotlin/JS + Kotlin/Wasm

Kotlin 编译器有 K1/K2 两个前端和四个后端(JVM、JS、Native、Wasm)。核心是共享的 Kotlin IR

Kotlin 源代码
  → K2 前端(FIR: Frontend Intermediate Representation)
  → Fir2Ir(FIR 到 Kotlin IR 的转换)
  → Kotlin IR(树状 IR,携带完整类型信息)
  → 后端特定 lowering:
      JS 后端  → TypeScript 可选输出 / ES module JS
      Wasm 后端 → WasmGC 二进制(利用 WasmGC 的 struct/array 表达 Kotlin 对象)

Kotlin IR 是真正的"一个 IR,多个后端"设计。JS 后端与 Wasm 后端共享相同的 lowering 基础设施(lazy 属性初始化、inline class 展开、协程状态机等),然后各自分叉做目标特定的代码生成。

Kotlin/Wasm 的关键突破是 2024 年 12 月 Safari 完成对 WasmGC 的支持,使 Kotlin/Wasm 应用能在所有主流浏览器运行。JetBrains 基准测试显示 Kotlin/Wasm 在 UI 密集型场景比 Kotlin/JS 快约 3 倍。

Kotlin/JS 的历史包袱是体积问题:stdlib 全量打包进去,一个 "Hello World" 的 JS 产出可能比等效 TypeScript 大 10 倍以上。这是携带整个 Kotlin 标准库 JS 实现的代价。

Scala.js + Scala.js Wasm Backend

Scala.js 的架构基于一个精心设计的专有 IR——SJSIR(Scala.js IR):

Scala 源代码
  → Scala 编译器前端(类型检查、desugaring)
  → SJSIR 生成(携带 Scala 语义的中间表示)
  → Linker(全程序分析、dead code elimination、优化)
  → 目标后端:
      JS 后端   → 优化的 ES module JavaScript
      Wasm 后端 → WasmGC 二进制(1.17.0,2024年9月实验性发布)

SJSIR 的设计极为务实:它是 Scala 对 JS 语义的精确建模,而非通用的低层 IR。SJSIR 明确包含:

  • 类层次结构(Class、TraitImpl、ModuleClass)
  • 显式 JS interop 节点(JSObjectConstr、JSMethodApply 等)
  • 精确的可空性标注(v1.17 引入 nullable: Boolean flag)
    Wasm 后端的挑战在于:SJSIR 包含大量 JS 互操作语义(@JSExport、字符串拼接中的隐式 toString 等),这些在 Wasm 沙箱中必须通过 JS 胶水层实现。Scala.js 1.19(2025年)已将 Wasm 后端的性能提升到在计算密集型场景超越 JS 后端。

路线 C:多目标平台系(Haxe)

Haxe(2005年,OCaml 实现)是 compile-to-multiple-targets 的先驱:

Haxe 源代码
  → Haxe 编译器前端(类型推断、宏展开)
  → Haxe AST(携带跨目标类型注解)
  → Dead Code Elimination(全程序 DCE)
  → 目标特定代码生成:
      JavaScript / TypeScript
      C++ / Java / C# / Python / Lua / PHP
      HashLink 字节码 / NekoVM 字节码
      SWF(Flash)

Haxe 的核心是 externs 系统:用 Haxe 类型注解描述外部平台 API,实现跨目标的类型安全 FFI。生成的 JS 代码极为干净(基准测试中常接近手写 JS 速度),因为 Haxe 在 AST 层面做了大量 inlining 和常量折叠。

Haxe 没有明确的 SSA IR:优化发生在类型化 AST 层面。这使其在单一代码库多目标部署场景(游戏引擎、跨端框架)中有独特优势,但也意味着深度数据流优化较难实现。


路线 D:原生语言→JS 转译(Nim · GopherJS)

Nim

Nim 的 JS 目标是其多后端(C / C++ / ObjC / JS)之一:

Nim 源代码
  → Nim 语义分析(类型系统、宏展开,compile-time 执行)
  → Nim IR(内部图 IR,偏向过程式)
  → C 后端 / JS 后端

Nim 的 JS 后端生成可读的惯用 JS,支持 {.importjs.} pragma 直接嵌入 JS 表达式。由于 Nim 的宏系统在 compile-time 执行,某些 meta-programming 模式可以零运行时开销地展开到 JS。Nim 不提供 Wasm 原生路线(需要通过 Emscripten 的 C 路线)。

GopherJS(Go → JavaScript)

GopherJS 的核心问题是:Go 的运行时假设与 JS 环境根本不兼容

Go 源代码
  → Go 编译器前端(类型检查)
  → GopherJS IR
  → JavaScript(携带完整 Go 运行时:goroutine 调度器、channel、GC)

GopherJS 必须在 JS 中模拟整个 Go 运行时,包括:协程调度(基于 continuation 变换)、goroutine 栈增长、channel 通信。这导致产出的 JS 体积极大(一个 Hello World 约 44KB),且性能低于等效原生 Go 约 30%。GopherJS 的维护重心已经部分转移到 GOOS=js GOARCH=wasm 官方 Wasm 目标(同样携带完整运行时,但在 Wasm 环境中更自然)。


路线 E:BEAM / 函数式系(ClojureScript · Gleam)

ClojureScript

ClojureScript 是 Clojure(JVM Lisp)的 JS 目标,通过 Google Closure Compiler 的高级优化模式:

ClojureScript 源代码
  → ClojureScript 编译器(JVM 上运行)
  → Google Closure Compiler AST(JS AST 格式)
  → Closure 高级优化(整程序 dead code elimination、property renaming)
  → 优化的 JavaScript

ClojureScript 的特殊之处:它把 Google Closure Compiler 作为优化器而非仅仅是打包工具,利用其高级模式做整程序分析和积极的 dead code elimination。这使 ClojureScript 在生产构建体积上远小于 Kotlin/JS。

不变数据结构(PersistentVector、PersistentHashMap)全部以 JS 对象实现,有运行时开销,但结合 React 的不可变 diff 模型极为自然。

Gleam

Gleam(2024年3月 v1.0 发布)的 JS 编译路线是双目标(Erlang BEAM + JavaScript)的产物:

Gleam 源代码
  → Gleam 编译器(Rust 实现,单一二进制)
  → Gleam AST(类型化,包含平台特定注解)
  → 目标后端:
      Erlang 后端 → .erl 文件(在 BEAM 上运行)
      JS 后端     → .mjs 文件(ES module)

Gleam 的 JS 输出非常轻量——它不携带任何运行时库,ADT 直接映射为 JS 对象字面量(tagged union 模式)。但 Gleam 的 JS 后端存在一个根本性约束:BEAM 的并发原语(actor、process、OTP)在 JS 环境中完全缺席gleam/otp 包只有 Erlang target。这意味着使用 Gleam/JS 时,你享有类型系统和函数式语义,但失去了 Gleam 对 BEAM 生态的最大价值主张。

2025年 Stack Overflow 调查显示 Gleam 以 70% 开发者满意度排名第二(仅次于 Rust 72%),主要驱动力是极佳的 DX(错误信息、工具链一体化、学习曲线)。


路线 F:Wasm 优先 / 纯 SSA 路线(MoonBit wasm-gc,续前)

MoonBit 已在路线 A 中覆盖。这里补充其作为"Wasm 原生优先"语言的独特设计原理:

MoonBit 的设计者 Hongbo Zhang 的核心论点是:现有语言(Go、Rust、Java)设计时未考虑 WasmGC 的语义约束,因此编译到 Wasm 时需要携带自己的 GC 或做大量适配,产出的 Wasm 二进制巨大且低效。MoonBit 则从类型系统层面就与 WasmGC 的类型层次(struct、array、managed heap)同构,使得编译到 Wasm 几乎是直接映射。


三、六大路线系统比较矩阵

维度 ReScript Kotlin/JS Scala.js Fable MoonBit Haxe Nim GopherJS ClojureScript Gleam
路线归属 ML/OCaml JVM多后端 JVM多后端 ML/OCaml Wasm原生 多目标平台 原生转译 原生转译 BEAM函数式 BEAM函数式
中层 IR Lambda IR Kotlin IR SJSIR Fable AST MoonBit IR 类型化 AST Nim IR GopherJS IR Closure AST Gleam AST
IR 形式 树+有限SSA 树+lowering 树+链接器 SSA-like 图IR JS AST格式
类型系统 Nominal/sound structural structural structural Nominal/sound structural structural dynamic dynamic Nominal/sound
JS 可读性 ★★★★★ ★★★☆☆ ★★★☆☆ ★★★★☆ ★★★☆☆ ★★★★★ ★★★★☆ ★★☆☆☆ ★★★☆☆ ★★★★☆
JS 互操作 ★★★★★ ★★★★☆ ★★★★☆ ★★★★★ ★★★☆☆ ★★★★★ ★★★★☆ ★★★★★ ★★★★☆ ★★★☆☆
Wasm 路线 一流(wasm-gc) 实验性 间接(→Rust→Wasm) 一流(wasm-gc) 无直接支持 间接(→C→Wasm)
编译速度 ★★★★★ ★★★☆☆ ★★★☆☆ ★★★★☆ ★★★★★ ★★★★☆ ★★★★☆ ★★★☆☆ ★★★☆☆ ★★★★★
产出体积 极小 极小(Wasm) 极大 极小
整程序优化 有限(文件级) 有(IR级) 有(链接期) 有限 有(DCE) 有限 有(Closure高级)

四、横切关注点:三个根本性张力

从六条路线的比较中,三个根本性张力反复出现:

张力 1:语义保留 vs. 互操作透明度

语义保留度高的语言(ReScript 的 nominal 类型、Gleam 的 ADT、MoonBit 的 struct)在 JS 互操作时必须引入类型边界。用户需要在边界处做显式转换,或者接受 FFI 逃逸到动态类型。语义保留度低的语言(GopherJS、Nim)生成更接近 JS 惯用代码,但源语言的某些保证在 JS 层面消失。

这是不可消解的设计权衡,没有银弹——但统一 IR 可以让这个权衡的位置从各语言的代码生成层提升到 IR 语义层,使其更可见、可配置。

张力 2:文件级快速编译 vs. 整程序优化

ReScript 和 Gleam 的文件级独立编译使增量构建极快,但放弃了跨文件的内联、逃逸分析和常量传播。Scala.js 的链接期优化、ClojureScript 对 Closure 高级模式的依赖,代价是整体构建时间延长。

WasmGC 的出现部分缓解了这个张力:Wasm 模块系统支持真正的增量链接,使得"快速构建 + 充分优化"成为可能,但需要 IR 设计支持跨模块类型流分析。

张力 3:JS 互操作 vs. Wasm 高性能

JS 互操作依赖 JS 对象模型(动态属性、原型链、隐式类型转换)。Wasm(尤其是 WasmGC)的类型系统与 JS 对象模型存在根本性的阻抗失配:Wasm struct 对 JS 是不透明的 externref,无法直接访问字段。Scala.js 在实现 Wasm 后端时面临这个问题的最尖锐形式——@JSExport 在 Wasm 目标上必须放弃。

解决这个张力的唯一正确方向是 Wasm Component Model:通过 WIT(WebAssembly Interface Types)定义高层类型接口,允许 Wasm 模块与 JS 以结构化类型而非原始指针交互。MoonBit 的 use-js-builtin-string 选项是这个方向的早期探索。


五、统一中层 SSA IR 架构设计

5.1 设计目标

一个面向 JS/Wasm 双目标的统一中层 SSA IR 应当满足:

  1. 表达力充分:能够无损表达 OCaml/Haskell 级别的 ADT、模式匹配、高阶函数、GC 语义
  2. 可优化:支持 constant folding、DCE、inline、逃逸分析、load/store elimination
  3. 双后端可实现:从同一 IR 出发,JS 后端和 WasmGC 后端可以独立实现,无需在 IR 层面做目标特定妥协
  4. 互操作注解友好:JS FFI 调用和 Wasm import/export 都可以在 IR 层面表达为一等公民,而非特殊情况
  5. 增量友好:支持模块化编译,模块接口可序列化(类似 ReScript 的 .cmj / Scala.js 的 .sjsir 文件)

5.2 IR 核心结构

命名为 JSWASMSSA(或简称 JWS IR):

Module
  └─ FunctionDef+
      ├─ Signature:(param-types, return-type, effect-annotation)
      ├─ BasicBlock+(SSA 形式)
      │   ├─ Phi 节点(φ 函数,在 join point 合并值)
      │   └─ Instruction+
      │       ├─ 值定义(每个值 exactly-once 赋值)
      │       └─ Terminator(branch / return / throw / tailcall)
      ├─ TypeDef 引用(本地 ADT、struct、enum)
      └─ FFI 注解(js-import / wasm-import / wasm-export)
 
TypeSystem
  ├─ Primitive:i32 / i64 / f64 / bool / unit / never
  ├─ GCRef<T>:指向 GC 管理堆对象的引用(对应 WasmGC externref/anyref)
  ├─ FuncRef<Sig>:可调用引用(对应 WasmGC funcref)
  ├─ Struct{fields: [(name, type, mutability)]}:命名积类型
  ├─ Variant{cases: [(tag: i32, payload: type?)]}:和类型(ADT)
  ├─ Array<T>:同质数组(GC 管理)
  ├─ JSAny:逃逸到 JS 动态类型的特殊类型
  └─ Nullable<T>:明确的可空性( nullable 是显式注解,非默认)

5.3 关键设计决策

决策 1:JSAny 作为隔离边界

JSAny 类型是 IR 的"脏舱":任何从 JS 世界传入的值必须先被标注为 JSAny,在 IR 中的使用必须经过显式的 cast<T>js-property-access 节点。这使得:

  • JS 后端JSAny 直接穿透为 JS 的动态值,cast 编译为运行时检查(或在类型安全证明后消除)
  • Wasm 后端JSAny 编译为 externref,需要通过 Wasm-JS 边界函数处理;cast 编译为显式的 JS 调用
    这个设计使互操作代价在 IR 层面可见,而非隐藏在代码生成器里。

决策 2:Variant 直接编码 ADT,不通过 tagged 对象

大多数 compile-to-JS 语言将 ADT 编译为 {tag: "Foo", field1: x, field2: y} 形式的 JS 对象。这在 JS 后端是自然的,但在 WasmGC 后端极其低效(需要动态属性访问)。

JWS IR 的 Variant 类型在 IR 层面就是一等的 sum type:

-- IR 表示
%result : Variant{Ok: i32, Err: GCRef<String>}
%result = switch %tag {
  0 => %ok_variant = inject-variant<Ok, %value>
  1 => %err_variant = inject-variant<Err, %error>
}
 
-- JS 后端输出
const result = tag === 0 ? {TAG: 0, _0: value} : {TAG: 1, _0: error}
 
-- WasmGC 后端输出
(struct.new $Variant_Ok (local.get $value))

两个后端从同一 IR 节点出发,选择各自最优的表示。

决策 3:Effect 注解替代异常模型

JS 的异常(throw/catch)和 Wasm 的异常处理(tag + exnref)有不同的语义和性能特征。JWS IR 引入轻量的 Effect 注解

@effect(throws: {ErrorType: GCRef<Error>}, async: bool, pure: bool)
fn parse(input: GCRef<String>) -> Variant{Ok: GCRef<AST>, Err: GCRef<ParseError>>
  • JS 后端throws effect 编译为 try/catchasync 编译为 async/await
  • Wasm 后端throws effect 编译为 Wasm Exception Handling proposal 的 throw/catch 指令
  • pure 注解:允许优化器做跨调用点的 common subexpression elimination

决策 4:双态 GCRef(managed / external)

GCRef<T, managed>   -- 由本语言运行时 GC 管理的对象(Wasm 后端 → WasmGC struct)
GCRef<T, external>  -- 来自 JS 环境的对象(Wasm 后端 → externref,JS 后端 → 直接引用)

这个区分使 Wasm 后端能精确生成 struct.new vs. externref,避免不必要的 boxing/unboxing。

5.4 优化 Pass 设计

在 JWS IR 层面,以下优化 Pass 对 JS 和 Wasm 后端都有价值:

  1. 常量折叠 + 常量传播(Sparse Conditional Constant Propagation,SCCP)

    • 消除 1 + 23,传播已知常量穿越 branch
  2. Dead Code Elimination(通过 SSA def-use 链实现)

    • 无 use 的 def 直接删除,比 AST 级 DCE 更精确
  3. 函数内联(基于 call site 频率注解和 IR 大小估计)

    • 小函数完全内联;递归函数做有限展开
  4. 逃逸分析(针对 GCRef<T, managed>)

    • 未逃逸出函数的对象可以在 JS 后端 stack-allocate(对象字面量),在 Wasm 后端保持 local 变量
  5. Variant 特化(当 Variant 在调用点的 tag 静态已知时)

    • 消除运行时 tag check,直接特化分支
  6. Phi 合并(Redundant Phi Elimination)

    • %x = φ(%a, %a)%x = %a

5.5 后端代码生成策略

JS 后端

从 JWS IR 生成 ES module JS 的关键映射:

JWS IR 节点 JS 输出
BasicBlock + Phi 合并为 let / const 声明序列,分支为 if/else 或条件表达式
Struct JS 对象字面量 {field: val} 或 class
Variant Tagged 对象 {TAG: n, _0: x}
FuncRef Arrow function 或 closure
GCRef<T, external> 直接 JS 引用,无包装
JSAny cast typeof x === 'T' ? x : throw 或 noop
Effect throws try { } catch(e) { }
Effect async async/await
Tail call 显式 while (true) trampoline 或 JS 原生 TCO(strict mode)

WasmGC 后端

JWS IR 节点 Wasm 输出
BasicBlock 标准 Wasm block/loop/if 结构
Phi 对应 block 参数(Wasm 2.0 multi-value)
Struct (struct.new $T ...)
Variant 对应 (struct.new $Variant_Case ...) + GC 子类型关系
FuncRef (func.ref $f)(ref.func ...)
GCRef<T, managed> WasmGC (ref $T)
GCRef<T, external> externref
JSAny externref + JS import
Effect throws Wasm Exception Handling 的 throw/try_table
Effect async 协程变换(Wasm Stack Switching proposal,待标准化)

5.6 模块接口序列化

每个编译单元产出两个制品:

  1. .jws:二进制序列化的 JWS IR(携带类型信息,用于 LTO 和跨模块内联)
  2. .jwsi:模块接口(类似 .cmi / .d.ts):导出符号的类型签名,不包含实现
    构建系统基于 .jwsi 做精确增量重编译(类似 ReScript 的 .cmj 依赖追踪)。.jws 文件用于全程序优化 Pass(类似 Scala.js 的链接器阶段)。

六、当前技术路线与统一 IR 的对应关系

各路线现有 IR 与 JWS IR 的可能映射:

语言 现有 IR 映射到 JWS IR 的工作量
ReScript Lambda IR 中:Lambda IR 已接近 SSA,需要显式 Phi 插入和 JSAny 边界标注
Scala.js SJSIR 低:SJSIR 本身有明确的 JS 互操作节点,可直接映射到 JWS 的 JSAny 和 GCRef
Kotlin/JS Kotlin IR 低:Kotlin IR 已是多后端 IR,JWS 可作为其 JS/Wasm 后端的公共中间层
Fable Fable AST 中:Fable AST 是树形结构,需要 ANF/SSA 变换
MoonBit MoonBit IR 极低:MoonBit IR 设计就与 WasmGC 类型系统高度对齐,几乎可直接降级到 JWS
Gleam Gleam AST 高:Gleam AST 尚未引入显式的 Wasm 路线,需要完整后端实现

七、结语:技术路线选择的决策框架

基于以上分析,建议开发者按以下框架选择技术路线:

如果你的首要关注是 JS 生态互操作透明度:选择 Fable(F#语义 + 一流 JS 互操作)或 Haxe(多目标 + 成熟 externs 系统)

如果你的首要关注是类型系统健全性:选择 ReScript(nominal + sound,极快编译)或 MoonBit(同时支持 Wasm 一流)

如果你的首要关注是 Wasm 性能:选择 MoonBit(专为 WasmGC 设计)或 Kotlin/Wasm(成熟工具链 + Compose Multiplatform)

如果你的首要关注是并发/分布式系统:选择 Gleam(BEAM actor + 类型安全,但 JS 目标功能受限)

如果你已有 JVM 代码库:选择 Scala.js(成熟 + 实验性 Wasm)或 Kotlin(JS + Wasm 双目标)

统一 SSA IR 的实际价值不在于替代这些语言各自的 IR,而在于提供一个语言无关的分析和优化框架,以及一个明确规范化的 JS/Wasm 互操作语义层——这恰恰是当前所有路线都不同程度缺乏的。随着 WasmGC、Wasm Component Model 和 JS Interop 提案的成熟,这个统一层的工程价值将在未来三到五年内显著提升。


报告基于截至 2025 年 4 月的公开技术文档、编译器源码和社区讨论。ReScript 12.0 (2025-12)、Scala.js 1.19 (2025)、Kotlin/Wasm Beta (2025-09)、MoonBit 开源 Wasm 后端 (2024-12)、Gleam v1.x (2024-03+) 的最新架构均已纳入分析。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions