// JAVARS — LANGUAGE REFERENCE

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

Docs GitHub
// Color scheme

>_LANGUAGE REFERENCE

Every name the current javars build implements — reserved and contextual keywords, literal forms, declaration types, operators, console IO, the java.lang.String and java.util method surfaces, the static library, the modeled throwables and functional interfaces, synthesized class members, method-reference forms, String.format conversions, and the runtime builtin id space. Each entry carries its signature, a description written from the implementation, and — where one clarifies the behaviour — a runnable example. Where javars computes something other than java does, the entry says so. This page is generated from the reference corpus (src/reference.rs) by the gen-docs binary, and the language server reads the same table, so the page, the editor tooling, and the runtime cannot drift apart.

Keywords

# class

[modifiers] class Name [extends Base] [implements I, J] { members }

Declares a class. Every class in a compilation unit — top-level siblings and nested static classes alike — is flattened into one namespace, so a nested class is referred to by its bare name. javars runs the main of whichever class declares public static void main(String[] args).

public class Main { public static void main(String[] args) { System.out.println("hi"); } }

# public

public <member or type declaration>

Access modifier. javars parses every access modifier and enforces none — visibility is a javac check, and a program that reaches javars has already passed it. Required spelling on the entry class and its main.

public static void main(String[] args) { }

# static

static <field, method, initializer block, or nested class>

Marks a class-level member. Static fields become chunk globals seeded with their type default; static { … } blocks run at class-initialization time in textual order with the static field initializers. Required on main.

class Counter { static int n = 0; static { n = 10; } static int bump() { return ++n; } }

# void

void method(params)

Declares a method with no return value. A bare return; leaves it early; return <value>; from void main is a compile error.

static void greet() { System.out.println("hi"); return; }

# if

if (cond) stmt-or-block [else stmt-or-block]

Conditional branch. The condition is lowered to Op::JumpIfFalse over the then block, so a false test costs one jump and no allocation.

if (x > 0) { System.out.println("pos"); } else { System.out.println("non-pos"); }

# else

else stmt-or-block

The fallback branch of an if. Chains as else if by nesting, exactly as in Java.

if (n == 0) { } else if (n == 1) { } else { System.out.println("many"); }

# while

while (cond) stmt-or-block

Pre-tested loop: the condition is evaluated before each iteration, including the first. Owns a break scope, so an unlabeled break/continue inside it targets this loop.

int i = 0; while (i < 3) { System.out.println(i); i++; }

# for

for (init; cond; update) body   |   for (T x : iterable) body

Both loop forms. The C-style form declares its own scope for the init clause; the enhanced form walks an array directly, or a collection snapshotted into an array by the JITER_ARRAY builtin, with a fresh binding per iteration so a lambda written in the body captures that iteration's value.

for (int i = 0; i < 3; i++) { System.out.println(i); }
for (String s : names) { System.out.println(s); }

# do

do body while (cond);

Post-tested loop: the body always runs at least once, then repeats while the condition holds.

int i = 0; do { i += 2; } while (i < 5);   // i == 6

# switch

switch (sel) { case L: … }   |   switch (sel) { case L -> … }   |   T v = switch (sel) { … };

Multi-way branch in both Java forms. The classic form falls through between groups until a break; the arrow form does not fall through and works as a statement or as an expression. The selector may be an int, a String, or an enum constant. Pattern labels, case null, and when guards are not implemented.

String r = switch (n) { case 1 -> "one"; case 2, 3 -> "few"; default -> "many"; };

# case

case L:   |   case L1, L2 ->

A switch label. In the classic form a matched label runs on into the following groups until a break or the end of the switch; in the arrow form one label list selects exactly one arm. Multiple labels per arm are supported in both forms.

switch (n) { case 1: System.out.println("one"); break; case 2: case 3: System.out.println("few"); break; }

# default

default:   |   default ->   |   default T m(params) { … }

The switch label taken when no case matches, and — in an interface body — the modifier that gives a method an inherited implementation. Both spellings lex to the same reserved word; the parser tells them apart by position.

switch (n) { default: System.out.println("other"); }
interface Shape { int area(); default String describe() { return "area " + area(); } }

# return

return;   |   return expr;

Leaves the current method, lowering to Op::ReturnValue (with null pushed for the bare form). Returning a value from void main is rejected at compile time.

static int max(int a, int b) { if (a > b) return a; return b; }

# break

break;   |   break label;

Exits the nearest enclosing loop or switch, or the one named by a label. The jump is backpatched once the enclosing construct's end address is known, so a labeled break out of nested loops is a single unconditional jump.

outer: for (int i = 0; ; i++) { for (int j = 0; ; j++) { if (i + j > 4) break outer; } }

# continue

continue;   |   continue label;

Skips to the next iteration of the nearest enclosing loop, or of the one named by a label. In a C-style for the jump lands on the update clause, not on the condition.

for (int i = 0; i < 5; i++) { if (i % 2 == 0) continue; System.out.println(i); }

# true

true

The boolean literal true. Lowers to Op::LoadTrue, and prints as true rather than fusevm's shell-flavoured 1.

boolean ok = true; System.out.println(ok);   // true

# false

false

The boolean literal false. Lowers to Op::LoadFalse, and prints as false.

boolean done = false; System.out.println(done);   // false

# new

new C(args)   |   new T[n]   |   new T[m][n]   |   new T[]{ … }   |   new ArrayList<>()

Allocation. A class instance goes through the JNEW builtin and then the field initializers and the selected constructor; an array through JARRAY_NEW/JARRAY_NEW_MULTI; a modeled java.util implementation through JCOLL_NEW. Every result is an opaque Value::Obj handle into the host heap, so assignment aliases by reference.

Point p = new Point(1, 2);
int[][] grid = new int[2][3];
List<String> xs = new ArrayList<>();

Contextual Keywords

# package

package name.of.pkg;

Accepted and discarded. javars has one flat namespace and no class path, so a package declaration carries no meaning; the parser skips the prologue line so real Java sources compile unchanged.

package com.example.demo;

# import

import name.of.Type;   |   import name.of.pkg.*;

Accepted and discarded, like package. The modeled library types (Math, ArrayList, the throwables, the functional interfaces) are always in scope, so no import is needed — and a fully-qualified use such as java.util.Arrays.toString(a) is *not* supported, because the qualifier parses as field access on an undefined variable.

import java.util.*;   // accepted; Arrays, List and friends are in scope regardless

# interface

interface Name [extends I, J] { abstract and default methods }

Declares an interface. Abstract methods, default bodies, multiple implements, and interface extends all work; an interface with exactly one abstract method is automatically a lambda target with no registration anywhere.

interface Shape { int area(); default String describe() { return "area " + area(); } }

# enum

enum Name [implements I] { A, B(args), C { body } ; members }

Declares an enum. Constants become singleton instances held in chunk globals, each carrying a synthesized name and ordinal; per-constant arguments and per-constant bodies (which compile to an anonymous subclass) are supported, as is implements.

enum Op { PLUS { int apply(int a, int b) { return a + b; } }; int apply(int a, int b) { return 0; } }

# record

record Name(T c1, U c2) { [compact constructor] [members] }

Declares a record. The parser synthesizes the canonical constructor, one accessor per component, toString() in Java's Name[c1=v1, c2=v2] form, a component-wise equals(Object) guarded by an instanceof test, and hashCode() as the 31 * h + componentHash fold — each component through its own wrapper's hashCode(x), so the widths stay apart. Declaring any of them yourself wins over the derived one.

record Point(int x, int y) { }
// new Point(1, 2) prints as Point[x=1, y=2]

# extends

class C extends Base   |   interface I extends J, K   |   <T extends Bound>

Names a superclass, a set of super-interfaces, or a type-parameter bound. The superclass link drives instanceof, the virtual-dispatch chain, and super(...) constructor chaining; a bound is erased and dropped.

class Dog extends Animal { }

# implements

class C implements I, J

Names the interfaces a class implements. Each becomes a supertype edge, so instanceof I succeeds and a default method of I is inherited unless the class overrides it.

class Sq implements Shape { public int area() { return 4; } }

# abstract

abstract class C { abstract T m(params); }

Marks a class that is never instantiated and a method that has no body. An abstract method contributes its name to the dispatch chain; a concrete subclass supplies the body.

abstract class Base { abstract String tag(); }
class Impl extends Base { String tag() { return "impl"; } }

# final

final <local, parameter, catch parameter, resource, field, or class>

Parsed everywhere Java allows it and then dropped. Immutability is a javac check that has already run, and javars's lambda capture is a by-value snapshot, so a final local and an effectively-final one compile identically.

final int limit = 10;
for (final String s : names) { System.out.println(s); }

# private

private <member>

Parsed and dropped, like every access modifier. javars enforces no visibility.

class Box { private int n; private int get() { return n; } }

# protected

protected <member>

Parsed and dropped, like every access modifier.

class Base { protected String tag() { return "base"; } }

# synchronized

synchronized <method> | synchronized (monitor) { … }

javars runs a program on one thread, so there is no monitor to acquire: as a method modifier it is dropped. As a *statement* the rest of the semantics still hold — the monitor expression is evaluated exactly once, and a null monitor throws NullPointerException before the body runs.

static synchronized void once() { System.out.println("once"); }
Object lock = new Object();
synchronized (lock) { System.out.println("critical"); }

# native

native <method>

Accepted as a method modifier and dropped. javars's own foreign-function path is the inline rust { … } block, not JNI.

static native void jni();   // accepted as a modifier; there is no JNI binding

# volatile

volatile <field>

Accepted as a field modifier and dropped — single-threaded execution gives it nothing to order.

class Flag { volatile boolean set; }

# transient

transient <field>

Accepted as a field modifier and dropped. javars has no serialization.

class Cache { transient int hits; }

# throws

T method(params) throws E1, E2

Parsed and discarded. javars performs no checked-exception analysis, so the clause is documentation; a method that throws without declaring it runs identically.

static int parse(String s) throws Exception { return Integer.parseInt(s); }

# throw

throw expr;

Raises a throwable. The value is parked as the host's pending exception by the JTHROW builtin and the compiler jumps to the innermost handler in the frame — or returns out of the frame, so the caller's post-call check sees it and repeats. There is no unwind opcode in fusevm; this is how the unwind is modeled.

throw new IllegalStateException("bad state");

# try

try { … } catch (E e) { … } finally { … }   |   try (R r = new R()) { … }

Guards a block. The value-stack depth is recorded on entry so a handler can discard the operands of the expression the throw abandoned. A try must carry at least one catch or a finally. The resource form is desugared in the parser into a finally that calls close() on each resource in reverse order.

try (Res r = new Res()) { use(r); } catch (Exception e) { System.out.println(e); }

# catch

catch (E e) { … }   |   catch (final E e) { … }

Handles a pending throwable whose runtime class is E or a subclass — the same supertype walk instanceof uses. Arms are tested in source order. A multi-catch (catch (A | B e)) lists alternative types before the one bound variable; its alternatives are tested in order and share the one body, and the variable's static type is the first of them (Java uses their least upper bound, which javars does not compute).

