Files
javademos/scripts/recap26.sh
T

102 lines
7.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# The Java 26 recap lane: what 26 changed that a 25 -> 27 upgrade meets in one go.
# ./scripts/recap26.sh -> docs/output/70-final-field-mutation.txt, 71-aot-cache-matrix.txt, 72-aot-startup.txt,
# 73-removed-in-26.txt, 74-api26.txt, 75-http3-fallback.txt
# Needs JDK 25 (LTS), 26 and 27 on disk.
set -uo pipefail
source "$(dirname "$0")/env.sh"
SRC="$ROOT/recap26/src"; B="$ROOT/build/recap26"; rm -rf "$B"; mkdir -p "$B/26" "$B/25" "$B/aot"
"$JDK26/bin/javac" -d "$B/26" "$SRC/Check.java" "$SRC/FinalFieldMutation.java" "$SRC/Api26.java" "$SRC/Http3Fallback.java"
"$JDK25/bin/javac" --release 25 -d "$B/25" "$SRC/FinalFieldMutation.java"
# ---------- JEP 500: final means final ----------
{
echo "# JEP 500: reflection writing to a final field, same class file behaviour on 25 and on 26"
echo; echo "=== JDK 25 (class compiled --release 25)"; "$JDK25/bin/java" -cp "$B/25" FinalFieldMutation 2>&1 | sed 's/^/ /'
echo; echo "=== JDK 26, defaults"; "$JDK26/bin/java" -cp "$B/26" FinalFieldMutation 2>&1 | sed 's/^/ /'
echo; echo "=== JDK 26, --enable-final-field-mutation=ALL-UNNAMED (the opt-in the warning asks for)"
"$JDK26/bin/java" --enable-final-field-mutation=ALL-UNNAMED -cp "$B/26" FinalFieldMutation 2>&1 | sed 's/^/ /'
echo; echo "=== JDK 26, --illegal-final-field-mutation=deny (what a future default will look like)"
"$JDK26/bin/java" --illegal-final-field-mutation=deny -cp "$B/26" FinalFieldMutation 2>&1 | sed 's/^/ /'
echo; echo "=== JDK 26, --illegal-final-field-mutation=debug (warning plus the offending stack trace)"
"$JDK26/bin/java" --illegal-final-field-mutation=debug -cp "$B/26" FinalFieldMutation 2>&1 | sed 's/^/ /' | head -12
for mode in "" "--enable-final-field-mutation=ALL-UNNAMED" "--illegal-final-field-mutation=deny"; do
echo; echo "=== JDK 26, JFR default settings, [${mode:-no flag}]: jfr print --events jdk.FinalFieldMutation"
rm -f "$B/ffm.jfr"
"$JDK26/bin/java" $mode -XX:StartFlightRecording:filename="$B/ffm.jfr",settings=default -cp "$B/26" FinalFieldMutation >/dev/null 2>&1
"$JDK26/bin/jfr" print --events jdk.FinalFieldMutation "$B/ffm.jfr" 2>&1 \
| grep -E 'jdk.FinalFieldMutation|declaringClass|fieldName|FinalFieldMutation.main' | sed 's/^ */ /' | sed -E 's/\{$//'
[ "${PIPESTATUS[0]}" -eq 0 ] || true
echo " events recorded: $("$JDK26/bin/jfr" summary "$B/ffm.jfr" | awk '/jdk.FinalFieldMutation /{print $2}')"
done
} > "$OUT/70-final-field-mutation.txt" 2>&1
# ---------- JEP 516: AOT object caching with any GC ----------
APP="$B/aot/AotApp"; mkdir -p "$APP"
cp "$ROOT/recap26/src/AotApp.java" "$APP/"
for v in 25 26 27; do jdk="JDK$v"; mkdir -p "$B/aot/$v/cls"
"${!jdk}/bin/javac" --release 25 -d "$B/aot/$v/cls" "$APP/AotApp.java"; (cd "$B/aot/$v/cls" && "${!jdk}/bin/jar" cf ../app.jar AotApp.class); done
mkcache() { # <jdk> <version> <gc> -> $B/aot/<version>/<gc>.aot
local j="$1" v="$2" gc="$3" d="$B/aot/$2"; rm -f "$d/$gc.aot" "$d/$gc.conf"
"$j/bin/java" -XX:+Use${gc}GC -XX:AOTMode=record -XX:AOTConfiguration="$d/$gc.conf" -cp "$d/app.jar" AotApp >/dev/null 2>&1
"$j/bin/java" -XX:+Use${gc}GC -XX:AOTMode=create -XX:AOTConfiguration="$d/$gc.conf" -XX:AOTCache="$d/$gc.aot" -cp "$d/app.jar" >/dev/null 2>&1; }
{
echo "# JEP 516: can the cache's archived heap objects be used? One row per (JDK, GC the cache was built under, GC at run time)."
echo "# 'heap objects' = the log says the archived module graph was used; 'unusable' = the JVM refused the whole cache."
for v in 25 26 27; do jdk="JDK$v"; for build in G1 Z; do mkcache "${!jdk}" "$v" "$build"
for run in Serial G1 Z; do
log="$("${!jdk}/bin/java" -XX:+Use${run}GC -XX:AOTCache="$B/aot/$v/$build.aot" -Xlog:aot=info -cp "$B/aot/$v/app.jar" AotApp 2>&1)"
if grep -q 'Unable to use AOT cache' <<<"$log"; then r="unusable: $(grep -o 'saved state of.*CDS will be disabled' <<<"$log" | head -1 | sed 's/, CDS will be disabled//')"
elif grep -q 'full module graph: enabled' <<<"$log"; then r="heap objects used (full module graph: enabled)"
else r="cache used, heap objects NOT used (full module graph: disabled)"; fi
printf 'JDK %s cache built under %-2s run under %-6s -> %s\n' "$v" "$build" "$run" "$r"
done; done; done
} > "$OUT/71-aot-cache-matrix.txt" 2>&1
median() { sort -n | awk '{a[NR]=$1} END{print a[int((NR+1)/2)]}'; }
bench() { # <jdk> <version> <gc> [cache-file]
local j="$1" v="$2" gc="$3" cache="${4:-}" i t0 t1; local -a ts=()
for i in 1 2 3 4 5 6 7 8 9; do
t0=$(date +%s%N)
if [ -n "$cache" ]; then "$j/bin/java" -XX:+Use${gc}GC -XX:AOTCache="$cache" -cp "$B/aot/$v/app.jar" AotApp >/dev/null 2>&1
else "$j/bin/java" -XX:+Use${gc}GC -Xshare:auto -XX:-AOTClassLinking -cp "$B/aot/$v/app.jar" AotApp >/dev/null 2>&1; fi
t1=$(date +%s%N); ts+=($(( (t1 - t0) / 1000000 )))
done; printf '%s\n' "${ts[@]}" | median; }
{
echo "# Wall-clock ms for 'java -cp app.jar AotApp' (median of 9), no AOT cache vs cache built under the same GC. INDICATIVE: shared 2-CPU sandbox."
for v in 25 26 27; do jdk="JDK$v"; for gc in G1 Z; do mkcache "${!jdk}" "$v" "$gc"
printf 'JDK %s %-2s no cache: %4s ms with cache: %4s ms\n' "$v" "$gc" "$(bench "${!jdk}" "$v" "$gc")" "$(bench "${!jdk}" "$v" "$gc" "$B/aot/$v/$gc.aot")"
done; done
} > "$OUT/72-aot-startup.txt" 2>&1
# committed transcripts must not carry the author's paths or per-run identity hashes
scrub() { sed -i -E "s#$ROOT#<repo>#g; s#@[0-9a-f]{6,8}#@<hash>#g" "$@"; }
scrub "$OUT/70-final-field-mutation.txt" "$OUT/71-aot-cache-matrix.txt" "$OUT/72-aot-startup.txt"
# ---------- JEP 504 and friends: things that were removed ----------
compile() { local jdk="$1" rel="$2" f="$3"; local out rc; out="$("$jdk/bin/javac" --release "$rel" -d "$B/rm" "$f" 2>&1)"; rc=$?
printf ' javac (JDK %s, --release %s): exit %s\n' "$4" "$rel" "$rc"; printf '%s\n' "$out" | grep -v '^Note:' | sed 's/^/ /'; }
mkdir -p "$B/rm"
{
echo "# Source that compiled on 25 and does not on 26"
for f in AppletGone ThreadStopGone; do echo; echo "=== recap26/src/broken/$f.java"
compile "$JDK25" 25 "$SRC/broken/$f.java" 25; compile "$JDK26" 26 "$SRC/broken/$f.java" 26; done
} > "$OUT/73-removed-in-26.txt" 2>&1
# ---------- new API in 26 ----------
{ "$JDK26/bin/java" -Dstdout.encoding=UTF-8 -cp "$B/26" Api26; echo "exit code: $?"; } > "$OUT/74-api26.txt" 2>&1
# ---------- JEP 517: HTTP/3 client, checkable half ----------
"$JDK26/bin/keytool" -genkeypair -alias demo -keyalg EC -groupname secp256r1 -dname "CN=localhost" -validity 2 -storetype PKCS12 \
-keystore "$B/ks.p12" -storepass changeit -ext san=dns:localhost >/dev/null 2>&1
{
echo "# JEP 517: a client that prefers HTTP/3 against a server that only speaks HTTP/1.1 over TLS (loopback, JDK's own HttpsServer)"
echo "# There is no live HTTP/3 server in this transcript: the sandbox has no UDP egress. The API surface is javap output below."
"$JDK26/bin/java" -cp "$B/26" Http3Fallback "$B/ks.p12" changeit 2>&1
echo; echo "# javap --module java.net.http -public java.net.http.HttpOption 'java.net.http.HttpOption\$Http3DiscoveryMode'"
"$JDK26/bin/javap" --module java.net.http -public 'java.net.http.HttpOption' 'java.net.http.HttpOption$Http3DiscoveryMode' 2>&1
echo; echo "# javap ... HttpClient\$Version"; "$JDK26/bin/javap" --module java.net.http -public 'java.net.http.HttpClient$Version' 2>&1 | grep -E 'HTTP_'
} > "$OUT/75-http3-fallback.txt" 2>&1
scrub "$OUT/73-removed-in-26.txt" "$OUT/74-api26.txt" "$OUT/75-http3-fallback.txt"