#1634
Add an embedded JavaScript engine
Summary
- Add fx's own copy of quickjs-ng v0.18.0 in
src/js/quickjs/, without its file, process, and timer modules. This is the first third-party C code fx builds. The maintainers agreed to add it for code mode, which needs a JavaScript runtime that every fx binary carries and that gives a program no file, network, or process access.FORK.mdrecords the release, its checksum, a script that compares the copy with upstream, and the one change fx makes to it. - Make the copy's array loops, such as
join,reverse,sort, andslice, check the deadline on each pass. They walk an object up to itslength, which a program can set as high as 2^53 - 1, and before this changeArray.prototype.join.call({ length: 2 ** 27 }, '')ran for more than a second against a 200 ms limit. - Add
src/js/engine.zig, which runs one program in a fresh runtime with limits on its heap (64 MiB), its time (30 s), and its native stack (4 MiB in Debug builds, 1 MiB otherwise). A program that hits the heap limit stops with an error it can't catch. The heap also has a cap of its own, because QuickJS's limit doesn't count the 4 KiB arenas it carves small blocks from. - Report a timeout, not a thrown error, when the deadline passes while a program's own
toJSONortoStringruns, and keep thrown text valid UTF-8 when a program throws a lone surrogate. - Run the engine's tests with
zig build test-js, whichzig build testalso runs. Nothing in the fx binary uses the engine yet. - Run the macOS check for any change to the engine's C.
- Describe
src/js/inAGENTS.md, and add quickjs-ng's license toTHIRD_PARTY_NOTICES.md.
Architecture
Before, fx had no JavaScript engine:
flowchart LR
test["zig build test"] --> fxtests["fx unit tests"]
fx["fx binary"]After, the engine is its own module, built and tested by zig build test-js. The fx binary doesn't link it yet:
flowchart LR
test["zig build test"] --> fxtests["fx unit tests"]
test --> testjs["zig build test-js"]
testjs --> engine["src/js/engine.zig: run"]
engine --> bindings["src/js/quickjs.zig"]
engine --> heap["src/js/capped_heap.zig"]
bindings --> fork["src/js/quickjs/: quickjs-ng v0.18.0 fork"]
heap --> fork
fx["fx binary"] -.-|not linked yet| engine