catch (NumberFormatException e) { System.out.println(e.getMessage()); }

# finally

finally { … }

A block emitted on both the normal and the exceptional path out of a try, and the target the try-with-resources desugar uses for close().

try { work(); } finally { System.out.println("always"); }

# instanceof

expr instanceof Type

Runtime type test through the JINSTANCEOF builtin, over every shape the value model names: a user class or interface walks the declared supertype graph, a boxed primitive answers its wrapper plus Number/Comparable/Serializable, a collection answers its concrete kind and the java.util interfaces above it, an array is Cloneable and Serializable, an enum is an Enum and a record a Record. Every non-null reference is an Object; null is an instance of nothing, including Object. A lambda answers only Object (the closure does not record its functional interface), and pattern binding (x instanceof Point p) is not implemented.

if (shape instanceof Sq) { System.out.println("square"); }

# this

this   |   this.field   |   this.method(args)   |   this::method

The receiver of the enclosing instance method or constructor, held in call-frame slot 0. A bare field name that is not a local resolves to this.name implicitly. Using this outside an instance method is a compile error. this(...) as the first statement of a constructor delegates to another constructor of the same class, which is what runs the super() chain and the instance initializers — so they run exactly once.

class Point { int x; Point(int x) { this.x = x; } int get() { return x; } }

# super

super(args); super.method(args); super.field

Three forms. super(args) is explicit superclass constructor chaining: every field in the chain is already seeded with its type default when the call runs, so the parent constructor executes on the same receiver — running its *own* instance initializers before its body, per JLS 12.5, which is why a virtual call it makes sees the subclass's fields still at their defaults. super.method(args) is a **non-virtual** call to the implementation the enclosing class inherits — resolution starts at the declaring class's superclass, never at the receiver's runtime class, which is what lets an override call the version it overrides without recursing; overload selection, varargs packing and a walk past a parent that does not declare the method all happen at that superclass. Everything inside the body it reaches still dispatches virtually, so a super.m() whose callee calls an unqualified n() reaches the subclass's n. super.field is the same field cell this.field names (javars merges a class's fields with its ancestors', so field *hiding* is not modeled — see BUGS.md). When no user class up the chain declares the member, super.toString/equals/hashCode answer java.lang.Object's.

class Cat extends Animal { public String toString() { return "Cat/" + super.toString(); } }

# yield

yield expr;

Supplies the value of a block-bodied arm of a switch *expression*. Recognized only while a switch expression is being parsed, so yield remains a usable identifier everywhere else.

int n = switch (k) { case 1 -> 10; default -> { int t = k * 2; yield t; } };

# null

null

The null reference. javars models it as Value::Undef — the value an unassigned fusevm slot already holds — so null needs no keyword: it lexes as an identifier that was never assigned. It prints as null, compares equal to itself under ==, and dereferencing it raises the matching NullPointerException.

String s = null;
System.out.println(s + " " + (s == null));   // null true

Literals

# int literal

0   42   2147483647

A decimal integer literal. Arithmetic on two statically int-width operands wraps at 32 bits, which the compiler emits as a native shift pair rather than a builtin so the JIT can still trace it. There are no hex, binary, or octal literals and no _ digit separators: 0x10 lexes as 0 followed by the identifier x10.

int big = 2147483647;
System.out.println(big + 1);   // -2147483648

# long literal

0L   9000000000L   42l

An L/l-suffixed integer literal. The suffix survives lexing as its own token kind because it is what types the value long and therefore exempts it from the 32-bit int wrap.

long big = 2147483647L;
System.out.println(big + 1);   // 2147483648

# double literal

3.0   1.5d   2D

A floating-point literal: any literal with a fractional part, an exponent, or a d/D/f/F suffix. It prints through Java's Double.toString rules — a trailing .0 on whole values, Infinity/NaN for the non-finite ones.

double d = 3.0;
System.out.println(d);   // 3.0

# float literal

1.5f   2F

An f/F-suffixed literal. javars has one floating kind (f64), so the suffix only marks the literal as floating; it does not narrow the value to 32-bit precision the way javac does.

float f = 1.5f;
System.out.println(f);   // 1.5

# exponent literal

1e3   1.5e-3   2E+2

Scientific notation, with or without a fractional part; either way the literal is floating point. The e is consumed only when a valid exponent follows, so 1.foo and an identifier beginning with e are left alone. Values outside [1e-3, 1e7) print in Java's uppercase scientific form.

System.out.println(1e3);          // 1000.0
System.out.println(25000000.0);   // 2.5E7

# string literal

"text"

