← SuperJ Manual

SuperJ v1.3

build v1.3.7107

SuperJ Community Edition. Free for personal use, open source projects, non-profits, educational institutions, and small businesses (< $5M annual revenue).

Install

curl -fsSL https://superj.dev/releases/v1.3/install.sh | sh -s -- https://superj.dev/releases/v1.3

This installs SuperJ to ~/superj and adds it to your PATH. Available on macOS arm64, Linux x86_64, and Linux arm64.

What's new in this build

Two logging fixes, from the merge history:

Earlier in v1.3 — build v1.3.6946 (published Oct 21): access errors by default, checker hardening, MemoryMapped

What's new in that build (carried forward, unedited)

The access check is an error by default (#5474)

SPEC §4.13 is enforced now: a package-private or protected member used from another package is error[E_ACCESS_VIOLATION], not a warning. Code mid-migration can demote it back with --no-error-access-violation (forwarded by superj build into child compiles); --test compiles stay exempt; --warn-access-violation is kept as an accepted no-op. The SDK's own surface was re-rowed for the flip: 23 ordinary-API declarations are published explicitly, and the ordinary-API census went 802 → 279.

The checker catches calls that silently computed the wrong thing

Three checker hardenings in one family — each of these compiled and ran, binding by name + arity:

MemoryMapped: a real MAP_PRIVATE, and range operations

Also in this build

Upgrading to v1.3.6946 — the access errors, the non-converting calls, and MAP_PRIVATE (carried from that build)

E_ACCESS_VIOLATION is an error by default (#5474): code that reached another package's package-private/protected members — including SDK internals — stops compiling. Run with --no-error-access-violation to demote it to W_ACCESS_VIOLATION while you migrate; the flag forwards through superj build to child compiles. --test compiles are exempt.

Calls that don't convert are compile errors (#6330, #6343, #6346): f(true) against f(int), a String in an int slot, Math.abs("hello") — previously some of these compiled and printed garbage (0, -1), the rest died in codegen. The fix is in the call's argument list; the error names the argument and the parameter.

MemoryMapped mode 2 no longer writes through to the file (#6328): if you opened with mode 2 and relied on writes landing, use MAP_RW (mode 1). Mode 2 is MAP_PRIVATE — copy-on-write, file untouched, sync()/grow() refuse.

A partial tuple in Build.sj is a manifest error (#6317): a tuple field like dependencies whose element is a bare string instead of the { name, path } tuple is rejected with the offending line named.

No SDK member is removed in this build, and there are no wire-layout changes.

Earlier in v1.3 — build v1.3.6883 (published Oct 20): bug fixes

What's new in that build (carried forward, unedited)

A bug-fix build: no new features, no SDK member removed, no wire-layout change. From the merge history:

Compiler

Runtime and SDK

Performance

Tooling — superj test tells the truth

Earlier in v1.3 — build v1.3.6835 (published Oct 19): the 3.2 MB compiler, tooling diagnostics, JsonBuilder rejection

What's new in that build (carried forward, unedited)

The compiler binary is 68% smaller

superj-self went from 10.1 MB to 3.2 MB (the community tarball from ~9.6 MB to ~2.5 MB): the selfhost link now dead-code-eliminates, and the compiler reaches only a fraction of the SDK archive it links. The stripped binary passed the pipeline's own self-smoke, emits byte-identical IR to the unstripped compiler, keeps every crypto native reachable, and passed units 465/465 and goldens 1730/1730 (#6289).

Tooling tells you why

Codegen is faster, with byte-identical IR

The recursion-defence call graph reads a once-per-method mangled id (911 → 869 ms on the self-compile, below the no-check floor) and the P6 guard validator returns early when a function holds no risky probe (914 → 710 ms) — both with IR byte-identical to the slow paths (#6257, #6256).

Correctness

Upgrading to v1.3.6835 — the ComplexMath renames and the JsonBuilder throws (carried from that build)

Removed and changed SDK members (ComplexMath follows SPEC §8.4a; #6264):

wasnow
ComplexMath.absF/argF/realF/imagF/normF(cfloat), returning doublethe overloads abs/arg/real/imag/norm(cfloat), returning float (C99 computes them in single precision)
ComplexMath.conjF/expF/logF/sqrtF/sinF/cosF/tanF/sinhF/coshF/tanhF(cfloat), powF(cfloat, cfloat)the overloads of the same names without F
ComplexMath.fromPairF(float, float)fromPair(float, float)
ComplexMath.fromPairNative / fromPairFNative (public)private implementation helpers; call fromPair(...)

JsonBuilder throws where it emitted broken JSON (#6271): builders that appended duplicate keys at one nesting level, appended values outside a key, or built a scalar root now throw IllegalArgumentException at the offending append. Code that "worked" was producing unparseable output — the fix is in the builder call sequence, not a try/catch.

No other SDK member is removed in this build.

Earlier in v1.3 — build v1.3.6793 (published Oct 15): the checker-hardening batch

What's new in that build (carried forward, unedited)

The checker-hardening batch: programs Java itself rejects now stop at compile with named diagnostics

This build lands a SPEC-alignment campaign across the checker: a dozen constructs that compiled fine and then failed at run time, linked badly, or aborted in codegen are compile errors with named codes now. Each rule is Java's own rule (SPEC cites the section); the porting table in "Upgrading" below lists every one with the fix.

Behaviour fixes

Claims enforcement is armed by presence, not size

Documents

Structs are documented as what they are — references to a packed byte region — in SPEC §4.14 and the language reference (no behaviour change); the design options for bounding var-length struct writes are recorded with an owner decision pending (#6223, #6207).

Upgrading to v1.3.6793 — the new compile errors carried from that build

New compile errors — each names its code and its fix; all were previously silent or runtime failures. If your tree built against v1.3.6592 and fails now, one of these is the cause and the diagnostic says which rule tripped:

now a compile error (ticket)wasfix
boolean used as a numeric operand — true + 1, false * 2, t << 1, t < 1, k += t (#6204)compiled, garbage runtime valueuse the boolean directly in conditionals; a numeric encoding is int
++/-- on a boolean (#6245), and on cdouble/cfloat (#6215)compiled to invalid or dead IRexpress the intent as an explicit assignment on the right type
@Test method with parameters, an instance method, or a non-void return (#6219)mis-discovered or mis-invoked at test timestatic void name() with no parameters
foreach over a type that is neither an array nor Iterable — including sj.util.ArrayList, which is INDEXED (#6213)codegen abortindex the loop (for i < list.size()), or use an SDK collection that implements the iteration protocol
a try-with-resources resource without a void close() (#6211)link failuregive the resource type a close(), or don't use try-with-resources on it
a struct's abstract or bodyless method (#6210)link failuregive the struct concrete methods, or model it as a class
SIMD bitwise/shift on the floating families (Float4/Float8/Double4, #6214)clang rejected the IRthose ops exist on integer families only
a surrogate \uXXXX escape in a char or string literal (#6212)codegen failurename the scalar: '\u{1F600}'

Also in that build: superj check refuses a second file argument instead of silently ignoring it (#6222); wildcard-typed receivers resolve members through the upper bound (#6216); nested enums' constants are typed through the qualified name and nested types with colliding simple names resolve in the declaring class (#6217, #6235); definite assignment honours a constant if-condition per JLS §16 (#6205); native boolean/byte/short returns are declared i32 and narrowed after the call (SPEC §9.5.1 — no source change) (#6218).

Earlier in v1.3 — build v1.3.6592 (published Oct 11): three silent wrong answers fixed

What's new in that build (carried forward, unedited)

Three silent wrong answers, fixed

Three SDK defects that reported a wrong answer with exit 0 — the worst failure class in a language with no exceptions-in-the-dark — are fixed in this build:

Everything else in this build is internal: the no-UB work advances (proofs/ea composition, SJCore field obligations), gate blind spots close, and the trusted build canonicalizes paths in-process.

Upgrading to v1.3.6592 — the MerkleTree throw contract (carried from that build)

One behavior change to read:

Earlier in v1.3 — build v1.3.6514 (published Oct 10–11): the text model lands

What's new in those builds (carried forward, unedited from its Oct-10 write)

The text model is final: char is a 32-bit Unicode scalar, String indexes are UTF-8 byte offsets (#5943, #5941)

Two interim builds shipped June- and October-era stepping stones; this build lands the end state, and it is the biggest change to String/char since the language shipped.

Byte-to-String paths are checked; the exact-byte natives exist for the rest (#5970, #5971)

The no-UB guarantee (#5487)

SPEC §1.1 now states the no-undefined-behaviour guarantee, backed by a Lean 4 development (proofs/ub) that proves each obligation from a named guard: integer division/remainder by zero and overflow, shifts, float→int conversions (out-of-range saturates, #5513; ±Inf saturates too, #5678), bounds checks (subtractive, #5513), the poison/undef operand surface, and the SIMD operations — shift counts mask to the lane width and a division by a zero lane throws like the scalar divide (#5534). An out-of-range constant lane is a compile error, E_SIMD_LANE_RANGE; a runtime one throws ArrayIndexOutOfBoundsException (#5587), and every SIMD array operand and the loaduByte16 window are checked (#5556). doctor wires the audit into the install check, and the trust-list (TCB) is published with it (#5511, #5514).

Stack traces print by default on an uncaught exception (#5613)

The default handler now captures and prints the frame chain for every throw, and the report names the source site even for runtime-thrown errors (#5581). SUPERJ_STACKTRACE=quiet is the harness mode that turns printing off — test infrastructure that assumed silence should set it.

Memory: the allocator's books get cleaner

Performance

New compile errors and default-on warnings

Correctness fixes worth knowing

println(obj) with a null toString() was UB that clang folded into a brk trap — fixed (#5847). Math.random() divides by RAND_MAX+1 so it can never return 1.0, pinned across five fresh processes (#5518). Overlapping struct/mapping copies are memmove, and every remaining codegen memcpy carries a disjointness note (#5588). The vendored Ed25519 implementation's shift UB is fixed at the class (183 carry sites) (#5683). bindUnixSocket/connectUnixSocket reject a pathLen the path array does not hold (#5718). Gguf.readRawBytes guards are subtractive and int-bounded (#5552). Nested classes no longer make a same-named top-level SDK class ambiguous under --sdk-source (#5860).

Tooling

superj test links the project library before the SDK archive (GNU ld order fix, #5517), honours --clang-path in the direct-link lane (#5762), and --sdk-source mode tests the SDK sources end-to-end — the batch runner honours UNIT_SDK_SOURCE, so a suite that meant to compile against source no longer silently tests the archive (the batch's SDK negative controls were vacuous), with a staleness gate over the baseline (#5845).

Upgrading to v1.3.6514 — 25 removals, the index-space changes, and the new default-on warnings carried from that build

This build carries 25 removed SDK members (each with a named replacement), the text-index-space changes for any non-ASCII content, two new default-on warnings, and three new compile errors. If your code is ASCII-only, the text model changes will not touch you; the removed members and the format-check throws are the ones to read first.

Removed and changed SDK members

wasnow
String.byteLength() (#5966)length() — String indexes are byte offsets again, so the separate accessor was a bridge for a distinction that no longer exists
StringBuilder.byteLength() (#5966)length() — same
String.codePointAt(int) (#5966)charAt(int) — the return carries the whole scalar; codePointAt had no distinct job left
the 22 char-typed 16-bit wire accessors (getChar, getCharLE, putChar, putCharLE, bytesToChar across the wire classes; #5956)the int twins (getInt/getIntLE/putInt/putIntLE) — a wire value is a number, not a character; ByteArray.putUTF8Char writes one scalar's full 1–4 byte UTF-8 sequence

The index space for non-ASCII text

If you wrote index arithmetic against the UTF-16-unit interim (the June→October builds), re-move it to byte offsets: the two spaces differ in BOTH directions — a 3-byte BMP scalar like € costs 3 bytes but 1 UTF-16 unit, an astral scalar costs 4 bytes but 2 units — so do not convert by habit; walk with nextCharIndex and compare with isCharBoundary. s.indexOf(x) + x.length() byte-slices are exact and pinned (#5583). The (char) casts required by the multibyte interim remain valid (they are no-ops now).

Behaviour changes that throw now where they used to compute

Two new default-on warnings

W_STRING_CONCAT_LOOP and W_JSON_STRINGBUILD (above). They may be promoted to errors with warningsAsErrors in Build.sj, silenced per member with @NoWarn, or disabled wholesale with --no-warn-string-concat-loop / --no-warn-json-stringbuild. If these light up in your tree, they point at the two biggest no-GC leaks — fix the code, not the warning.

No wire-layout changes

Struct layouts are unchanged this build: byte[N] remains raw inline bytes and cdouble remains 16 bytes. putUTF8Char's change is encoding semantics, not layout (#5956).

Older v1.3 builds

This page is the upgrade document for the v1.3 line: the current build in full, then the last few builds. Every earlier build's complete notes are preserved with that build's release.

SuperJ — manual · generated from RELEASE_NOTES.md at pack time