Add jep512 module: compact source files and instance main methods on JDK 25 and 27
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
This commit is contained in:
Executable
+113
@@ -0,0 +1,113 @@
|
||||
#!/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"
|
||||
Reference in New Issue
Block a user