#!/usr/bin/env bash # Sealed classes and interfaces: permits rules, exhaustive switch without default, the traps around them, and what the JVM enforces at run time. # Regenerates every file in output/. 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" t() { "$1/bin/java" -version 2>&1 | sed -n 2p | sed 's/.*(build \(.*\))/\1/'; } mk() { mktemp -d -p "$B"; } # compile + run cr() { local jdk="$1" src="$2" cls="$3" o; o="$(mk)" echo "\$ javac $src && java $cls ($(t "$jdk"))" "$jdk/bin/javac" -d "$o" "$src" 2>&1 && "$jdk/bin/java" -cp "$o" "$cls" 2>&1; } # compile only, show diagnostics co() { local jdk="$1" src="$2" o; o="$(mk)" echo "\$ javac $src ($(t "$jdk"))" "$jdk/bin/javac" -d "$o" "$src" 2>&1 | grep -v '^Note:'; } all3() { local fn="$1"; shift; [ -n "$JDK21" ] && { "$fn" "$JDK21" "$@"; echo; }; "$fn" "$JDK25" "$@"; echo; "$fn" "$JDK27" "$@"; } # 01 - the payment model { all3 cr src/PaymentDemo.java PaymentDemo; } > "$OUT/01-payment-demo.txt" # 02 - permits, the three modifiers, reflection and the class-file attribute { all3 cr src/Reflect.java Reflect; echo; all3 cr src/ImplicitPermits.java ImplicitPermits; echo o="$(mk)"; "$JDK25/bin/javac" -d "$o" src/PaymentDemo.java echo "\$ javap -v -cp . 'PaymentDemo\$Payment' | grep -A3 PermittedSubclasses ($(t "$JDK25"))" "$JDK25/bin/javap" -v -cp "$o" 'PaymentDemo$Payment' | grep -A3 '^PermittedSubclasses'; } > "$OUT/02-permits-and-modifiers.txt" # 03 - the compile errors, one file each { for f in WalletNotHandled NotPermitted MissingModifier NoSubclass NonSealedWrongPlace AnonymousSealed; do all3 co broken/$f.java; echo; done xp() { local jdk="$1" o; o="$(mk)"; echo "\$ javac broken/xpkg/a/Base.java broken/xpkg/b/Impl.java ($(t "$jdk"))" "$jdk/bin/javac" -d "$o" broken/xpkg/a/Base.java broken/xpkg/b/Impl.java 2>&1; } all3 xp; } > "$OUT/03-compile-errors.txt" # 04 - a default branch turns the check off { all3 cr src/WithDefault.java WithDefault; } > "$OUT/04-default-trap.txt" # 05 - generic and recursive hierarchies { all3 cr src/ResultDemo.java ResultDemo; echo; all3 cr src/ExprDemo.java ExprDemo; } > "$OUT/05-generic-and-recursive.txt" # 06 - the visitor, before sealed types { all3 cr src/VisitorVersion.java VisitorVersion; echo; all3 co broken/VisitorAdded.java; } > "$OUT/06-visitor.txt" # 07 - what the JVM enforces at run time (a class file whose permits clause was different when it was compiled) rt() { local jdk="$1" o; o="$(mk)"; mkdir "$o/v1" "$o/evil" "$jdk/bin/javac" -d "$o/v1" sep/v1/*.java sep/Main.java 2>&1; "$jdk/bin/javac" -d "$o/evil" sep/evil/*.java 2>&1 cp "$o/evil/Hack.class" "$o/v1/" echo "\$ java -cp v1+Hack.class Main (Hack was compiled against a Payment that permitted it) ($(t "$jdk"))" "$jdk/bin/java" -cp "$o/v1" Main 2>&1 | head -3; } { all3 rt; } > "$OUT/07-runtime-enforcement.txt" # 08 - sealed across packages needs a named module mm() { local jdk="$1" o; o="$(mk)" echo "\$ javac -d out \$(find multi/demo.pay -name '*.java') && java -p out -m demo.pay/pay.app.Main ($(t "$jdk"))" "$jdk/bin/javac" -d "$o" $(find multi/demo.pay -name '*.java') 2>&1 && "$jdk/bin/java" -p "$o" -m demo.pay/pay.app.Main 2>&1; } { all3 mm; } > "$OUT/08-modules.txt" # 09 - which language level each feature needs (sealed is final in 17; a switch over the sealed type needs 21) rl() { local jdk="$1" rel="$2" src="$3" o; o="$(mk)" echo "\$ javac --release $rel $src ($(t "$jdk"))" "$jdk/bin/javac" --release "$rel" -d "$o" "$src" 2>&1 | grep -v '^Note:'; echo "exit=${PIPESTATUS[0]}"; } { rl "$JDK25" 16 src/ImplicitPermits.java; echo; rl "$JDK25" 17 src/ImplicitPermits.java; echo; rl "$JDK25" 17 src/PaymentDemo.java | head -6; } > "$OUT/09-release-levels.txt" # 10 - Jackson 3.2.3: does a sealed type help JSON polymorphism? (needs jackson/fetch.sh first; jars are not committed) jj() { local jdk="$1" src="$2" cls="$3" o cp; o="$(mk)" cp="jackson/lib/jackson-databind-3.2.3.jar:jackson/lib/jackson-core-3.2.3.jar:jackson/lib/jackson-annotations-2.22.jar" echo "\$ javac -cp jackson/lib/* $src && java $cls ($(t "$jdk"))" "$jdk/bin/javac" -cp "$cp" -d "$o" "$src" 2>&1 && "$jdk/bin/java" -cp "$cp:$o" "$cls" 2>&1; } if [ -f jackson/lib/jackson-databind-3.2.3.jar ]; then { all3 jj jackson/JsonPayment.java JsonPayment; echo; all3 jj jackson/JsonForgotten.java JsonForgotten; echo; all3 jj jackson/JsonNoSubTypes.java JsonNoSubTypes; } > "$OUT/10-jackson.txt" else echo "skipped 10-jackson.txt: run jackson/fetch.sh first"; fi echo done