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));
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}
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.