#!/usr/bin/env bash # The 21 -> 25 hub lane: every claim in ankurm.com/java-21-to-25-lts-features and /java-21-to-25-upgrade-guide-ai-prompts # that can be checked on a JDK, checked on JDK 21, 25, 26 and 27. # ./scripts/lanes.sh -> docs/output/80-*.txt ... 91-*.txt set -uo pipefail source "$(dirname "$0")/env.sh" SRC="$ROOT/lanes/src"; B="$ROOT/build/lanes"; rm -rf "$B"; mkdir -p "$B" jdk() { case "$1" in 21) echo "$JDK21";; 25) echo "$JDK25";; 26) echo "$JDK26";; 27) echo "$JDK27";; esac; } scrub() { sed -i -E "s#$ROOT##g; s#@[0-9a-f]{6,8}#@#g" "$@"; } # javac with the JDK's own compiler; prints javac's words and the real exit code jc() { local v="$1"; shift; local out rc; out="$("$(jdk "$v")/bin/javac" "$@" 2>&1)"; rc=$? printf '%s\n' "$out" | grep -v -E '^(Note: |$)' | sed 's/^/ /'; echo " exit=$rc"; return $rc; } # java with the JDK's own launcher jr() { local v="$1"; shift; "$(jdk "$v")/bin/java" "$@" 2>&1 | sed 's/^/ /'; } # JDK 23 and 24 are only used to pin down WHEN something changed; they run from the Corretto images dj() { local v="$1"; shift; docker run --rm -v "$B:/b" "amazoncorretto:$v" "$@" 2>&1 | sed 's/^/ /'; } djc() { local v="$1"; shift; local out rc; out="$(docker run --rm -v "$B:/b" -v "$SRC:/src:ro" "amazoncorretto:$v" javac "$@" 2>&1)"; rc=$? printf '%s\n' "$out" | grep -v -E '^(Note: |$)' | sed 's/^/ /'; echo " exit=$rc"; return $rc; } # ---------- 80: the four Java 21 features, compiled once with --release 21, run on 21, 25, 26, 27 ---------- { echo "# One class file (javac 21 --release 21), four JVMs" mkdir -p "$B/hub"; "$(jdk 21)/bin/javac" --release 21 -d "$B/hub" "$SRC/Check.java" "$SRC/HubExamples.java" 2>&1 for v in 21 25 26 27; do echo; echo "\$ java -cp hub HubExamples (JDK $v)"; jr $v -cp "$B/hub" HubExamples; done } > "$OUT/80-hub-examples.txt" # ---------- 81: scoped values, final in 25 ---------- { echo "# ScopedValue (JEP 506). Preview API in 21, final in 25." echo; echo "\$ javac ScopedValueDemo.java (JDK 21, no flag)"; jc 21 --release 21 -d "$B/sv21" "$SRC/Check.java" "$SRC/ScopedValueDemo.java" for v in 25 26 27; do echo; echo "\$ javac ScopedValueDemo.java (JDK $v, no flag)"; mkdir -p "$B/sv$v" jc $v -d "$B/sv$v" "$SRC/Check.java" "$SRC/ScopedValueDemo.java" && { echo "\$ java ScopedValueDemo (JDK $v)"; jr $v -cp "$B/sv$v" ScopedValueDemo; } done } > "$OUT/81-scoped-values.txt" # ---------- 82: structured concurrency, NOT final in 25 ---------- { echo "# StructuredTaskScope (JEP 505 in 25). Fifth preview in 25, sixth in 26, seventh in 27." for v in 25 26 27; do echo; echo "\$ javac StructuredHub.java (JDK $v, no flag)"; jc $v -d "$B/st$v-nopreview" "$SRC/Check.java" "$SRC/StructuredHub.java" echo "\$ javac --enable-preview --release $v StructuredHub.java (JDK $v)"; mkdir -p "$B/st$v" jc $v --enable-preview --release $v -Xlint:-preview -d "$B/st$v" "$SRC/Check.java" "$SRC/StructuredHub.java" \ && { echo "\$ java --enable-preview StructuredHub (JDK $v)"; jr $v --enable-preview -cp "$B/st$v" StructuredHub; } done for v in 26 27; do echo; echo "\$ javac --enable-preview --release $v StructuredHubNew.java (JDK $v)"; mkdir -p "$B/stn$v" jc $v --enable-preview --release $v -Xlint:-preview -d "$B/stn$v" "$SRC/Check.java" "$SRC/StructuredHubNew.java" \ && { echo "\$ java --enable-preview StructuredHubNew (JDK $v)"; jr $v --enable-preview -cp "$B/stn$v" StructuredHubNew; } done } > "$OUT/82-structured-not-final.txt" # ---------- 89: the Joiner factory methods on 25, 26, 27 ---------- { echo "# javap --module java.base -public 'java.util.concurrent.StructuredTaskScope\$Joiner', static factories only" for v in 25 26 27; do echo; echo "== JDK $v"; "$(jdk $v)/bin/javap" --module java.base -public 'java.util.concurrent.StructuredTaskScope$Joiner' 2>&1 \ | grep -E '^ public static' | sed -E 's/java\.util\.concurrent\.//g; s/java\.util\.stream\.//g; s/java\.util\.function\.//g; s/java\.util\.//g; s/java\.lang\.//g'; done } > "$OUT/89-joiner-api.txt" # ---------- 83: finalization ---------- { echo "# Is finalization removed by default in 25? (JEP 421 deprecated it for removal in 18)" for v in 21 25 26 27; do mkdir -p "$B/fin$v"; "$(jdk $v)/bin/javac" -Xlint:-removal -d "$B/fin$v" "$SRC/FinalizerDefault.java" 2>&1 | grep -v -E '^(Note: |$)' echo; echo "\$ java FinalizerDefault (JDK $v)"; jr $v -cp "$B/fin$v" FinalizerDefault echo "\$ java --finalization=disabled FinalizerDefault (JDK $v)"; jr $v --finalization=disabled -cp "$B/fin$v" FinalizerDefault; done } > "$OUT/83-finalization.txt" # ---------- 84: security manager ---------- { echo "# System.setSecurityManager(new SecurityManager())" for v in 21 25; do mkdir -p "$B/sm$v"; "$(jdk $v)/bin/javac" -Xlint:-removal -d "$B/sm$v" "$SRC/SecurityManagerGone.java" 2>&1 | grep -v -E '^(Note: |$)' echo; echo "\$ java SecurityManagerGone (JDK $v)"; jr $v -cp "$B/sm$v" SecurityManagerGone; done } > "$OUT/84-security-manager.txt" # ---------- 85: Thread.stop / suspend / resume ---------- { echo "# Thread.stop(), suspend(), resume(): when did they actually go?" for v in 21 25; do mkdir -p "$B/ts$v"; "$(jdk $v)/bin/javac" -Xlint:-removal -d "$B/ts$v" "$SRC/ThreadStopRuntime.java" 2>&1 | grep -v -E '^(Note: |$)' echo; echo "\$ java ThreadStopRuntime (JDK $v)"; jr $v -cp "$B/ts$v" ThreadStopRuntime; done for v in 21 25 26; do echo; echo "\$ javac ThreadSuspendGone.java (JDK $v)"; jc $v -d "$B/sus$v" "$SRC/ThreadSuspendGone.java"; done for v in 23 24; do echo; echo "\$ javac ThreadSuspendGone.java (JDK $v, Corretto image)"; mkdir -p "$B/sus$v"; djc $v -d /b/sus$v /src/ThreadSuspendGone.java; done } > "$OUT/85-thread-stop-suspend.txt" # ---------- 86: Unsafe and JNI warnings ---------- { echo "# sun.misc.Unsafe memory access (JEP 498) and JNI (JEP 472): what does the JVM say?" for v in 21 25; do mkdir -p "$B/us$v"; "$(jdk $v)/bin/javac" -Xlint:-removal -d "$B/us$v" "$SRC/UnsafeWarning.java" >/dev/null 2>&1 echo; echo "\$ java UnsafeWarning (JDK $v)"; jr $v -cp "$B/us$v" UnsafeWarning; done echo; echo "\$ java UnsafeWarning (JDK 24, Corretto image)"; dj 24 java -cp /b/us21 UnsafeWarning for v in 21 25; do mkdir -p "$B/jni$v"; "$(jdk $v)/bin/javac" -d "$B/jni$v" "$SRC/JniWarning.java" 2>&1 echo; echo "\$ java JniWarning (JDK $v)"; jr $v -cp "$B/jni$v" JniWarning echo "\$ java --enable-native-access=ALL-UNNAMED JniWarning (JDK $v)"; jr $v --enable-native-access=ALL-UNNAMED -cp "$B/jni$v" JniWarning; done echo; echo "\$ java JniWarning (JDK 24, Corretto image)"; dj 24 java -cp /b/jni21 JniWarning } > "$OUT/86-unsafe-and-jni.txt" # ---------- 87: pinning ---------- { echo "# 4 virtual threads on ONE carrier, each sleeping 200 ms inside synchronized (JEP 491, Java 24)" for v in 21 25 26 27; do mkdir -p "$B/pin$v"; "$(jdk $v)/bin/javac" -d "$B/pin$v" "$SRC/PinningDemo.java" 2>&1 echo; echo "\$ java -Djdk.virtualThreadScheduler.parallelism=1 -Djdk.virtualThreadScheduler.maxPoolSize=1 PinningDemo (JDK $v)" jr $v -Djdk.virtualThreadScheduler.parallelism=1 -Djdk.virtualThreadScheduler.maxPoolSize=1 -cp "$B/pin$v" PinningDemo; done for v in 23 24; do echo; echo "\$ java -Djdk.virtualThreadScheduler.parallelism=1 -Djdk.virtualThreadScheduler.maxPoolSize=1 PinningDemo (JDK $v, Corretto image)" dj $v java -Djdk.virtualThreadScheduler.parallelism=1 -Djdk.virtualThreadScheduler.maxPoolSize=1 -cp /b/pin21 PinningDemo; done } > "$OUT/87-pinning.txt" # ---------- 88: non-generational ZGC ---------- { echo "# -XX:+UseZGC -XX:-ZGenerational -Xlog:gc+init -version : generational ZGC is the only ZGC from Java 24 (JEP 490)" keep='Version:|Using|Generational|Ignoring|Unrecognized|obsolete|Error: Could' for v in 21 25 26 27; do echo; echo "\$ java -XX:+UseZGC -XX:-ZGenerational -Xlog:gc+init -version (JDK $v)" jr $v -XX:+UseZGC -XX:-ZGenerational -Xlog:gc+init -version | grep -E "$keep"; done for v in 23 24; do echo; echo "\$ java -XX:+UseZGC -XX:-ZGenerational -Xlog:gc+init -version (JDK $v, Corretto image)" dj $v java -XX:+UseZGC -XX:-ZGenerational -Xlog:gc+init -version | grep -E "$keep"; done } > "$OUT/88-zgc-generational.txt" # ---------- 90: unnamed variables and compact source files ---------- { echo "# _ (unnamed variables and patterns) and compact source files with instance main()" echo; echo "\$ javac UnnamedVariables.java (JDK 21, no flag)"; jc 21 -d "$B/un21" "$SRC/UnnamedVariables.java" for v in 25; do echo; echo "\$ javac UnnamedVariables.java (JDK $v, no flag)"; mkdir -p "$B/un$v" jc $v -d "$B/un$v" "$SRC/UnnamedVariables.java" && { echo "\$ java UnnamedVariables (JDK $v)"; jr $v -cp "$B/un$v" UnnamedVariables; }; done echo; echo "\$ java CompactHello.java (JDK 21, no flag)"; jr 21 "$SRC/CompactHello.java" echo; echo "\$ java CompactHello.java (JDK 25, no flag)"; jr 25 "$SRC/CompactHello.java" } > "$OUT/90-unnamed-and-compact.txt" # ---------- 91: JEP 500 on 27 (70-final-field-mutation.txt covers 25 and 26) ---------- { echo "# Reflection writing to a final field on JDK 27: same behaviour as 26 (compare 70-final-field-mutation.txt)" mkdir -p "$B/ffm27"; "$JDK27/bin/javac" -d "$B/ffm27" "$ROOT/recap26/src/FinalFieldMutation.java" 2>&1 | grep -v -E '^(Note: |$)' echo; echo "=== JDK 27, defaults"; jr 27 -cp "$B/ffm27" FinalFieldMutation echo; echo "=== JDK 27, --illegal-final-field-mutation=deny"; jr 27 --illegal-final-field-mutation=deny -cp "$B/ffm27" FinalFieldMutation } > "$OUT/91-final-field-mutation-27.txt" scrub "$OUT"/8[0-9]-*.txt "$OUT"/90-*.txt "$OUT"/91-*.txt echo "wrote $OUT/80..91"