A JVM written in TypeScript that runs unmodified Java 25 bytecode (classes and JARs) in the browser, in the spirit of CheerpJ. It runs the real OpenJDK 25 class library: the JDK's own classes do the work, and this project implements the JVM underneath them plus the native methods they call. It also runs under Node, which is how the tests run.
- Compatibility first. Run JDK classes unmodified and implement what HotSpot does for them (natives, VM upcalls, init order). Do not patch or replace JDK classes. When behavior differs from HotSpot, HotSpot is right.
- Performance second, and always measured. Startup time and memory matter as
much as throughput; check numbers against
javaon the same machine. - Small and memory efficient. It should run on low-end devices. Avoid dependencies; the runtime has none.
- The browser API mirrors CheerpJ's (
cheerpjInit,cheerpjRunMain,cheerpjRunJar,/app/and/files/paths) so existing pages can switch. - Headless Java first: console apps, libraries and javac. The
image has every Java SE module,
java.desktopfor its headless parts (2D geometry, images,Graphics2Ddrawing on aBufferedImage, JavaBeans); GUI apps (AWT and Swing windows) are out of scope for now. Text draws with one bundled font, DejaVu Sans, rasterized in JS; its pixels differ from HotSpot's FreeType output, everything else follows a Linux HotSpot that has only that font. - Prove it with tests: programs whose output must match HotSpot exactly, and OpenJDK's own regression tests where they can run.
- master is used: pages build pinned commits of it and rely on
cheerpjInit(natives,javaProperties),cheerpjRunMain,cheerpjRunLibrary(static String calls),cheerpOSAddStringFile,cjFileBlob,/files/in IndexedDB and one VM per about:blank iframe. Keep these working. - The release form is an npm package of the built files only
(
java-web.js,java-web-worker.js,node.js(the Node entry),cli.js(thejava-webcommand),boot.pack, thejdk/*.jarimage files,jdk/app.pack,jdk/javac.pack, and OpenJDK's notices injdk/legal/), versioned, with nothing built at install. A GitHub Actions workflow triggered by a release publishes it to npm; never publish by hand.
src/classfile.tsclass file parsersrc/runtime.tsclasses, methods, fields, objects, arrayssrc/interp.tsbytecode interpreter and per-thread run loopsrc/jvm.tsloading, linking, initialization, resolution, scheduler, boot;src/jimage.tsreads the linked JDK imagesrc/indy.tsinvokedynamic, method handlessrc/snapshot.tsboot snapshots: saving and restoring the VM as it is after bootsrc/library.tslibrary mode in the VM: object handles, calls, field access, value conversion;src/bridge.tsthe JS proxies over itsrc/natives/JS implementations of JDK native methods, by package;java2d*.tsport libawt's Java2D loops and must match HotSpot's pixels;font*.tsare the font natives (scaler, fontconfig, layout) over one TrueType font;crypto.tsthe SHA, AES, BigInteger and P-256 field methods HotSpot replaces with intrinsicssrc/sockets.tssockets (Sock, POSIX readiness) and the in-VM loopback backend;src/net-node.tshost sockets, DNS and interfaces for Nodesrc/node.tsNode entry, overbuild/jdkor, built asdist/node.js, the packaged image (src/image.ts);scripts/build-jdk.shbuildsbuild/jdksrc/bootpath.tsclass files on the-Xbootclasspath/aand--patch-modulepaths;src/posixtz.tsPOSIXTZrules such asMEZ-1MESZ,M3.5.0,M10.5.0/3src/http-proxy.tsthe browser's virtual HTTP proxy (fetch);scripts/tls/thejavaweb.tlspass-through TLS provider it relies on forhttps:
pnpm jdkdownloads the pinned JDK 25 and buildsbuild/jdkfrom it (needed before anything runs)node src/node.ts -cp DIR Mainruns a class;--debuglogs VM eventspnpm testruns the tests,pnpm typecheckchecks types; the OpenJDK suite needs inputs first (see docs/development.md)JW_JIT_STATS=1prints what the JIT compiled, fell back on and entered through the run loop;JW_JIT_DUMP=<method>prints a method's generated JS- Pull requests run the OpenJDK tests in
test/jtreg/passing.txtthat their changes need (scripts/jtreg/select.ts); releases run all of them. A test that fails without a code change is fixed or taken out ofpassing.txt, so a green pull request stays green onmasterand at release project516-review-botreviews every PR. Merge only once its review of the head commit has every finding fixed or answered on its thread. When it cannot read the whole diff, a subagent reviews the files it skipped- Commit messages and PR titles follow Conventional Commits:
type(scope): summary, one sentence. Types:feat,fix,perf,refactor,test,docs,ci,build,chore. The scope is optional and names the area (jit,net,fs,zip,jtreg). Mark a change that breaks the page API or the package layout with!(feat!: ...). PRs merge by squash, so the PR title becomes the commit onmaster. PR bodies follow.github/pull_request_template.md - A release is a
chore: release X.Y.ZPR that bumpspackage.json, thengh release create vX.Y.Z --target <its commit on master>. The notes are a few sentences for users, then the output ofnode scripts/release-notes.ts vPREVIOUS vX.Y.Z: the merged PRs by type, linked, and a compare link. The release workflow publishes to npm
- java.home image:
build/jdk, a linked image with the Linux x64 JDK's class library inlib/modules, built the same on every host and mounted at/jdkin the VFS. - VFS: the virtual file system the Java side sees (
src/fs.ts). - native: a JS function implementing a Java
nativemethod, keyed byclass.name(desc). A plain native returns its result; a generator native yields requests (call Java, wait on a promise) and returns at the end. - NativeFrame: a frame on a Java thread's stack that runs a generator native or VM work such as class initialization.
- re-execute: an instruction that needs class loading or initialization pushes that work and leaves its pc unchanged, so it runs again afterwards.
- PendingThrow: thrown by JS code to raise a new Java exception; the interpreter builds it with its real constructor.
- mirror: the
java.lang.Classobject of a runtime class. - handle: how the VM names a Java object it gave to JS; the page's proxy holds it until the proxy is dropped.
- wire value: a value as it crosses between the VM and JS (library.ts).
- boot snapshot: the VM right after the JDK's boot, saved and loaded on
later starts with the same key (build, class path, properties, options,
working directory) instead of booting. Kept in
~/.cache/java-web/snapshotsunder Node (docs/node.md) and IndexedDB in browsers. - Sock: a socket, event or epoll file descriptor behind the
sun.nio.chnatives. A backend (host sockets under Node, loopback inside the VM) moves its bytes.