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:
- Logging guards mean "enabled", not "exactly this level" (#6323):
AbstractLogger's eleven guards (logAll()…logFatal()andisDebugEnabled()…isFatalEnabled()) tested equality with the configured level, so at DEBUGlogInfo()was false andif (log.logInfo()) { ... }— the guardmanual/logging.mdrecommends — dropped an enabled line; at ALL every guard butlogAll()was false. Each guard is now enabled-at-or-more-verbose (the same test the level selectors already used), and the docs say so. Owner decision recorded on the ticket. - A level selector starts a fresh record (#6321): a chain that never reached
flush()kept its bytes in the logger's buffer, and they were prepended to the next flushed record (never flushedkept error true). Each level selector now clears the buffer first, so a forgottenflush()loses only its own line; a barelog(...)without a selector still continues the current record. Owner decision recorded on the ticket.
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:
f(true)againstf(int)printed0;f("s")died later asE_COERCE. An argument that does not convert to a primitive parameter is now E_NO_APPLICABLE_METHOD at compile time, with the argument and parameter named (#6330).- The same check now reaches
sj.langstatics:Math.abs("hello")is a compile error (#6343). - Enclosing-class statics and static-import calls get the argument type checks too —
f(true)through a static import printed-1before (#6346).
MemoryMapped: a real MAP_PRIVATE, and range operations
- Mode 2 is a real copy-on-write mapping (#6328): it was accepted as "private" but mapped
MAP_SHAREDread-write — writes through it reached the file.MAP_RO/MAP_RW/MAP_PRIVATEare public constants now;MAP_PRIVATEopens the file read-only (never creates or extends it), maps copy-on-write, andsync()/grow()refuse withIllegalStateException. Unknown modes are refused by the constructor as well asreopen. - Range operations (#6327):
sync(offset, len, async),advise,lock/unlock, andresident/residentPages— with the private-mapping guards applied per operation.
Also in this build
- A resolved static-import call gets a callee binding, so it links under
--sdk-source(#6350). - A bare array initializer takes its reference component type from the declaration:
Base[] a = { sub }compiles (#6329). - A partial tuple in a
Build.sjtuple field (dependencies = { "../util" }) is a manifest error with the offending line named (#6317). superj manifest's text form printswarningsandwarningsAsErrors— text/JSON parity (#6318).- Documentation: the manual diagnostics reference is complete — all 74 remaining codes (#6332); new pages for
sj.sql(#6313),sj.seda.appsgateway + admin console (#6337),sj.seda.test/events/service/topology (#6326), S3 and logging agent pages (#6320), andsuperj manifest/sources/doctor(#6316); the logging Quick Start createslogs/and runs as written (#6322).
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
new T[a, b]evaluates each dimension once and overflow-checks the product (#6281): every dimension expression was generated twice — once for the size, once for the header's dimension words — and the product was a plain 32-bit multiply, so65536 x 65536wrapped to 0 and ordinary indexing wrote past the allocation. The product is now computed in 64 bits and capped; an oversized request throwsOutOfMemoryErrorlike every other allocation (previously it silently corrupted).
Runtime and SDK
- Math and ComplexMath take Java's IEEE-754 edge contract (#6286):
Math.getExponentreturns Java's biased-field values (NaN/±∞ → 1024, zero and subnormals → -1023) instead of Cilogb's raw contract;Math.signumpasses NaN through and preserves-0.0's sign; an infinite imaginary part no longer turns the real half of a complex result into NaN (arg(1+∞i)is π/2, not NaN). - Gguf builds Strings from model-file bytes through the lossy UTF-8 door (#6295): an invalid sequence — or a vocab token cut mid-character by the 512-byte cap — repairs to U+FFFD instead of making the loader throw on a strange model file.
- Runtime throw messages are repaired to valid UTF-8 (#6284): a diagnostic formatted into a fixed buffer (regex compile patterns, class-cast messages, assert clauses) can no longer end in a partial multi-byte sequence. A message loses at most its tail; ASCII messages are byte-identical.
Crypto.ecdsaP256SignPemrefuses an output window shorter than the 72-byte DER signature (#6282): the guard checked 64 bytes while the entry writes up to 72, so a caller with 64–71 bytes of room got a heap overwrite past the array. Those windows now return -1.Region.close()throwsIllegalStateExceptionif the region is still armed anywhere on theenter()stack (#6283), and the region stays OPEN: closing an armed region dropped its arena while a laterleave()could still install that handle as the ambient arena (a use-after-free). Leave or close the regions above it first, then close it here.- The arena's handle allocation is checked (#6285): a failed allocation takes the OOM path instead of writing through NULL.
Performance
- binarytrees back to baseline on x86 (#5198): the arena's sole-chunk threshold accessor had been left out of line in the allocation hot path — every allocation paid one to two extra calls (binarytrees 3.47 s vs the 3.05 s baseline). The accessor is a static inline again, with the one-time environment parse moved to a cold function, and a new preflight gate disassembles the shipped allocator to keep it there.
Tooling — superj test tells the truth
- A suite that fails to compile is counted in the summary — it used to vanish from the totals, so a run could read green while suites never ran (#6274).
- A failed library pre-compile is a hard error with the failing step's output — no silent per-suite fallback to the slow lane (#6275).
- The run phase deletes each suite's result files before launching: a previous run's verdict is never replayed as this run's answer, and a run batch that failed to start is reported (#6291).
superj test <file>inside a project prints the compiler's actual error instead of a misleading "library is not linked" that buried it (#6304).
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
- A link that fails on the project's own natives now names the failure: the missing symbols, the C files that define them, the
nativeSourcesline to fix, ending in onesuperj: link FAILEDmarker (#6279). superj testnames its pipeline in the summary (mode=library|fallback|inline, #6275), and the library sidecar sees the SDK — a bodied@NoRetaincalling SDK methods no longer silently falls back to the slow lane (#6272).
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
JsonBuilderrejects the shapes that emitted malformed JSON (duplicate keys at the same nesting level, values outside a key, a scalar at the document root) and documents nesting by call order (#6271).- A method reference to a native returning
boolean/byte/shortwidens the parameter correctly — the same sub-int return classShort.toStringtripped over, fixed in the thunk path (#6251). E_STRUCT_NOT_A_CLASSno longer calls a struct a "value type with no identity" — all three spellings match SPEC §4.14's reference semantics (#6254).
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):
| was | now |
|---|---|
ComplexMath.absF/argF/realF/imagF/normF(cfloat), returning double | the 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
- A base-case-free mutual recursion now throws
StackOverflowErrorat-O3instead of folding to an infinite loop — every function in a call cycle carries the defence (#6136). - An exception object kept past its handler is promoted out of the throw scratch (#6208):
catch { holder = e; }withholderdeclared outside kept a reference to memory the unwind released; the promote decision now asks whether a holder named outside the handler reaches the parameter. Five growth baselines follow the new promotion. - A null
Throwablereference stringifies asnullin concat /println/StringBuilder.appendinstead of crashingsj_exc_to_str(#6209);Math.ulpagrees with Java on every class ofdouble(#6153);MemoryMapped.copyFrom's source-range check is subtractive (no wrap) (#6149); a struct field store past the count throws instead of writing past it (#6194). - Subprocess allocation-failure hardening (#6145): a failed
spawnmalloc kills and reaps the forked child instead of returning invalid handle 0;readLinechecks its allocations and returns the partial line on failure — no leak, no NULL store.
Claims enforcement is armed by presence, not size
- An empty
capabilities = { }flips the project to default-deny (#6200) — the key's presence arms enforcement, not the number of entries. A project that listed the key once and left it empty now denies everything it did not list. - An
architecturekey that omits a declared package isE_ARCH_DENIEDnaming it — complete-or-absent (#6202).
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) | was | fix |
|---|---|---|
boolean used as a numeric operand — true + 1, false * 2, t << 1, t < 1, k += t (#6204) | compiled, garbage runtime value | use the boolean directly in conditionals; a numeric encoding is int |
++/-- on a boolean (#6245), and on cdouble/cfloat (#6215) | compiled to invalid or dead IR | express 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 time | static 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 abort | index 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 failure | give the resource type a close(), or don't use try-with-resources on it |
a struct's abstract or bodyless method (#6210) | link failure | give the struct concrete methods, or model it as a class |
SIMD bitwise/shift on the floating families (Float4/Float8/Double4, #6214) | clang rejected the IR | those ops exist on integer families only |
a surrogate \uXXXX escape in a char or string literal (#6212) | codegen failure | name 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:
- JSON strings escaped wrongly on block boundaries (#6116).
JsonStringScanner's lane-mask escape filter had degenerated: the escaped-quote pairing (resolveEscapes+ its pairing mask) inverted, so a backslash-quote pair (\") inside a JSON string was never filtered out of the real-quote set, and short blocks (< 16 bytes) scanned with an out-of-bounds read. Parsing content with escaped quotes produced wrong string boundaries; both are fixed and pinned. MerkleTree.computeTreeNativethrew nothing and computed nothing (#6127). The silent refusal for unusable input returned an all-zero root — indistinguishable from a real (empty) tree — and the scratch path'smemcpyread exactlynumLeaves*32bytes from a shorter source, folding adjacent heap bytes into the root. It now always fillsrootOut(in place when the window holds the tree, into a scratch otherwise) and throws on null/negative/undersized arguments; an over-shorthasheswindow throws the catchableArrayIndexOutOfBoundsException.System.envName/envValuewith a negative index returned entry 0 (#6113). The index guard walkedi < index, soindex <= -1skipped the walk and aliasedenviron[0]while>= countcorrectly answered empty. Every out-of-range index now answers the same way:""/empty across all four accessors (envName,envValue,envNameBytes,envValueBytes).
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:
MerkleTree.computeTreeNativethrows where it silently produced a zero root. Code that relied on the old all-zero refusal must handleIllegalArgumentException(null or negative arguments) andArrayIndexOutOfBoundsException(arootOutsmaller than needed, or ahasheswindow shorter thannumLeaves*32). Nothing else in the build removes or changes an SDK member.
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.
charis now a 32-bit unsigned Unicode scalar. It holds precisely what is a character —0x0..0x10FFFFminus the surrogates.Character.MAX_VALUEis0x10FFFF(owner decision retained). A constant that is not a scalar (negative, a surrogate0xD800–0xDFFF, above0x10FFFF) is a compile error,E_CHAR_RANGE; the same value computed at run time throwsIllegalArgumentException(#5959). Char literals name scalars:'A','\u{1F600}'; surrogate escapes ('\uD83D') are rejected (#5958).- String indexes are UTF-8 byte offsets.
length()is the byte count (back to its original meaning),substring/indexOf/startsWith/split/replace/trim/replaceAlltake and return byte offsets, andhashCode()/compareTo()hash/compare UTF-8 bytes — byte-identical behaviour for ASCII text, which is why most code moved with no edits (#5964, #5967). charAtreturnschar(the scalar) and throws inside a character:"a😀b".charAt(1)is U+1F600;charAt(2)throws, naming the byte. Walk characters withnextCharIndex— thefor (int i = 0; i < s.length(); i = s.nextCharIndex(i))idiom is documented alongside the pinnedindexOf + length()byte-slice idiom (#5959, #5583).- New position/conversion API on
String:isCharBoundary,nextCharIndex,floorCharBoundary,ceilCharBoundary(#5961);toUtf16()/fromUtf16(short[])for explicit UTF-16 round-trips — an unpaired surrogate unit throws (unpaired UTF-16 surrogate 0xD83D at index 3); there is no lossy UTF-16 form because a String never holds half a character (#5969).StringBuilderfollows the same model: byte-offset positions, O(1)length(),charAtreturning the scalar (#5965).
Byte-to-String paths are checked; the exact-byte natives exist for the rest (#5970, #5971)
new String(byte[]),String.fromBytes,String.fromCStringnow validate UTF-8 and throwIllegalArgumentException(invalid UTF-8 at byte N). When you want the tolerance behaviour, the new lossy forms repair per maximal subpart:String.fromBytesLossy,String.fromCStringLossy(U+FFFD replacement, like UTF-8's RECOMMENDED practice).argvand the environment are lossy-decoded (invalid bytes become U+FFFD). The exact-byte forms sit beside them:System.argCount(),System.argBytes(i),System.getenvBytes(name),System.envNameBytes(i),System.envValueBytes(i). NativeString-returning methods are checked; a method reference to a non-String native returning String lifts the foreign pointer through the checked path (#6015).- Wire/IO side:
ByteArrayBuilder.appendCharwrites the scalar's UTF-8 encoding, not the low byte (#5944);%cinprintf/String.formatwrites the scalar's whole UTF-8 encoding (#6014);printf/String.formatcheck argument count and%stype, and a null String printsnull(#5594);HfTokenizer.decodesteps characters, not bytes (#6012);File.getName/getParentstopped allocating a second string (and leaking the raw buffer) per call (#5722).
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
- Constructor buffers join the enclosing arena block (#5806):
new Session(i)inside anarena {}block places the object and its backing buffers in the block, reclaiming on exit. - A loop-carried
s = s + xconcat now arms the implicit frame region, so the iteration intermediates are reclaimed at return instead of leaking O(n²) global bytes (#5667). StringBuilder(String)/StringBuilder(int)built inside anarena {}block are reclaimed with it (#5745).- Only the thrown object (and a built-in cause) may live in the exception scratch —
throw new MyExc(new Obj(7))no longer leavese.heldpointing at released memory (#5813). a = System.resize(a, n)keeps the array's elements in their own block (#5885).
Performance
- One String implementation, one vtable: String dispatch goes through the runtime vtable (
sj_rt_vtable_String), the compiler stops synthesizing its own String vtable/itables, and the 13 remaining intrinsified members are declarednativeso the body a method reference executes is the SAME body as the direct call — the "two implementations drift" bug class is gone forString(#5949, #5950, #5951, #5952, #5953, #5903). The vtable's measured cost and the deferred optimisation are documented (#5954). String.replacecounts first, allocates once (#5901); stack concat buffers stop serving a stale position-cache entry (#5896).- Builds through one source path are byte-identical (deterministic object path + pinned mtime), which is what makes the IR-parity gates trustworthy (#5522).
New compile errors and default-on warnings
E_CHAR_RANGE— a constant that is not a Unicode scalar converted tochar(#5959).E_SIMD_LANE_RANGE— a constant SIMD lane outside[0, lanes)(#5587).- A private constructor is callable only from its own nest (#6024) — matches Java's rule; code that reached another class's private constructor is rejected now.
W_STRING_CONCAT_LOOP(default-on) — a String accumulator assigned inside a loop (s = s + x/s += xwhere the RHS reads the target): the quadratic signature, up to 2N intermediate Strings per N iterations in an arena that never reclaims them. Silence with--no-warn-string-concat-loopor@NoWarnon the member; the fix is one reusedStringBuilderand a singletoString()after the loop (#5582).W_JSON_STRINGBUILD(default-on, heuristic) — aStringBuilder.appendof a literal starting with the{"JSON shape, where the escaping bugs live; usesj.json.JsonBuilder(#5582).- BREAKING: a package-private or protected member used from another package is now an error,
E_ACCESS_VIOLATION(SPEC §4.13, #5474), including the protected-through-superclass refinement (#5858). Widen the member or move the caller.--no-error-access-violationdemotes it to the warningW_ACCESS_VIOLATIONwhile you migrate;--testcompiles are exempt.--warn-access-violation, which armed the old opt-in warning, is still accepted and does nothing.
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
| was | now |
|---|---|
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
new String(byte[])/fromBytes/fromCStringon invalid UTF-8 →IllegalArgumentException; use theLossyforms to keep tolerance (#5970).charAt/StringBuilder.charAton a non-boundary index → throws; it used to return (possibly garbage) units (#5959, #5965).printf/String.formaton a wrong argument count or%sapplied to a non-String → throws; a null String formats asnull(#5594).- Checked char conversion at run time →
IllegalArgumentException(#5959); SIMD runtime lane out of range →ArrayIndexOutOfBoundsException(#5587).
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.
- v1.3.6793 (Oct 15) — the checker-hardening batch: constructs Java itself rejects became compile errors with named codes; a base-case-free mutual recursion throws
StackOverflowErrorat-O3. - v1.3.6592 (Oct 11) — three silent wrong answers fixed (MerkleTree throw contract among them).
- v1.3.6514 (Oct 10–11) — the text model lands:
charis a 32-bit Unicode scalar, String indexes are UTF-8 byte offsets, byte-to-String paths are checked; the no-UB guarantee; 25 removed SDK members with named replacements; two new default-on warnings. - v1.3.5900 / v1.3.6353 (Oct 3–10) — String goes Java-parity on multibyte text (
charAtreturns the full codepoint, case mapping is Unicode-wide); subclassequals()/hashCode()/instanceoffixes;ByteArraygrowth contract corrected;Randommatchesjava.util.Random. - Earlier v1.3 builds — crashes became exceptions; silent wrong answers fixed; stack traces print by default;
NioAdapter.connectTlsrequires a verification policy (#3365) — the one breaking TLS change:tlsConfig = nullno longer silently means "do not verify the server"; the memory model's one default allocation domain, and the measurable allocator.