Runnable sources, broken examples and captured transcripts for the ankurm.com JEP 512 article, including the measured launch order and a private-constructor difference between 25 and 27. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01TF9JWFvJSNm6HVzswzZU5a
114 lines
6.0 KiB
Bash
Executable File
114 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# JEP 512 (Compact Source Files and Instance Main Methods): final in JDK 25, no --enable-preview.
|
|
# Regenerates every file in output/. Needs JDK 25 and JDK 27; a JDK 21 is optional (for the "before" transcript).
|
|
# JDK25=/path JDK27=/path [JDK21=/path] ./run.sh
|
|
set -uo pipefail
|
|
unset JAVA_TOOL_OPTIONS
|
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
JDK25="${JDK25:-/opt/jdks/jdk-25.0.4.1+1}"; JDK27="${JDK27:-/opt/jdks/jdk27}"; JDK21="${JDK21:-}"
|
|
OUT="$HERE/output"; mkdir -p "$OUT"; B="$(mktemp -d)"; trap 'rm -rf "$B"' EXIT
|
|
cd "$HERE"
|
|
v() { "$1/bin/java" -version 2>&1 | sed -n 2p; } # runtime line only, no vendor banner noise
|
|
|
|
# 01 - the source launcher: one command, no javac, no class file left behind
|
|
{
|
|
echo "\$ cat src/Hello.java"; cat src/Hello.java; echo
|
|
echo "\$ java src/Hello.java (JDK 25: $(v $JDK25))"; "$JDK25/bin/java" src/Hello.java 2>&1
|
|
echo; echo "\$ java src/Hello.java (JDK 27: $(v $JDK27))"; "$JDK27/bin/java" src/Hello.java 2>&1
|
|
echo; echo "\$ ls src/*.class"; ls src/*.class 2>&1 | sed 's/^/ /'
|
|
if [ -n "$JDK21" ]; then
|
|
echo; echo "\$ java src/Hello.java (JDK 21: $(v $JDK21))"; "$JDK21/bin/java" src/Hello.java 2>&1
|
|
fi
|
|
} > "$OUT/01-source-launcher.txt"
|
|
|
|
# 02 - javac produces an ordinary class file; javap shows what the compiler invented
|
|
{
|
|
echo "\$ javac -d out src/Hello.java && java -cp out Hello"
|
|
"$JDK25/bin/javac" -d "$B/o" src/Hello.java 2>&1 && "$JDK25/bin/java" -cp "$B/o" Hello 2>&1
|
|
echo; echo "\$ javap -p -cp out Hello"; "$JDK25/bin/javap" -p -cp "$B/o" Hello 2>&1
|
|
echo; echo "\$ javap -v -cp out Hello | grep -E 'major|flags'"
|
|
"$JDK25/bin/javap" -v -cp "$B/o" Hello 2>&1 | grep -E 'major version|^ flags' | head -2
|
|
echo; echo "\$ java Reflect (asks the loaded class)"
|
|
"$JDK25/bin/javac" -cp "$B/o" -d "$B/o" src/Reflect.java 2>&1 && "$JDK25/bin/java" -cp "$B/o" Reflect 2>&1
|
|
} > "$OUT/02-javac-and-javap.txt"
|
|
|
|
# 03 - the class body: members, nested types, implicit imports
|
|
{
|
|
echo "\$ java src/Members.java"; "$JDK25/bin/java" src/Members.java 2>&1
|
|
echo; echo "\$ java src/WithArgs.java one two"; "$JDK25/bin/java" src/WithArgs.java one two 2>&1
|
|
echo; echo "\$ java src/InstanceMainInClass.java"; "$JDK25/bin/java" src/InstanceMainInClass.java 2>&1
|
|
echo; echo "\$ java src/NeedsImport.java (java.net.http is outside java.base: a normal import fixes it)"; "$JDK25/bin/java" src/NeedsImport.java 2>&1
|
|
echo; echo "\$ java src/NeedsModuleImport.java (or import the whole module)"; "$JDK25/bin/java" src/NeedsModuleImport.java 2>&1
|
|
echo; echo "\$ java multi/App.java (App.java + Greeter.java in one directory)"; "$JDK25/bin/java" multi/App.java 2>&1
|
|
} > "$OUT/03-members-and-imports.txt"
|
|
|
|
# 04 - the IO helper
|
|
{
|
|
echo "\$ printf 'Ankur\n30\n' | java src/ReadLn.java"; printf 'Ankur\n30\n' | "$JDK25/bin/java" src/ReadLn.java 2>&1
|
|
echo; echo "\$ java src/IoInAnyClass.java"; "$JDK25/bin/java" src/IoInAnyClass.java 2>&1
|
|
echo; echo "\$ java src/StaticImportIO.java (import static java.lang.IO.* makes the bare form work)"; "$JDK25/bin/java" src/StaticImportIO.java 2>&1
|
|
echo; echo "\$ java broken/NoIoPrefix.java (JDK 27: same error as on 25)"; "$JDK27/bin/java" broken/NoIoPrefix.java 2>&1 | head -8
|
|
echo; echo "\$ printf '' | java src/ReadLn.java (stdin closed: what does readln return?)"
|
|
printf '' | "$JDK25/bin/java" src/ReadLn.java 2>&1
|
|
} > "$OUT/04-io-helper.txt"
|
|
|
|
# 05 - which main wins. Generate every pair of mains that can coexist and ask the launcher.
|
|
gen() { # name, body
|
|
printf 'class %s {\n%s\n}\n' "$1" "$2" > "$B/$1.java"
|
|
}
|
|
gen SS_i0 ' static void main(String[] a) { IO.println("static main(String[])"); }
|
|
void main() { IO.println("instance main()"); }'
|
|
gen S0_iS ' static void main() { IO.println("static main()"); }
|
|
void main(String[] a) { IO.println("instance main(String[])"); }'
|
|
gen SS_S0 ' static void main(String[] a) { IO.println("static main(String[])"); }
|
|
static void main() { IO.println("static main()"); }'
|
|
gen IS_I0 ' void main(String[] a) { IO.println("instance main(String[])"); }
|
|
void main() { IO.println("instance main()"); }'
|
|
{
|
|
for j in "$JDK25" "$JDK27"; do
|
|
echo "== $(v $j)"
|
|
for c in SS_i0 S0_iS SS_S0 IS_I0; do
|
|
printf '%-8s declares: %-58s -> ran: ' "$c" "$(grep -o '\(static \)\?void main([^)]*)' "$B/$c.java" | tr '\n' ' ')"
|
|
"$j/bin/java" "$B/$c.java" 2>&1 | head -1
|
|
done
|
|
echo "-- same four classes, compiled first and launched as a class (java -cp out NAME)"
|
|
rm -rf "$B/cls"; mkdir -p "$B/cls"; "$j/bin/javac" -d "$B/cls" "$B"/SS_i0.java "$B"/S0_iS.java "$B"/SS_S0.java "$B"/IS_I0.java 2>&1
|
|
for c in SS_i0 S0_iS SS_S0 IS_I0; do
|
|
printf '%-8s -> ran: ' "$c"; "$j/bin/java" -cp "$B/cls" "$c" 2>&1 | head -1
|
|
done
|
|
echo
|
|
done
|
|
} > "$OUT/05-launch-order.txt"
|
|
|
|
# 06 - every way to get it wrong, with the compiler's own words
|
|
{
|
|
for f in NoMain NoIoPrefix NotInJavaBase WithPackage Other WrongParam PrivateMain CtorWithArgs; do
|
|
if [ "$f" = Other ]; then
|
|
echo "\$ javac -d out broken/Other.java broken/Target.java (JDK 25; Target.java is a compact file)"
|
|
"$JDK25/bin/javac" -d "$B/x" broken/Other.java broken/Target.java 2>&1 | head -12
|
|
echo; echo "\$ java broken/Other.java (JDK 25; source launcher, Target.java sits next to it)"
|
|
"$JDK25/bin/java" broken/Other.java 2>&1 | head -12
|
|
else
|
|
echo "\$ java broken/$f.java (JDK 25)"; "$JDK25/bin/java" broken/$f.java 2>&1 | head -12
|
|
fi
|
|
echo
|
|
done
|
|
} > "$OUT/06-compile-errors.txt"
|
|
|
|
# 07 - the one behaviour that differs between 25 and 27
|
|
{
|
|
for j in "$JDK25" "$JDK27"; do
|
|
echo "\$ java broken/PrivateCtor.java ($(v $j))"
|
|
"$j/bin/java" broken/PrivateCtor.java 2>&1 | head -6; echo
|
|
done
|
|
} > "$OUT/07-private-constructor-25-vs-27.txt"
|
|
|
|
# 08 - what java.lang.IO actually is
|
|
{
|
|
echo "\$ javap -p java.lang.IO"; "$JDK25/bin/javap" -p java.lang.IO 2>&1
|
|
echo; echo "\$ javap -p -c java.lang.IO | grep -E 'public static|System.out|readLine|IOError'"
|
|
"$JDK25/bin/javap" -p -c java.lang.IO 2>&1 | grep -E 'public static|System.out|readLine|IOError|flush'
|
|
} > "$OUT/08-io-class.txt"
|
|
|
|
echo "wrote $OUT"
|