diff --git a/containersQa/108_HUM151_jreCompile.sh b/containersQa/108_HUM151_jreCompile.sh new file mode 100644 index 0000000..6da5341 --- /dev/null +++ b/containersQa/108_HUM151_jreCompile.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -ex +set -o pipefail + +## resolve folder of this script, following all symlinks, +## http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in +SCRIPT_SOURCE="${BASH_SOURCE[0]}" +while [ -h "$SCRIPT_SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + SCRIPT_DIR="$( cd -P "$( dirname "$SCRIPT_SOURCE" )" && pwd )" + SCRIPT_SOURCE="$(readlink "$SCRIPT_SOURCE")" + # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located + [[ $SCRIPT_SOURCE != /* ]] && SCRIPT_SOURCE="$SCRIPT_DIR/$SCRIPT_SOURCE" +done +readonly SCRIPT_DIR="$( cd -P "$( dirname "$SCRIPT_SOURCE" )" && pwd )" + +source $SCRIPT_DIR/testlib.bash + +parseArguments "$@" +processArguments +setup +tryJreCompilation 2>&1| tee $REPORT_FILE diff --git a/containersQa/InProcessCompileDemo.java b/containersQa/InProcessCompileDemo.java new file mode 100644 index 0000000..f1c6bc9 --- /dev/null +++ b/containersQa/InProcessCompileDemo.java @@ -0,0 +1,111 @@ +//17 and up, originally by mizdebsk@redhat.com. + +import javax.tools.*; +import javax.tools.JavaFileObject.Kind; +import java.net.URI; +import java.util.*; + +public class InProcessCompileDemo { + public static void main(String[] args) throws Exception { + String jvmVersion="17"; + if (args.length>0) { + jvmVersion=args[0]; + } + String className = "Hello"; + String sourceCode = """ + public class Hello { + public static void main(String[] args) { + System.out.println("Hello from in-memory compiled code!"); + } + } + """; + + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + if (compiler == null) { + System.err.println("❌ No system compiler found — you're probably on a stripped JRE."); + return; + } + System.out.println("✅ Compiler loaded: " + compiler.getClass().getName()); + + // in-memory source + JavaFileObject source = new StringJavaFileObject(className, sourceCode); + + // file manager that captures class bytes in memory + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + StandardJavaFileManager std = compiler.getStandardFileManager(diagnostics, null, null); + MemoryFileManager memFM = new MemoryFileManager(std); + + // compile for a specific release (22 here) + JavaCompiler.CompilationTask task = compiler.getTask( + null, memFM, diagnostics, + List.of("--release", jvmVersion), null, List.of(source)); + + boolean ok = task.call(); + diagnostics.getDiagnostics().forEach(System.out::println); + + if (!ok) { + System.out.println("❌ Compilation failed"); + return; + } + System.out.println("✅ Compilation succeeded"); + + // load and run main() + ClassLoader loader = new MemoryClassLoader(memFM.getClassBytes()); + Class hello = loader.loadClass(className); + hello.getMethod("main", String[].class).invoke(null, (Object) new String[0]); + } + + // ===== Helpers ===== + + // Source stored entirely in memory + static final class StringJavaFileObject extends SimpleJavaFileObject { + private final String code; + StringJavaFileObject(String className, String code) { + super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); + this.code = code; + } + @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { return code; } + } + + // Captures compiled class bytes in memory + static final class MemoryFileManager extends ForwardingJavaFileManager { + private final Map classFiles = new HashMap<>(); + MemoryFileManager(StandardJavaFileManager fileManager) { super(fileManager); } + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) { + ByteArrayJavaFileObject file = new ByteArrayJavaFileObject(className, kind); + classFiles.put(className, file); + return file; + } + Map getClassBytes() { + Map out = new HashMap<>(); + for (var e : classFiles.entrySet()) out.put(e.getKey(), e.getValue().getBytes()); + return out; + } + } + + // Holds class bytes + static final class ByteArrayJavaFileObject extends SimpleJavaFileObject { + private final java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); + ByteArrayJavaFileObject(String name, Kind kind) { + super(URI.create("mem:///" + name.replace('.', '/') + kind.extension), kind); + } + @Override public java.io.OutputStream openOutputStream() { return out; } + byte[] getBytes() { return out.toByteArray(); } + } + + // ClassLoader that defines classes from captured bytes + static final class MemoryClassLoader extends ClassLoader { + private final Map classes; + MemoryClassLoader(Map classes) { + super(InProcessCompileDemo.class.getClassLoader()); + this.classes = classes; + } + @Override + protected Class findClass(String name) throws ClassNotFoundException { + byte[] bytes = classes.get(name); + if (bytes == null) throw new ClassNotFoundException(name); + return defineClass(name, bytes, 0, bytes.length); + } + } +} diff --git a/containersQa/testlib.bash b/containersQa/testlib.bash index ade3099..c991c51 100644 --- a/containersQa/testlib.bash +++ b/containersQa/testlib.bash @@ -79,6 +79,7 @@ function pretest() { SKIPPED2="!skipped! no local maven !skipped!" SKIPPED3="!skipped! broken right now !skipped!" SKIPPED4="!skipped! jre only testing does not support this feature." + SKIPPED44="!skipped! jdk only testing does not support this feature." SKIPPED5="!skipped! reproducers security now must be enabled by OTOOL_RUN_SECURITY_REPRODUCERS=true" SKIPPED6="!skipped! rhel 7 based images do not support this functionality." SKIPPED7="!skipped! rhel 7 Os version of Podman does not support this functionality." @@ -100,12 +101,21 @@ function setup() { # With the addition of a JRE Runtime container we should not run for full jdk features like # Maven, S2i, Javac. function skipIfJreExecution() { + # maybe replace OTOOL_jresdk with check on javac command? if [ "$OTOOL_jresdk" == "jre" ] ; then echo "$SKIPPED4" exit fi } +function skipIfJdkExecution() { + # maybe replace OTOOL_jresdk with check on javac command? + if [ "$OTOOL_jresdk" == "jdk" ] ; then + echo "$SKIPPED44" + exit + fi +} + # Use this flag to skip tests when executing on a Rhel 7 host. In some cases backend container # functionality the tests are looking for is not available in that version. function skipIfRhel7Execution() { @@ -218,6 +228,10 @@ function runOnBaseDirBash() { $PD_PROVIDER run -i $HASH bash -c "$1" } +function runOnBaseDirBashWithMount() { + $PD_PROVIDER run -v=$LIBCQA_SCRIPT_DIR:/testsDir -i $HASH bash -c "$1" +} + function runOnBaseDirBashOtherUser() { $PD_PROVIDER run -i -u 12324 $HASH bash -c "$1" } @@ -1130,3 +1144,16 @@ function assertCryptoProviders() { runOnBaseDirBash "$commandProviders" 2>&1| tee $REPORT_FILE set -x } + + +function tryJreCompilation() { + runOnBaseDirBashWithMount "ls /testsDir | grep \\.java$" + local v=$(runOnBaseDirBashWithMount "java -version " 2>&1 | grep "openjdk version" | head -n 1) + if echo "$v" | grep '"1.8' || echo "$v" | grep '"11' ; then + echo '!skipped! Skipping runtime compilation on 8 and 11' + exit 0 + else + echo '17+, going on' + fi + runOnBaseDirBashWithMount "java /testsDir/InProcessCompileDemo.java" +}