A double-quoted string. Multi-byte characters are decoded as full UTF-8 scalars, so a non-ASCII literal survives lexing intact. Text blocks (""") are not implemented.

String s = "hello";

# char literal

'c'   '\n'

A single-quoted character, modeled as a one-character *string* rather than an integer. That is javars's one deliberate char divergence: 'A' + 1 concatenates to "A1" where Java's numeric promotion gives 66.

char c = 'A';
System.out.println(c + 1);   // A1  (Java prints 66)

# escape sequence

\n   \t   \r   \0   \\   \"   \'

The escapes the lexer decodes inside a string or char literal. Any other escaped character passes through as itself, so \q is a literal q. Unicode escapes (\uXXXX) are not decoded.

System.out.println("a\tb\nc");

# array initializer

{ e0, e1, … }   |   new T[]{ e0, e1, … }

An array literal. Elements are evaluated left to right and handed to the JARRAY_LIT builtin, which allocates one heap array. Nested braces build a rectangular multi-dimensional array.

int[] a = {1, 2, 3};
int[][] g = {{1, 2}, {3, 4}};

Types

# multi-declarator declaration

T a [= e], b [= e], …;   |   T a[] = { … }, b;

One declaration statement declaring several variables — as a local, in a for init clause, or as a field. Declarators run left to right, so a later initializer may read an earlier name, and any may be left uninitialized. A C-style array suffix binds to its own *declarator*, so int p[] = {1}, q; is an int[] and an int; the suffix is accepted on locals, fields, and parameters. var is rejected here, as it is by javac.

int a = 1, b = a + 1, c;
int p[] = {4, 5}, q = 6;
for (int i = 0, n = 3; i < n; i++) { System.out.println(i); }

# int

int name [= expr];

32-bit integer declaration. Arithmetic between two int-width operands wraps at 32 bits, matching javac; compound assignment and ++ narrow back to int on store.

int n = 42;

# long

long name [= expr];

64-bit integer declaration. A long operand suppresses the 32-bit wrap, so the arithmetic runs at fusevm's native i64 width.

long big = 9000000000L;

# short

short name [= expr];

16-bit integer declaration. Java promotes short to int before any binary operation, so javars treats it as int-width for the wrap decision and never narrows the stored value to 16 bits.

short s = 7;

# byte

byte name [= expr];

8-bit integer declaration. Like short, it promotes to int for arithmetic and is not narrowed on store.

byte b = 1;

# double

double name [= expr];

64-bit floating-point declaration — javars's only floating kind. Division follows IEEE-754 through the JDIV builtin, so a zero divisor yields a signed infinity or NaN instead of faulting.

double d = 3.0;
System.out.println(d);   // 3.0

# float

float name [= expr];

Java's 32-bit floating-point type, kept at 32 bits rather than aliased to double: every operation rounds *once* at 32 bits (on the host, so it cannot round twice), and the value prints as the shortest decimal that round-trips at 32 bits. Float.MIN_VALUE prints 1.4E-45 — the two-digit widening Java's toString specification applies whenever the shortest form has a single digit.

float f = 1.5f;
System.out.println(1.0f / 3.0f);   // 0.33333334

# boolean

boolean name [= expr];

Boolean declaration. Prints as true/false through the Java-formatting print builtins rather than fusevm's shell-flavoured 1/0.

boolean ok = true;

# char

char name [= expr];

Character declaration. A char value is a one-character string, so + concatenates rather than promoting to int, and String index arithmetic counts Unicode scalars rather than UTF-16 code units.

char c = 'A';

# String

String name [= expr];

String declaration, and the receiver type that routes an instance call to the java.lang.String methods. + with a String operand concatenates through the host numeric hook using Java's value-to-string rules.

String s = "hi " + 1 + 2;   // "hi 12"

# var

var name = expr;

Local variable with an inferred type (Java 10+). The inferred type is *recorded*, not just the value, so a var participates in /-truncation, the 32-bit int wrap, and class-typed dispatch exactly as the explicit spelling would. var a = 1, b = 2; is rejected, as it is by javac: each declarator would need its own inference.

var s = "inferred";
var i = 7; System.out.println(i / 2);   // 3, not 3.5

# Object

Object name [= expr];   |   new Object()

The universal reference type, and the erasure of every generic type parameter. Any reference is assignable to it (at a deliberately high overload-resolution cost, so a more specific overload always wins), and a String satisfies it. new Object() allocates the fieldless root instance — a distinct identity per allocation, usable as a lock, a sentinel, or a map key — and the methods a class inherits without overriding answer from Object: equals is reference identity, getClass().getName() is java.lang.Object, toString() is java.lang.Object@<hash>.

Object o = "anything";
Object lock = new Object();
System.out.println(lock.equals(lock));   // true

# T[]

T[] name [= expr];   |   T name[];   |   T[][] name;

An array type. Arrays are host heap objects with reference (aliasing) assignment, a .length field, and a bounds check on every access that raises Java's ArrayIndexOutOfBoundsException with its exact detail message.

int[] a = new int[3];
String[][] grid = new String[2][2];

Operators

# +

a + b

Addition when both operands are numeric, string concatenation when either is not. The concatenating path runs in the host's strict numeric hook and renders both sides with Java's String.valueOf rules, so true, 3.0, and null all print the way java prints them.

System.out.println(1 + 2);            // 3
System.out.println("n=" + 3.0);       // n=3.0

# -

a - b

Subtraction. A non-numeric operand is a type error in Java, so javars reports it rather than coercing: "a" - 1 is a run-time operator Sub is not defined error, not a silent zero.

System.out.println(5 - 2);   // 3

# *

a * b

Multiplication, wrapping at 32 bits when both operands are statically int-width.

System.out.println(6 * 7);   // 42

# /

a / b

Division. Statically-integral division stays on fusevm's native op — so the JIT can trace it — and a zero divisor raises ArithmeticException: / by zero. A floating operand routes to the JDIV builtin instead, where IEEE-754 gives a signed infinity or NaN and never faults.

System.out.println(7 / 2);       // 3
System.out.println(7.0 / 0.0);   // Infinity

# %

a % b

Remainder, taking the sign of the dividend exactly as Java does.

System.out.println(-7 % 3);   // -1

# unary -

-a

Arithmetic negation, lowered to Op::Negate. Applying it to a non-numeric value is reported as a type error rather than coerced.

int n = -5;

# unary +

+a

Changes no bits, but it is not a no-op: JLS 5.6.1 applies unary numeric promotion, so a byte, short or char operand becomes an int. For char that is visible, because the promoted type is what picks the rendering — "" + +c prints the code point where "" + c prints the letter.

char c = 'A';
System.out.println("" + +c);   // 65
System.out.println("" + c);    // A

# !

!a

Logical negation, lowered to Op::LogNot.

if (!done) { System.out.println("still going"); }

# ==

a == b

Equality. Numbers and booleans compare by value and heap objects by handle identity — but two equal *strings* also compare equal, because javars models a String as a value rather than a reference. That is the one place == diverges: "hi" == "h" + "i" is true here and false in Java.

System.out.println("hi" == "h" + "i");   // true  (Java prints false)

# !=

a != b

Inequality — the exact negation of ==, including its string behaviour.

if (n != 0) { System.out.println(100 / n); }

# <

a < b

Less-than. Numeric operands compare numerically; a string operand compares lexicographically by char through the numeric hook, which Java would reject at compile time.

if (i < 3) { System.out.println(i); }

# >

a > b

Greater-than, with the same numeric-or-lexicographic behaviour as <.

if (score > best) { best = score; }

# <=

a <= b

Less-than-or-equal.

for (int i = 1; i <= 10; i++) { }

# >=

a >= b

Greater-than-or-equal.

if (n >= 0) { System.out.println("non-negative"); }

# &&

a && b

Short-circuiting conjunction: the right operand is not evaluated when the left is false. Lowered to Op::JumpIfFalseKeep, so no builtin call and no allocation are involved.

if (s != null && s.length() > 0) { System.out.println(s); }

# ||

a || b

Short-circuiting disjunction, the mirror of && via Op::JumpIfTrueKeep.

if (n < 0 || n > 100) { System.out.println("out of range"); }

# =

name = expr;   |   a[i] = expr;   |   obj.f = expr;

Assignment, in the three target forms javars lowers separately: a local or global slot, an array element through JARRAY_SET, and an instance field through JFIELD_SET. Assignment is a statement, not an expression — x = y = 0 does not parse.

int n = 0;
n = 5;
arr[0] = n;

# +=

target += expr;

Compound addition, on any of the three assignable targets. When the target is declared int the result is narrowed back to 32 bits on store, so int overflow wraps exactly as javac compiles it. With a String target it appends.

int i = 5; i += 2;   // 7
String s = "a"; s += "b";   // "ab"

# -=

target -= expr;

Compound subtraction, with the same int narrowing on store.

int i = 5; i -= 2;   // 3

# *=

target *= expr;

Compound multiplication.

int i = 5; i *= 2;   // 10

# /=

target /= expr;

Compound division, following the same integral-versus-floating split as /.

int i = 10; i /= 2;   // 5

# %=

target %= expr;

Compound remainder.

int i = 10; i %= 4;   // 2

# ++

name++;   |   a[i]++;   |   obj.f++;

Post-increment. JLS 15.14.2 defines it as += 1 with the implicit narrowing cast a compound assignment carries, so byte b = 127; b++ is -128 and an int wraps at 32 bits. In value position it evaluates to the value the target *held*, with the update applied after; the prefix form ++i updates first and evaluates to the new value. Any lvalue works — a local, an array element, an instance field, a static — and the array, index, or receiver is evaluated exactly once, so a[idx()]++ calls idx() a single time.

for (int i = 0; i < 3; i++) { }
int v = 5; System.out.println(v++ + "," + v);   // 5,6
int w = 5; System.out.println(++w + "," + w);   // 6,6
int[] a = {1}; System.out.println(a[0]++ + "," + a[0]);   // 1,2

# --

name--;   |   a[i]--;   |   obj.f--;

Post-decrement, with the same value-position rule as ++: n-- evaluates to the old value, --n to the new one.

int n = 3; n--;   // 2
int m = 3; System.out.println(--m);   // 2

# &

a & b

Bitwise AND on integral operands (fusevm's native Op::BitAnd), and Java's *non-short-circuiting* logical AND on boolean operands — where the result must stay a boolean rather than the 0/1 an integer op would leave, so it lowers to Op::LogAnd instead. The operand types decide which.

System.out.println(5 & 3);        // 1
System.out.println(true & false); // false

# |

a | b

Bitwise OR, and the non-short-circuiting logical OR on booleans — the mirror of &. Also the separator of a multi-catch's alternative types.

System.out.println(5 | 3);       // 7
System.out.println(true | false); // true

# ^

a ^ b

Bitwise XOR on integers; on two booleans it is exactly "they differ", lowered to Op::NumNe so the result prints true/false.

System.out.println(5 ^ 3);       // 6
System.out.println(true ^ true); // false

# ~

~a

Bitwise complement of an integral operand (Op::BitNot). Unary numeric promotion widens byte/short/char to int and leaves long alone.

System.out.println(~5);   // -6

# <<

a << n

Left shift. Java masks the distance to the width of the **left** operand — 5 bits for int, 6 for long — so 1 << 33 is 1 << 1; only that operand is promoted, so 1 << 2L is still an int. The mask is emitted explicitly because fusevm's Op::Shl always masks to 6 bits, and an int result is narrowed afterwards.

System.out.println(1 << 31);  // -2147483648
System.out.println(1 << 33);  // 2
System.out.println(1L << 40); // 1099511627776

# >>

a >> n

Arithmetic (sign-propagating) right shift, with the same width masking as <<. Lexed as one token, which is why every generic-argument skipper weighs a closing token by how many > it spells — otherwise List<List<String>> would not parse.

System.out.println(-8 >> 1);   // -4

# >>>

a >>> n

Logical (zero-fill) right shift. Java zero-fills at the operand's width, which fusevm has no native op for — Op::Shr is always arithmetic on 64 bits — so this routes through the JUSHR builtin with the width the compiler determined.

System.out.println(-8 >>> 1);   // 2147483644
System.out.println(-8L >>> 1);  // 9223372036854775804

# &= |= ^= <<= >>= >>>=

target &= expr;   |   target <<= expr;   |   target >>>= expr;

The bitwise and shift compound assignments, on any of the three assignable targets. Each applies the same rules as its binary form: the shifts mask their distance to the *target's* width and >>>= zero-fills at it, and &=/|=/^= stay logical when the operand is a boolean.

int v = 1; v <<= 3; v |= 1; v &= 14; v ^= 3;
System.out.println(v);   // 11

# (type) expr

(int) d   |   (byte) n   |   (char) code   |   (Object) x

A cast. Java's narrowing primitive conversions are real value changes and route through the JCAST builtin: floating to integral *saturates* ((int) 1e18 is Integer.MAX_VALUE) and truncates toward zero, and the integral narrowings are two's-complement. A widening or identity cast emits the operand alone, so (int) i stays native. A reference cast is a no-op (see ClassCastException). (char) n produces the one-character string javars models a char as.

System.out.println((int) 3.99);    // 3
System.out.println((byte) 200);    // -56
System.out.println((char) 65);     // A
System.out.println((double) 7 / 2); // 3.5

# ?:

cond ? a : b

The conditional expression. Exactly one arm is evaluated; the untaken arm's code is jumped over, so a side effect in it never runs.

String label = n > 4 ? "big" : "small";

# ->

(params) -> expr   |   (params) -> { … }   |   p -> expr

The lambda arrow, and the arrow form of switch. A lambda becomes a heap closure that snapshots every enclosing local by value at the point the literal runs, which is observationally exact because Java only permits capture of effectively-final locals.

Function<Object,Object> up = s -> s + "!";
System.out.println(up.apply("hi"));   // hi!

# ::

Type::member   |   value::method   |   this::method

The method-reference separator. Every form is desugared in the compiler into an equivalent lambda with synthesized parameter names, so a method reference and the lambda it stands for share one code path.

Function<Object,Object> up = String::toUpperCase;

# []

array[index]

Array subscript, through JARRAY_GET for a read and JARRAY_SET for a write. Both are bounds-checked; an out-of-range index raises ArrayIndexOutOfBoundsException carrying Java's Index N out of bounds for length M message, and a null array raises a NullPointerException.

int[] a = {1, 2, 3};
System.out.println(a[1]);   // 2

# .

recv.field   |   recv.method(args)   |   Class.staticMember

Member access. A field read routes through JFIELD_GET (which also serves an array's .length), an instance call through the receiver's dispatch chain, and a static member through the owning class's chunk global. A null receiver raises a NullPointerException naming the operation.

System.out.println(p.x);
System.out.println(arr.length);

# ()

name(args)   |   recv.name(args)   |   Class.name(args)

Invocation. A bare name resolves to a user static method (choosing the overload with the lowest assignment cost) or, in a program carrying an inline rust { … } block, to an FFI export dispatched by name. Every call site in a program that uses exceptions also carries a pending-exception check.

System.out.println(Helper.twice(3));

Console IO

# System

System.out   |   System.err

The System class. javars models exactly two of its members — the two console streams. System.in is rejected at parse time with a message naming it, so a program that reads stdin fails loudly rather than silently returning nothing.

System.out.println(42);

# System.out

System.out

The standard output stream. It is not an object javars can pass around: System.out is recognized only as the receiver of print/println or as the left side of a System.out::println method reference.

System.out.print("x");

# System.err

System.err

The standard error stream, recognized in exactly the same positions as System.out and lowered to the stderr pair of print builtins.

System.err.println("oops");

# System.out.println

void System.out.println([Object x])

Prints one argument in Java's String.valueOf form followed by a newline, then returns nothing. The Java formatting is the point of the builtin: true prints as true (not 1), a whole double as 3.0 (not 3), and a null reference as null. Called with no argument it prints just the newline.

System.out.println("hello");
System.out.println();

# System.out.print

void System.out.print([Object x])

As println, but with no trailing newline.

System.out.print("no newline");

# System.err.println

void System.err.println([Object x])

As System.out.println, writing to stderr. stdout and stderr are locked and written independently, so redirecting one does not reorder the other.

System.err.println("to stderr");

# System.err.print

void System.err.print([Object x])

As System.err.println, with no trailing newline.

System.err.print("partial");

String Methods

# length

int length()

The number of characters in the string. javars counts Unicode scalars where Java counts UTF-16 code units, so the two agree for every ASCII and BMP string and differ by one per astral character.

System.out.println("Hello".length());   // 5

# isEmpty

boolean isEmpty()

True when the string has no characters.

System.out.println("".isEmpty());   // true

# charAt

char charAt(int index)

The character at a zero-based scalar index, returned as a one-character string (javars's char model). An out-of-range index raises StringIndexOutOfBoundsException with Java's exact Index N out of bounds for length M message.

System.out.println("Hello".charAt(1));   // e

# substring

String substring(int beginIndex)

The suffix beginning at beginIndex, on scalar indices.

System.out.println("Hello".substring(1));   // ello

# substring

String substring(int beginIndex, int endIndex)

The half-open range [beginIndex, endIndex), with Java's bounds rule 0 <= begin <= end <= length. A violation raises StringIndexOutOfBoundsException carrying Java's Range [b, e) out of bounds for length n message.

System.out.println("Hello".substring(1, 3));   // el

# indexOf

int indexOf(String str)

The scalar index of the first occurrence of str, or -1. The result is converted from the byte offset the search returns, so it is a character index and not a UTF-8 offset.

System.out.println("Hello".indexOf("ll"));   // 2

# contains

boolean contains(CharSequence s)

True when the string contains s as a substring.

System.out.println("Hello".contains("ell"));   // true

# equals

boolean equals(Object other)

Value equality on the character sequence. Note that javars's == already compares strings by value, so equals and == agree here where Java's would not.

System.out.println("Hello".equals("Hello"));   // true

# equalsIgnoreCase

boolean equalsIgnoreCase(String other)

Value equality after lowercasing both sides with Unicode case folding.

System.out.println("Hello".equalsIgnoreCase("HELLO"));   // true

# compareTo

int compareTo(String other)

Java's specified ordering value, not merely its sign: the difference of the first differing character, or the length difference when one string is a prefix of the other. That exactness is why a plain lexicographic comparison cannot stand in for it.

System.out.println("Hello".compareTo("Hellp"));   // -1

# compareToIgnoreCase

int compareToIgnoreCase(String other)

As compareTo, comparing the case-folded characters.

System.out.println("Hello".compareToIgnoreCase("hello"));   // 0

# toUpperCase

String toUpperCase()

The string uppercased with Unicode case mapping (locale-independent).

System.out.println("Hello".toUpperCase());   // HELLO

# toLowerCase

String toLowerCase()

The string lowercased with Unicode case mapping.

System.out.println("Hello".toLowerCase());   // hello

# trim

String trim()

Removes leading and trailing characters whose code point is at or below U+0020 — Java's trim() rule, which is deliberately not the same as strip()'s Character.isWhitespace test (that one keeps the non-breaking spaces U+00A0/U+2007/U+202F and removes the wider Unicode separators).

System.out.println("  x ".trim() + "|");   // x|

# startsWith

boolean startsWith(String prefix)

True when the string begins with prefix.

System.out.println("Hello".startsWith("He"));   // true

# startsWith

boolean startsWith(String prefix, int offset)

True when the string begins with prefix at offset. An offset outside the string answers false rather than throwing.

System.out.println("Hello".startsWith("ll", 2));   // true

# endsWith

boolean endsWith(String suffix)

True when the string ends with suffix.

System.out.println("Hello".endsWith("lo"));   // true

# concat

String concat(String str)

The receiver followed by str. Equivalent to + here, since javars's + already concatenates through the same value-to-string rules.

System.out.println("Hello".concat("!"));   // Hello!

# replace

String replace(CharSequence target, CharSequence replacement)

Every occurrence of target replaced by replacement. This is the literal two-argument overload; there is no regex replaceAll.

System.out.println("Hello".replace("l", "L"));   // HeLLo

# repeat

String repeat(int count)

The string repeated count times. A negative count raises IllegalArgumentException with Java's count is negative: n message.

System.out.println("ab".repeat(3));   // ababab

Static Library

# Math.abs

int Math.abs(int a)   |   double Math.abs(double a)

Absolute value, keeping the operand's kind: an integral argument returns an integer, anything else returns a double. The result type is deliberately not modeled statically, because claiming either would mis-type the other overload.

System.out.println(Math.abs(-3) + " " + Math.abs(-3.5));   // 3 3.5

# Math.max

int Math.max(int a, int b)   |   double Math.max(double a, double b)

The larger of two values; integral when both operands are integral, double otherwise. A NaN operand propagates (unlike C's fmax and Rust's f64::max, which return the other operand), and max(-0.0, 0.0) is 0.0.

System.out.println(Math.max(1, 2));   // 2

# Math.min

int Math.min(int a, int b)   |   double Math.min(double a, double b)

The smaller of two values, with the same integral-versus-double rule as max — and the same NaN propagation, which f64::min does not do.

System.out.println(Math.min(1.5, 2));   // 1.5

# Math.pow

double Math.pow(double a, double b)

a raised to the power b, always as a double. Java departs from IEEE pow twice: a NaN exponent gives NaN even for a base of 1, and |a| == 1 with an infinite exponent gives NaN. A zero exponent still gives 1.0 for every base.

System.out.println(Math.pow(2, 10));   // 1024.0

# Math.sqrt

double Math.sqrt(double a)

Square root, always as a double. A negative argument gives NaN rather than faulting, as in Java.

System.out.println(Math.sqrt(9));   // 3.0

# Math.floor

double Math.floor(double a)

The largest double less than or equal to a that is a mathematical integer.

System.out.println(Math.floor(1.7));   // 1.0

# Math.ceil

double Math.ceil(double a)

The smallest double greater than or equal to a that is a mathematical integer.

System.out.println(Math.ceil(1.2));   // 2.0

# Math.round

long Math.round(double a)   |   int Math.round(float a)

Java's rounding: ties go toward positive infinity, so round(-2.5) is -2 where Rust's half-away-from-zero round gives -3. It is *not* floor(a + 0.5) either — that was the pre-Java-7 implementation and it answers 1 for 0.49999999999999994, where Java answers 0. A float argument selects the overload returning int, which saturates at Integer.MAX_VALUE.

System.out.println(Math.round(-2.5));   // -2

# Math.rint

double Math.rint(double a)

Rounds to the nearest integral double, ties going to the **even** neighbour — the IEEE roundToIntegralTiesToEven operation, and therefore not Math.round's ties-toward-positive-infinity: rint(2.5) is 2.0 while round(2.5) is 3.

System.out.println(Math.rint(2.5) + " " + Math.rint(3.5));   // 2.0 4.0

# Math.copySign

double Math.copySign(double magnitude, double sign)

The magnitude of the first argument with the sign of the second. The sign of a zero counts, so copySign(3.0, -0.0) is -3.0.

System.out.println(Math.copySign(3.0, -0.0));   // -3.0

# Math.ulp

double Math.ulp(double d)

The distance from d to the next representable double away from zero — the size of one unit in the last place. ulp(Double.MAX_VALUE) is measured downward, because one step further would be infinity.

System.out.println(Math.ulp(1.0));   // 2.220446049250313E-16

# Math.nextUp

double Math.nextUp(double d)   |   double Math.nextDown(double d)

The representable double adjacent to d toward positive (nextUp) or negative (nextDown) infinity. Both zeros step to the smallest subnormal of the target's sign.

System.out.println(Math.nextUp(1.0));   // 1.0000000000000002

# Math.nextAfter

double Math.nextAfter(double start, double direction)

The representable double adjacent to start in the direction of direction, or direction itself when the two are equal.

System.out.println(Math.nextAfter(1.0, 0.0));   // 0.9999999999999999

# Math.fma

double Math.fma(double a, double b, double c)

a * b + c computed as one fused operation, rounded **once** — so it is not the same number as writing the two operations separately when the exact product needs more than 53 bits.

System.out.println(Math.fma(2.0, 3.0, 4.0));   // 10.0

# Math.addExact

int Math.addExact(int a, int b)   |   long Math.addExact(long a, long b)   |   subtractExact   |   multiplyExact

The arithmetic that raises ArithmeticException instead of wrapping. The overload is chosen from the arguments' **static** types and the two disagree exactly where the method is interesting: the int one overflows at 2^31 with integer overflow, the long one at 2^63 with long overflow. A call whose argument types javars cannot infer is refused, naming the method.

System.out.println(Math.addExact(2000000000L, 2000000000L));   // 4000000000

# Math.toIntExact

int Math.toIntExact(long value)

value narrowed to an int, or ArithmeticException: integer overflow when it does not fit — the checked counterpart of the (int) cast, which wraps silently.

System.out.println(Math.toIntExact(5L));   // 5

# Math.clamp

int Math.clamp(long v, int min, int max)   |   long   |   double   |   float

v confined to [min, max]. The **bounds** pick the overload, so clamp(aLong, 1, 10) answers an int and clamp(aLong, 1L, 10L) a long. min > max is IllegalArgumentException: "<min> > <max>", a NaN bound is min is NaN / max is NaN, and a NaN value passes through.

System.out.println(Math.clamp(15, 1, 10));   // 10

# Integer.parseInt

int Integer.parseInt(String s)

Parses a signed decimal integer with java.lang.Integer's exact rules: no surrounding whitespace is tolerated and the value must fit 32 bits. Every failure raises NumberFormatException carrying Java's own For input string: "…" message.

System.out.println(Integer.parseInt("42"));   // 42

# Integer.parseInt

int Integer.parseInt(String s, int radix)

As above in the given radix, which must lie between Character.MIN_RADIX and Character.MAX_RADIX. A bad radix and a bad digit string produce Java's two distinct NumberFormatException messages.

System.out.println(Integer.parseInt("ff", 16));   // 255

# Integer.valueOf

int Integer.valueOf(String s)   |   int Integer.valueOf(int i)

Parses a decimal string with parseInt's rules, or returns an integral argument unchanged. javars does no boxing, so there is no Integer object and no identity cache to observe.

System.out.println(Integer.valueOf("7"));   // 7

# Integer.toString

String Integer.toString(int i)

The decimal rendering of an integer.

System.out.println(Integer.toString(255));   // 255

# Integer.toString

String Integer.toString(int i, int radix)

The rendering in radix 2 through 36, sign-prefixed for negatives. An out-of-range radix falls back to 10, exactly as Java specifies.

System.out.println(Integer.toString(255, 16));   // ff

# Long.parseLong

long Long.parseLong(String s)

Parses a signed decimal long, using the same rules and the same NumberFormatException messages as parseInt but with the 64-bit width check.

System.out.println(Long.parseLong("9000000000"));   // 9000000000

# Boolean.parseBoolean

boolean Boolean.parseBoolean(String s)

True when the string equals "true" ignoring ASCII case, false for everything else — including null and malformed input, which Java also never rejects here.

System.out.println(Boolean.parseBoolean("TRUE"));   // true

# String.valueOf

String String.valueOf(Object x)

Renders any value with Java's println rules: true/false, a whole double with its trailing .0, null for a null reference, and ClassName@hash for an instance whose class declares no toString().

System.out.println(String.valueOf(3.0));   // 3.0

# String.copyValueOf

String String.copyValueOf(char[] data)

The characters of data as a String. The JDK implements it as one call to String.valueOf(char[]), and so does javars.

System.out.println(String.copyValueOf(new char[]{'a', 'b'}));   // ab

# StringBuilder

new StringBuilder()   |   new StringBuilder(int capacity)   |   new StringBuilder(String s)

The mutable character sequence, and StringBuffer alongside it (javars runs one thread, so the two differ only in their class name). append/insert/delete/deleteCharAt/replace/reverse/setCharAt/setLength/charAt/substring/indexOf/lastIndexOf/length/isEmpty/capacity/compareTo/toString are implemented, the mutators answering the receiver so a chain keeps building. A builder is a reference: passing one to a method or storing it in a collection denotes the one object, equals stays Object's identity comparison, and toString — and therefore println(sb), "" + sb and %s — is the contents. capacity() reproduces the JDK's growth curve (16 to start, then 2 * old + 2), because it is observable.

StringBuilder b = new StringBuilder();
for (int i = 0; i < 3; i++) { b.append(i).append(','); }
System.out.println(b);   // 0,1,2,

# String.format

String String.format(String fmt, Object... args)

printf-style formatting — a faithful subset of java.util.Formatter covering the conversions d s S f e E g G b B h H x X o c % and %n, all seven flags - # + 0 , (, an optional width, an optional .precision, and explicit argument indexes (%2$s). %f rounds HALF_UP on the double's exact value, as Java does. System.out.printf is the same formatter with no trailing newline. An unsupported conversion is reported rather than rendered wrong.

System.out.println(String.format("%05.2f|%-6s|%+d", 3.14159, "ab", 7));   // 03.14|ab    |+7

# Arrays.toString

String Arrays.toString(Object[] a)

The shallow [e0, e1, …] rendering of an array, each element through the same Java value-to-string rules. A null reference renders as null; a nested array renders as its handle, since the rendering is shallow.

System.out.println(Arrays.toString(new int[]{1, 2, 3}));   // [1, 2, 3]

Collection Types

# ArrayList

new ArrayList<>()   |   new ArrayList<>(other)

A mutable List, stored as a vector of values in list order. The copy constructor seeds it from any sequence-shaped argument — an array, a List, or a Set in that set's presentation order.

List<String> xs = new ArrayList<>();
xs.add("a");

# LinkedList

new LinkedList<>()   |   new LinkedList<>(other)

Constructible, and backed by the same vector ArrayList uses. javars models the List contract, not the node-per-element representation, so the two differ only in name; the Deque/Queue methods are not implemented.

List<Integer> q = new LinkedList<>();

# HashMap

new HashMap<>()   |   new HashMap<>(other)

A Map whose iteration and toString order reproduce Java's real bucket order: entries are laid out in a power-of-two table indexed by (capacity - 1) & (h ^ (h >>> 16)), which makes the order a stable sort of the insertion sequence by bucket. Verified against OpenJDK 26 for String and Integer keys, including across the resize at 13 entries.

Map<String,Integer> m = new HashMap<>();
m.put("one", 1);

# LinkedHashMap

new LinkedHashMap<>()   |   new LinkedHashMap<>(other)

A Map that iterates in insertion order. Since javars always *stores* entries in insertion order, this implementation is the one that costs nothing extra.

Map<String,Integer> m = new LinkedHashMap<>();

# TreeMap

new TreeMap<>()   |   new TreeMap<>(other)

A Map that iterates in ascending natural key order: numbers numerically, strings lexicographically by char, null first. The navigation methods (firstKey, headMap, …) are not implemented.

Map<String,Integer> m = new TreeMap<>();

# HashSet

new HashSet<>()   |   new HashSet<>(other)

A Set with HashMap's bucket iteration order. Building one from a sequence keeps the first of each repeated value.

Set<String> s = new HashSet<>();
s.add("a");

# LinkedHashSet

new LinkedHashSet<>()   |   new LinkedHashSet<>(other)

A Set that iterates in insertion order.

Set<String> s = new LinkedHashSet<>();

# TreeSet

new TreeSet<>()   |   new TreeSet<>(other)

A Set that iterates in ascending natural order of its elements.

Set<String> s = new TreeSet<>();
s.add("z"); s.add("a");
System.out.println(s);   // [a, z]

# List

List<T> name = …;   |   List.of(…)

Declaration-only: the interface types the variable but cannot be instantiated — new List<>() is not a thing in Java either. A user class named List wins over this modeling, so declaring your own is still legal.

List<String> xs = new ArrayList<>();

# Collection

Collection<T> name = …;

Declaration-only, treated as a list-shaped receiver for the purpose of typing a call's result.

Collection<String> c = new ArrayList<>();

# Iterable

Iterable<T> name = …;

Declaration-only, list-shaped. An enhanced for over any collection works through the JITER_ARRAY builtin regardless of the declared type; there is no Iterator object to obtain.

Iterable<String> it = new ArrayList<>();

# Map

Map<K,V> name = …;

Declaration-only. The map methods available on the variable are the ones the modeled implementations provide; entrySet is not among them.

Map<String,Integer> m = new HashMap<>();

# Set

Set<T> name = …;   |   Set.of(…)

Declaration-only as a type, though Set.of is a usable static factory.

Set<String> s = new HashSet<>();

Collection Statics

# Arrays.asList

List<T> Arrays.asList(T... a)

A *fixed-size* list view: set replaces an element, but add, remove, and clear raise UnsupportedOperationException exactly as Java's does. A lone array argument spreads into its elements rather than becoming a one-element list, matching Java's varargs rule for reference arrays.

List<Integer> xs = Arrays.asList(1, 2, 3);
xs.set(0, 9);   // fine
// xs.add(4);   // UnsupportedOperationException

# List.of

List<T> List.of(T... elements)

A fully immutable list: every structural change *and* every element replacement raises UnsupportedOperationException.

System.out.println(List.of(1, 2, 3));   // [1, 2, 3]

# Set.of

Set<T> Set.of(T... elements)

An immutable set with HashSet iteration order, keeping the first of each repeated element.

System.out.println(Set.of("p"));   // [p]

# Collections.sort

void Collections.sort(List<T> list)

Sorts the list in place into ascending natural order, so the change is visible through every reference to it. Mixed value kinds compare as equal, which keeps the sort stable rather than panicking.

Collections.sort(xs);

# Collections.sort

void Collections.sort(List<T> list, Comparator<T> cmp)

Sorts in place with a comparator lambda. A user comparator may not define a total order, so javars uses a stable insertion sort — which cannot panic on an inconsistent comparator the way a merge sort can — and matches Java's guarantee that List.sort is stable. A null comparator means natural order.

Collections.sort(xs, (a, b) -> a.length() - b.length());

# Collections.reverse

void Collections.reverse(List<T> list)

Reverses the list in place.

Collections.reverse(xs);

# Collections.max

T Collections.max(Collection<T> c)

The largest element in ascending natural order. An empty collection raises NoSuchElementException — which is not one of the modeled java.lang throwables, so it cannot be named in a catch clause and reaches the uncaught report unqualified.

System.out.println(Collections.max(xs));

# Collections.min

T Collections.min(Collection<T> c)

The smallest element in ascending natural order; empty raises the same uncatchable NoSuchElementException as Collections.max.

System.out.println(Collections.min(xs));

List Methods

# size

int size()

The number of elements.

System.out.println(xs.size());

# isEmpty

boolean isEmpty()

True when the list holds no elements.

if (xs.isEmpty()) { System.out.println("none"); }

# add

boolean add(T e)

Appends an element and returns true. On a fixed-size or immutable list this raises UnsupportedOperationException rather than succeeding silently.

xs.add("c");

# add

void add(int index, T e)

Inserts at index, shifting the tail right. An index outside [0, size] raises IndexOutOfBoundsException with Java's Index: i, Size: n message — the distinct wording this overload uses.

xs.add(0, "first");

# get

T get(int index)

The element at index; out of range raises IndexOutOfBoundsException carrying Index i out of bounds for length n.

System.out.println(xs.get(0));

# set

T set(int index, T e)

Replaces the element at index and returns the previous value. Permitted on a fixed-size list (that is what Arrays.asList gives you) and refused on an immutable one.

xs.set(0, "z");

# remove

T remove(int index) | boolean remove(Object o)

Both of Java's overloads, chosen at compile time from the argument's *static* type exactly as javac chooses them: an int/short/byte/char argument removes and returns the element at that index, and any reference argument — a boxed Integer, an Integer.valueOf(x), a String — removes the first element equal to it and answers whether one was found. An argument javars cannot type statically keeps the by-index reading.

xs.remove(0);                    // by index
xs.remove(Integer.valueOf(20));  // by value

# subList

List<T> subList(int fromIndex, int toIndex)

A **view** of the half-open range, not a copy: it owns no elements, so writes cross in both directions — list.set(i, v) shows through the view and view.set(i, v) shows in the list — and view.add/remove/clear splice the backing list itself. A view of a view composes offsets down to the same list. Structurally modifying the backing list *behind* an outstanding view invalidates it, and the next operation on it (or rendering it) raises ConcurrentModificationException. fromIndex < 0 or toIndex > size() is an IndexOutOfBoundsException naming the offending index; fromIndex > toIndex is an IllegalArgumentException.

List<Integer> l = new ArrayList<>(List.of(10, 20, 30, 40));
List<Integer> v = l.subList(1, 3);
v.set(0, 99);
System.out.println(l);   // [10, 99, 30, 40]

# clear

void clear()

Removes every element. Structural, so it is refused on a fixed-size or immutable list.

xs.clear();

# contains

boolean contains(Object o)

True when some element equals o by value for strings, numbers, and booleans, or by handle identity for a heap object. A user equals override is not consulted.

System.out.println(xs.contains("a"));

# indexOf

int indexOf(Object o)

The index of the first matching element, or -1.

System.out.println(xs.indexOf("a"));

# lastIndexOf

int lastIndexOf(Object o)

The index of the last matching element, or -1.

System.out.println(xs.lastIndexOf("a"));

# addAll

boolean addAll(Collection<T> other)

Appends every element of other in its presentation order and returns whether anything was added. The argument is snapshotted before the receiver's heap borrow is taken, so xs.addAll(xs) is well defined.

xs.addAll(ys);

# equals

boolean equals(Object other)

Element-wise equality against another sequence of the same length.

System.out.println(xs.equals(ys));

# sort

void sort(Comparator<T> cmp)

Sorts in place with a comparator lambda, or in natural order when given null. Handled before the heap borrow is taken, because the comparator body is user code that may allocate.

xs.sort((a, b) -> a.length() - b.length());

# forEach

void forEach(Consumer<T> action)

Invokes the action once per element, in order. Like sort, the elements are snapshotted first so the lambda body can safely allocate.

xs.forEach(x -> System.out.println(x));

# toString

String toString()

AbstractCollection.toString[a, b, c], each element rendered with Java's value-to-string rules. This is also what System.out.println(list) prints, without any explicit call.

System.out.println(xs);   // [a, b, c]

Map Methods

# size

int size()

The number of entries.

System.out.println(m.size());

# isEmpty

boolean isEmpty()

True when the map holds no entries.

if (m.isEmpty()) { System.out.println("empty"); }

# put

V put(K key, V value)

Associates key with value and returns the previous value, or null when the key was absent. Re-putting an existing key keeps the entry's original insertion position, which is what Java's linked and bucket layouts both do.

m.put("one", 1);

# putIfAbsent

V putIfAbsent(K key, V value)

Inserts only when the key is absent; returns the existing value when it was present, null otherwise.

m.putIfAbsent("one", 99);

# get

V get(Object key)

The value for key, or null when absent. Keys are matched with the same value-or-identity equality the rest of the collections use.

System.out.println(m.get("one"));

# getOrDefault

V getOrDefault(Object key, V fallback)

The value for key, or fallback when the key is absent.

System.out.println(m.getOrDefault("nope", 0));   // 0

# containsKey

boolean containsKey(Object key)

True when the map holds an entry for key.

System.out.println(m.containsKey("one"));

# containsValue

boolean containsValue(Object value)

True when some entry's value matches — a linear scan, as in Java.

System.out.println(m.containsValue(1));

# remove

V remove(Object key)

Removes the entry and returns its value, or null when the key was absent.

m.remove("one");

# clear

void clear()

Removes every entry.

m.clear();

# keySet

Set<K> keySet()

A Set of the keys, already materialized in the map's own presentation order — so it iterates and prints exactly as the map does. It is a snapshot, not a live view: removing from it does not change the map.

System.out.println(m.keySet());

# values

Collection<V> values()

A fixed-size list of the values, in the map's presentation order and therefore aligned position-for-position with keySet(). Also a snapshot.

System.out.println(m.values());

# forEach

void forEach(BiConsumer<K,V> action)

Invokes a two-argument action per entry, in presentation order. The map's forEach is distinguished from a list's by the receiver, so the same lambda arity rules as Java apply.

m.forEach((k, v) -> System.out.println(k + "=" + v));

# toString

String toString()

AbstractMap.toString{k=v, k=v} in presentation order, which for a HashMap is Java's real bucket order.

System.out.println(m);   // {one=1, two=2, three=3}

Set Methods

# size

int size()

The number of distinct elements.

System.out.println(s.size());

# isEmpty

boolean isEmpty()

True when the set holds no elements.

if (s.isEmpty()) { System.out.println("empty"); }

# add

boolean add(T e)

Adds the element if it is not already present, returning whether the set changed.

System.out.println(s.add("a"));   // true the first time

# contains

boolean contains(Object o)

Membership test, by value for strings, numbers, and booleans and by handle identity for a heap object.

System.out.println(s.contains("a"));

# remove

boolean remove(Object o)

Removes the element if present, returning whether the set changed. Unlike List.remove, this overload is by value rather than by index.

s.remove("a");

# clear

void clear()

Removes every element.

s.clear();

# addAll

boolean addAll(Collection<T> other)

Adds every element of other that is not already present, returning whether the set changed.

s.addAll(other);

# forEach

void forEach(Consumer<T> action)

Invokes the action once per element, in the set's presentation order.

s.forEach(x -> System.out.println(x));

# toString

String toString()

[a, b, c] in presentation order — bucket order for a HashSet, insertion order for a LinkedHashSet, ascending for a TreeSet.

System.out.println(s);

Throwables

# Throwable

class Throwable { Throwable(); Throwable(String message); String getMessage(); String toString(); }

The root of the modeled hierarchy and the only class that carries state: a detailMessage field the whole tree inherits, its accessor, and a toString() that prints java.lang.Throwable alone or with ": " + detailMessage. The no-argument constructor leaves the message at its field default, which is exactly the null Java's getMessage() returns.

throw new Throwable("root cause");

# Error

class Error extends Throwable

Java's unchecked-error branch. javars never raises one itself; it exists so a program that throws or catches an Error compiles and runs.

catch (Error e) { System.out.println(e); }

# Exception

class Exception extends Throwable

The base of the checked branch, and the arm that catches everything javars raises. catch (Exception e) matching a NumberFormatException is just the instanceof supertype walk the runtime already performs.

try { risky(); } catch (Exception e) { System.out.println(e.getMessage()); }

# RuntimeException

class RuntimeException extends Exception

The unchecked branch, and the superclass of every fault the runtime raises.

throw new RuntimeException("boom");

# IllegalArgumentException

class IllegalArgumentException extends RuntimeException

Raised by String.repeat with a negative count, carrying Java's count is negative: n message. Also throwable directly.

throw new IllegalArgumentException("bad input");

# NumberFormatException

class NumberFormatException extends IllegalArgumentException

Raised by Integer.parseInt, Integer.valueOf, and Long.parseLong for a malformed string, an out-of-width value, or an out-of-range radix. The detail message is Java's own — For input string: "zz", and the radix-qualified variant for a non-decimal radix.

try { Integer.parseInt("zz"); } catch (NumberFormatException e) { System.out.println(e.getMessage()); }

# IllegalStateException

class IllegalStateException extends RuntimeException

Never raised by the runtime; supplied so program code can throw and catch it.

throw new IllegalStateException("bad state");

# ArithmeticException

class ArithmeticException extends RuntimeException

Raised by integral / and % with a zero divisor, carrying Java's message / by zero for both operators. The check is emitted inline by the compiler rather than by a builtin, so it costs one compare and one branch and stays JIT-traceable. Floating division never raises it — IEEE-754 gives an infinity instead.

try { int z = 0; System.out.println(1 / z); } catch (ArithmeticException e) { System.out.println(e.getMessage()); }   // / by zero

# NullPointerException

class NullPointerException extends RuntimeException

Raised on every null dereference javars models: an array load, store, or .length; an instance field read or assignment; a method call on a null receiver; iterating a null; and invoking a null functional-interface target. Java's "helpful" messages name the bytecode local slot of the null reference (because "<local3>" is null), which javars has no javac slot numbering to reproduce, so it keeps the operation half of Java's wording and drops the provenance clause.

String s = null;
// s.length();   // Cannot invoke "String.length()" because the receiver is null

# ClassCastException

class ClassCastException extends RuntimeException

Raised by a *checked* reference cast whose target the value's runtime class does not satisfy, with Java's own detail message. The cast changes no representation — the host heap already carries each object's class — so verifying one is all it does. Primitive casts are real conversions and cannot fail.

catch (ClassCastException e) { System.out.println(e); }

# UnsupportedOperationException

class UnsupportedOperationException extends RuntimeException

Raised by a structural change to a fixed-size list (Arrays.asList) or by any change at all to an immutable one (List.of). One divergence to know: javars constructs it with an empty string where Java leaves the message null, so getMessage() returns "" instead of null and toString() prints a trailing ": " that Java omits.

List<Integer> xs = List.of(1, 2);
try { xs.add(3); } catch (UnsupportedOperationException e) { System.out.println("[" + e.getMessage() + "]"); }   // []

# NegativeArraySizeException

class NegativeArraySizeException extends RuntimeException

Raised by new T[n] and by the multi-dimensional form when any dimension is negative. The detail message is the offending size, as in Java.

try { int[] a = new int[-1]; } catch (NegativeArraySizeException e) { System.out.println(e.getMessage()); }   // -1

# IndexOutOfBoundsException

class IndexOutOfBoundsException extends RuntimeException

Raised by the List accessors — get, set, remove, and the two-argument add. The first three carry Index i out of bounds for length n; add(index, e) carries Java's distinct Index: i, Size: n wording.

try { xs.get(99); } catch (IndexOutOfBoundsException e) { System.out.println(e.getMessage()); }

# ArrayIndexOutOfBoundsException

class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException

Raised by an out-of-range array read or write, carrying Java's exact Index i out of bounds for length n. The bounds check runs inside the heap borrow and the throwable is allocated after it is released, since raising allocates on the same heap.

int[] a = new int[2];
try { System.out.println(a[5]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println(e.getMessage()); }

# StringIndexOutOfBoundsException

class StringIndexOutOfBoundsException extends IndexOutOfBoundsException

Raised by charAt and both substring overloads. charAt carries Index i out of bounds for length n; substring carries Range [b, e) out of bounds for length n.

try { "hi".charAt(9); } catch (StringIndexOutOfBoundsException e) { System.out.println(e.getMessage()); }

# ConcurrentModificationException

class ConcurrentModificationException extends RuntimeException

Raised by a List.subList view whose backing list was structurally modified behind it — an add, a remove, or a Collections.sort (which bumps Java's modCount even though the length is unchanged). One of the modeled throwables outside java.lang, and prints as java.util.ConcurrentModificationException.

List<Integer> l = new ArrayList<>(List.of(1, 2, 3));
List<Integer> v = l.subList(0, 2);
l.add(4);
// v.get(0);   // ConcurrentModificationException

# NoSuchElementException

class NoSuchElementException extends RuntimeException

Raised by Collections.max/min on an empty collection. Java's carries no detail message there, and neither does this one; it prints as java.util.NoSuchElementException.

try { Collections.max(new ArrayList<>()); } catch (NoSuchElementException e) { System.out.println(e); }

# IllegalFormatException

class IllegalFormatException extends IllegalArgumentException

The base of the java.util.Formatter failures. It is not thrown directly; catching it catches the conversion mismatch below.

try { String.format("%d", 1.5); } catch (IllegalFormatException e) { System.out.println("bad format"); }

# IllegalFormatConversionException

class IllegalFormatConversionException extends IllegalFormatException

Raised when a String.format/printf conversion does not accept its argument's boxed type — %d of a Double, %f of an Integer, %c of a String. The detail message is Java's <conversion> != <class>.

try { System.out.println(String.format("%.2f", 3)); } catch (IllegalFormatConversionException e) { System.out.println(e.getMessage()); }   // f != java.lang.Integer

Functional Interfaces

# Runnable

interface Runnable { void run(); }

A no-argument, no-result action. javars runs one thread, so a Runnable is only ever invoked directly — there is no Thread to hand it to.

Runnable r = () -> System.out.println("ran");
r.run();

# Callable

interface Callable { Object call(); }

A no-argument action that produces a result. Java's declares throws Exception; javars performs no checked-exception analysis, so the clause is omitted with no behavioural difference.

Callable c = () -> 42;
System.out.println(c.call());

# Supplier

interface Supplier { Object get(); }

A source of values. The return type is the erasure Java itself uses, which is why a Supplier result printed directly renders with the default ClassName@hash form rather than through a user toString() override — the compiler has no static type to dispatch on.

Supplier s = () -> "made";
System.out.println(s.get());

# Consumer

interface Consumer { void accept(Object t); default Consumer andThen(Consumer after); }

A one-argument action with no result — the type List.forEach and Set.forEach expect. andThen runs both actions on the same argument, in order.

Consumer c = x -> System.out.println(x);
xs.forEach(c);

# BiConsumer

interface BiConsumer { void accept(Object t, Object u); default BiConsumer andThen(BiConsumer after); }

A two-argument action with no result — the type Map.forEach expects, receiving each key and value. andThen runs both actions on the same pair, in order.

m.forEach((k, v) -> System.out.println(k + "=" + v));

# Function

interface Function { Object apply(Object t); default Function andThen(Function after); default Function compose(Function before); static Function identity(); }

A one-argument transformation. f.andThen(g) applies f first, f.compose(g) applies g first, and Function.identity() returns its argument — the JDK's own bodies, so they compose lambdas without any builtin.

Function f = s -> s + "!";
System.out.println(f.apply("hi"));   // hi!

# BiFunction

interface BiFunction { Object apply(Object t, Object u); default BiFunction andThen(Function after); }

A two-argument transformation. andThen feeds its result through a one-argument Function, which is why that parameter is a Function rather than a BiFunction.

BiFunction add = (a, b) -> a + "/" + b;
System.out.println(add.apply(1, 2));   // 1/2

# UnaryOperator

interface UnaryOperator { Object apply(Object t); static UnaryOperator identity(); }

A transformation whose argument and result are the same type. Erasure makes its abstract method identical to Function's; the distinct name is what a target type is written as.

UnaryOperator u = s -> s + s;
System.out.println(u.apply("ab"));   // abab

# BinaryOperator

interface BinaryOperator { Object apply(Object t, Object u); static BinaryOperator minBy(Comparator c); static BinaryOperator maxBy(Comparator c); }

A two-argument operator over one type, erased to the same shape as BiFunction. minBy/maxBy turn an ordering into the operator that keeps the smaller/larger of its two arguments.

BinaryOperator pick = (a, b) -> a;
System.out.println(pick.apply("x", "y"));   // x

# Predicate

interface Predicate { boolean test(Object t); default Predicate and(Predicate other); default Predicate negate(); default Predicate or(Predicate other); static Predicate not(Predicate target); }

A one-argument boolean test. The return type stays primitive rather than erased, so a call participates in javars's numeric typing instead of being an opaque Object. and/or short-circuit exactly as the JDK's bodies do.

Predicate p = s -> s.equals("yes");
System.out.println(p.test("yes"));   // true

# BiPredicate

interface BiPredicate { boolean test(Object t, Object u); default BiPredicate and(BiPredicate other); default BiPredicate negate(); default BiPredicate or(BiPredicate other); }

A two-argument boolean test, with the same three short-circuiting combinators Predicate carries.

BiPredicate eq = (a, b) -> a.equals(b);
System.out.println(eq.test("a", "a"));   // true

# Comparator

interface Comparator { int compare(Object a, Object b); default Comparator reversed(); default Comparator thenComparing(Comparator other); static Comparator naturalOrder(); static Comparator reverseOrder(); static Comparator comparing(Function key); }

An ordering, and the type List.sort and the two-argument Collections.sort accept. The int return is kept rather than erased, because the sort reads its sign. The three statics order elements by their own compareTo, which reaches a user Comparable's body through the receiver's runtime class. A sort naming no comparator uses naturalOrder().

xs.sort(Comparator.comparing(P::name).reversed());

# IntSupplier

interface IntSupplier { int getAsInt(); }

A source of int values — the primitive specialization, whose result stays numerically typed.

IntSupplier s = () -> 7;
System.out.println(s.getAsInt() + 1);   // 8

# IntPredicate

interface IntPredicate { boolean test(int value); default IntPredicate and(IntPredicate other); default IntPredicate negate(); default IntPredicate or(IntPredicate other); }

An int-argument boolean test, with the primitive-specialized forms of Predicate's three combinators.

IntPredicate even = n -> n % 2 == 0;
System.out.println(even.test(4));   // true

# IntConsumer

interface IntConsumer { void accept(int value); default IntConsumer andThen(IntConsumer after); }

An int-argument action with no result. andThen runs both actions on the same value, in order.

IntConsumer show = n -> System.out.println(n);
show.accept(3);

# IntUnaryOperator

interface IntUnaryOperator { int applyAsInt(int operand); default IntUnaryOperator compose(IntUnaryOperator before); default IntUnaryOperator andThen(IntUnaryOperator after); static IntUnaryOperator identity(); }

An int to int transformation; both sides stay primitive, so the 32-bit wrap applies to arithmetic in the body. Composes in both directions, and identity() returns its argument.

IntUnaryOperator sq = n -> n * n;
System.out.println(sq.applyAsInt(5));   // 25

# IntBinaryOperator

interface IntBinaryOperator { int applyAsInt(int left, int right); }

A two-int to int transformation.

IntBinaryOperator add = (a, b) -> a + b;
System.out.println(add.applyAsInt(2, 3));   // 5

# ToIntFunction

interface ToIntFunction { int applyAsInt(Object value); }

A reference-to-int transformation, keeping the primitive result type.

ToIntFunction len = s -> s.length();
System.out.println(len.applyAsInt("abcd"));   // 4

# ToDoubleFunction

interface ToDoubleFunction { double applyAsDouble(Object value); }

A reference-to-double transformation.

ToDoubleFunction half = s -> s.length() / 2.0;
System.out.println(half.applyAsDouble("abc"));   // 1.5

Class Members

# main

public static void main(String[] args)   |   public static void main(String... args)

The entry point. javars scans the compilation unit for a class declaring it and runs that one; a unit with none is rejected before any code is emitted. Both the array and the varargs spelling are accepted.

public class Main { public static void main(String[] args) { System.out.println("hi"); } }

# args

String[] args

main's parameter, bound by a prologue call to the JARGV builtin to a fresh array of whatever the CLI collected after the file name. Because it is freshly allocated, mutating it inside main cannot affect anything else.

System.out.println(args.length);
for (String a : args) { System.out.println(a); }

# instance field

[modifiers] T name [= initializer];

Per-instance state, held in the instance's field map on the host heap. Every field in the class chain is seeded with its type's default when the object is allocated. The *initializers* run later, inside each constructor, at the point JLS 12.5 fixes: after the superclass constructor and before the constructor body. So a field initializer may read an inherited field the superclass constructor just assigned, and a superclass constructor that calls an overridden method sees the subclass's fields at their defaults.

class Point { int x; int y = 5; }

# constructor

[modifiers] ClassName(params) { … }

Initializes a freshly allocated instance, in the three steps JLS 12.5 fixes: the superclass constructor, then this class's field initializers and { … } blocks in textual order, then the body. Constructors overload on parameter type, resolved by the same lowest-assignment-cost rule methods use. A class that declares none gets the implicit no-argument form, whose body is exactly super() followed by those initializers. Opening the body with this(...) delegates instead, and the delegate runs both steps, so they never run twice.

class Point { int x; Point(int x) { this.x = x; } }

# implicit super()

class Sub extends Base { /* no declared constructor */ }

JLS 8.8.7: a constructor body that does not open with an explicit this(...) or super(...) runs an implicit super() first, and a class that declares no constructor at all gets a default one that does exactly that. Both are emitted, so a parent's constructor body runs whether or not the subclass wrote one, and an intermediate class that declares no constructor still contributes its own { … } blocks at its point in the chain.

class Animal { String sound; Animal() { sound = "generic"; } }
class Dog extends Animal { }
System.out.println(new Dog().sound);   // generic

# static field

static T name [= initializer];

Class-level state, compiled to a chunk global seeded with the type default. Reads resolve through the owning class, so both C.n and a bare n inside C reach the same global.

class Counter { static int n = 0; }
System.out.println(Counter.n);

# static initializer

static { … }

A class-initialization block. Blocks and static field initializers run in textual order, eagerly, before main — javars initializes every class up front rather than lazily on first use.

class Counter { static int n; static { n = 10; } }

# toString

String toString()

The rendering used by println, String.valueOf, and string concatenation. A declared override is dispatched by the compiler *before* the value reaches the host formatter; a class that declares none falls back to ClassName@hash, where the hash is the heap handle (deterministic within a run, unlike a JVM identity hash).

class Box { public String toString() { return "Box!"; } }
System.out.println(new Box());   // Box!

# equals

boolean equals(Object other)

Derived automatically for a record (component-wise, guarded by an instanceof test) and for an enum (identity, since constants are singletons). A class that declares its own keeps it. The collections do *not* consult an override — their membership tests use value equality for scalars and handle identity for objects.

record Point(int x, int y) { }
System.out.println(new Point(1,2).equals(new Point(1,2)));   // true

# getMessage

String getMessage()

The Throwable accessor for the detail message, declared once on the root and inherited by every modeled throwable. It returns the field's default — a null reference — when the no-argument constructor was used.

catch (NumberFormatException e) { System.out.println(e.getMessage()); }

# name

String name()

An enum constant's declared name, read from a field the parser synthesizes. A user-declared name() wins over the derived one.

System.out.println(Color.RED.name());   // RED

# ordinal

int ordinal()

An enum constant's zero-based declaration position, from a synthesized field.

System.out.println(Color.RED.ordinal());   // 0

# values

E[] EnumType.values()

The static factory returning a fresh array of the enum's constants in declaration order — fresh on every call, exactly as Java hands out a copy so the caller cannot corrupt the enum.

System.out.println(Arrays.toString(Color.values()));   // [RED, GREEN, BLUE]

# valueOf

E EnumType.valueOf(String name)

The static lookup by constant name, compiled to a chain of string comparisons. No match raises IllegalArgumentException with Java's No enum constant Type.NAME message.

System.out.println(Color.valueOf("BLUE"));   // BLUE

# record accessor

T componentName()

One derived accessor per record component, named for the component and returning its field. Declaring one by hand suppresses the derived version.

record Point(int x, int y) { }
System.out.println(new Point(1, 2).x());   // 1

# close

void close()

What try-with-resources calls. The parser desugars the resource list into a finally that closes each resource in reverse declaration order; there is no AutoCloseable interface to implement, so any class declaring close() qualifies.

class Res { public void close() { System.out.println("closed"); } }
try (Res r = new Res()) { }   // prints closed

# length

int array.length

An array's element count — a field read, not a method call, routed through JFIELD_GET. Reading it on a null reference raises Java's array-length NullPointerException.

int[] a = {1, 2, 3};
System.out.println(a.length);   // 3

Method References

# Type::new

Supplier s = ClassName::new;

A constructor reference. The arity comes from the class's single declared constructor (or zero when it declares none); a class with two or more constructors is rejected as ambiguous, because javars does not target-type a reference to pick an arity.

Supplier make = Box::new;
System.out.println(make.get());

# Type::instanceMethod

Function f = ClassName::method;

An unbound instance reference: the synthesized lambda takes the receiver as its first parameter, then the method's own. The name must resolve to exactly one method of the class, otherwise it is rejected rather than guessed.

Function area = Sq::area;
System.out.println(area.apply(new Sq()));

# Type::staticMethod

Function f = ClassName::staticMethod;

A reference to a user class's static method, with the method's own arity.

class Helper { static int twice(int n) { return n * 2; } }
Function t = Helper::twice;

# Class::stdlibStatic

Function f = Integer::parseInt;

A reference to a modeled stdlib static. Only the single-arity entries are nameable — Integer.parseInt, Long.parseLong, Boolean.parseBoolean, Math.sqrt/floor/ceil/round, Arrays.toString, and the two-argument Math.max/min/pow. Names Java overloads on arity (Integer::toString, String::valueOf) are refused, because a reference has no arity until it is target-typed.

Function pi = Integer::parseInt;
System.out.println(pi.apply("21"));   // 21

# String::instanceMethod

Function f = String::toUpperCase;

An unbound reference to a java.lang.String method, taking the receiver as its first parameter. substring and indexOf are excluded for the same arity-ambiguity reason.

Function up = String::toUpperCase;
System.out.println(up.apply("abc"));   // ABC

# value::method

Supplier s = variable::method;   |   Supplier s = this::method;

A bound reference. The receiver must be a plain name or this so the synthesized lambda captures it by value — which is exactly Java's rule that the receiver expression is evaluated once, at the reference.

Box b = new Box();
Supplier bound = b::label;
System.out.println(bound.get());

# System.out::println

Consumer c = System.out::println;   |   Consumer c = System.err::print;

A reference to a console stream's print/println. It is special-cased because printing lowers to a builtin rather than to a dispatchable method; any other member name after System.out:: is refused.

Consumer show = System.out::println;
xs.forEach(show);

Format Conversions

# %d

String.format("%d", int)

Decimal integer. Numeric, so a 0 flag zero-pads it after any leading sign.

System.out.println(String.format("%d", 42));   // 42

# %f

String.format("%f", double)

Fixed-point decimal with six fraction digits by default, or as many as .precision names.

System.out.println(String.format("%.2f", 3.14159));   // 3.14

# %s

String.format("%s", Object)

The argument's Java string form — true, 3.0, null, or a class's toString() rendering. A .precision truncates it to that many characters.

System.out.println(String.format("%.2s", "abcdef"));   // ab

# %S

String.format("%S", Object)

As %s, uppercased.

System.out.println(String.format("%S", "abc"));   // ABC

# %b

String.format("%b", Object)

Java's boolean conversion: the value itself for a boolean, false for a null reference, and true for any other non-null value.

System.out.println(String.format("%b %b %b", true, null, "x"));   // true false true

# %B

String.format("%B", Object)

As %b, uppercased.

System.out.println(String.format("%B", true));   // TRUE

# %x

String.format("%x", int)

Lowercase hexadecimal. javars formats the value at 64-bit width, so a negative number renders with sixteen digits where Java's int conversion gives eight — the one place this conversion diverges.

System.out.println(String.format("%x", 255));   // ff

# %X

String.format("%X", int)

Uppercase hexadecimal, with the same 64-bit width note as %x.

System.out.println(String.format("%X", 255));   // FF

# %o

String.format("%o", int)

Octal.

System.out.println(String.format("%o", 8));   // 10

# %c

String.format("%c", char)

The argument's string form. Because javars models a char as a one-character string, a genuine char argument renders correctly — but an *integer* code point renders as its digits rather than as the character Java would produce.

System.out.println(String.format("%c", 'A'));   // A

# %%

String.format("%%")

A literal percent sign; consumes no argument.

System.out.println(String.format("100%%"));   // 100%

# %n

String.format("%n")

A line separator, always emitted as \n rather than the platform's separator, and consuming no argument.

System.out.print(String.format("line%n"));

# flag -

String.format("%-8s", x)

Left-justify within the field width, padding on the right with spaces.

System.out.println(String.format("[%-6s]", "ab"));   // [ab    ]

# flag 0

String.format("%08d", x)

Zero-pad to the field width. Applies only to the numeric conversions, and pads after a leading - or + so the sign stays leftmost.

System.out.println(String.format("%05.2f", 3.14159));   // 03.14

# flag +

String.format("%+d", x)

Always show a sign: a + on non-negative numbers, with the existing - left alone.

System.out.println(String.format("%+d", 7));   // +7

# width

String.format("%12s", x)

Minimum field width in characters. A value already at or beyond the width is never truncated by it — only .precision shortens.

System.out.println(String.format("[%8.3f]", 3.14159));   // [   3.142]

# .precision

String.format("%.3f", x)   |   String.format("%.2s", x)

Fraction digits for %f, or a maximum character count for %s/%S.

System.out.println(String.format("%.3f", 2.0));   // 2.000

# ignored flags

String.format("%,d", x)   |   %#x   |   %(d   |   % d

The grouping, alternate-form, parenthesized-negative, and leading-space flags are accepted and have no effect, so a format string carrying them formats without error but without their decoration. Any conversion character javars does not model is reported as an error rather than rendered wrong.

System.out.println(String.format("%,d", 1234));   // 1234  (Java prints 1,234)

Runtime Builtins

# JPRINTLN

id 700 · stack [arg?] -> null

System.out.println. Formats the argument through the Java value-to-string rules and appends a newline — the reason printing is a builtin at all, since fusevm's native print renders shell-style (true as 1, 3.0 as 3).

# JPRINT

id 701 · stack [arg?] -> null

System.out.print — as JPRINTLN with no trailing newline.

# DBG_LINE

id 702 · stack [] -> null

The --dap per-statement marker. Registered only by install_debug and emitted only by the debug compile path, so a normal run never carries it. It hands control to the DAP server, which pauses in place when the line is a breakpoint or a step target.

# JFFI_COMPILE

id 703 · stack [base64] -> null

Compiles an inline rust { … } block to a cdylib and registers its exports. The parser desugars the block into a __rust_compile("<base64>", line) call, which lowers to this builtin.

# JFFI_CALL

id 704 · stack [args…, name] -> result

Calls an FFI export by name. In a program containing a rust { … } block, any otherwise-unresolved call becomes one of these; without such a block an unknown name stays a compile-time error.

# JSTR_DISPATCH

id 705 · stack [recv, args…, method] -> result

An instance method call on a String receiver. It also serves as the fallback for a receiver whose static type the compiler could not pin down: a closure receiver routes to its single abstract method, a collection receiver to the collection methods, and a null receiver to the matching NullPointerException.

# JEPRINTLN

id 706 · stack [arg?] -> null

System.err.println.

# JEPRINT

id 707 · stack [arg?] -> null

System.err.print.

# JSTATIC_DISPATCH

id 708 · stack [args…, class, method] -> result

A static stdlib call. The collection statics are tried first, because two of them (Collections.sort with a comparator) run user code and therefore need the VM; the rest fall through to the pure static table.

# JARRAY_NEW

id 709 · stack [size, default] -> array

new T[n] — allocates an array filled with the element type's default. A negative size raises NegativeArraySizeException.

# JARRAY_LIT

id 710 · stack [e0, …, eN] -> array

An array literal: pops the already-evaluated elements deepest-first and pushes a fresh array handle.

# JARRAY_GET

id 711 · stack [array, index] -> element

a[i], bounds-checked. The lookup runs inside the heap borrow and any fault is raised after it is released, because raising allocates the throwable on that same heap.

# JARRAY_SET

id 712 · stack [array, index, value] -> value

a[i] = v, bounds-checked, returning the stored value.

# JNEW

id 713 · stack [className] -> instance

new C(…) — allocates an instance with an empty field map. The compiler emits the field defaults, the field initializers, and the constructor call immediately after.

# JFIELD_GET

id 714 · stack [recv, name] -> value

A field read: an array's .length or an instance field. An absent instance field reads as null; a null receiver raises the NullPointerException whose wording matches the operation.

# JFIELD_SET

id 715 · stack [recv, name, value] -> value

A field write, returning the stored value.

# JINSTANCEOF

id 716 · stack [obj, className] -> boolean

x instanceof C, resolved by walking the supertype graph the compiler installed before the run.

# JCLASSOF

id 717 · stack [obj] -> className

The runtime class name of an instance, which drives the compiler's virtual-dispatch chain. A closure reports the sentinel #lambda — not a legal Java identifier, so a user class can never collide with it — and that is the arm routing a functional-interface call to the lambda body.

# JARRAY_NEW_MULTI

id 718 · stack [s0, …, sK, leafDefault] -> array

new T[m][n]… — builds K+1 nested levels of default-valued arrays. Trailing unsized dimensions (new int[2][]) get null leaves.

# JDIV

id 719 · stack [a, b] -> double

Java's floating-point division. fusevm's native Div yields Undef for a zero divisor because its shell/awk flavour has no infinities; only the floating path routes here, so statically-integral division keeps the native op and stays JIT-traceable.

# JIDIV

id 745 · stack [a, b] -> long

Java's 64-bit integral division. fusevm's native Div computes in f64, which is exact for two int operands but not for a long: above 2^53 the operand does not survive the round trip, and Long.MIN_VALUE / -1 saturates where Java wraps. int-width division keeps the native op pair and stays JIT-traceable; only a long routes here. A zero divisor is rejected by the compiler's inline check before the call.

long q = Long.MAX_VALUE / 2;   // 4611686018427387903, not 4611686018427387904

# JTHROW

id 720 · stack [throwable] -> null

throw e — parks the value as the host's pending exception. The compiler emits the jump to the handler, or the frame exit, immediately after.

# JEXC_PENDING

id 721 · stack [] -> boolean

Is an exception in flight? Emitted after every call in a program that uses exceptions — that per-call-site check is how the unwind is modeled, since fusevm has no unwind opcode.

# JEXC_TAKE

id 722 · stack [] -> throwable

Claims the pending exception, clearing it. Emitted at the top of a handler.

# JEXC_DEPTH

id 723 · stack [] -> int

The current value-stack depth, recorded on entry to a try.

# JEXC_CUT

id 724 · stack [depth] -> null

Truncates the value stack back to a recorded depth, discarding the operands of the expression the throw abandoned. Without it those operands would pile up once per throw, forever, inside a loop.

# JEXC_ABORT

id 725 · stack [] -> null

Reports an exception that reached the top of main in Java's own Exception in thread "main" <class>: <message> form and halts, so the process exits non-zero the way java does.

# JFAULT

id 726 · stack [className, message] -> null

Raises a fault the compiler detected inline — integral division by zero, and an enum valueOf with no matching constant — through the same path a host-detected fault takes, so the throwable is catchable.

# JARGV

id 727 · stack [] -> array

main's String[] args, as a fresh array of the program arguments the CLI collected. Called once by the prologue, so the array the program sees is its own.

# JMAKE_CLOSURE

id 728 · stack [caps…, nameIdx, params, ncap] -> closure

Builds a lambda closure, snapshotting the enclosing locals by value. A javars local lives in a call-frame slot that does not outlive the frame, so a by-value snapshot is the only model that also gives the enhanced for its per-iteration capture.

# JCLOSURE_CALL

id 729 · stack [closure, args…] -> result

Invokes a closure in its own fusevm call frame through a nested run. A mismatched arity is padded with null or truncated rather than corrupting the frame, and an exception already in flight suppresses the call so a unwinding frame cannot re-run side effects.

# JCOLL_NEW

id 730 · stack [kindName, seed] -> collection

Allocates a modeled java.util collection, optionally seeded by a copy constructor's argument. Collections live on the same heap slab as arrays and instances, so aliasing, == identity, and passing one to a method all behave like Java references with no extra machinery.

# JCOLL_DISPATCH

id 731 · stack [recv, args…, method] -> result

An instance method on a collection receiver. Methods that run user code (sort with a comparator, forEach) snapshot the elements and re-enter the VM with no heap borrow held, because a lambda body can allocate.

# JITER_ARRAY

id 732 · stack [iterable] -> array

The elements of an enhanced-for iterable, as an array. An array receiver is handed back unchanged so an array loop keeps aliasing; a collection is snapshotted. Emitted only when the compiler could not prove the iterable is already an array.

More