DEBUG.WIKI
腾讯云;让你的想法跑起来;轻量服务器 · 建站 / Docker / API;查看产品 →
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →
搬瓦工;先看线路,再选 VPS;CN2 GIA · 对应线路套餐;查看线路 →

Bun 旧 canary 冻结时间后 Date.now 又读到真实时钟,检查 JIT 路径

旧 Bun 1.4 canary 的 Date.now 热调用可能绕过模拟时间。区分 JIT 路径与测试清理,核对正式 1.4.0 引擎处理及热调用验收。

测试调用 setSystemTime() 后,少量断言能通过,较大的测试集却出现有效 TOTP 被判无效或时间断言偶发失败。若环境是旧 Bun 1.4.0-canary.1+05895486d,还要检查 Date.now() 的热调用路径是否忽略了模拟时间,不能只归因于文件之间忘了清理 mock。

这条已定位的故障发生在 JavaScriptCore 的 JIT 优化路径。正式版 Bun 1.4.0 采用的引擎源码已包含相应处理,并带有针对热调用的回归用例;旧 canary 的现象不代表当前所有 Bun 1.4 版本都受影响。

同一个 Date.now 调用点可能在运行中换路径

Date.now() 的调用足够热时,会被引擎优化成专门的操作。受影响实现的解释执行路径会读取模拟时间,优化后的路径却直接读取真实墙钟。测试前半程与后半程于是可能观察到不同时间来源。

这一条件解释了为什么只跑一个小文件看似正常、完整测试集却失败,也解释了为什么每文件隔离可能暂时隐藏现象:新的执行环境让相关代码没有那么快进入原来的热路径。它不证明测试文件顺序本身就是根因。

普通的 mock 状态泄漏仍须单独修正。任何改变测试时间的用例,都应在原有清理钩子中恢复时钟,例如:

import { afterEach, setSystemTime } from "bun:test";

afterEach(() => {
  setSystemTime();
});

这段合并到负责改变时钟的测试文件中,不替代对引擎版本的核对。若当前用例本来就需要冻结时间,单纯在每次断言之前重置成真实时间,会改变被测条件。

修正需要落到实际 Bun 引擎

已核对的正式 Bun 1.4.0 所固定的 JavaScriptCore 实现,让 JIT 的 operationDateNow 通过当前全局对象读取统一的时间入口。Bun 侧还配合调整了恢复真实时间时的状态表示,不能只把引擎补丁与任意旧 Bun 源码拼在一起。

仍使用上述旧 canary 的项目,应通过原来的运行时管理方式切换到包含修正的正式版本,并确认 CI、容器及本地测试实际运行的是同一组预期版本。只更新测试依赖,不会替换 Bun 自带的引擎。

暂时无法切换时,恢复每个测试自己改变的时钟,并在该版本支持的情况下评估 bun test --isolate。隔离会改变文件之间的全局状态共享,可能增加执行开销;它是针对受影响环境的规避方式,不是证明 JIT 已修复。

检查结果要覆盖足够多次读取同一时钟调用点,让原先只在热路径出现的差异有机会暴露,同时验证恢复真实时间后不再保留冻结值。不要认定固定第几次调用必然触发优化,这取决于版本、构建和执行情况。

原有 TOTP 或时间窗口测试也应保留。通过扩大令牌有效期、跳过鉴权断言来让测试变绿,会掩盖本来需要验证的行为;这一引擎问题也不意味着所有验证码失败都与模拟时间有关。