返回资讯中心
外部精选
软件工程
#开发者实时流

earendil 发布 djs:用 Rust 写的 JavaScript/TypeScript 引擎,替代 QuickJS 运行 pi codemode 脚本

来源标题:Djs: A JavaScript and ts engine for pi's codemode by earendil

earendil 发布 djs,一个用 Rust 编写的小型 JavaScript/TypeScript 引擎,专门在 WebAssembly 沙箱中运行语言模型在 pi codemode 里生成的脚本,取代 QuickJS。其设计围绕 codemode 脚本短小、调用慢速工具、数据量大且需随时暂停恢复的特点:状态存放在单一可复制的字节数组中,JavaScript 调用不占用 Rust 栈,宿主可直接从内存读取值,源码一次编译为字节码。

Hacker News 最新RebelPotato2026/11/3 · 今天13 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

djs 是一个用 Rust 编写的小型 JavaScript 和 TypeScript 引擎。它只为一件事而存在:在 WebAssembly 沙箱中运行语言模型在 pi 的 codemode 里编写的脚本,替代 QuickJS。本指南解释它如何工作,更重要的是,它为何被设计成这样。djs 中几乎所有不寻常的决策都源于这一个任务,所以本页从这里开始。

简而言之。codemode 脚本很短,调用慢速工具,移动大量数据,并且必须能在任意时刻停止和恢复。因此,djs 将所有状态保存在一个可复制的字节数组中,从不用 Rust 栈执行 JavaScript 调用,让宿主直接从内存中读取值,并且一趟将源码编译为字节码。

模型不是逐个调用工具,而是编写一个小程序来调用它们、合并结果并返回想要展示的内容:

// 宿主提供的工具的替身;在 pi 中它是 tools.search。 const search = async (q) => [ { name: "djs", stars: 120, lang: "Rust" }, { name: "quickjs", stars: 9000, lang: "C" }, ]; const hits = await search("small js engines"); const rust = hits.filter((h) => h.lang === "Rust"); console.log(${rust.length} of ${hits.length} in Rust:, rust.map((h) => h.name).join(", ")); return rust; 输出字节码堆栈与快照 1 of 2 in Rust: djs function 0 <anonymous>: params=2 locals=4 stack=6 captures=0 handler [3, 94) -> 94 depth 0 0 async_start loc0 (<state>) 3 fclosure child0 8 set_loc1 (search) ; 6:20 9 undefined 10 push_const8 #0 ("small js engines") 12 call1 ; 6:26 13 await loc0 (<state>) ; 6:14 16 set_loc2 (hits) ; 7:14 17 get_method "filter" ic0 ; 7:18 24 fclosure child1 29 call1 ; 7:25 30 put_loc3 (rust) 31 get_arg1 ; 8:1 32 get_method "log" ic1 ; 8:8 39 get_loc3 (rust) ; 8:16 40 get_field "length" ic2 ; 8:20 47 to_string ; 8:14 48 push_const8 #1 (" of ") 50 get_loc2 (hits) ; 8:34 51 get_field "length" ic3 ; 8:38 58 to_string ; 8:32 59 push_const8 #2 (" in Rust:") 61 concat 4 64 get_loc3 (rust) ; 8:58 65 get_method "map" ic4 ; 8:62 72 fclosure child2 77 call1 ; 8:66 78 get_method "join" ic5 ; 8:81 85 push_const8 #3 (", ") 87 call1 ; 8:86 88 call2 ; 8:12 89 drop 90 get_loc3 (rust) ; 9:8 91 async_return loc0 (<state>) 94 async_reject loc0 (<state>) function 0.0 search: params=1 locals=1 stack=3 captures=0 handler [3, 68) -> 68 depth 0 0 async_start loc0 (<state>) 3 object 4 push_const8 #0 ("djs") 6 define_field "name" ic0 13 push_i8 120 15 define_field "stars" ic1 22 push_const8 #1 ("Rust") 24 define_field "lang" ic2 31 object 32 push_const8 #2 ("quickjs") 34 define_field "name" ic3 41 push_i32 9000 46 define_field "stars" ic4 53 push_const8 #3 ("C") 55 define_field "lang" ic5 62 array_from 2 65 async_return loc0 (<state>) 68 async_reject loc0 (<state>) function 0.1 <anonymous>: params=1 locals=0 stack=2 captures=0 0 get_arg0 ; 7:33 1 get_field "lang" ic0 ; 7:34 8 push_const8 #0 ("Rust") 10 strict_eq ; 7:40 11 return function 0.2 <anonymous>: params=1 locals=0 stack=1 captures=0 0 get_arg0 ; 8:74 1 get_field "name" ic0 ; 8:75 8 return 运行示例以遍历 VM 的 arena。 运行示例以查看它挂起并生成快照。

这类脚本有一个特定的特征,而它的每一部分都成为了设计目标:

这些目标按优先级排序;下一章《djs 为哪些目标优化》会逐一解释。还有两个约束塑造了一切:wasm32 才是真正重要的目标(内存和速度以 V8 的 wasm 层级来评判,D016),并且行为要与 QuickJS 一致,连错误消息都一致,因为模型会阅读这些消息,而 pi 的测试也期望如此。

djs 的各个部分及其共享的 arena 宿主(JavaScript) pi 的 codemode,一个 Sandbox 脚本源码 JS 或 TypeScript 工具函数 稍后应答 线路编码器 值输入 堆读取器 值输出,就地读取 djs-core:Rust,no_std,运行在 wasm 中 编译器 token 到字节码 解释器 燃料计数,无递归 复制式 GC 压缩 arena 待处理的宿主调用 和收件箱 函数模板 字节码、常量 值栈、帧 每次调用 realm、内建对象 基础快照 任务队列 promise 反应 对象、字符串 形状、绳索、数组 原子 属性键 句柄 宿主持有的值 arena:一个字节数组,包含所有 VM 状态,通过 u32 偏移链接 快照就是这些字节的副本 调用 结果 读取 djs 的各个部分及其共享的 arena djs 始终处于严格模式,没有 ES 模块,没有 Intl,也没有 JIT,还有其它一些有意省略的东西;非目标章节列出了它们以及原因。

导览从底层开始:arena 与值、单元与对象、编译器、解释器、挂起、收集器、快照、内建对象、确定性以及宿主接口。每一章都解释其权衡:该设计付出什么代价,以及哪些替代方案未被采纳。各章在关键之处解释每个决策;决策的 ID(D1050)链接到决策日志中的条目,该日志是构建 djs 期间保存的记录,包含完整推理及其背后的测量数据。

标记为 live 的代码示例会在文档站点的浏览器中于 djs-wasm 里运行。每个示例下方显示的输出是在检查本指南时用此构建的 djs 运行该示例所产生的,因此它不会偏离 djs 的实际行为。

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →