<!-- GENERATED by scripts/sync-java.mjs from java-online-compiler@0.39.0 (b00eddf254edc4545e4856c2bf729adfc6d7265c). Do not edit. -->

> This is the compatibility table of the Java engine that code-playpad ships (engine 0.39.0).
> Paths such as `tests/…`, `SPEC.md` and `docs/…` refer to the engine's source (`java-engine/` in code-playpad's repository), which is not part of the package.

# Compatibility — engine 0.39.0 (Stage A, compact source files, objects, numbers and control flow, collections, lambdas, practice mode, String, format, regex, Math, Random, System, Scanner and IO.readln, inheritance, interfaces, Object, instanceof, enums, records, exceptions, hash order, fixed collections, TreeMap, TreeSet, LinkedList, Stack, lambdas, method references, generic classes and methods, var, annotations, varargs, anonymous classes, streams, subList, local classes, raw types, float, byte and short, pattern matching, sealed classes, BigInteger, BigDecimal, EnumMap, EnumSet, fast I/O, static imports, initializer blocks, text blocks, enum constant bodies, Interface.super, ListIterator, reversed views, access-ordered LinkedHashMap, var lambda parameters, array clone, library subclasses, bridge methods, Vector, Hashtable, StringBuffer, StringJoiner, java.util.regex, outer.new, BitSet, parallel streams, java.time dates, AbstractList, LocalTime, LocalDateTime, Duration, Instant, time zones, packages)

This document records what the engine accepts, what it rejects, and the evidence that it behaves like Java. Every method of every library class, with its status and the tests that call it, is in docs/feature-manifest.json (`npm run manifest`). The reference is Java SE 25 (SPEC.md §4), and what the engine implements is **a documented subset of Java 25**, not "Java 25".

Every feature has one status:

- **Supported:** parsing, type checking, execution, diagnostics, and tests are all implemented.
- **Supported with limits:** the same, but the limits are listed here.
- **Unsupported:** the engine reports "… not supported in this version" and never runs the code with a different meaning.

> **Evidence:** everything below is checked against the pinned JDK, Temurin 25.0.4.1 (docs/reference-jdk.md).
> - **Golden programs:** all 47 in `tests/programs` (the SPEC.md §4.1 Stage A corpus among them) match byte for byte: stdout, stderr, and exit code.
> - **Recorded snippets:** all 1636 cases in `tests/jdk-cases` match. 1613 match byte for byte (once the JDK's own stack frames are removed, as *Known divergences* explains); the other 23 are listed under *Known divergences*, each with the narrower comparison the tests apply to it.
> - **Practice corpus:** all 10 Stage 4 programs in `tests/corpus` (docs/practice-corpus.md) run and match.
> - **Field corpus:** 337 of 339 programs written as learners and solvers write Java, kept whether or not the engine supports them, match the JDK exactly. None runs differently; 1 is rejected as unsupported (`Thread`), 1 stops on an engine limitation and none on the instruction limit (`corpus/REPORT.md`, docs/corpus-harness.md).

## Program structure

| Feature | Status | Notes |
| --- | --- | --- |
| **Compact source files** (Java 25): top-level methods, fields and classes, with no class declaration | Supported | They form an implicitly declared class named after the file (`Main` for `Main.java`), which the program cannot refer to by name (`Main.f()` is `cannot find symbol`, as in javac). Classes declared in the file are its member classes: inner unless `static`, named `Main$Node` at run time and `Main.Node` in diagnostics, as javac does. |
| **Several top-level classes** | Supported | Any number, in any order, each with its own fields, methods and constructors. `duplicate class: Node`. A public class need not match the file name (source-file mode allows it). |
| **Member classes**: `static` nested and inner | Supported with limits | `static class Node` inside `Main`, or an inner class, whose instances hold their enclosing instance (`this$0`) and can use its fields and methods by simple name. `new Inner()` needs an enclosing instance (`non-static variable this cannot be referenced from a static context`). Names: `Main$Node` at run time, `Node` in diagnostics, `Main.Node` as a qualified type. Classes nest to any depth (`static class MyList<T> { class Node { T val; } }`): an inner class reaches each enclosing instance through javac's `this$N` fields, so NPE messages name them as the JDK does. An inner class of a generic class, seen from outside it, keeps its outer class's type variables. `Outer.this` names an enclosing instance. `outer.new Inner()` (JLS 15.9.2) gives the enclosing instance explicitly, with javac's `NullPointerException` for a null one and `qualified new of static class`; with an anonymous class body, or of a generic class or a generic class's inner class, it is unsupported. |
| **Launch protocol** (Java 25) | Supported | The first top-level class runs if it has a `main`, otherwise the class named after the file (`can't find class: Main` if there is none). `main(String[])` is preferred over `main()`. Either may be `static` or an instance method, and need not be `public` (but not `private`). An instance `main` runs on an instance made with the no-argument constructor, which may be private (`can't find no argument constructor in class: A`). `String... args` and `String args[]` are accepted; `args` is empty. |
| Methods: parameters, `return`, recursion, overloading; `static` and instance | Supported | Overload resolution follows JLS 15.12.2: the strict phase, then the boxing phase, then the most specific method, with javac's "cannot be applied", "no suitable method", and "ambiguous" reports. An ambiguous call names the two methods javac names (its most-specific fold over each class's methods, last declared first: `sb.append(null)` names `append(CharSequence)` and `append(char[])`). An argument that is a generic call whose result only an implicit lambda or an inexact method reference decides (`collect(groupingBy(f, TreeMap::new, counting()))`, `collect(collectingAndThen(toList(), l -> …))`) does not count for applicability (JLS 15.12.2.2) for any overloaded method or constructor, the program's own included: it fits every parameter its type variable's bound allows, so `show(String)` beats `show(Object)` and `show(String)`/`show(Integer)` is ambiguous, then fails with javac's `inference variable R has incompatible bounds` and its `where` clause. Calls are `f(x)`, `this.f(x)`, `obj.f(x)`, `Node.f(x)`, or an enclosing class's `f(x)`. Using an instance member from a static context gives javac's `non-static … cannot be referenced from a static context`. |
| **Constructors** | Supported with limits | Overloaded, with javac's resolution errors (`constructor Point in class Point cannot be applied to given types;`, `no suitable constructor found for Point(String)`, `reference to Pair is ambiguous`). A class without one gets the default constructor. `this(...)` as the first statement, with `recursive constructor invocation` detected. Statements before `this(...)` (Java 25's flexible constructor bodies) and `super(...)` are unsupported. |
| `new C(args)` | Supported | Objects have identity: `==`, `!=`, `null`, and `Object.equals(Object)` by reference. |
| Fields: `static` and instance, `final`, initializers, `this.x`, `obj.x`, `Node.x` | Supported | Defaults are 0, false, null. Instance initializers run at the start of each constructor that does not call `this(...)`, in source order. `final` fields with constant initializers are constants. `illegal forward reference` and `self-reference in initializer` follow JLS 8.3.3. Blank `final` fields must be assigned exactly once by every constructor: `variable y might not have been initialized`, `might already have been assigned`, `might be assigned in loop`, `not initialized in the default constructor`. |
| **Class initialization** (JLS 12.4) | Supported | Each class initializes on its first active use (`new`, a static method call, a non-constant static field), not at startup; reading a constant does not initialize it. An exception in a static initializer becomes `ExceptionInInitializerError` where the class was first used, with `Caused by:`. |
| Access | Supported | `private` members are visible within their top-level class, nested classes included: `secret has private access in Box`, `hide() has private access in Box`, `Box() has private access in Box`. `public`, `protected` and package access are equivalent here (one package). |
| `toString()`, `equals(Object)`, `hashCode()` | Supported | Declared or inherited, they are used wherever the JDK uses them: string conversion and `println`, `HashMap` and `HashSet` keys, `List.contains`. Without them, `Object`'s: `Node@hash` and identity. `super.toString()` is `Object`'s own. `@Override` is checked with javac's reasons. |
| **Inheritance** (`extends`, JLS 8.1.4) | Supported with limits | A class extends one class of the program: inherited fields and methods, overriding with dispatch on the object's class, `super(...)` (or the implicit `super()`), `super.f()` and `super.x`, covariant return types, field hiding, static methods inherited and hidden. Superclass constructors and field initializers run first, so a superclass constructor calling an overridden method sees the subclass's fields at their defaults, as on the JDK. `abstract` classes and methods, `final` classes and methods, `protected`. javac's errors: `B is not abstract and does not override abstract method f() in A`, `A is abstract; cannot be instantiated`, `cannot inherit from final A`, `f() in B cannot override f() in A` (with `overridden method is final`, `attempting to assign weaker access privileges; was public`, `return type long is not compatible with int`, `overriding method is static`), `cyclic inheritance involving A`, `abstract method f() in A cannot be accessed directly`, `x has private access in A` for a superclass's private member. NPE messages name the receiver's static type (`Cannot invoke "Main$B.f()"`), as the JDK does. A class may also extend the JDK's exception classes (*Exceptions* below) and its collections (*Library subclasses* below). javac's bridge methods (JLS 15.12.4.5) are reproduced: where an override's erasure differs from what it overrides (`compareTo(Box)` for `Comparable<Box>`, `Integer apply(Integer)` for `Function`, a covariant return type), a call through the supertype goes through a bridge, so a stack trace shows its frame at the class's line, and a raw call with a wrong argument is the bridge's `ClassCastException`. Extending other library classes, extending an inner (non-static) class, and statements before `super(...)` (Java 25's flexible constructor bodies) are unsupported. |
| **Interfaces** (JLS 9) | Supported with limits | Interfaces of the program, nested or top-level: abstract methods, `default` methods, `static` methods, `private` methods, constants; `extends` among interfaces; a class implementing several. Implementing methods must be `public` (`g() in C cannot implement g() in I`), and two unrelated defaults conflict (`types I and J are incompatible;`). A class may also implement the library's `Comparable<T>`, `Comparator<T>`, `Iterable<T>` and `Iterator<T>`: then sorting, `PriorityQueue`, `Collections.sort`, for-each and `Objects.compare` call its methods as Java code. Their other methods are its too, declared or not: `cmp.reversed()` of an abstract class implementing `Comparator`, `op.applyAsInt(2, 3)` of an enum whose constant bodies implement `IntBinaryOperator`. `Interface.super.f()` calls a direct superinterface's default method (JLS 15.12.3), also from an interface extending it and inside a lambda, with javac's errors: `not an enclosing class: I` (not a direct superinterface, or a static context), `bad type qualifier I in default super call` (`redundant interface I is extended by J`), `abstract method m() in I cannot be accessed directly`, `illegal static interface method call`. `Main.super.f()` in Main is `super.f()`; `Outer.super.f()` for an enclosing class and implementing other library interfaces are unsupported. |
| The `Object` type | Supported | Variables, parameters, fields, arrays (`Object[]`), returns. Any value converts to `Object` (primitives box). Its methods on any value: `toString()`, `equals(Object)`, `hashCode()` (each dispatched on the value's class: a String compares by content, an `Integer` by value) and `getClass()`. `new Object()` makes an object with nothing but identity (`java.lang.Object@…`, equal only to itself). `wait` and `notify` are unsupported, and so is `clone` except on arrays (below). |
| Casts and `instanceof` (JLS 15.16, 15.20.2) | Supported | Casts between classes, interfaces, library types and arrays, checked as javac checks them (`incompatible types: A cannot be converted to B` for impossible ones) and when they run: `ClassCastException: class Main$A cannot be cast to class Main$B (…)`, as the JDK words it. `(int) object` checks for an `Integer`, then unboxes. `x instanceof T`, raw types included (`x instanceof List`), and the type pattern `x instanceof T t`, whose variable is in scope where the test is true: in `&&`, the `if` or `while` body, after `if (!(x instanceof T t)) return;` (JLS 6.3.1). A store into an array seen through a supertype is checked: `ArrayStoreException: java.lang.String`. A cast the run cannot fully check (`(T) o`, `(T[]) new Object[n]`, `(List<Integer>) o`) checks the erasure and gives javac's `[unchecked] unchecked cast` warning (with `required:`, `found:` and a `where` clause); a cast between different parameterizations (`(List<String>) intList`) is javac's `incompatible types`. Record patterns: `o instanceof Line(Point(int a, int b), var to)`, their variables in scope as a type pattern's are. `instanceof` with a parameterized type (`x instanceof List<Integer>`) is unsupported. |
| **Enums** (JLS 8.9) | Supported with limits | Nested or top-level, with fields, constructors (with the hidden name and ordinal, so NPE messages say `<parameter3>`), methods, static members, and `implements`. `values()` (a new array each time), `valueOf(String)` (`IllegalArgumentException: No enum constant Main.Color.PURPLE`, `NullPointerException: Name is null`, with the enum's own frame), `name()`, `ordinal()`, `compareTo` (by ordinal), `toString()` (overridable), `getDeclaringClass()`, `equals` and `hashCode` (identity). The constants are made when the enum initializes, before its other static fields; an exception there is `ExceptionInInitializerError`. `switch` on an enum, with simple or qualified labels (`case RED`, `case Color.RED`); a switch expression naming every constant needs no `default`; a null selector fails as the JDK's does (`Cannot invoke "Main$Color.ordinal()"`). Enums are Comparable, so they sort and queue in declaration order. javac's errors: `enum classes may not be instantiated`, `modifier public not allowed here`, `name() in E cannot override name() in Enum`. `EnumMap` and `EnumSet` are supported (see *Library*). Constants with a class body (`PLUS { int apply(int a, int b) { return a + b; } }`): each is an anonymous subclass of the enum (`Main$Op$1`, numbered with the anonymous classes of the constants' arguments, before the members'), with its own fields, methods, `super` calls and initializer blocks; `getClass()` is the body's class and `getDeclaringClass()` the enum, also in EnumMap and EnumSet. An enum may leave abstract methods to its constants' bodies. javac's errors: `<anonymous Main$Op$2> is not abstract and does not override abstract method apply(int,int) in Op`, `Op is abstract; cannot be instantiated` (a constant without a body), `non-static variable symbol cannot be referenced from a static context` (the enum's private members are not inherited by a body), `equals(Object) in <anonymous Main$Op$1> cannot override equals(Object) in Enum`. |
| **Records** (JLS 8.10) | Supported with limits | Nested or top-level, with components, accessors (generated, or declared: `invalid accessor method in record R`), the canonical constructor (generated, compact `R { ... }` with the fields assigned after its body, or explicit), other constructors (which must call another: `constructor is not canonical, so it must invoke another constructor of class R`), static fields and methods, instance methods, and `implements`. `toString()` (`Point[x=1, y=2]`, components as String.valueOf shows them, with the record's own frame when one fails), `equals` (doubles compared as `Double.compare` does) and `hashCode` (`31 * h + hash(component)`) as the JDK computes them, unless declared. javac's errors: `field declaration must be static`, `cannot assign a value to final variable v`. Generic records (`record Pair<A, B>(A first, B second)`) are supported; so are local records and record patterns (see *Pattern matching*). |
| `getClass()`, `X.class` | Supported with limits | A `Class` value per class: `getName()` (`Main$Node`, `[I`), `getSimpleName()` (`Node`, `int[]`), `isInterface()`, `isArray()`, printing as `class Main$Node`, and `==` between classes, as `equals` methods use it. `X.class` is a `Class<X>` (so `new EnumMap<>(Day.class)` infers its key type); `Class<?>` may be declared. The reflection methods are unsupported. |
| Running a file, and programs in several files (JEP 458) | Supported with limits | Behaves like `java <entry>` in source-file mode. The program's other `.java` files (`Session.java` beside `Main.java`) are compiled as the JDK's launcher compiles them: a file when the program first names the class it is named after, so a file nothing names is never compiled, and its errors never reported. Each file has its own imports. A class of the entry file wins over another file's of the same name. Errors and stack traces name each file (`Session.java:2: error: …`, `at Session.describe(Session.java:5)`); where the JDK prints another file's absolute path on disk, the engine prints its name. A file's warnings print again when the program first uses one of its classes, as the launcher's second compilation prints them (a class literal, `Helper.class`, is not counted as a use). Limits: 32 files, 500,000 characters per file and 1,000,000 together. A requested entry that is not among the files fails as `java Missing.java` does (`Error: Could not find or load main class Missing.java`); another file never runs in its place. Folders are packages (see the next row); a name with `\\` is reported as not supported. A file first needed by the launch file's declarations (an import, a field's or parameter's type) rather than its code is compiled, as by the launcher, only when the program first loads one of its classes: its code's errors print then, after the output so far, with exit status 2, and a class never loaded is never compiled (ADR 0033). Unsupported: another compact source file with a `main`, and using another file's secondary class (one not named after its file) from outside that file, which the JDK finds only if that file happened to be compiled first. |
| Packages: `package` declarations, files in folders (ADR 0033) | Supported with limits | `util/Tool.java` declares `package util;` and holds class `util.Tool`, found as the launcher finds it: under the root that the entry's package gives (`app/Main.java` in package `app`: the root is the folder above `app`; `src/Main.java` in no package: `src/`). Single-type, on-demand and static imports of the program's classes and member classes, fully qualified names (`util.MathUtil.square(3)`, `new app.model.Item()`), and JLS 6.4.1's order: the file's classes, single-type imports (the library's too), the package's classes, then on-demand imports, two of which giving a name are javac's `reference to Stack is ambiguous`. Access across packages (JLS 6.6): `Hidden is not public in util; cannot be accessed from outside package`, the same for package-private members and member classes, `describe() has protected access in Shape` (protected members from subclasses, instance ones through the subclass; protected constructors through `super(...)` and anonymous classes), and an inaccessible overload is listed as not applicable. A named package does not see the unnamed one. Binary names everywhere: `getClass().getName()` is `util.Counter`, `getPackageName()` is `util`, stack traces are `at util.MathUtil.boom(MathUtil.java:6)`, uncaught exceptions `errors.AppException: …`. javac's errors for an entry whose folders do not match its package (`end of path to source file does not match its package name app: Main.java`), a package in a compact file, a missing package or class (`package nope does not exist`; `cannot find symbol` with `location: package util`), two imports of one simple name, and an import of the file's own class. Not supported: a file whose package is not its folder's (`util/Tool.java` declaring another package or none; javac reports `bad source file`), and on-demand imports of a class's member types (`import util.Outer.*;`). |
| No class / no usable main | Supported | `error: no class declared in source file`; `error: can't find main(String[]) or main() method in class: Main`; `error: can't find class: Main`; for a compact file, `compact source file does not have main method in the form of void main() or void main(String[] args)` |
| `import`: single-type and on-demand | Supported with limits | `import java.util.List;` and `import java.util.*;` for the supported java.util classes (see *Library*); the java.lang ones need no import. A compact source file sees java.util without imports, as Java 25's implicit `import module java.base` gives it. A wrong name gets javac's error: `package java.utill does not exist`, or `cannot find symbol` with `location: package java.util`. Real JDK classes outside the subset (`Clock`, `UUID`, …) are unsupported where they are used. Fully qualified names work without an import, in types and in expressions, for every package the subset has: `java.util.stream.IntStream.range(0, 3)`, `java.util.Map.Entry.comparingByKey()`; `java.time.Clock.systemUTC()` is unsupported, not `cannot find symbol`. Classes of java.io, java.math, java.util.function and java.util.stream that the subset has are imported the same way. |
| `import static`: single and on-demand (JLS 7.5.3, 7.5.4) | Supported | Methods and fields of the supported classes: `import static java.lang.Math.*;`, `import static java.lang.System.out;`, `import static java.util.stream.Collectors.*;`, `import static java.util.Map.entry;`, and member types (`import static java.util.Map.Entry;`). A simple name means the program's own local, field or method first, then a single import, then an on-demand one (JLS 6.4.1). A method of the class or an enclosing one, Object's included, hides every import of that name, so `toString(a)` with `Arrays.*` imported is javac's `method toString in class Object cannot be applied`. Overloads from several on-demand imports are resolved together (`sort(int[])` from Arrays, `sort(List)` from Collections). javac's errors: `reference to MAX_VALUE is ambiguous` (fields, and methods equally specific in two classes), `cannot find symbol` with `symbol: static foo`, a missing class or package, `static import only from classes and interfaces`. Imported constants are constants (switch labels). Importing from a JDK class outside the subset (`TimeUnit`) is fine until a name from it is used, which is unsupported. |
| Variable-arity methods and constructors (`int... xs`, JLS 8.4.1) | Supported | Chosen in the third phase of overload resolution, as javac chooses (`f(int)` over `f(int...)`, `g(Integer)` over `g(int...)`); the trailing arguments go into a new array, or an array is passed as it is. javac's `varargs parameter must be the last parameter`, `varargs mismatch; …`, and its `[unchecked]` warnings for a parameter type the run cannot check: `Possible heap pollution from parameterized vararg type T` at the declaration and `unchecked generic array creation for varargs parameter of type List<Integer>[]` at the call, unless the method is `@SafeVarargs`. |
| **Anonymous classes** (JLS 15.9.5) | Supported with limits | `new Comparator<Integer>() { public int compare(...) { ... } }`, `new Shape("sq") { double area() { ... } }`, `new Iterator<>() { ... }`: extending a class of the program (its constructor's arguments passed on) or a built-in exception, or implementing an interface of the program or the library's `Comparable`, `Comparator`, `Iterable`, `Iterator` and functional interfaces (`Runnable`, `Function`, …), with fields, methods, `@Override`, and the diamond inferred from where the object goes. Named as javac names them (`Main$1`, `Main$Holder$1`, `Main$2$1`, in source order per enclosing class), so stack traces, `getClass().getName()` and `<anonymous Main$1>` in diagnostics match; `getSimpleName()` is empty. They capture effectively final locals of the code around them into javac's `val$x` fields (NPE messages say `"this.val$s"`) and use the enclosing instance's members. javac's errors: `<anonymous Main$1> is not abstract and does not override abstract method …`, `anonymous class implements interface; cannot have arguments`, `cannot inherit from final F`, `local variables referenced from an inner class must be final or effectively final`. Extending an inner class or a library class is unsupported. |
| **Local classes, interfaces, enums and records** (JLS 14.3) | Supported | `class Point { ... }` (or `record Pair(...)`, `enum Dir { ... }`, `interface Shape { ... }`) declared in a block, visible from its declaration to the end of the block. Named as javac names them, `Main$1Point`, numbered per simple name in source order (`Main$1Helper`, `Main$2Helper`), alongside anonymous classes (`Main$1`). A local class in instance code is inner; it captures the locals it uses as `val$x` fields, so NPE messages name `this.val$s`. javac's errors: `cannot find symbol` outside the block, `class A is already defined in method main(String[])`, `local variables referenced from an inner class must be final or effectively final`, `illegal start of expression` for `static class` in a block. |
| Initializer blocks (JLS 8.6, 8.7) | Supported | `static { ... }` runs once when the class initializes, with the static field initializers in source order (after an enum's constants); `{ ... }` runs in every constructor that does not call `this(...)`, after the superclass constructor, with the instance field initializers in source order. In classes, enums, records (static only), nested, local and anonymous classes (`new Base("x") {{ add(1); }}`). They may declare locals, loop, catch and use lambdas (`lambda$static$0`, `lambda$new$0`), and assign blank `final` fields, with javac's definite-assignment errors across blocks and constructors. An exception in a static block is `ExceptionInInitializerError`, then `NoClassDefFoundError: Could not initialize class`, as on the JDK. javac's errors: `return outside method`, `initializer must be able to complete normally`, `illegal forward reference`, `unreported exception` (an instance block may throw what every constructor declares), `initializers not allowed in interfaces`, `instance initializers not allowed in records`. |
| **Generic classes, interfaces, records and methods** (JLS 8.1.2, 8.4.4) | Supported with limits | `class Node<T>`, `class BST<K extends Comparable<K>, V> implements Iterable<K>`, `interface Fn<A, B>`, `static <T extends Comparable<? super T>> void sort(T[] a)`, `<R> Pair<K, R> withValue(R r)`. Members are seen through the type arguments (`T val` of a `Node<Integer>` is an `Integer`; a type variable's members are its bound's), supertypes are parameterized (`class Strs extends Base<String>`, with the bridge methods javac generates so overriding dispatches), the diamond and generic methods are inferred from the arguments, the target, implicit lambdas' bodies and the parameter a diamond is passed to, and explicit type arguments (`Main.<Integer>repeat(7, 2)`) work. Bounds are checked: `type argument String is not within bounds of type-variable T`, `inference variable T has incompatible bounds` (with javac's `upper bounds`, `lower bounds` and `equality constraints` lines), `cannot infer type arguments for Box<>`. javac's other errors: `non-static type variable T cannot be referenced from a static context`, `generic array creation`, `unexpected type` for `new T()`, `wrong number of type arguments`, `name clash: … have the same erasure`, `cannot use '<>' with non-generic class`. Every diagnostic that names a type variable ends with javac's `where T is a type-variable:` clause. As javac does, a generic member's value is checked against the type it has where it is used, so `Integer[] a = box.items` fails with the JDK's `ClassCastException` when the array is really an `Object[]`. Raw types of the program's generic classes are supported as the library's are (see *Generic types at use sites*). Generic constructors are unsupported. |
| Annotations (JLS 9.7) | Supported with limits | `@Override`, `@SuppressWarnings` (a string or an array of them; `"unchecked"` and `"deprecation"` silence those warnings on the class, method, field or local declaration, and `"all"` silences nothing, as in the source launcher), `@FunctionalInterface` (javac's `Unexpected @FunctionalInterface annotation` with its reason), `@SafeVarargs` (javac's `Invalid SafeVarargs annotation` errors) and `@Deprecated` (`since`, `forRemoval`; on a local variable, javac's `[deprecation] … has no effect on this variable declaration` warning), on classes, methods, constructors, fields, parameters and locals. javac's errors: `cannot find symbol` for an unknown annotation, `annotation interface not applicable to this kind of declaration`, `… is not a repeatable annotation interface`, `annotation @SuppressWarnings is missing a default value for the element 'value'`, `incompatible types` for a non-String value. Other annotations (the JDK's, or declared with `@interface`), annotations on types and type arguments, and values other than literals are unsupported. |
| Warnings | Supported | javac's `[unchecked]` and `[deprecation]` warnings, as the source launcher prints them: before the program's output on a run, among the errors in source order when compilation fails, and counted (`1 warning`, `2 warnings`) after the errors. |
| Declaring annotation interfaces | Unsupported | |

## Types and values

| Feature | Status | Notes |
| --- | --- | --- |
| `int` | Supported | 32-bit two's complement: overflow wraps, `/` truncates, `%` takes the dividend's sign, `MIN_VALUE / -1` wraps. `/ by zero` throws. Shift distances are masked. |
| `char` | Supported | A UTF-16 code unit (0–65535). Arithmetic promotes to `int` (`'a' + 1` is `98`). `char c = 65` is allowed because the constant fits. `c = c + 1` is an error ("possible lossy conversion"), while `c += 1` and `c++` wrap. |
| `long` | Supported | 64-bit two's complement, like `int`: wrapping, truncating `/`, `/ by zero`, `MIN_VALUE / -1`, shifts masked to 6 bits, `>>>`. Literals with `L`, in any radix, with underscores (`integer number too large`). Long values stay exact past 2^53 (engine numbers become BigInts there, an implementation detail). |
| `double` | Supported | IEEE 754 binary64, as Java: `NaN`, infinities, `-0.0`, `%` as fmod, division by zero giving `Infinity`. Literals in decimal and hexadecimal (`0x1.8p1`), with `d`/`D`, `floating-point number too large` / `too small`. Printed exactly as `Double.toString` (`0.30000000000000004`, `1.0E7`, `9.999E-4`, `4.9E-324`). |
| Numeric promotion and conversions (JLS 5.1–5.6) | Supported | Widening in assignment, invocation, and operators (`int` + `long` is `long`, anything with `double` is `double`); casts in every direction with Java's rules (`(int) 1e300` saturates, `(int) NaN` is 0, `(char) 65601L` keeps the low bits); compound assignment's implicit cast (`long l = 5; l += 2.7;` is `7`); `possible lossy conversion from long to int`. Constant expressions fold with the same arithmetic. |
| `boolean` | Supported | |
| `String` | Supported with limits | Literals and text blocks (`"""`, JLS 3.10.6: the incidental indentation and trailing spaces are stripped, line ends become `\n`, and escapes, `\s` and the line-joining `\<newline>` included, are interpreted last; javac's `illegal text block open delimiter sequence, missing line terminator`, `unclosed text block`, `illegal escape character`), `null`, concatenation with any supported type, and `==` by reference (constants are interned). Methods: see the Library section; `new String(...)` makes a new object, so `new String("a") == "a"` is `false`. Contents are arbitrary Unicode (UTF-16). |
| Wrapper classes: `Integer`, `Long`, `Double`, `Character`, `Boolean` | Supported with limits | Boxing and unboxing (JLS 5.1.7–5.1.8) in assignment, invocation, operators, `?:`, compound assignment and `++`/`--`. `==` compares references, and the JDK's caches are reproduced (`Integer` and `Long` -128 to 127, `Character` 0 to 127, both `Boolean`s): `Integer a = 127, b = 127;` gives `a == b` true, and 128 gives false. Unboxing null is the JDK's NPE, `Cannot invoke "java.lang.Integer.intValue()" because …`. The deprecated constructors (`new Integer(5)`, `new Double("3.5")`, and those of `Long`, `Float`, `Short`, `Byte`, `Character`, `Boolean`) make a new object every time, never a cached one, with javac's `[deprecation] Integer(int) in Integer has been deprecated` warnings. A switch on a wrapper is unsupported. |
| Generic types at use sites: `List<Integer>`, `Map<String, List<Integer>>`, `new ArrayList<>()` | Supported with limits | Type arguments are checked as javac checks them: invariance (`ArrayList<Integer> cannot be converted to List<String>`), `unexpected type` with `required: reference` for `List<int>`, and `wrong number of type arguments`. The diamond is inferred from the target type, the constructor's arguments, or the parameter it is passed to. Types print as javac prints them (`Map<String,Integer>`). Wildcards (`List<?>`, `List<? extends T>`, `Comparator<? super T>`) are supported: a value read through one has its bound's type, and a parameter of `? super T` takes a `T`. Generic array creation is javac's error, except of raw types (`new ArrayList[n]`, `new List[n]`) and unbounded wildcards (`new List<?>[n]`); a raw array assigned, returned or initialized as `List<Integer>[]` is javac's `[unchecked] unchecked conversion` warning. Raw types (`List raw = new ArrayList();`, `Map m`, `Box b = new Box("s")`, `(List) o`, `o instanceof List`) are supported: their members are erased (`raw.get(0)` is an `Object`), and javac's warnings are reported: `[unchecked] unchecked call to add(E) as a member of the raw type List`, `unchecked conversion` (raw to parameterized), `unchecked method invocation`, `unchecked assignment to variable v as member of raw type Box`, each with its `where` clause, and silenced by `@SuppressWarnings("unchecked")`. An argument that converts only unchecked makes the call `unchecked method invocation` and its type erased (`new ArrayList<>(raw)` is a raw `ArrayList`). As on the JDK, heap pollution through a raw type fails where the value is used (`int v = ints.get(0)` of a `String` is a `ClassCastException`). Generic calls are target-typed as javac types them: arguments bound a type variable from below (`List.of(1, 2.5)` is a `List<Number>`, `List.of(new Sq(1), new Circle(2))` a `List<Shape>` when both implement `Shape`), and an assignment, return or parameter that names the type argument decides it (`List<Shape> s = List.of(new Sq(1))`, `take(List.of(new Sq(1)))`), with javac's `inference variable E has incompatible bounds` when an argument does not fit. The engine has no intersection types: where javac infers one (`List.of(1, "a")` with no target), it keeps a common class or interface, or Object. |
| The program's classes as types | Supported | `Node n = new Node(1);`, parameters, return types, fields. `incompatible types: Bag cannot be converted to Box`, `incomparable types: Box and Bag`. |
| Arrays of any supported type, any number of dimensions | Supported | `new T[n]`, `new int[3][4]`, `new int[3][]` (jagged), `new T[]{…}`, nested `{{…}, {…}}` initializers, `a[i][j]`, and `.length`. Arrays are default-initialized, bounds-checked, and compared by reference. `int a[]` and `int[] a[]` declarations are accepted. They print with JVM descriptors: `[[I@hash`, `[J@hash`, `[LNode;@hash`. `a.clone()` is a shallow copy of the same type (`int[][]`'s rows are shared), as on the JDK. |
| Casts between `int` and `char`, and identity casts | Supported | `(char) 65601` keeps the low 16 bits. A cast from boolean to int is an error, as in Java. |
| `var` (JLS 14.4.1) | Supported | For locals, `for` and enhanced-`for` variables and resources: the type of the initializer (a diamond nothing decides is `Object`'s, `new ArrayList<>()` an `ArrayList<Object>`). javac's errors: `cannot infer type for local variable x` with `(variable initializer is 'null')`, `(lambda expression needs an explicit target-type)`, `(method reference needs an explicit target-type)`, `(array initializer needs an explicit target-type)`, `(cannot use 'var' on variable without initializer)` and `(cannot use 'var' on self-referencing variable)`; `'var' is not allowed in a compound declaration`, `… as an element type of an array`, and `'var' is not allowed here` for a field. `var` lambda parameters (`(var a, var b) -> a + b`, also with `final`), with javac's `invalid lambda parameter declaration` and its `(cannot mix 'var' and explicitly-typed parameters)` / `(cannot mix 'var' and implicitly-typed parameters)` details. |
| `float` (JLS 4.2.3) | Supported | IEEE 754 binary32, as Java: every operation is the double one rounded once to float (exact for `+ - * / %`), and `long` → `float` rounds once (`(float) 9007199254740993L` is `9.007199E15`). Literals with `f`/`F`, decimal or hexadecimal, rounded once and correctly (`1.0000000596046447753906250001f` is `1.0000001`), with `floating-point number too large` / `too small`. Printed as JDK 19+'s `Float.toString` (the shortest decimal that rounds back, computed exactly: 3,000 random floats print as on JDK 25). Promotion, widening and javac's `possible lossy conversion from double to float` as JLS 5.1–5.6 say; `Math.abs`, `max`, `min`, `round` (an `int`), `signum`, `ulp`, `nextUp`, `nextDown`, `clamp` of floats; `float[]`. A `switch` on a float is javac's `primitive patterns are a preview feature`. |
| `byte`, `short` (JLS 4.2.1) | Supported | Promoted to `int` in arithmetic; `b += 120` and `b++` narrow back, wrapping as Java does; `(byte) 300.5f` goes through `int`. A constant that fits may be assigned without a cast (`byte b = 10;`, `short s = 'B';`, `Byte b = 100;`), others are javac's `possible lossy conversion from int to byte`. `byte[]`, `short[]`, `switch` on either (a label out of range is a lossy-conversion error), and JLS 15.25's conditional typing (`byte` with `short` is `short`). |

## Statements and expressions

| Feature | Status | Notes |
| --- | --- | --- |
| Blocks; local variables (several declarators, `final`); `;` | Supported | Blank `final` locals are unsupported. |
| `if`/`else`, `while`, `do`/`while`, basic `for` (declaration or expression-list init, comma updates) | Supported | |
| Enhanced `for` over arrays | Supported | Element conversions as in Java (`for (long w : intArray)`). Its three hidden locals are allocated where javac puts them, so NPE messages name the same slots (`"<local7>"`), and a null array is reported as the hidden copy (`"<local2>"`). `for-each not applicable to expression type`. |
| Enhanced `for` over an `Iterable` (any collection) | Supported | Through an iterator in one hidden local, as javac compiles it. A null collection is `Cannot invoke "java.util.List.iterator()" because "<local1>" is null`, and unboxing a null element is `… because the return value of "java.util.Iterator.next()" is null`. |
| `break`, `continue` (labeled and unlabeled), labeled statements, `return` | Supported | `break outside switch or loop`, `continue outside of loop`, `undefined label: x`, `not a loop label: block`, `label x already in use`, `missing return statement`, `unexpected return value`, `missing return value` |
| `switch` statements and expressions (JLS 14.11, 15.28) | Supported with limits | Over `int`, `char` and `String`, in the `:` form (with fall-through) and the `->` form, several labels per case, `yield`. A String switch has javac's two hidden locals (a null selector fails as `Cannot invoke "String.hashCode()"`). Checked as javac does: `duplicate case label`, `constant expression required`, `different case kinds used in the switch`, `the switch expression does not cover all possible input values`, `switch rule completes without providing a value`, `bad type in switch expression`, `yield outside of switch expression`, `attempt to break out of a switch expression`; a `long`/`double`/`boolean` selector gives Java 25's preview-feature error. A rule may be `throw` (`default -> throw new IllegalArgumentException()`). Also over `byte`, `short`, enums with simple or qualified labels (`case Color.GREEN`), and, with patterns, any reference type (see *Pattern matching*). |
| **Pattern matching** (JLS 14.11.1, 14.30, 15.20.2) | Supported with limits | `switch` with type patterns (`case Circle c ->`), guards (`case Integer i when i > 0`), record patterns, nested and with `var` (`case Line(Point(var x, var y), Point p)`), the unnamed pattern `_`, `case null` and `case null, default`, constants alongside patterns (String, wrapper and enum constants, qualified enum constants on a sealed interface), in both forms and as statements or expressions. Cases are tried in order; a pattern's variables are in scope in its guard and body only; a record pattern calls the accessors, and its nested patterns that cover their component match null. A null selector without `case null` is the JDK's `NullPointerException`. Exhaustiveness as JLS 14.11.1.1 defines it: a default, an unconditional pattern, every permitted subclass of a sealed type (recursively), every enum constant, or record patterns covering every combination of their components (`the switch expression does not cover all possible input values`, also for a pattern switch statement). javac's other errors: `this case label is dominated by a preceding case label`, `switch has both an unconditional pattern and a default label`, `illegal fall-through to a pattern`, `deconstruction patterns can only be applied to records`, `incorrect number of nested patterns`, a guard that is constant `false`, and the preview error for primitive patterns. Several patterns in one label (`case A _, B _`) are unsupported. |
| **Sealed classes and interfaces** (JLS 8.1.1.2, 9.1.1.4) | Supported | `sealed` with a `permits` clause, or without one, permitting the file's classes that extend it; `non-sealed`; records and enums as permitted subclasses. javac's errors: `class is not allowed to extend sealed class: S (as it is not listed in its 'permits' clause)`, `sealed, non-sealed or final modifiers expected`, `invalid permits clause` (with its reason), `sealed class must have subclasses`, `non-sealed modifier not allowed here`, `illegal combination of modifiers: final and sealed`, `local classes must not extend sealed classes`, `anonymous classes must not extend sealed classes`, and a sealed interface is not a functional interface. |
| `throw`, `try`/`catch`/`finally`, multi-catch (JLS 14.18, 14.20) | Supported | `catch` matches the thrown object's class, inner handlers first; `finally` runs on every way out: normally, by an exception, `return` (after the value is computed), `break` and `continue`. A `return` or exception in `finally` replaces the one in flight, as in Java. `throw null` is `Cannot throw exception because "<local1>" is null`. javac's slots are kept: a catch parameter and each copy of a `finally` block get the slots javac gives them, so NPE messages name the same `<localN>`. javac's errors: `exception X has already been caught`, `Alternatives in a multi-catch statement cannot be related by subclassing`, `multi-catch parameter e may not be assigned`, `'try' without 'catch', 'finally' or resource declarations`, `incompatible types: String cannot be converted to Throwable`. |
| Try-with-resources (JLS 14.20.3) | Supported | Resources are a program class implementing `AutoCloseable`, a `Scanner`, a java.io reader or writer, or an effectively final variable. They close in reverse order, also when the block throws; an exception from `close()` is added to the one in flight with `addSuppressed`, and printed as `Suppressed:`. `try-with-resources not applicable to variable type`. |
| Checked exceptions (JLS 11.2) | Supported | Checked as javac checks them, after its other flow errors: `unreported exception X; must be caught or declared to be thrown` (also for the implicit `close()` of a resource), `exception X is never thrown in body of corresponding try statement`, `overridden method does not throw X`. Library methods declare theirs (`BufferedReader.readLine() throws IOException`). `throws` clauses, and precise rethrow: `catch (Exception e) { throw e; }` throws only what the block can throw. |
| `assert` | Supported | Checked, and not run: `java Main.java` runs with assertions disabled. |
| `synchronized` methods | Supported | No effect, since a program has one thread; javac's errors for them are kept. `synchronized` statements are unsupported. |
| All `int` operators, `char` arithmetic, comparison, `&&`/`\|\|`, `++`/`--` on locals, fields and array elements, every compound assignment | Supported | Operands are evaluated left to right; `a[i++] += 5` evaluates `a` and `i` once. |
| `new C(args)` as a statement | Supported | JLS 14.8. |
| `?:` (JLS 15.25) | Supported with limits | Two booleans give a boolean; numbers follow the table (`t ? 'a' : 0` is a `char`, `t ? 'a' : i` an `int`, `t ? 1 : 2L` a `long`). Reference operands are checked against the assignment or return type (`incompatible types: bad type in conditional expression`), otherwise their type is the common one. Constant conditionals fold. A primitive with a reference boxes or unboxes as JLS 15.25 says (`t ? 1 : null` is an `Integer`). A boolean with a number is unsupported. |
| `this` | Supported | |
| Lambda expressions (JLS 15.27) | Supported with limits | `x -> expression`, `(a, b) -> { statements }`, with parameter types inferred from the target or written out exactly, for any functional interface: the library's (`Comparator`, `Runnable`, `java.util.function`'s `Function`, `BiFunction`, `Supplier`, `Consumer`, `BiConsumer`, `Predicate`, `BiPredicate`, `UnaryOperator`, `BinaryOperator`, the `Int…`, `Long…`, `Double…` and `To…` forms) or the program's own (an interface with one abstract method, whose default methods then run on the lambda). **Captures:** effectively final locals and parameters of the code around it, and `this` with instance members; javac's `local variables referenced from a lambda expression must be final or effectively final` and `might not have been initialized`. Each compiles to a synthetic method named as javac names it (`lambda$main$0`, `lambda$static$0`, `lambda$new$0`), its captured variables passed first in order of first use, so stack traces and NPE messages (`"<parameter2>"`) match. A lambda that captures nothing is one object; one that captures is a new object each time. javac's errors: `incompatible parameter types in lambda expression`, `bad return type in lambda expression`, `lambda body is not compatible with a void functional interface`, `X is not a functional interface` (with `multiple non-overriding abstract methods found` or `no abstract method found`), `lambda expression not expected here`. Generic methods infer their type arguments from lambdas as javac does where the target or the lambda decides (`Comparator.comparing(p -> p.age())` inside `list.sort(...)`; as a receiver of `.reversed()` the lambda's parameter is an `Object`, as in javac). The program's generic interfaces are targets too (`Transformer<String, Integer> len = String::length`). Lambdas implementing `Iterable` are unsupported. |
| Method references (JLS 15.13) | Supported | `Integer::compare`, `String::length`, `Main::helper`, `this::visit`, `super::toString`, `list::add`, `System.out::println`, `ArrayList::new`, `int[]::new`, `String[]::new`. They resolve as javac resolves them (a static method taking every parameter, or an instance method of the first), with javac's `invalid method reference` errors (`cannot find symbol`, `reference to … is ambiguous`, `unexpected instance method … found in unbound lookup`, `bad return type in method reference`). As on the JDK they have no frame of their own, and a null receiver is a `NullPointerException` without a message. An exact reference (`Person::age`) decides a generic method's type arguments (`Comparator.comparing(Person::age).reversed()`); one to a generic class's method (`Pair::first`, `Box::get`) takes its types from the receiver the function type has. Explicit type arguments (`Foo::<T>bar`) and references on a parameterized type (`Pair<String, Integer>::first`) are unsupported. |

## Library

| API | Status | Notes |
| --- | --- | --- |
| `IO.println(Object)`, `IO.println()`, `IO.print(Object)` (`java.lang.IO`, Java 25) | Supported | Arguments box, so `IO.println(42)` and `IO.println('c')` work. An array prints as `type@hash` (even a `char[]`, unlike `System.out.println`). The output goes to System.out. |
| `IO.readln()`, `IO.readln(String prompt)` (Java 25) | Supported | Reads a line of standard input (below), ended by `\n`, `\r` or `\r\n`, without its terminator; `null` at the end of input. The prompt is printed first, without a newline (`null` prints as `null`). |
| Standard input (`System.in`) | Supported with limits | The run's input is given in full before it starts (code-playpad's input box); reading never waits or prompts, as with input piped to `java Main.java`. A program that reads past the end gets the JDK's exception, and the run result carries a hint about the input box (`hintAbout: 'stdin'`). `System.in` can be passed to a `Scanner` or an `InputStreamReader`; its own methods (`read`, …) are unsupported. One reader per run: a second `Scanner` or `BufferedReader` over `System.in`, or `IO.readln` together with either, is an engine limitation (below). |
| `java.util.Scanner` | Supported with limits | Over `System.in` or a `String`: `next`, `nextLine`, `nextInt`, `nextLong`, `nextDouble`, `nextBoolean`, their `hasNext…` forms, `hasNextLine`, `close`. Tokens are separated by Java's default delimiter (every `Character.isWhitespace` character, such as U+2003 but not the no-break space U+00A0), and `nextLine` ends lines at `\r\n`, `\n`, `\r`, U+2028, U+2029 and U+0085, so `nextInt()` followed by `nextLine()` returns the rest of the line, often empty, as in Java. The JDK's exceptions: `NoSuchElementException` (`No line found` from `nextLine`), `InputMismatchException` without consuming the token (`For input string: "99999999999"` when out of range), `IllegalStateException: Scanner closed`. Numbers are read as under the reference JDK's English locale (owner decision, 2026-10-18): a sign, digits with optional `,` group separators in groups of three (`1,234,567`; `12,34` is not a number), and for doubles a `.` decimal point, an exponent, `NaN`, `Infinity`, or the locale's `∞` (which matches and then fails to parse, as on the JDK). Hexadecimal floating point (`0x1.8p1`) is read as Java reads it; non-ASCII digits are an engine limitation. `nextFloat`, `nextByte` and `nextShort` (and their `hasNext…` forms) read as the JDK does, with Byte's and Short's `Value out of range. Value:"200" Radix:10`. Radix overloads, `useDelimiter`, `findInLine` and the rest are unsupported. |
| `java.io.BufferedReader`, `InputStreamReader`, `StringReader` | Supported | Over `System.in` or a `String`: `readLine` (lines end at `\n`, `\r` or `\r\n`; null at the end), `read()`, `read(char[][, off, len])` (everything requested that the input has), `ready`, `skip`, `lines()` (read when the stream's terminal operation runs), `close`. They throw `IOException`, which must be caught or declared (`unreported exception IOException`). After `close`: `IOException: Stream closed`. Their messages for bad arguments are the JDK's (`Range [2, 2 + 5) out of bounds for length 4`, `Buffer size <= 0`). Charsets, `mark` and `reset` are unsupported. |
| `java.io.PrintWriter`, `BufferedWriter`, `OutputStreamWriter`, `StringWriter` | Supported | Over `System.out`, `System.err`, each other, or a `StringWriter`: every `print`/`println` overload, `printf`, `format`, `write`, `append`, `newLine`, `flush`, `close`, `checkError`, autoflush. They buffer as the JDK's do (ADR 0016): what is not flushed when the program ends is lost, and exactly the part the JDK writes out reaches the output (4,000 unflushed lines print their first 24,576 bytes). `PrintWriter` never throws `IOException`; the others do, after `close`. File names and charsets are unsupported, as is `StringWriter.getBuffer()`. |
| `java.util.StringTokenizer` | Supported | `hasMoreTokens`, `nextToken`, `nextToken(delimiters)`, `hasMoreElements`, `nextElement`, `countTokens`; the default delimiters `" \t\n\r\f"`, custom ones, and returning delimiters as tokens. `NoSuchElementException` at the end; the JDK's `NullPointerException`s for a null string or delimiters. |
| `java.io.StreamTokenizer` | Supported with limits | `nextToken`, the public fields `ttype`, `nval` and `sval`, `TT_EOF`, `TT_EOL`, `TT_NUMBER`, `TT_WORD`, `pushBack`, `lineno`, `toString`, and the syntax methods (`wordChars`, `whitespaceChars`, `ordinaryChar(s)`, `commentChar`, `quoteChar`, `parseNumbers`, `resetSyntax`, `eolIsSignificant`, `lowerCaseMode`). Numbers have the JDK's exact values, including its rounding of long digit strings. `slashStarComments` and `slashSlashComments` are unsupported. |
| `System.out` / `System.err`: `print` and `println` | Supported | Overloads are resolved as in javac across all nine (`println` also has a no-argument form): `println('x')` prints the char, `println(charArray)` prints the characters, and other arrays print as `type@hash`. `println(null)` is ambiguous, as in Java, and so is a generic call whose result only an implicit lambda or an inexact method reference decides (`println(stream.collect(collectingAndThen(toList(), l -> …)))`), with javac's two errors; one a constructor reference decides (`println(stream.collect(groupingBy(f, TreeMap::new, counting())))`) chooses `println(String)` and fails, as javac does (see Methods). |
| `System.out` / `System.err` as values; `flush()`, `close()`, `checkError()` | Supported | Passed to `PrintWriter` or `OutputStreamWriter` as the `OutputStream` they are. `System.out` prints at once, so `flush()` has nothing to do. Closing it, directly or through a writer over it, drops everything printed to it afterwards, as on the JDK; a closed `System.err` drops an uncaught exception's trace too. `PrintStream` as a declared type, `write` and `append` are unsupported. |
| `System.out` / `System.err`: `printf(String, Object...)`, `format(String, Object...)` | Supported with limits | As `String.format`, below. The call is a statement: the `PrintStream` it returns cannot be used (`System.out.printf(...).println()` is unsupported). The `Locale` overloads are unsupported. |
| `String.length()`, `charAt(int)`, `equals(Object)`, `isEmpty()`, `toCharArray()`, `compareTo(String)` | Supported | `equals` boxes primitives, so `"x".equals(5)` is `false`, as in Java. `compareTo` gives the JDK's values (the first differing character's difference, else the length difference) and its NPE messages. These need no Java library. |
| Other `String` methods | Supported | `substring`, `indexOf` and `lastIndexOf` (of a `String` or a character, from an index), `contains`, `startsWith`, `endsWith`, `toUpperCase`, `toLowerCase`, `trim`, `strip`, `stripLeading`, `stripTrailing`, `isBlank`, `repeat`, `lines` (a `Stream<String>`, lines ended by `\n`, `\r` or `\r\n`), `stripIndent`, `translateEscapes` (`Invalid escape sequence: \q \\u0071`), `indent`, `replace` (chars or strings), `equalsIgnoreCase`, `compareToIgnoreCase`, `concat`, `hashCode`, `toString`, `split` (with and without a limit), `replaceAll`, `replaceFirst`, `matches`, `formatted`; statics `valueOf` (every supported type, `char[]` and a range of one), `copyValueOf`, `format`, `join` (varargs, an array, or an `Iterable` such as a `List<String>`); constructors `()`, `(String)`, `(char[])`, `(char[], int, int)`, `(StringBuilder)`. The JDK's exceptions and NPE messages: `Range [4, 3) out of bounds for length 3` (from `"abc".substring(4)`), `Range [1, 1 + 5) out of bounds for length 2`, `count is negative: -1`, `Cannot invoke "String.isEmpty()" because "this.pattern" is null`. `split` follows Java's rules (no leading empty string from a zero-width match, trailing empty strings removed when the limit is 0). Case mapping is the JDK's root-locale mapping (`"straße".toUpperCase()` is `STRASSE`). Parameters the JDK declares as `CharSequence` take a `String` here. `intern()` returns the run's one String of that value, the same object as an equal literal. `String.CASE_INSENSITIVE_ORDER` (exact for Latin-1). `chars()` (an `IntStream`), `codePointAt` (`Index 5 out of bounds for length 3`), `getBytes()` (UTF-8, a lone surrogate encoded as `?`), `transform`. `getBytes(String)` and the other charset forms, `codePoints`, `regionMatches`, `contentEquals`, `subSequence` and the rest are unsupported. |
| Regular expressions (`split`, `replaceAll`, `replaceFirst`, `matches`, `java.util.regex`) | Supported with limits | Java's syntax, translated to JavaScript's engine with Java's meanings: `.` and `$` know Java's line terminators (including `\r\n` and U+0085), `\s` is ASCII whitespace, `\h`, `\v`, `\R`, `\A`, `\z`, `\Z`, `\Q…\E`, POSIX classes (`\p{Alpha}`, `\p{Upper}`, …), named groups and back references, a leading `(?i)` or `(?s)`. Replacements as Java reads them: `$1`, `${name}`, `\$`, with `IllegalArgumentException: Illegal group reference` and `No group 2`. Malformed patterns are `PatternSyntaxException` with the JDK's description, index and caret (`Unclosed group near index 1`, `Dangling meta character '*' near index 0`). Constructs that do not translate exactly are engine limitations (below). **`Pattern` and `Matcher`:** `Pattern.compile` (with `CASE_INSENSITIVE`, `LITERAL` and `DOTALL`; the other flags are limitations), `matches`, `quote`, `matcher`, `split`, `pattern`, `flags`; `find` (after an empty match, one character further on, as the JDK's), `find(int)`, `matches`, `lookingAt`, `group`, `start` and `end` by number and by name, `groupCount`, `reset`, `replaceAll` (a String or a `Function<MatchResult, String>`), `replaceFirst`, `appendReplacement` and `appendTail`, `results()`, `toMatchResult()`, `toString` (`java.util.regex.Matcher[pattern=z region=0,3 lastmatch=]`), with the JDK's `No match found`, `No group 3`, `No group with name <x>`, `Illegal start index`. Regions, `hitEnd`, `usePattern`, `asPredicate` and `PatternSyntaxException`'s `getIndex` and friends are unsupported. |
| `String.format`, `formatted` | Supported with limits | Conversions `%d %o %x %X %s %S %c %C %b %B %h %H %e %E %f %g %G %n %%`, with argument indexes (`%2$s`, `%<s`), flags (`-#+ 0,(`), width and precision. Floating point rounds as Java's Formatter does, half-up on the shortest decimal: `%.2f` of `1.005` is `1.01` (C's printf gives `1.00`). The JDK's exceptions: `IllegalFormatConversionException: d != java.lang.String`, `MissingFormatArgumentException: Format specifier '%d'`, `UnknownFormatConversionException: Conversion = 'q'`, `FormatFlagsConversionMismatchException`, and others. Date and time (`%t`) and hexadecimal floating point (`%a`) are engine limitations; the `Locale` overloads are unsupported. |
| `Math.abs`, `max`, `min` (int, long, double), `sqrt`, `floor`, `ceil`, `round(double)`, `Math.PI`, `Math.E` | Supported | Resolved against all of Math's overloads, so `Math.max(1, 2L)` is the long one and errors list every candidate as javac does. `Math.abs(Integer.MIN_VALUE)` is `MIN_VALUE`; `Math.round` rounds half up and saturates. These need no Java library. |
| Other `Math` methods | Supported with limits | `pow`, `exp`, `log`, `log10`, `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`, `sinh`, `cosh`, `tanh`, `cbrt`, `hypot`, `expm1`, `log1p`, `toRadians`, `toDegrees`, `signum`, `rint`, `ulp`, `nextUp`, `nextDown`, `random`; exact integer arithmetic (`addExact`, `subtractExact`, `multiplyExact`, `incrementExact`, `decrementExact`, `negateExact`, `toIntExact`, `absExact`) with `ArithmeticException: integer overflow` / `long overflow`; `floorDiv`, `floorMod` (`/ by zero`); `clamp` (`IllegalArgumentException: 5 > 2`). The transcendental functions may differ from the JDK in the last digit (see Known divergences). Their float overloads too. `ceilDiv` and `ceilMod` (int, long, and long with int). `fma`, `copySign`, `scalb`, `divideExact`, `ceilDivExact` and the rest are unsupported. |
| `System` methods | Supported | `currentTimeMillis`, `nanoTime`, `exit`, `arraycopy`, `lineSeparator` (`"\n"`), `identityHashCode`. `System.exit(n)` ends the run at once and prints nothing, as the JDK does; the run's exit code is `n` (status `exited` when `n` is not 0, `success` when it is). `arraycopy` has the JDK's checks and messages: `arraycopy: last source index 8 out of bounds for int[5]`, `arraycopy: type mismatch: can not copy int[] into long[]`, and overlapping copies within one array. `getProperty`, `getenv`, `setOut` and the others are unsupported. |
| `java.util.Objects` | Supported | `equals`, `hash` (`Arrays.hashCode` of its arguments, calling their `hashCode()`), `hashCode`, `toString` (with a default), `isNull`, `nonNull`, `requireNonNull` (with a message), `requireNonNullElse`, `checkIndex` (`Index 5 out of bounds for length 3`), `compare` with a comparator. |
| `java.util.Random` | Supported with limits | `nextInt()`, `nextInt(bound)`, `nextInt(origin, bound)`, `nextLong()` (and its bounded forms), `nextDouble()` (and its bounded forms), `nextBoolean`, `nextGaussian()`, `setSeed`; constructors `()` and `(long seed)`. A seeded generator produces exactly the JDK's sequence (the 48-bit generator the API documentation specifies). An unseeded one, like `Math.random()`, is unpredictable, as on the JDK. `nextFloat()`. The `ints`/`doubles` streams and `nextGaussian(mean, stddev)` are unsupported. |
| `Integer.MAX_VALUE`/`MIN_VALUE`/`SIZE`/`BYTES`, `Long.MAX_VALUE`/`MIN_VALUE`/`SIZE`/`BYTES`, `Character.MAX_VALUE`/`MIN_VALUE`/`MIN_RADIX`/`MAX_RADIX`/`SIZE`/`BYTES`, `Double.MAX_VALUE`/`MIN_VALUE`/`MIN_NORMAL`/`POSITIVE_INFINITY`/`NEGATIVE_INFINITY`/`NaN`/`SIZE`/`BYTES` | Supported | Constants, folded like any constant. Using one does not load the Java library. `Boolean.TRUE` and `Boolean.FALSE` are the cached `Boolean`s (`Boolean.TRUE == Boolean.valueOf(true)`). |
| `ArrayList`, `List` | Supported | `add(E)`, `add(int, E)`, `get`, `set`, `remove(int)`, `remove(Object)`, `size`, `isEmpty`, `contains`, `indexOf`, `lastIndexOf`, `clear`, `addAll`, `containsAll`, `removeAll`, `retainAll`, `iterator`, `listIterator` (from the start or an index: both directions, `nextIndex`/`previousIndex`, `set`, `add`, `remove`, failing fast as the JDK's), `getFirst`, `getLast`, `removeFirst`, `removeLast`, `addFirst`, `addLast`, `reversed()` (a live reverse-ordered view, `java.util.ReverseOrderListView`, whose changes go through), `sort(Comparator)` (natural order for null; it counts as a change, so sorting inside a for-each over the list is a `ConcurrentModificationException`), `subList` (a view: changes go through to the list and back, a structural change to the list makes the view throw `ConcurrentModificationException`, the JDK's range messages `fromIndex = -1`, `toIndex = 4`, `fromIndex(2) > toIndex(1)`; on every list kind, with the JDK's class names, `ArrayList$SubList`, `AbstractList$SubList` for `LinkedList`, `ImmutableCollections$SubList` for `List.of`, `AbstractList$RandomAccessSubList` for `Arrays.asList`), `equals`, `hashCode`, `toString`. Constructors `()`, `(int)`, `(Collection)`. Overload resolution decides as javac does: `list.remove(1)` removes at an index, `list.remove(Integer.valueOf(1))` removes an element. The JDK's exceptions: `Index 5 out of bounds for length 1`, `Index: 3, Size: 2` from `add(int, E)`, `Illegal Capacity: -1`. The iterator fails fast exactly as the JDK's does: removing the second-to-last element during for-each does not throw. |
| `TreeMap`, `SortedMap`, `NavigableMap` | Supported | Natural order or a `Comparator`: `put`, `get`, `getOrDefault`, `putIfAbsent`, `containsKey`, `containsValue`, `remove` (both forms), `replace`, `putAll`, `size`, `isEmpty`, `clear`, `firstKey`, `lastKey`, `floorKey`, `ceilingKey`, `lowerKey`, `higherKey` and their `…Entry` forms, `firstEntry`, `lastEntry`, `pollFirstEntry`, `pollLastEntry`, `comparator`, `keySet`, `navigableKeySet`, `descendingKeySet`, `values`, `entrySet`, `equals`, `hashCode`, `toString`. The live range views `headMap`, `tailMap`, `subMap` (with or without inclusive bounds) and `descendingMap`, as deep as wanted, with the JDK's `key out of range`, `fromKey out of range`, `toKey out of range` and `fromKey > toKey`. Constructors `()`, `(Comparator)`, `(Map)`, `(SortedMap)` (which keeps the comparator). The JDK's exceptions: `ClassCastException` for keys that are not Comparable or not mutually comparable, `NullPointerException` for a null key in natural order (`Cannot invoke "java.lang.Comparable.compareTo(Object)" because "k1" is null` when the map is empty), `NoSuchElementException` from `firstKey` of an empty map, `UnsupportedOperationException` from `setValue` on `firstEntry()`'s snapshot. Iterators fail fast as the JDK's do, and removing through one continues correctly. `comparator()` of a descending view is an engine limitation. |
| `TreeSet`, `SortedSet`, `NavigableSet` | Supported | `add`, `remove`, `contains`, `size`, `isEmpty`, `clear`, `first`, `last`, `floor`, `ceiling`, `lower`, `higher`, `pollFirst`, `pollLast`, `iterator`, `descendingIterator`, `comparator`, `reversed()` (its `descendingSet()`), `headSet`, `tailSet`, `subSet` (live views, with or without inclusive bounds), `descendingSet`, `addAll`, `containsAll`, `toArray`, `equals`, `hashCode`, `toString`. Constructors `()`, `(Comparator)`, `(Collection)`, `(SortedSet)` (which keeps the comparator). Exceptions as for `TreeMap`. |
| `LinkedList` | Supported | A `List` and a `Deque`: every `ArrayList` method above, and `offer`, `offerFirst`, `offerLast`, `push`, `pop`, `poll`, `pollFirst`, `pollLast`, `peek`, `peekFirst`, `peekLast`, `element`, `remove()`, `removeFirstOccurrence`, `removeLastOccurrence`, `descendingIterator`, `reversed()` (a live view that is a `List` and a `Deque`). Null elements are allowed. Messages as the JDK's: `Index: 7, Size: 3` for an index out of range, `NoSuchElementException` from `getFirst`, `removeFirst`, `pop` or `element` on an empty list (`peek` and `poll` return null). Iteration fails fast as `ArrayList`'s does, and `sort` is not a structural change, so sorting during for-each does not throw. |
| `Vector`, `Stack`, `Enumeration` | Supported | `Vector`: every `List` method, constructors `()`, `(int)` (`Illegal Capacity: -1`), `(int, int)` (a capacity increment) and `(Collection)`, `elements()` (an `Enumeration` that reads the vector as it is, `NoSuchElementException: Vector Enumeration` past the end), `setSize`, `trimToSize`, `ensureCapacity`. `Stack`: `push`, `pop`, `peek` (`EmptyStackException` when empty), `empty`, `search`, and Vector's `elementAt`, `setElementAt`, `insertElementAt`, `removeElementAt`, `addElement`, `removeElement`, `removeAllElements`, `firstElement`, `lastElement`, `capacity`. Its capacity starts at 10 and grows by the increment or doubles, and shows in its messages: `Array index out of range: 5`, `Index -1 out of bounds for length 10`, `5 >= 2`, `5 > 2`. |
| `ArrayDeque`, `Deque`, `Queue` | Supported with limits | Both ends (`addFirst`/`addLast`, `offerFirst`/`offerLast`, `pollFirst`/`pollLast`, `peekFirst`/`peekLast`, `getFirst`/`getLast`, `removeFirst`/`removeLast`), as a stack (`push`, `pop`, `peek`) and a queue (`offer`, `poll`, `peek`, `element`, `remove()`, `add`). Also `contains`, `remove(Object)`, `removeFirstOccurrence`, `removeLastOccurrence`, `size`, `isEmpty`, `clear`, `iterator`, `descendingIterator`, `addAll`, `removeAll`, `retainAll`, `reversed()` (a live view), `toString`. Constructors `()`, `(int)`, `(Collection)`. Null elements are an NPE, and an empty deque's `pop()` is `NoSuchElementException`, as in the JDK. Changing a deque while iterating over it is an engine limitation. |
| `PriorityQueue` | Supported with limits | Natural order or a `Comparator`: `offer`, `add`, `poll`, `peek`, `element`, `remove()`, `remove(Object)`, `contains`, `size`, `isEmpty`, `clear`, `iterator`, `addAll`, `removeAll`, `retainAll` (the survivors re-heapified as the JDK does), `toString`. Constructors `()`, `(int)`, `(Comparator)`, `(int, Comparator)`, `(Collection)` (a priority queue is copied with its comparator). The heap is laid out as the JDK lays it out, so it prints in the same order (`[1, 3, 2, 5, 9, 8, 7]`). Elements are `String`, `StringBuilder` or wrapper values; the JDK's `ClassCastException` for arrays and library objects is reproduced. In natural order, objects of the program's own classes are an engine limitation; give the queue a comparator for them. |
| `HashMap`, `Map`, `LinkedHashMap` | Supported with limits | `get`, `put`, `getOrDefault`, `putIfAbsent`, `containsKey`, `containsValue`, `remove(Object)`, `remove(key, value)`, `replace` (both forms), `putAll`, `size`, `isEmpty`, `clear`, `keySet`, `values`, `entrySet`, `equals`, `hashCode`, `toString`. Constructors `()`, `(int)`, `(int, float)` (`Illegal initial capacity: -1`, `Illegal load factor: 0.0`), `(Map)`, and `LinkedHashMap(int, float, boolean)`. **Iteration order is the JDK's:** the table is laid out as JDK 25 lays it out (power-of-two buckets, the spread hash, chains appended in order, doubling at three quarters full with each chain split in order, a bucket of 9 or more colliding keys turned into a red-black tree whose root comes first), so a map prints and iterates as on the JDK, `{over=1, mat=1, a=1, quick=1, …}`. `LinkedHashMap` iterates in insertion order, or, made with `accessOrder` true, in access order: `get`, `getOrDefault`, `put` of an existing key and the other reads move an entry to the end, a structural change as on the JDK (a `get` while iterating is a `ConcurrentModificationException`). A load factor sizes the table as the JDK's does, and a copy (`new HashMap<>(map)`, `putAll`) is sized as JDK 25 sizes it. The views write through: `keySet().remove`, `values().remove`, an iterator's `remove`, and `Map.Entry.setValue`. Their iterators fail fast exactly as the JDK's: removing the last key during for-each does not throw, adding one does. Lookups are exact: `Integer` 1 and `Long` 1 are different keys, and so are `0.0` and `-0.0`. Null keys and values work, and a key's declared `hashCode()` runs where the JDK runs it. Where the JDK's order cannot be known, iterating is an engine limitation (below). The lambda-taking methods (`merge`, `compute…`, `forEach`) are under *Higher-order collection methods*. A subclass's `removeEldestEntry` is under *Library subclasses*. `LinkedHashMap`'s `reversed()` and the other sequenced views, and `putFirst`/`putLast`, are unsupported. |
| **Library subclasses** (ADR 0023) | Supported with limits | A class may extend `ArrayList`, `LinkedList`, `HashMap`, `LinkedHashMap`, `TreeMap`, `HashSet`, `LinkedHashSet`, `TreeSet`, `ArrayDeque` or `PriorityQueue`, named, anonymous or generic (`class Bag<T> extends ArrayList<T>`, `new HashMap<>() {{ put("a", 1); }}`), and a program class may extend such a class in turn. It has its own fields, constructors (`super(16, 0.75f, true)`, or the implicit `super()`), and methods; it calls the inherited ones by simple name (`size()`, `merge(k, 1, Integer::sum)`), through `super.`, or from outside; it is an `instanceof` its library types and goes wherever they are expected (for-each, `new TreeMap<>(counter)`, `Collections.sort(this)`); it prints, compares and hashes as its library class does unless it overrides `toString`, `equals` or `hashCode`. **Overriding:** `LinkedHashMap.removeEldestEntry`, which the map asks after `put`, `putIfAbsent`, `putAll` (per mapping), `computeIfAbsent`, `compute` and `merge` add a mapping, as the JDK's does (an LRU cache: `super(capacity, 0.75f, true)` and `size() > capacity`); `ArrayList.add(E)`, `get(int)` and `size()`; `HashMap`'s and `LinkedHashMap`'s `put`, `get`, `containsKey` and `remove(Object)`; `TreeMap`'s `get` and `containsKey`. These are methods the JDK's own code in that class does not call on the object, so the rest of the class behaves the same. Overriding other library methods is unsupported (`overriding ArrayList.remove(int) is not supported in this version`). Where a library method given the object would call one of its overrides on the JDK (`Collections.addAll` calls `add`, a `Map`'s `equals` calls `get`, `Collections.unmodifiableList` calls everything), that call is an engine limitation rather than running without the override. `removeEldestEntry` is protected: javac's `has protected access` from other code. **`AbstractList`** (ADR 0025): a subclass implements `get` and `size` (javac's `is not abstract and does not override abstract method size() in AbstractCollection` otherwise) and may override `set`, `add(int, E)`, `remove(int)` and `add(E)`; the inherited `toString`, `equals`, `hashCode`, `contains`, `indexOf`, `lastIndexOf`, `isEmpty`, `iterator` (with `remove`), `forEach`, `add(E)` and `subList` call them back exactly as often and in the order the JDK's do. Passing such an object to a library method that reads it as a collection (`new ArrayList<>(range)`), and its other `List` methods (`stream`, `sort`, …), are engine limitations. `AbstractMap` and the other abstract collections are unsupported. |
| `Hashtable`, `Dictionary` | Supported | Every `Map` method, `contains`, and `keys()`/`elements()` enumerations, constructors `()`, `(int)` (`Illegal Capacity: -1`), `(int, float)` and `(Map)`. **Iteration order is the JDK's:** its own table (11 buckets, a key's hashCode modulo the length, new entries first in their bucket, growing to twice + 1 at three quarters full), iterated from the last bucket down; `forEach` and `replaceAll` go from the first bucket up, as the JDK's do. Enumerations read the table as it is (no `ConcurrentModificationException`); iterators fail fast. Null keys and values are the JDK's `NullPointerException`s (`Cannot invoke "Object.hashCode()" because "key" is null`). |
| `StringJoiner` | Supported | `add` (null is `"null"`), `merge`, `setEmptyValue`, `length`, `toString`; constructors with a delimiter, or with a prefix and suffix too, and the JDK's `The delimiter must not be null`. |
| `Map.Entry` | Supported | `getKey`, `getValue`, `setValue`, `equals`, `hashCode` (key's hash xor value's), `toString` (`k=v`). Named `Map.Entry`, or `Entry` after `import java.util.Map.Entry;`. |
| `HashSet`, `Set`, `LinkedHashSet` | Supported with limits | `add`, `contains`, `remove`, `size`, `isEmpty`, `clear`, `addAll`, `containsAll`, `equals`, `hashCode`, `iterator`, `toArray`, `toString`. `removeAll` and `retainAll`. Constructors `()`, `(int)`, `(int, float)`, `(Collection)`, sized as the JDK sizes them, so their order is the JDK's too. A `HashSet` is a `HashMap` of its elements, as in the JDK, and iterates in the same order; `LinkedHashSet` in insertion order. Membership is exact, and a `Set<List<Integer>>` compares lists by content. `LinkedHashSet`'s `reversed()` and its `addFirst`/`getFirst` family are unsupported. |
| `List.of`, `Set.of`, `Map.of`, `Map.ofEntries`, `Map.entry`, `copyOf` | Supported with limits | Immutable, as the JDK's: changing one is `UnsupportedOperationException`, null elements are `NullPointerException` (with the JDK's message for each size class: `Cannot invoke "Object.hashCode()" because "pk" is null` from a `Map.of` of several entries), `get` out of range is `Index: 1 Size: 1` or `Index 5 out of bounds for length 3`, duplicates are `IllegalArgumentException: duplicate element: 1` / `duplicate key: k`. `List.of` iterates in order. **Their order is the JDK's** (ADR 0026): a `Set.of` or `Map.of` lays its elements out by `hashCode()` and is read from a start and in a direction that a salt chosen once per run decides, as the JDK chooses it at startup. So the order differs from run to run, as on the JDK, and every order the engine prints is one the JDK prints (checked against every output of 248 JDK runs); a program whose output does not depend on the order prints what the JDK prints. `Set.copyOf` goes through a HashSet first, as the JDK's does. Elements hashed by identity (objects without `hashCode()`) are an engine limitation where the order shows; lookups, `size` and `equals` are exact. |
| `Arrays.asList` | Supported | A fixed-size list over the array itself: `set` and the array see each other's changes, `add` and `remove` are `UnsupportedOperationException`, `Collections.sort` sorts the array. `Arrays.asList(intArray)` is a `List<int[]>` of one element, as in Java. |
| `Collections` | Supported with limits | `sort`, `reverse`, `swap`, `max`, `min` (natural order or a `Comparator`), `frequency`, `addAll`, `fill`, `shuffle` (with a seeded `Random`, the JDK's permutation), `nCopies`, `emptyList`, `emptySet`, `emptyMap`, `singletonList`, `singleton`, `singletonMap`, `reverseOrder` (with or without a comparator), `binarySearch` (natural order or a `Comparator`, probing the middle elements the JDK probes), `disjoint`, and the `unmodifiableList`, `Set`, `Map` and `Collection` views, with the JDK's exceptions (`List length = -1`, `Index: 1, Size: 1`, `NoSuchElementException` from `max` of an empty collection). `rotate`, `copy`, `indexOfSubList`, the synchronized and checked views and the rest are unsupported. |
| `toArray()`, `toArray(T[])` | Supported | On every collection: a new `Object[]`, or the given array when the elements fit (the next slot set to null), else a new array of its type; an element it cannot hold is the JDK's `ArrayStoreException: arraycopy: element type mismatch: …`. |
| `Comparator`, `Collections.sort` | Supported | `compare`; `reversed`, `thenComparing` (a comparator, a key, or both), `thenComparingInt`/`Long`/`Double`; `Comparator.comparing` (with or without a key comparator), `comparingInt`/`Long`/`Double`, `naturalOrder`, `reverseOrder`, `nullsFirst`, `nullsLast`; `Map.Entry.comparingByKey`/`comparingByValue`; `Collections.reverseOrder`. They compare as their documentation specifies, calling the program's lambdas and methods as Java code, with the JDK's NPE messages (`Cannot invoke "java.lang.Comparable.compareTo(Object)" because the return value of "java.util.function.Function.apply(Object)" is null`). A comparator sort is TimSort, written from its published description with Java's parameters, so even a comparator that breaks its contract (`(a, b) -> a - b` overflowing) orders elements as the JDK does, is called as many times, and fails with the same `IllegalArgumentException: Comparison method violates its general contract!` where the JDK's does. An exception in a comparator shows the lambda's frame. |
| Higher-order collection methods | Supported | `forEach` (every collection and map, and iterators' `forEachRemaining`), `removeIf`, `List.replaceAll`, `Map.replaceAll`, `computeIfAbsent`, `computeIfPresent`, `compute`, `merge`, `toArray(String[]::new)`, and the functions' `andThen`, `compose`, `identity`, `and`, `or`, `negate`, `Predicate.not`, `Predicate.isEqual`, `BinaryOperator.minBy`/`maxBy`. Each follows its class's JDK behavior: `HashMap.merge`, `compute` and `computeIfAbsent` add a new key at the front of its bucket (unlike `put`), so a map built with them prints in the JDK's order; a function that changes the map is a `ConcurrentModificationException`; `HashMap.forEach` finishes before reporting a change, `ArrayList.forEach` stops at it, `TreeMap.forEach` reports it at once; `ArrayList.removeIf` tests every element before removing any. Immutable collections reject them with `UnsupportedOperationException`. |
| Streams (`java.util.stream`) | Supported with limits | `Stream`, `IntStream`, `LongStream`, `DoubleStream`, from `collection.stream()`, `Arrays.stream` (whole or a range), `Stream.of`, `IntStream.range`/`rangeClosed`/`of`, `iterate`, `generate`, `concat`, `empty`, `String.chars()`. Intermediate: `filter`, `map`, `mapToInt`/`Long`/`Double`/`Obj`, `boxed`, `asLongStream`/`asDoubleStream`, `flatMap`, `distinct`, `sorted` (natural or a `Comparator`), `peek`, `limit`, `skip`, `takeWhile`, `dropWhile`. Terminal: `forEach`, `collect`, `toList` (unmodifiable, nulls allowed), `toArray`, `reduce`, `count`, `sum`, `min`, `max`, `average`, `summaryStatistics`, `anyMatch`/`allMatch`/`noneMatch`, `findFirst`/`findAny`. A pipeline runs as the JDK's does, which a program can observe through side effects: nothing happens before the terminal operation; each element goes through every stage before the next (`filter 1`, `filter 2`, `map 2`, …); `sorted` takes every element, then passes them on; `limit`, `findFirst` and the matches stop the source early; and `count()` of a pipeline whose size is known without running it (no `filter`, `distinct` or `flatMap`) runs nothing, so a `peek` before it prints nothing. A stream used twice is the JDK's `IllegalStateException: stream has already been operated upon or closed`. `DoubleStream.sum` and `average` use the JDK's compensated summation. **Parallel streams** (`parallel()`, `parallelStream()`, `isParallel()`, `sequential()`) run sequentially, which gives every result the JDK's parallel run gives (sums, counts, `collect`, `reduce` with an associative operator, `sorted`, `forEachOrdered`, `findFirst`, `limit`); what the JDK's threads decide (the order of `forEach` and `peek`, which element `findAny` returns) is an engine limitation on a parallel stream, and `unordered()` is unsupported. A lambda's side effects run in encounter order, one legal order of the JDK's. `iterator()`, `mapMulti`, the three-argument `collect` and `reduce`, and `LongStream`/`DoubleStream`'s `iterate`/`generate`/`concat` are unsupported. |
| `Collectors` | Supported with limits | `toList`, `toUnmodifiableList`, `toSet`, `toCollection`, `toMap` (two, three or four arguments; a duplicate key is the JDK's `IllegalStateException: Duplicate key a (attempted merging values 1 and 1)`), `groupingBy` (one, two or three arguments), `partitioningBy` (`{false=..., true=...}`), `counting` (a `Long`), `summingInt`/`Long`/`Double`, `averagingInt`/`Long`/`Double`, `summarizingInt`/`Long`/`Double`, `joining` (with a delimiter, prefix and suffix), `mapping`, `filtering`, `flatMapping`, `collectingAndThen`, `reducing`, `minBy`, `maxBy`, `teeing` (`downstream1`, `merger` NPE messages). Maps are filled as the JDK fills them (`groupingBy` through `computeIfAbsent`, so a `HashMap` prints in the JDK's order). `toUnmodifiableSet`/`Map` and the concurrent collectors are unsupported. Type arguments are inferred as javac infers them in the common forms: `collect(Collectors.toList())`, `groupingBy(w -> w.charAt(0), TreeMap::new, Collectors.counting())`, `collectingAndThen(Collectors.toList(), List::size)`. |
| `Optional`, `OptionalInt`, `OptionalLong`, `OptionalDouble`, `IntSummaryStatistics` (and `Long`, `Double`) | Supported | `of`, `ofNullable`, `empty`, `get`/`getAsInt`/…, `isPresent`, `isEmpty`, `orElse`, `orElseGet`, `orElseThrow` (with or without a supplier), `ifPresent`, `ifPresentOrElse`, `map`, `flatMap`, `filter`, `or`, `stream`; printed as `Optional[3]`, `Optional.empty`, `OptionalDouble[1.5]`; `NoSuchElementException: No value present`. The statistics' `getCount`, `getSum`, `getMin`, `getMax`, `getAverage`, `accept`, and `IntSummaryStatistics{count=3, sum=80, min=20, average=26.666667, max=30}`. |
| `BitSet` | Supported | `set`, `clear`, `flip` (one bit or a range, `set(i, boolean)`), `get` (a bit, or a range as a new BitSet), `length`, `size`, `isEmpty`, `cardinality`, `nextSetBit`, `nextClearBit`, `previousSetBit`, `previousClearBit`, `and`, `or`, `xor`, `andNot`, `intersects`, `clone`, `valueOf(long[])`, `toLongArray`, `stream`, `toString` (`{1, 3, 64}`), `equals` and `hashCode` (the JDK's). Its words as the JDK lays them out: `size()` starts at 64 (or the bits asked for, rounded up to 64), grows to twice as many words or what is needed, and `clone()` trims it unless the size came from the constructor. The JDK's messages: `bitIndex < 0: -1`, `fromIndex: 5 > toIndex: 3`, `nbits < 0: -1`. |
| **`java.time`** (ADRs 0025, 0029, 0032) | Supported with limits | `LocalDate` (`of`, `parse`, `ofEpochDay`, `ofYearDay`, `now()` from the browser's clock, the getters, `plus`/`minus` days, weeks, months and years (the day clamped to the month: `2024-01-31` plus a month is `2024-02-29`), `plus(long, TemporalUnit)`, `plus(TemporalAmount)`, `with…`, `atTime`, `atStartOfDay`, `isBefore`/`isAfter`/`isEqual`, `until`, `datesUntil`, `format`, `toString` with the JDK's year signs, `hashCode`, Comparable). `LocalTime` (`of`, `ofSecondOfDay`, `ofNanoOfDay`, `parse`, `now()`, `MIN`/`MAX`/`MIDNIGHT`/`NOON`, the getters, `plus`/`minus` hours to nanoseconds around the clock, `with…`, `truncatedTo`, `until`, `atDate`, `toSecondOfDay`, `toNanoOfDay`, `format`, `toString` as the JDK's: `09:05`, `01:02:03.400`, `01:02:03.000000007`). `LocalDateTime` (`of` with all its overloads, `parse`, `now()`, the date's and the time's getters and `with…`, `plus`/`minus` from years to nanoseconds with the days carried, `until` that leaves out an incomplete last day, `truncatedTo`, `toLocalDate`/`toLocalTime`, `format`, `2024-01-31T22:30`). `Duration` (`of…`, `between` two times or date-times, `parse`, `plus`/`minus`, `multipliedBy`, `dividedBy`, `negated`, `abs`, `truncatedTo`, `to…` and `to…Part`, `PT2H15M` and `PT-1M-29.5S`, long overflow as the JDK's `ArithmeticException`). `Period` (`between` with the JDK's borrow, `of…`, the getters, `normalized`, `multipliedBy`, `P1Y2M3D`), `DayOfWeek` and `Month` (`of`, `getValue`, `plus`, `minus`, `length`), `ChronoUnit` (`between` for any of the three types; a unit a type does not have is the JDK's `UnsupportedTemporalTypeException: Unsupported unit: Hours`), and `DateTimeFormatter` (`ofPattern` with the letters `y u M L d D E H h k K m s S n a`, quoted text and optional sections (`yyyy-MM-dd[ HH:mm[:ss]]`: printed when the value has their fields, tried and skipped when parsing), `ISO_LOCAL_DATE`, `ISO_LOCAL_TIME`, `ISO_LOCAL_DATE_TIME`). The parameter types are the JDK's interfaces (`Temporal`, `TemporalAmount`, `TemporalUnit`, `TemporalAccessor`), which a program can name. Mixing types works as on the JDK: `Duration.between(time, dateTime)` takes the date-time's time, the other way round is `Unable to obtain LocalDateTime from TemporalAccessor`, and `date.plus(duration)` is `Unsupported unit: Seconds`. Parsing resolves as the JDK's: an `ofPattern` formatter clamps `31/04` to April 30, the ISO ones reject `2024-02-30`, `h` needs `a`; the JDK's `DateTimeException` and `DateTimeParseException` messages, with the cause, and helpful `NullPointerException` messages. A switch on `DayOfWeek`, `Month`, `ChronoUnit` or `RoundingMode` takes their constants as labels. `now()` reads the clock to the millisecond (the JDK's to the microsecond). `Instant` (`ofEpochSecond`, `ofEpochMilli`, `parse`, `now()`, `EPOCH`/`MIN`/`MAX`, `plus`/`minus` up to days, `until`, `truncatedTo`, `atZone`, `toEpochMilli`, `2023-11-14T22:13:20.123Z`). `ZoneId` (`of` with region IDs, offsets and `UTC`/`GMT`/`UT` prefixes, `systemDefault()`, `normalized`, the JDK's `ZoneRulesException: Unknown time-zone ID` and invalid-format messages), `ZoneOffset` (`of` read as the JDK reads it, `ofHours`, `ofHoursMinutes`, `ofHoursMinutesSeconds`, `ofTotalSeconds`, `UTC`/`MIN`/`MAX`, the JDK's range and sign messages) and `ZonedDateTime` (`of`, `ofInstant`, `parse`, `now`, the getters, `withZoneSameInstant`/`withZoneSameLocal`, `withEarlierOffsetAtOverlap`/`withLaterOffsetAtOverlap`, `withFixedOffsetZone`, `plus`/`minus` with dates local and times on the instant, `until`, `truncatedTo`, `toInstant`, `toEpochSecond`, `2024-03-10T03:30-04:00[America/New_York]`). A zone's rules are the browser's (`Intl`): a daylight saving gap moves the time forward and an overlap keeps the earlier offset, as on the JDK; the browser's time zone data can be a different version from the JDK's. `LocalDateTime.atZone`, `toInstant(ZoneOffset)`, `toEpochSecond(ZoneOffset)`, `ofInstant`, `ofEpochSecond`, `LocalDate.atStartOfDay(ZoneId)`, `now(ZoneId)` and `from` on each type. The formatter's zone letters `VV`, `X`, `x` and `Z`, and `ISO_OFFSET_DATE_TIME`, `ISO_ZONED_DATE_TIME` and `ISO_INSTANT`. `ZoneRules` (`getRules()`), `OffsetDateTime` and `OffsetTime` (they exist only so that `date.atTime(null)` is ambiguous, as in javac), `Clock`, the localized letters `O`, `z`, `v` and `ZZZZ`, other pattern letters, `getDisplayName` and printing a formatter are unsupported or engine limitations, as is a parse whose JDK message lists fields it could not resolve (the JDK's order of them changes from run to run). |
| `EnumMap`, `EnumSet` | Supported | An enum's constants in ordinal order. `EnumMap(Class)`, `(EnumMap)`, `(Map)` (an empty non-EnumMap is `IllegalArgumentException: Specified map is empty`), with every `Map` method; a null key is the JDK's NPE on `put` and simply absent on `get`, `containsKey` and `remove`. `EnumSet.noneOf`, `allOf`, `of` (one to five constants, or varargs), `range` (`C > A` when reversed), `complementOf`, `copyOf` (`Collection is empty`), `clone`, with every `Set` method plus `removeAll` and `retainAll`; its iterator walks a snapshot, so removing while iterating never fails, as on the JDK. `getClass()` is `java.util.EnumMap` and `java.util.RegularEnumSet` (`JumboEnumSet` above 64 constants). |
| `java.math.BigInteger` | Supported | `ZERO`, `ONE`, `TWO`, `TEN`, `valueOf` (sharing -16..16 as the JDK does, so `valueOf(1) == ONE`), `new BigInteger(String[, radix])` with the JDK's messages (`Zero length BigInteger`, `For input string: "x10"` naming the failing digit group, `Radix out of range`, `Illegal embedded sign character`); `add`, `subtract`, `multiply`, `divide`, `remainder`, `mod`, `divideAndRemainder`, `pow`, `modPow`, `modInverse`, `gcd`, `abs`, `negate`, `signum`, `sqrt`, `min`, `max`, `compareTo`, `equals`, `hashCode` (the JDK's), `toString` (any radix), the `…Value` and `…ValueExact` conversions, bit operations (`shiftLeft`, `and`, `testBit`, `bitLength`, `bitCount`, …), `isProbablePrime`, `nextProbablePrime`, with the JDK's `ArithmeticException` messages. `%d`, `%x` and `%o` format it (signed: `-ff`). Comparable, so it sorts and keys TreeMaps; content-equal in HashMaps. Non-ASCII digits are an engine limitation; `probablePrime` and `toByteArray` are unsupported. |
| `java.math.BigDecimal`, `RoundingMode`, `MathContext` | Supported | A BigInteger unscaled value and a scale, as the JDK's: `new BigDecimal(String)` (with its messages), `(double)` (the double's exact value: `0.1000000000000000055511151231257827021181583404541015625`), `(int)`, `(long)`, `(BigInteger[, scale])`, `valueOf(double)` (via `Double.toString`), `valueOf(long[, scale])` (sharing 0..10 as the JDK does); `add`, `subtract`, `multiply` (scale rules as documented), `divide` exact (the preferred scale, or the smallest that holds the quotient: `5 / 0.25` is `2E+1`; `Non-terminating decimal expansion; no exact representable decimal result.`), with a scale and `RoundingMode`, or a `MathContext`; `divideToIntegralValue`, `remainder`, `setScale` (`Rounding necessary`), `round`, `sqrt`, `pow`, `movePointLeft`/`Right`, `scaleByPowerOfTen`, `stripTrailingZeros`, `precision`, `unscaledValue`, `compareTo` (by value) and `equals` (value and scale: `1.10` is not `1.1`), `hashCode` (the JDK's), `toString` (scientific when the JDK's is: `1E+2`, `1.234E-7`), `toPlainString`, the conversions. All eight `RoundingMode`s (`values()`, `valueOf`, `name`, `ordinal`) and `MathContext` (`DECIMAL32`, `DECIMAL64`, `DECIMAL128`, `UNLIMITED`, its constructors). `%f`, `%e` and `%g` format a BigDecimal from its exact digits. Scanner's `nextBigInteger` and `nextBigDecimal` read them. The `MathContext` forms of `add` and the other arithmetic, `ulp` and `toEngineeringString` are unsupported. |
| `Iterator`, `ListIterator`, `Iterable`, `Collection` | Supported | `hasNext`, `next`, `remove`, with the JDK's `NoSuchElementException`, `IllegalStateException` and `ConcurrentModificationException`; a `ListIterator` adds `hasPrevious`, `previous`, `nextIndex`, `previousIndex`, `set` and `add`. `Collection`'s `removeAll` and `retainAll` on every collection (`UnsupportedOperationException` on the immutable and unmodifiable ones). `parallelStream()` is a parallel stream (*Streams*). `spliterator` is unsupported. |
| `StringBuilder`, `StringBuffer` | Supported | `append` (every supported type, `char[]` and a range of one, a `CharSequence` and a range of one, a `StringBuffer`, and objects through their `toString()`, with javac's overloads), `insert`, `delete`, `deleteCharAt`, `replace`, `reverse` (surrogate pairs stay in order), `charAt`, `setCharAt`, `setLength`, `length`, `isEmpty`, `indexOf`, `lastIndexOf`, `substring`, `compareTo`, `equals` and `hashCode` (identity), `toString`, and `capacity`, `ensureCapacity`, `trimToSize` with the JDK's sizes (16, `n`, or a String's length + 16; growing to twice + 2, or what is needed; `append(double)` makes room for 24 chars first, `append(float)` for 15). Constructors `()`, `(int)`, `(String)`. `StringBuffer` has the same methods and its own class. Index errors with the JDK's messages: `Index 10 out of bounds for length 3`, `Range [4, 3) out of bounds for length 3`, `String index out of range: -1`. |
| `Number`, `Integer`, `Long`, `Double`, `Float`, `Byte`, `Short`, `Boolean` methods | Supported with limits | `Number` is the numeric wrappers' superclass, with `byteValue`, `shortValue`, `intValue`, `longValue`, `floatValue` and `doubleValue` (`List<Number>`, `T extends Number`, `List<? extends Number>`; `new Number()` is javac's `Number is abstract; cannot be instantiated`). Statics: `parseInt` and `parseLong` (with a radix), `parseDouble`, `parseBoolean`, `valueOf`, `toString`, `compare`, `max`, `min`, `sum`, `signum`, `bitCount`, `toBinaryString`, `toHexString`, `toOctalString`, `numberOfLeadingZeros`, `numberOfTrailingZeros`, `highestOneBit`, `reverse`, `hashCode`, `isNaN`, `isInfinite`, `isFinite`, `logicalAnd`, `logicalOr`, `logicalXor`. On boxed values: `intValue`, `longValue`, `doubleValue`, `charValue`, `booleanValue`, `isNaN`, `isInfinite`, `compareTo`, `equals`, `hashCode`, `toString`. The JDK's `NumberFormatException` messages: `For input string: "12a"`, `… under radix 16`, `Cannot parse null string`, `empty String`. `Float`: `parseFloat` (rounded once, never through double), `floatToIntBits`, `floatToRawIntBits`, `intBitsToFloat`, and Double's other statics; `Float.valueOf` never caches, as on the JDK. `Byte` and `Short`: `parseByte`/`parseShort` (with a radix; `Value out of range. Value:"128" Radix:10`), `toUnsignedInt`, `toUnsignedLong`, and the caches (every `Byte`, `Short` in -128..127). `parseDouble` and `parseFloat` read hexadecimal strings, and report `multiple points` as the JDK does. |
| `Character` methods | Supported with limits | `isLetter`, `isDigit`, `isLetterOrDigit`, `isAlphabetic`, `isUpperCase`, `isLowerCase`, `isWhitespace`, `isSpaceChar`, `toUpperCase`, `toLowerCase` (the `char` and `int` forms), `getNumericValue`, `digit`, `forDigit`, `valueOf`, `toString`, `compare`, `hashCode`, `toChars` (`Not a valid Unicode code point: 0x110000`). Exact for Latin-1 (U+0000 to U+00FF), whose answers were recorded from JDK 25: `é` is a letter, `ÿ` upper-cases to `Ÿ`, and `ß` stays `ß`. Other characters are an engine limitation (SPEC.md §4.2.2). |
| `Arrays` | Supported with limits | `toString`, `deepToString`, `sort` (whole or a range, for every primitive array but `boolean[]`, and object arrays; objects in natural order or by a `Comparator`, stably), `fill` (whole or a range), `equals`, `hashCode`, `deepEquals`, `deepHashCode`, `binarySearch`, and `copyOf` and `copyOfRange` (an object array's copy keeps its class: a `String[]` stays a `String[]`). The JDK's exceptions: `fromIndex(2) > toIndex(1)`, `Array index out of range: 4`, `ArrayStoreException: java.lang.Integer`, and `Cannot read the array length because "a" is null`. `asList` is below. `stream` is below. Sorting a primitive array with a comparator is javac's `no suitable method` error. Sorting an object array that contains null is an engine limitation. |
| Other `PrintStream` methods, `System.in`'s own methods, the `System`, `Math` and `String` methods not listed here, array methods; other java.util classes; the library methods not listed here | Unsupported | A real method of a supported class is reported as `the List method parallelStream() is not supported in this version`, not as `cannot find symbol`. An overload outside the subset is chosen as javac would choose it, then reported (`the constructor HashSet(int, float) is not supported`). |

## Practice mode

Instead of running `main`, a host can call one method with test cases and get a result per case (ADR 0005). code-playpad exposes it as the `practice` option.

| Feature | Status | Notes |
| --- | --- | --- |
| Calling a method with cases | Supported | By name, or by name and parameter types (`params: ["int[]", "int"]`) when it is overloaded; static or instance (on a fresh instance from the no-argument constructor; not in an inner class). The program needs no `main`. Each case runs in a fresh runtime, so static fields and identity start over. At most 500 cases. |
| Values | Supported with limits | JSON: `int` (number), `long` (decimal string), `char` (one-character string), `boolean`, `double` (number, or `"NaN"`, `"Infinity"`, `"-Infinity"`), `String`, wrapper types, `null`, arrays of these (nested), `List`/`ArrayList`, and linked-list and tree nodes (values in order; level order with `null` gaps), recognized from their fields or described in `nodes`. Maps, sets and `Object` are not supported yet: the spec is rejected with a message. |
| Comparison | Supported with limits | Ordered deep equality of the encoded values, or within a `tolerance` for doubles. A `void` method is checked through an argument after the call (`compare: { kind: "argument", index }`). Unordered comparison and stateful multi-call exercises are not supported yet. |
| Case results | Supported | `passed`, `failed` (with expected and actual), `completed` (no expected value), `runtime-error` (with the stack trace a program would print), `resource-limit`, `engine-limitation`, `invalid-case` (the JSON does not fit the parameters), `not-run` (with the reason), plus what the case printed. A cyclic or oversized result is `ResultEncodingError`. |

## Compile-time checks

These all use javac's wording and caret positions, as evidenced by `tests/jdk-cases`:

- **Name and type errors:**
  - cannot find symbol (variable, method, class), with `symbol:`/`location:` lines
  - `incompatible types` (including `possible lossy conversion from int to char`)
  - bad operand types, and `incomparable types`
  - `X cannot be dereferenced`
  - `array required, but int found`
  - `illegal initializer for int`
  - `cannot assign a value to final variable length`
  - `already defined` for variables, methods, constructors and member classes; `duplicate class`
  - `non-static … cannot be referenced from a static context`
  - `… has private access in …`
  - `invalid method declaration; return type required`, `modifier static not allowed here`, `explicit constructor invocation may only appear within a constructor body`
- **Method and constructor calls** (0.37.0 brought javac's longer forms, ADR 0031: a generic owner with its type variables, `method put in interface Map<K,V>`, and its `where` lines; a generic library candidate as declared, `Collections.<T#1>sort(List<T#1>)`, with javac's inference reasons and numbered type variables; a lambda argument whose body does not fit, reported as the call's argument mismatch; a diamond that cannot be inferred, `cannot infer type arguments for Pair<>`; a wildcard receiver captured, `List.add(CAP#1)`; every JDK overload a call could mean, listed in the JDK's order):
  - `method f in class Main cannot be applied to given types;` with `required`/`found`/`reason` (and the same for constructors)
  - `no suitable method found for …` / `no suitable constructor found for …`, with every candidate and why it does not apply
  - `reference to f is ambiguous`
- **Definite assignment (JLS 16):** includes `&&`/`||`/`!`/`?:`, constant conditions, `for` loops, and `break`; definite unassignment for blank `final` fields in constructors, with javac's second pass over loops.
- **Reachability (JLS 14.22):** after `return`/`break`/`continue`, after loops that cannot exit, and for a `while (false)` or `for (; false; )` body. `if (false)` is allowed.
- **`missing return statement`**
- **Error order:** one top-level class at a time, in source order: its attribution errors (nested classes where they are declared), then reachability, then definite assignment, with nested classes first. **Once any error has been reported, flow analysis does not run**, exactly as in javac, so a type error hides a missing return in the same file, and so does an error in an earlier class.
- **Syntax, lexical, and number-literal errors:** as in 0.1.0, and since 0.35.0 every one of them, as javac's recovery finds them (ADR 0030): a missing `;`, `)`, `]`, `}` or `>` is reported and taken as there; an expression or statement that cannot be read is reported and skipped to the next statement; a cascade from one mistake is not reported; a lexical error (`"hi;`, `'ab'`, `#`) is reported where the parse reaches it, and silences what follows from it. With a syntax error in the file, as with javac, no other kind of error is reported. At most 100 errors are shown, with javac's `only showing the first 100 errors` line.

**Output format:** the same as `java Main.java`. **Warnings:** none (JDK 25 source-file mode prints none for these programs either).

## Exceptions

| Feature | Status | Notes |
| --- | --- | --- |
| The JDK's exception classes | Supported with limits | `Throwable`, `Exception`, `Error`, `RuntimeException`, and in `java.lang`: `ArithmeticException`, `ArrayIndexOutOfBoundsException`, `ArrayStoreException`, `ClassCastException`, `CloneNotSupportedException`, `IllegalArgumentException`, `IllegalStateException`, `IndexOutOfBoundsException`, `InterruptedException`, `NegativeArraySizeException`, `NullPointerException`, `NumberFormatException`, `ReflectiveOperationException`, `ClassNotFoundException`, `StringIndexOutOfBoundsException`, `UnsupportedOperationException`, `AssertionError`, `LinkageError`, `ExceptionInInitializerError`, `NoClassDefFoundError`, `VirtualMachineError`, `StackOverflowError`, `OutOfMemoryError`; in `java.util`: `NoSuchElementException`, `InputMismatchException`, `ConcurrentModificationException`, `EmptyStackException`, and the `IllegalFormatException` family (catchable, not constructible); `java.util.regex.PatternSyntaxException` (catchable); in `java.io` (imported): `IOException`, `FileNotFoundException`, `EOFException`, `UncheckedIOException` (whose cause may be any Throwable here). Their public constructors: `()`, `(String)`, and where the JDK has them `(String, Throwable)`, `(Throwable)` (the message is the cause's `toString()`), `(int)` for the index exceptions (`Index out of range: 5`), `AssertionError(Object)`. What the engine and the library throw are these classes, so `catch` matches them. Other constructors, such as the protected `(String, Throwable, boolean, boolean)`, are unsupported. |
| The program's exception classes | Supported | `class InsufficientFunds extends Exception`, with fields, constructors calling `super(message)` or `super(message, cause)`, and overrides of `getMessage()`, `toString()` or `fillInStackTrace()`. Anonymous exception classes are unsupported. |
| `Throwable` methods | Supported with limits | `getMessage`, `getLocalizedMessage`, `getCause`, `initCause` (`Can't overwrite cause with …`, `Self-causation not permitted`), `toString` (`Main$InsufficientFunds: short by 14`), `printStackTrace()` to `System.err`, `addSuppressed` (`Self-suppression not permitted`, `Cannot suppress a null exception.`), `getSuppressed`, `fillInStackTrace`, and `getClass().getName()` / `getSimpleName()`. A trace is recorded when the exception is made, not where it is thrown, as in Java. `getStackTrace`, `setStackTrace` and `printStackTrace(PrintStream)` are unsupported. |

## Runtime behavior

- **Uncaught exceptions:** printed exactly as the JDK prints them, with one frame per active method call, innermost first, each at the line of its current statement, then each `Suppressed:` exception and `Caused by:` with its frames. The exception's own `toString()` runs as a Java call; if it throws, the JVM's `Exception: … thrown from the UncaughtExceptionHandler in thread "main"` is printed instead.
  - `ArithmeticException: / by zero`
  - `ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3`
  - `NegativeArraySizeException: -2`
  - `StringIndexOutOfBoundsException: Index 5 out of bounds for length 3`
  - `StackOverflowError`
- **NullPointerException messages** match the JDK's helpful messages:
  - Examples: `Cannot load from int array because "<local1>" is null`, `Cannot invoke "String.length()" because "<local2>[<local1>]" is null`, `… because the return value of "Main.find(String[], char)" is null`.
  - Local variables are named by JVM slot, as the JDK names them for code compiled without debug information (source-file mode). The engine allocates slots the way javac does, reusing a slot once its variable's block has ended.
- **Recursion:** the default depth is 50,000 calls; beyond that, a Java `StackOverflowError`. JDK 25's own limit varies from run to run: 45,810 to 94,701 frames were measured for simple methods.
- **Initializer failures:** an exception in a static initializer becomes `ExceptionInInitializerError` at the first use of the class, with `Caused by:` and frames down to `Table.<clinit>` (an `Error` such as `StackOverflowError` passes through unwrapped). The class is then erroneous: each later use throws `NoClassDefFoundError: Could not initialize class Main$Config`, caused by `ExceptionInInitializerError: Exception java.lang.ArithmeticException: / by zero [in thread "main"]` with the original frames, as JDK 25 reports it. One in a constructor or instance field initializer is reported with `Counter.<init>` frames.
- **Field and object NPE messages:** as the JDK words them: `Cannot read field "value" because "<local2>" is null`, `Cannot assign field "next" …`, `Cannot invoke "Node.show(int)" …`, `Cannot invoke "Object.hashCode()" …`, and sources such as `"this.data"`, `"Main.data"`, `"<parameter1>.next"`, `"this.this$0.data"`, `"Main.make().next"`. An inner class constructor's parameters are numbered after its hidden enclosing-instance parameter, as on the JVM (`<parameter3>`).
- **`toString()` calls** from `println` and string concatenation run as real Java calls: an exception inside one shows its frame.
- **Library exceptions** carry the JDK's messages: `IndexOutOfBoundsException`, `NoSuchElementException`, `ConcurrentModificationException`, `IllegalArgumentException`, `ClassCastException`, `NumberFormatException`, `ArrayStoreException`, and the helpful NPEs thrown inside the JDK (`Cannot invoke "java.util.Collection.toArray()" because "c" is null`). The stack shows the program's frames (see *Known divergences*).
- **Callbacks:** the `toString()` and `hashCode()` methods that the library calls run as Java calls, in the same order and number as on the JDK, so their output and exceptions appear as they would there.
- **Fresh state:** each run starts from fresh state; nothing (intern pool, arrays, identity hashes, static fields) survives between runs.

## Known divergences from the JDK

| Case | JDK 25 | Engine | How the tests compare it |
| --- | --- | --- | --- |
| Exceptions thrown inside the JDK (`charAt` out of range, `println` of a null `char[]`, any library method), inside a `toString()` called by `println`, `+` or a collection, or during class initialization or instance creation; and every trace `printStackTrace()` prints | The program's frames plus the JDK's own frames (`at java.base/…` such as `String.valueOf`, the source launcher's five frames below `main`, `at jdk.compiler/…`) | Same messages and program frames, without the JDK's frames. `... N more` counts the launcher's frames, so it is the JDK's number | Identical once the JDK's frames are removed |
| `no suitable method found for sort(...)` with a generic `Arrays.sort` candidate given an array of arrays or a lambda it cannot type | Inference details such as `upper bounds: int[],Object` | The candidates as javac lists them (`Arrays.<T#1>sort(T#1[],…)`, its `where` clause), with a narrower reason for those | Each error's header, source line and caret identical |
| `StackOverflowError` trace length | Capped at 1024 frames; 1019 were printed in the recorded case | 1024 frames | First lines identical |
| Several syntax errors in one file, lexical ones among them | Every one javac's recovery finds | The same errors in nearly every case: the engine recovers as javac does (ADR 0030; 87 recorded cases of beginners' mistakes). Where its recovery takes another path, the first error is still javac's, and later ones may differ (`void static() { }`; an unclosed text block whose `\"""` hides its end) | Identical for the recorded cases; first diagnostic for the one text block case |
| Printing an array or an object without `toString()` (`[I@5fdba6f9`, `Node@6dde5c8c`), or `hashCode()` | Random identity hash | Deterministic per-run hash with the same format | Format checked by unit tests |
| `Math.sin`, `exp`, `pow`, `log` and the other transcendental functions | Results of the platform's math library | JavaScript's; about 3% of probed values differed in the last digit (accepted by the owner, 2026-10-18) | The recorded cases print them with `printf("%.12f")`; unit tests allow a few ulps. Java permits this: `Math`, unlike `StrictMath`, need not be reproducible across platforms |
| Unseeded `new Random()`, `Math.random()`, `System.currentTimeMillis()`, `nanoTime()`, `identityHashCode` | Differ on every run | Differ on every run (identity hashes are per-run, as above) | Ranges checked by unit tests |
| Passing a `String[]` to an `Object...` parameter (`String.format("%s", strings)`) | javac's `non-varargs call of varargs method with inexact argument type` warning | The same behavior, without the warning | Engine-only test |
| A regex that backtracks exponentially (`(a+)+b` on a long run of `a`) | Runs for a very long time | Also runs for a very long time; JavaScript's regex engine is not interrupted by the instruction limit | Stopping terminates the worker (SPEC.md §6.1) |
| A `ClassCastException` about the program's classes | Names the source launcher's class loader with its identity hash (`MemoryClassLoader @78186a70`), different on every run | The same message with a fixed hash | Recorded cases mask the hash (`loader-hash`) |
| `Scanner.nextDouble()` of `3.5` | Depends on the JDK's default locale: `3.5` under English (the reference JDK's), not a number under German, where the decimal separator is `,` | The English locale's rules, always: `.` is the decimal separator and `,` groups thousands (owner decision, 2026-10-18: online judges and the reference JDK run under English, and the result does not depend on the machine) | Recorded under the reference JDK's locale (`sc-*`) |
| `System.out.println(stream.collect(Collectors.groupingBy(f, TreeMap::new, Collectors.counting())))`, where only `println(String)` applies to the generic result | javac's `cannot be applied` error lists every inference bound and type variable of the nested calls (`T#1`, `T#2`, …) | The same header, caret, required and found types and first reason line | `first-diagnostic` |
| Requesting an array of about 2³¹ elements | `OutOfMemoryError: Requested array size exceeds VM limit` | `resource-limit` (array budget) | Engine-only test |

## Engine limitations (reported as `engine-limitation`)

Where reproducing the JDK would mean guessing, the engine stops instead: `Stopped: … (limitation of this in-browser Java engine).`, with status `engine-limitation` and exit code 1. Output printed before it is kept.

| The program | Why the engine stops |
| --- | --- |
| Iterates over or prints a `HashMap` or `HashSet` of more than one element whose keys are hashed by identity (objects of a class without `hashCode()`, enums, arrays) | The JDK's identity hash codes, and so its order, differ on every run. `LinkedHashMap`/`LinkedHashSet` iterate in insertion order and need no hash codes |
| Iterates over or prints a `Set.of`, `Set.copyOf`, `Map.of` or `Map.copyOf` of objects hashed by identity, or a hash table filled from one | Their layout depends on identity hash codes, which differ on every JDK run |
| Iterates over a `HashMap` or `HashSet` after removing keys from a bucket of 9 or more colliding keys, or where such a bucket holds many keys with equal hash codes | The JDK rebalances the bucket's tree (or orders equal hashes by identity); that part of its order is not reproduced |
| Changes an `ArrayDeque` while iterating over it | The JDK's result depends on its internal array layout |
| Orders objects of its own classes (`PriorityQueue`, `Arrays.sort`) | The JDK's `ClassCastException` names the class loader with an identity hash |
| Sorts objects in natural order when one is null, or makes a priority queue whose natural-order comparisons meet a null element on the left | The JDK's NPE message names a local variable of its sorting code |
| Prints a lambda | The JDK prints its generated class and address (`Main$$Lambda/0x…@…`) |
| Calls `Iterator.remove()` on a PriorityQueue's iterator | The JDK reorders the heap in a way that is not reproduced |
| Classifies or case-maps a character outside Latin-1, or parses digits outside Latin-1 | The Unicode tables are not bundled (SPEC.md §4.2.2) |
| Uses a regex construct JavaScript's engine has no equivalent for: possessive quantifiers (`a++`), atomic groups (`(?>…)`), class intersections (`[a-z&&[^b]]`) and nested classes, inline flags other than a leading `(?i)`/`(?s)`, the `m`, `x`, `u`, `U`, `d` and `c` flags, `\G`, Unicode categories (`\p{L}`), negated classes inside a class (`[\S ]`), or supplementary characters in the pattern | Translating them would change what matches |
| Matches case-insensitively against non-ASCII text | Java's and JavaScript's case folding differ outside ASCII |
| Formats a date or time (`%t`) or hexadecimal floating point (`%a`) | Not implemented |
| Reads `System.in` through two readers: two `Scanner`s or `BufferedReader`s, one of each, or `IO.readln` together with one | On the JDK the first reader buffers the input, so the second sees part of it or none, depending on buffer sizes; make one Scanner and pass it around |
| Reads a number written with non-ASCII digits with a Scanner | Not implemented |
| Prints a `Scanner` | The JDK prints its locale's number symbols |

## Limits (engine-specific; reported as `resource-limit`, never as Java exceptions)

| Limit | Default | Notes |
| --- | --- | --- |
| Instructions | 200,000,000 | A step is one bytecode instruction, some of which do the work of several (ADR 0019); 200M steps take roughly 0.6–1.5 s on the development Mac (a sieve of 5,000,000 numbers: 128M steps, 424 ms; fib(30): 95 ms). A library call into the program (a comparator, `merge`'s function) costs about 0.1–0.2 µs (ADR 0020). Allocation, concatenation, and output are charged extra. Deterministic. |
| Array elements | 16,000,000 per run | Checked before allocating. Collection entries count too. |
| Output | 1,000,000 characters | Truncated exactly at the limit. |
| String length | 10,000,000 characters | A `StringBuilder` too. |
| Call depth | 50,000 | Exceeding it is a Java `StackOverflowError`, not a resource limit. |
| Source | 500,000 characters per file, 32 files | |
| Nesting | 200 levels of statements and parentheses; expressions 1,000 deep | |
| Wall clock | none in the engine | Stop terminates the worker (SPEC.md §6.1). |
