From 919066e8d10ca15e9f5ab2297cd7c9a4ecd546fa Mon Sep 17 00:00:00 2001 From: Roland Mesde Date: Mon, 8 Dec 2025 09:09:33 -0800 Subject: [PATCH] Backport 8f87fdce0b17f3edd453054461895330b82e8a71 --- src/hotspot/share/cds/lambdaFormInvokers.cpp | 2 +- src/hotspot/share/classfile/classLoader.cpp | 5 +- .../cds/appcds/aotCache/JNIDefineClass.java | 138 ++++++++++++++++++ .../appcds/aotCache/libJNIDefineClassApp.c | 36 +++++ 4 files changed, 176 insertions(+), 5 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/JNIDefineClass.java create mode 100644 test/hotspot/jtreg/runtime/cds/appcds/aotCache/libJNIDefineClassApp.c diff --git a/src/hotspot/share/cds/lambdaFormInvokers.cpp b/src/hotspot/share/cds/lambdaFormInvokers.cpp index d6a51c87513..acaa05794a3 100644 --- a/src/hotspot/share/cds/lambdaFormInvokers.cpp +++ b/src/hotspot/share/cds/lambdaFormInvokers.cpp @@ -191,7 +191,7 @@ void LambdaFormInvokers::regenerate_holder_classes(TRAPS) { // make a copy of class bytes so GC will not affect us. char *buf = NEW_RESOURCE_ARRAY(char, len); memcpy(buf, (char*)h_bytes->byte_at_addr(0), len); - ClassFileStream st((u1*)buf, len, nullptr); + ClassFileStream st((u1*)buf, len, "jrt:/java.base"); regenerate_class(class_name, st, CHECK); } } diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp index e6c10859371..b8734dcea25 100644 --- a/src/hotspot/share/classfile/classLoader.cpp +++ b/src/hotspot/share/classfile/classLoader.cpp @@ -1192,10 +1192,7 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik, oop loader = ik->class_loader(); char* src = (char*)stream->source(); if (src == nullptr) { - if (loader == nullptr) { - // JFR classes - ik->set_shared_classpath_index(0); - } + ik->set_shared_classpath_index(-1); // unsupported location return; } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JNIDefineClass.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JNIDefineClass.java new file mode 100644 index 00000000000..58c2cb42681 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/JNIDefineClass.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary classes defined with JNI DefineClass should be excluded from the AOT config file and AOT cache. + * @bug 8368182 + * @requires vm.cds + * @requires vm.cds.supports.aot.class.linking + * @library /test/lib + * @build JNIDefineClass + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar + * JNIDefineClassApp ExcludedDummy ExcludedDummy2 + * @run main/native JNIDefineClass + */ + +import java.io.InputStream; +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; + +public class JNIDefineClass { + static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + static final String mainClass = "JNIDefineClassApp"; + + public static void main(String[] args) throws Exception { + Tester tester = new Tester(); + tester.run(new String[] {"AOT", "--two-step-training"} ); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + return new String[] { + "--enable-native-access=ALL-UNNAMED", + "-Xlog:aot,aot+class=debug", + "-Djava.library.path=" + System.getProperty("java.library.path"), + }; + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] {mainClass}; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) { + if (runMode.isApplicationExecuted()) { + out.shouldContain("@@loader = null"); + out.shouldContain("@@name = ExcludedDummy"); + + out.shouldMatch("@@loader2 = .*AppClassLoader"); + out.shouldContain("@@name2 = ExcludedDummy2"); + } + if (runMode == RunMode.TRAINING) { + out.shouldContain("Skipping ExcludedDummy: Unsupported location"); + } + + // Must not have a log like this + /// [0.378s][debug ][aot,class] klasses[ 65] = 0x0000000800160490 boot ExcludedDummy + /// [0.378s][debug ][aot,class] klasses[ 66] = 0x0000000800160490 app ExcludedDummy2 + out.shouldNotContain("aot,class.* klasses.*ExcludedDummy"); + out.shouldNotContain("aot,class.* klasses.*ExcludedDummy2"); + } + } +} + +class JNIDefineClassApp { + + static native Class nativeDefineClass(String name, ClassLoader ldr, byte[] class_bytes); + + static { + System.loadLibrary("JNIDefineClassApp"); + } + + public static void main(java.lang.String[] unused) throws Exception { + ClassLoader appLoader = JNIDefineClassApp.class.getClassLoader(); + + try (InputStream in = appLoader.getResourceAsStream("ExcludedDummy.class")) { + byte[] b = in.readAllBytes(); + System.out.println(b.length); + Class c = nativeDefineClass("ExcludedDummy", null, b); + System.out.println("@@loader = " + c.getClassLoader()); + System.out.println("@@name = " + c.getName()); + } + + try (InputStream in = appLoader.getResourceAsStream("ExcludedDummy2.class")) { + byte[] b = in.readAllBytes(); + System.out.println(b.length); + Class c = nativeDefineClass("ExcludedDummy2", appLoader, b); + System.out.println("@@loader2 = " + c.getClassLoader()); + System.out.println("@@name2 = " + c.getName()); + } + + System.out.println("TEST PASSED"); + } +} + +// This class is loaded into the bootstrap loader using JNI DefineClass() with a null code source, +// so it should be excluded from the AOT configuration (and hence excluded from AOT cache) +class ExcludedDummy { + +} + +// This class is loaded into the app loader using JNI DefineClass() with a null code source, +// so it should be excluded from the AOT configuration (and hence excluded from AOT cache) +class ExcludedDummy2 { + +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/libJNIDefineClassApp.c b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/libJNIDefineClassApp.c new file mode 100644 index 00000000000..ec3ed3bc8f6 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/libJNIDefineClassApp.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include + +JNIEXPORT jclass JNICALL +Java_JNIDefineClassApp_nativeDefineClass(JNIEnv* env, jclass clazz /*unused*/, + jstring className, jobject classLoader, jbyteArray bytecode) { + const char* classNameChar = (*env)->GetStringUTFChars(env, className, NULL); + jbyte* arrayContent = (*env)->GetByteArrayElements(env, bytecode, NULL); + jsize bytecodeLength = (*env)->GetArrayLength(env, bytecode); + jclass returnValue = (*env)->DefineClass(env, classNameChar, classLoader, arrayContent, bytecodeLength); + (*env)->ReleaseByteArrayElements(env, bytecode, arrayContent, JNI_ABORT); + (*env)->ReleaseStringUTFChars(env, className, classNameChar); + return returnValue; +}