Malte Ubl f8d18bdc9b Inline the already-buffered check into emitted object releases
Every release of a traced class or record object that left it alive
called scr_cyc_on_release out of line, only for the runtime to store
PURPLE and return because the object was already buffered. On the tsc-ts
self-check that was 730M calls for 11M enqueues, the hottest function in
the profile. Emit the same purple store and buffered test the inline RC
helpers already mirror, and call the runtime only to enqueue.

tsc-ts self-check (median of 7): 4.14s -> 3.95s.

Landing note (this series rebased onto main after #741, #768 and #781).
The work item also tenured collector survivors (a survivor moved to a
tenured list, was never buffered again on release, and only heap-doubling
major passes rooted trial deletion at it) and backed off major passes that
freed almost nothing. main's three-generation collector with
yield-scheduled growth and backlog triggers now addresses the same cost,
re-walking a large long-lived graph for little garbage. Ported onto main's
generations (objects promoted into OLD tenured instead of re-buffered, the
full-pass schedule left to main's yield rule), tenuring measured no better
on the tsc-ts self-check: 2.706 s with this series as landed vs 2.737 s
with tenuring added (median of 7, same binary otherwise), and it gives up
main's old-backlog trigger (test_cycle's dead-mature-ring check fails).
Both the tenuring and the backoff commits are therefore dropped. The
series keeps main's scheduling unchanged and contributes the emitted
release fast paths, collector-only colors and borrowed identity operands.
The tenuring item's corpus programs stay as coverage for late-dying old
cycles and weak keys held by old objects (4413, 4414).

tsc-ts self-check on main (median of 7 or 5): previous branch 3.63 s ->
2.70 s with this series. The per-commit timings in this series were
measured on the work item's base (13633c08).
2026-10-09 11:59:26 -05:00
2026-07-22 18:08:04 -05:00
2026-10-07 19:07:02 -05:00
2026-10-08 21:18:31 -05:00
2026-07-22 18:08:04 -05:00
2026-07-22 18:08:04 -05:00

scriptc

Vercel Labs Experiment npm version: scriptc License: Apache-2.0 npm downloads per month: scriptc

scriptc compiles TypeScript and JavaScript to native executables and WebAssembly. It uses TypeScript's type information to compile supported code to native instructions. Static builds run without Node.js or a JavaScript engine.

For npm dependencies and any-typed code, enable --dynamic to include the embedded quickjs-ng engine. Dependency JavaScript is included at build time.

scriptc is experimental and supports a subset of JavaScript, TypeScript, and Node.js APIs. See the limitations and Node.js compatibility reference before using it for an existing project.

Installation

Install with Node.js 24 or later and npm:

$ npm install -g scriptc

The installed compiler runs natively on supported macOS, Linux, and Windows hosts. Native compilation and execution do not require Node.js. Standalone compiler archives are also available from GitHub Releases.

Executable builds require a platform linker and SDK/sysroot. See the quickstart for prerequisites and platform support for host requirements, cross-compilation, and WebAssembly.

Build a program

Create hello.ts:

const who = process.argv.length > 2 ? process.argv[2] : "world";
console.log(`hello, ${who}`);

Compile and run it in one step:

$ scriptc run hello.ts
hello, world

Or write a standalone executable:

$ scriptc build hello.ts -o hello
$ ./hello scriptc
hello, scriptc

These examples use POSIX shell syntax. On Windows, build hello.exe and run it with .\hello.exe.

Use scriptc coverage hello.ts to check which operations compile statically, require dynamic execution, or are unsupported. See coverage reports for details.

Use Node APIs

Supported Node APIs compile to the native runtime. For example, server.ts:

import { createServer } from "node:http";

const server = createServer((req, res) => {
  res.setHeader("content-type", "application/json");
  res.end(JSON.stringify({ path: req.url }));
});

server.listen(8080, () => {
  console.log("listening on http://localhost:8080");
});
$ scriptc build server.ts -o server
$ ./server
listening on http://localhost:8080

Use npm packages

Pass --dynamic to embed an npm package's JavaScript in the executable. The result does not read node_modules at runtime.

Create cli.ts:

import pc from "picocolors";

console.log(pc.green("hello from scriptc"));
$ npm install picocolors
$ scriptc build cli.ts --dynamic -o cli
$ ./cli
hello from scriptc

Enabling --dynamic does not add support for unsupported statically typed calls. See npm dependencies for package resolution and the boundary between static and dynamic code.

Documentation

Contributing

See CONTRIBUTING.md for source setup, testing, native artifact builds, and docs development.

S
Description
GitHub Trending: vercel-labs/scriptc
Readme Apache-2.0
208 MiB
Languages
TypeScript 67.9%
C 23.5%
JavaScript 7.9%
C++ 0.6%