// JAVARS — ENGINEERING REPORT

javars v0.1.9 · Java on fusevm · lex/parse → AST → bytecode → Cranelift JIT · no bespoke VM · no JVM · MIT · in active development

Docs Reference GitHub
// Color scheme

>_ENGINEERING REPORT

javars is a Java frontend in Rust, hosted on the fusevm bytecode VM and its three-tier Cranelift JIT. This report describes the architecture, the value model, the current state of the tree, and the dependency posture. The statements below are facts about the design and the manifest, not aspirational metrics.

Java
fusevm language host
3
Cranelift JIT tiers
v0.1.9
version
MIT
license · free / OSS

Summary

Every prior Java runtime targets the JVM. javars takes a different path: it lexes and parses Java to an AST, lowers that to fusevm bytecode, and runs it on a compiled VM with a Cranelift JIT. Arithmetic and comparison operators lower to native VM ops so the JIT can trace hot loops; Java-specific behaviour — the String + overload and Java-flavoured value formatting — is served through the host numeric hook and a pair of numbered print builtins. It joins fusevm alongside zshrs, stryke, awkrs, elisp, and rubylang, and reuses that shared bytecode VM and JIT rather than shipping its own. There are no .class files and no JVM.

The current tree is a single-class, single-main slice: the lexer/parser, AST-to-bytecode lowering, local declarations, plain and compound assignment, C-style if/while/for with break/continue, post-increment/decrement, the short-circuiting &&/||, String concatenation, and System.out.print/println. On top of the runtime it ships editor tooling: a read-only LSP (--lsp) with completion, hover, and parser-driven diagnostics, and a DAP debugger (--dap) with line breakpoints, stepping, a stack trace, and local-variable inspection. User methods, classes, objects, arrays, and the standard library are future waves — see BUGS.md for the honest ledger.


Hosting on fusevm

javars contains no virtual machine or JIT of its own. The execution path is:

Java source  →  lexer  →  parser → AST  →  lower to fusevm bytecode  →  fusevm VM + Cranelift JIT
                                                  │
                                     host: String + concat, Java value formatting, print builtins

Shared engine

fusevm is pulled from crates.io with the jit, jit-disk-cache, and aot features. JIT and VM improvements land once and benefit zshrs, stryke, awkrs, elisp, rubylang, and javars together.

Native arithmetic

Operators lower to native fusevm ops; a strict numeric hook supplies Java's String + concatenation only for the non-numeric operand cases the VM does not compute natively (src/host.rs).

Persistent native code

jit-disk-cache persists compiled machine code across runs, keyed by chunk hash, so hot Java loops trace-compile once and warm runs skip recompilation.

Editor tooling in-binary

The same java binary speaks LSP (--lsp) and DAP (--dap) over stdio, so completion, diagnostics, and a stepping debugger work from any editor that speaks the protocols.


Value model

Immediate values (int, long, double, float, boolean, char) ride through the VM as fusevm::Value, with float kept at 32-bit precision and char carried as the 16-bit integral code point it is in Java; a String literal is a heap string value. Everything with reference identity — a class instance (including new Object()), an array, a java.util collection, a lambda closure — lives on javars's own host heap in src/host.rs, and the program holds an opaque Value::Obj handle to it, which is what gives assignment Java's aliasing and == its reference semantics. Locals in main are addressed by name through GetVar/SetVar into the VM's global slot table, which is also what the --dap debugger reads to list a frame's locals; a method body's locals live in call-frame slots instead, so recursion is well-behaved.

Two places need Java semantics the VM's default flavour does not supply. Printing lowers System.out.print/println to registered builtins that format through Java's String.valueOf rules — true/false, a whole double as 3.0, an unbound value as null — rather than the shell-flavoured default. And Java's + is string concatenation when either operand is a String: once the strict numeric hook is installed, any operation with a non-numeric operand delegates to it, where + concatenates via the same Java formatting and non-+ arithmetic on a String is reported as a type error.


Component status

ComponentStateNotes
lexer / parser → ASTImplementedHand-written tokenizer + recursive-descent parser with precedence climbing (src/lexer.rs, src/parser.rs).
AST → fusevm bytecodeImplementedNative arithmetic + CallBuiltin print dispatch, backpatched control flow; no local VM (src/compiler.rs).
entry class + main bodyImplementedSingle-class, single-main subset; other members skipped by brace matching (src/parser.rs).
locals / assignment / compoundImplementedint x = e;, x += e;, post-inc/dec; declared type retained for diagnostics.
control flowImplementedif/else, while, C-style for, break/continue, short-circuiting &&/||.
String + & Java formattingImplementedHost numeric hook concat; true/false, 3.0, null (src/host.rs).
console IOImplementedSystem.out.print/println lowered to Java-formatting print builtins.
LSP server (--lsp)ImplementedCompletion + hover from the keyword/type/IO corpus, parser-driven diagnostics on syntax errors (src/lsp.rs).
DAP debugger (--dap)ImplementedLine breakpoints, stepping, stack trace, main-locals inspection via per-statement debug line markers (src/dap.rs).
reference doc generatorImplementedgen-docs renders docs/reference.html from the LSP corpus (src/bin/gen_docs.rs).
dumps (--dump-tokens/--dump-ast/--disasm)ImplementedPrint an artifact and exit (src/main.rs).
user methods / classes / objects / arraysPlannedMultiple classes, instance state, method calls, arrays, and a String/object heap are future waves (see BUGS.md).
standard library / generics / exceptionsPlannedgenerics, try/catch, lambdas, and the java.util collections run today; streams and the wider java.util surface pending.
inline rust { } FFIPlannedRequires general call-expression support the current single-main parser does not yet have.

Dependency posture

Dependencies are kept foundational and durable — the goal is a crate that still builds cleanly years from now. Direct dependencies from Cargo.toml:

CrateRole
fusevmLanguage-agnostic bytecode VM + three-tier Cranelift JIT (jit, jit-disk-cache, aot)
thiserrorError type derivation
lsp-server / lsp-typesLSP transport + protocol types (--lsp server)
serde / serde_jsonDAP JSON-RPC message plumbing
libcDAP pipe + dup2 to capture the debuggee's stdout as output events

Compatibility & longevity

Reference semantics

Behaviour tracks java where the slice is implemented — String + concatenation, a whole double printed as 3.0 — with expected outputs frozen byte-for-byte in the integration tests.

Corpus-driven docs

The LSP completion/hover corpus is the single source of truth for docs/reference.html, so the static page and the language server never drift.

Cross-architecture

macOS aarch64 and Linux x86_64 / aarch64 via the Cranelift JIT; portable bytecode underneath.

Standalone crate

An explicit empty [workspace] keeps javars buildable on its own, independent of the meta repo.


Links