Robobun 修复 Bun 模块加载器的 UTF-8 编码错误
Bun 的 PR #33864 修复了一个模块加载器错误:当使用 `--target=bun` 且包含非 ASCII 的 banner/footer 时,UTF-8 字节被错误地解释为 Latin-1,导致文本乱码和语法错误。修复后,加载器正确解码 UTF-8,同时保持 ASCII 快速路径。此外,还修复了字节码缓存键不匹配的问题。
Bun 项目的 PR #33864 近日修复了一个影响模块加载器的编码问题。当开发者使用 bun build --target=bun 并添加自定义 banner 或 footer 时,如果这些内容包含非 ASCII 字符(如中文或表情符号),Bun 会错误地将 UTF-8 编码的字节当作 Latin-1(ISO 8859-1)处理。这种误解导致字符串字面量出现乱码(mojibake),而非 ASCII 标识符则直接抛出 SyntaxError。
问题根源在于,Bun 的内置模块加载器在遇到 // @Bun 编译指示时,会将文件直接交给 JavaScriptCore(JSC)处理。然而,加载器在构建内部字符串时使用了 String::clone_latin1 方法,该方法将每个字节视为一个 Latin-1 代码单元。对于大部分由 ASCII 字符组成的代码来说,这并无问题;但当 banner/footer 包含 UTF-8 编码的非 ASCII 字符时,多字节序列被拆解为多个 Latin-1 字符,导致数据损坏。
Robobun 的修复方案是将两处加载逻辑改为使用 String::clone_utf8。新方法利用了 simdutf 库:对于纯 ASCII 输入,它仍通过快速路径使用 Latin-1 复制(性能不变);对于非 ASCII 输入,则正确执行 UTF-8 解码。这一改动确保了无论是源代码还是附加的 banner/footer,都能以正确的编码格式传递给 JSC。
修复并未止步于此。随后的提交(68c1594)发现,当同时启用 --bytecode 标志时,字节码缓存也会受到影响。由于缓存键(SourceCodeKey)在加载器和代码生成器之间使用了不同的字符串构造方式——加载器生成 UTF-8 解码的键,而缓存生成器仍使用 Latin-1 键——导致 decodeCodeBlock 拒绝匹配,JSC 不得不重新解析代码,使得预编译的 .jsc 文件成为无效的负担。该提交通过一个辅助函数统一了两种场景下的字符串构造,该函数同样利用 simdutf 进行 ASCII 快速路径或 UTF-8 解码。
PR #33864 由 Robobun 编写,Claude 机器人进行了代码审查,并标记了“claude”和“slop”标签。虽然 CI 在多种平台上出现了 5 项测试失败(包括 WebSocket、WebView 和代理压力测试),但其中部分失败与本次编码修复无关。最终,该 PR 在 2026 年 7 月 9 日被关闭并合并。此修复对于需要国际化支持或使用自定义 banner 的 Bun 用户来说至关重要,它消除了一个可能在生产环境中引发隐蔽错误的源头。