8276184: Exclude lambda proxy class from the CDS archive if its caller class is excluded

Reviewed-by: iklam, dholmes
This commit is contained in:
Calvin Cheung 2021-11-16 02:34:36 +00:00
parent a59c9b2ac2
commit bd92674be5
14 changed files with 511 additions and 68 deletions

View File

@ -112,7 +112,6 @@ public:
// Block concurrent class unloading from changing the _dumptime_table
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
SystemDictionaryShared::check_excluded_classes();
SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary();
// save dumptime tables
SystemDictionaryShared::clone_dumptime_tables();

View File

@ -521,7 +521,6 @@ void VM_PopulateDumpSharedSpace::doit() {
// Block concurrent class unloading from changing the _dumptime_table
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
SystemDictionaryShared::check_excluded_classes();
SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary();
StaticArchiveBuilder builder;
builder.gather_source_objs();

View File

@ -245,6 +245,14 @@ bool SystemDictionaryShared::is_registered_lambda_proxy_class(InstanceKlass* ik)
return (info != NULL) ? info->_is_archived_lambda_proxy : false;
}
void SystemDictionaryShared::reset_registered_lambda_proxy_class(InstanceKlass* ik) {
DumpTimeClassInfo* info = _dumptime_table->get(ik);
if (info != NULL) {
info->_is_archived_lambda_proxy = false;
info->set_excluded();
}
}
bool SystemDictionaryShared::is_early_klass(InstanceKlass* ik) {
DumpTimeClassInfo* info = _dumptime_table->get(ik);
return (info != NULL) ? info->is_early_klass() : false;
@ -325,6 +333,7 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
for (int i = 0; i < len; i++) {
InstanceKlass* intf = interfaces->at(i);
if (check_for_exclusion(intf, NULL)) {
ResourceMark rm;
log_warning(cds)("Skipping %s: interface %s is excluded", k->name()->as_C_string(), intf->name()->as_C_string());
return true;
}
@ -663,6 +672,8 @@ void SystemDictionaryShared::check_excluded_classes() {
ExcludeDumpTimeSharedClasses excl;
_dumptime_table->iterate(&excl);
_dumptime_table->update_counts();
cleanup_lambda_proxy_class_dictionary();
}
bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) {
@ -1602,9 +1613,19 @@ class CleanupDumpTimeLambdaProxyClassTable: StackObj {
public:
bool do_entry(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) {
assert_lock_strong(DumpTimeTable_lock);
for (int i = 0; i < info._proxy_klasses->length(); i++) {
InstanceKlass* caller_ik = key.caller_ik();
if (SystemDictionaryShared::check_for_exclusion(caller_ik, NULL)) {
// If the caller class is excluded, unregister all the associated lambda proxy classes
// so that they will not be included in the CDS archive.
for (int i = info._proxy_klasses->length() - 1; i >= 0; i--) {
SystemDictionaryShared::reset_registered_lambda_proxy_class(info._proxy_klasses->at(i));
info._proxy_klasses->remove_at(i);
}
}
for (int i = info._proxy_klasses->length() - 1; i >= 0; i--) {
InstanceKlass* ik = info._proxy_klasses->at(i);
if (!ik->can_be_verified_at_dumptime()) {
if (SystemDictionaryShared::check_for_exclusion(ik, NULL)) {
SystemDictionaryShared::reset_registered_lambda_proxy_class(ik);
info._proxy_klasses->remove_at(i);
}
}

View File

@ -136,6 +136,7 @@ class SharedClassLoadingMark {
class SystemDictionaryShared: public SystemDictionary {
friend class ExcludeDumpTimeSharedClasses;
friend class CleanupDumpTimeLambdaProxyClassTable;
public:
enum {
FROM_FIELD_IS_PROTECTED = 1 << 0,
@ -173,6 +174,8 @@ private:
static void write_dictionary(RunTimeSharedDictionary* dictionary,
bool is_builtin);
static void write_lambda_proxy_class_dictionary(LambdaProxyClassDictionary* dictionary);
static void cleanup_lambda_proxy_class_dictionary();
static void reset_registered_lambda_proxy_class(InstanceKlass* ik);
static bool is_jfr_event_class(InstanceKlass *k);
static bool is_registered_lambda_proxy_class(InstanceKlass* ik);
static bool check_for_exclusion_impl(InstanceKlass* k);
@ -288,7 +291,6 @@ public:
static size_t estimate_size_for_archive();
static void write_to_archive(bool is_static_archive = true);
static void adjust_lambda_proxy_class_dictionary();
static void cleanup_lambda_proxy_class_dictionary();
static void serialize_dictionary_headers(class SerializeClosure* soc,
bool is_static_archive = true);
static void serialize_vm_classes(class SerializeClosure* soc);

View File

@ -2598,6 +2598,11 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
// retrieved during dump time.
// Verification of archived old classes will be performed during run time.
bool InstanceKlass::can_be_verified_at_dumptime() const {
if (MetaspaceShared::is_in_shared_metaspace(this)) {
// This is a class that was dumped into the base archive, so we know
// it was verified at dump time.
return true;
}
if (major_version() < 50 /*JAVA_6_VERSION*/) {
return false;
}

View File

@ -24,7 +24,7 @@
/*
* @test
* @bug 8274944
* @bug 8274944 8276184
* @summary VM should not crash during CDS dump when a lambda proxy class
* contains an old version of interface.
* @requires vm.cds
@ -49,30 +49,35 @@ public class LambdaContainsOldInf {
String classList = namePrefix + ".list";
String archiveName = namePrefix + ".jsa";
// dump class list
CDSTestUtils.dumpClassList(classList, "-cp", appJar, mainClass);
String[] mainArgs = { "dummy", "addLambda" };
// create archive with the class list
CDSOptions opts = (new CDSOptions())
.addPrefix("-XX:ExtraSharedClassListFile=" + classList,
"-cp", appJar,
"-Xlog:class+load,cds")
.setArchiveName(archiveName);
OutputAnalyzer output = CDSTestUtils.createArchiveAndCheck(opts);
TestCommon.checkExecReturn(output, 0, true,
"Skipping OldProvider: Old class has been linked");
output.shouldMatch("Skipping.LambdaContainsOldInfApp[$][$]Lambda[$].*0x.*:.*Old.class.has.been.linked");
for (String mainArg : mainArgs) {
// dump class list
CDSTestUtils.dumpClassList(classList, "-cp", appJar, mainClass, mainArg);
// run with archive
CDSOptions runOpts = (new CDSOptions())
.addPrefix("-cp", appJar, "-Xlog:class+load,cds=debug")
.setArchiveName(archiveName)
.setUseVersion(false)
.addSuffix(mainClass);
output = CDSTestUtils.runWithArchive(runOpts);
TestCommon.checkExecReturn(output, 0, true,
"[class,load] LambdaContainsOldInfApp source: shared objects file");
output.shouldMatch(".class.load. OldProvider.source:.*lambdacontainsoldinf.jar")
.shouldMatch(".class.load. LambdaContainsOldInfApp[$][$]Lambda[$].*/0x.*source:.*LambdaContainsOldInf");
// create archive with the class list
CDSOptions opts = (new CDSOptions())
.addPrefix("-XX:ExtraSharedClassListFile=" + classList,
"-cp", appJar,
"-Xlog:class+load,cds")
.setArchiveName(archiveName);
OutputAnalyzer output = CDSTestUtils.createArchiveAndCheck(opts);
TestCommon.checkExecReturn(output, 0, true,
"Skipping OldProvider: Old class has been linked");
output.shouldMatch("Skipping.LambdaContainsOldInfApp[$][$]Lambda[$].*0x.*:.*Old.class.has.been.linked");
// run with archive
CDSOptions runOpts = (new CDSOptions())
.addPrefix("-cp", appJar, "-Xlog:class+load,cds=debug")
.setArchiveName(archiveName)
.setUseVersion(false)
.addSuffix(mainClass)
.addSuffix(mainArg);
output = CDSTestUtils.runWithArchive(runOpts);
TestCommon.checkExecReturn(output, 0, true,
"[class,load] LambdaContainsOldInfApp source: shared objects file");
output.shouldMatch(".class.load. OldProvider.source:.*lambdacontainsoldinf.jar")
.shouldMatch(".class.load. LambdaContainsOldInfApp[$][$]Lambda[$].*/0x.*source:.*LambdaContainsOldInf");
}
}
}

View File

@ -24,6 +24,7 @@
/*
* @test
* @bug 8276184
* @summary AppCDS handling of signed JAR.
* @requires vm.cds
* @library /test/lib
@ -42,24 +43,42 @@ public class SignedJar {
// Test class exists in signed JAR
String signedJar = TestCommon.getTestJar("signed_hello.jar");
OutputAnalyzer output;
output = TestCommon.dump(signedJar, TestCommon.list("Hello"));
TestCommon.checkDump(output, "Skipping Hello: Signed JAR");
// At runtime, the Hello class should be loaded from the jar file
// instead of from the shared archive since a class from a signed
// jar shouldn't be dumped into the archive.
output = TestCommon.exec(signedJar, "-verbose:class", "Hello");
String expectedOutput = ".class,load. Hello source: file:.*signed_hello.jar";
// "testlambda" is for testing JDK-8276184
String[] mainArgs = { "dummy", "testlambda" };
String mainClass = "Hello";
try {
output.shouldMatch(expectedOutput);
} catch (Exception e) {
TestCommon.checkCommonExecExceptions(output, e);
String skipMsg = "Skipping Hello: Signed JAR";
String lambdaInArchive = "klasses.*=.*app.*Hello[$][$]Lambda[$].*hidden";
String loadFromJar = ".class,load. Hello source: file:.*signed_hello.jar";
String lambdaLoadFromHello = ".class.load. Hello[$][$]Lambda[$].*/0x.*source.*Hello";
for (String mainArg : mainArgs) {
output = TestCommon.dump(signedJar, TestCommon.list(mainClass),
"-Xlog:cds+class=debug", mainClass, mainArg);
TestCommon.checkDump(output, skipMsg);
output.shouldNotContain(lambdaInArchive);
// At runtime, the Hello class should be loaded from the jar file
// instead of from the shared archive since a class from a signed
// jar shouldn't be dumped into the archive.
output = TestCommon.exec(signedJar, "-verbose:class", mainClass, mainArg);
try {
output.shouldMatch(loadFromJar);
if (mainArg.equals("testlambda")) {
output.shouldMatch(lambdaLoadFromHello);
}
} catch (Exception e) {
TestCommon.checkCommonExecExceptions(output, e);
}
// Test class exists in both signed JAR and unsigned JAR
String jars = signedJar + System.getProperty("path.separator") + unsignedJar;
output = TestCommon.dump(jars, TestCommon.list(mainClass),
"-Xlog:cds+class=debug", mainClass, mainArg);
TestCommon.checkDump(output, skipMsg);
output.shouldNotContain(lambdaInArchive);
}
// Test class exists in both signed JAR and unsigned JAR
String jars = signedJar + System.getProperty("path.separator") + unsignedJar;
output = TestCommon.dump(jars, TestCommon.list("Hello"));
TestCommon.checkDump(output, "Skipping Hello: Signed JAR");
}
}

View File

@ -24,7 +24,7 @@
/*
* @test
* @bug 8274944
* @bug 8274944 8276184
* @summary VM should not crash during CDS dump when a lambda proxy class
* contains an old version of interface.
* @requires vm.cds
@ -53,29 +53,34 @@ public class LambdaContainsOldInf extends DynamicArchiveTestBase {
String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar");
String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
dump(topArchiveName,
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
"-Xlog:class+load=debug,cds=debug,cds+dynamic=info",
use_whitebox_jar,
"-cp", appJar, mainClass)
.assertNormalExit(output -> {
output.shouldContain("Skipping OldProvider: Old class has been linked")
.shouldMatch("Skipping.LambdaContainsOldInfApp[$][$]Lambda[$].*0x.*:.*Old.class.has.been.linked")
.shouldHaveExitValue(0);
String[] mainArgs = { "dummy", "addLambda" };
for (String mainArg : mainArgs) {
dump(topArchiveName,
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
"-Xlog:class+load=debug,cds=debug,cds+dynamic=info",
use_whitebox_jar,
"-cp", appJar, mainClass, mainArg)
.assertNormalExit(output -> {
output.shouldContain("Skipping OldProvider: Old class has been linked")
.shouldMatch("Skipping.LambdaContainsOldInfApp[$][$]Lambda[$].*0x.*:.*Old.class.has.been.linked")
.shouldHaveExitValue(0);
});
run(topArchiveName,
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
use_whitebox_jar,
"-Xlog:class+load=debug",
"-cp", appJar, mainClass)
.assertNormalExit(output -> {
output.shouldContain("[class,load] LambdaContainsOldInfApp source: shared objects file (top)")
.shouldMatch(".class.load. OldProvider.source:.*lambda_contains_old_inf.jar")
.shouldMatch(".class.load. LambdaContainsOldInfApp[$][$]Lambda[$].*/0x.*source:.*LambdaContainsOldInf")
.shouldHaveExitValue(0);
run(topArchiveName,
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
use_whitebox_jar,
"-Xlog:class+load=debug",
"-cp", appJar, mainClass, mainArg)
.assertNormalExit(output -> {
output.shouldContain("[class,load] LambdaContainsOldInfApp source: shared objects file (top)")
.shouldMatch(".class.load. OldProvider.source:.*lambda_contains_old_inf.jar")
.shouldMatch(".class.load. LambdaContainsOldInfApp[$][$]Lambda[$].*/0x.*source:.*LambdaContainsOldInf")
.shouldHaveExitValue(0);
});
}
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2021, 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
* @bug 8276184
* @summary Archive an old interface in the base archive and an app class which
* uses the old interface via a lambda expression in the dynamic archive.
* The lambda proxy class of the app class should be in the dynamic archive.
* @requires vm.cds
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes
* @build LambdaContainsOldInfApp sun.hotspot.WhiteBox OldProvider
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar old-inf-base-archive.jar LambdaContainsOldInfApp OldProvider
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. LambdaForOldInfInBaseArchive
*/
import java.io.File;
import jdk.test.lib.cds.CDSOptions;
import jdk.test.lib.cds.CDSTestUtils;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.helpers.ClassFileInstaller;
public class LambdaForOldInfInBaseArchive extends DynamicArchiveTestBase {
static final String classList = CDSTestUtils.getOutputFileName("classlist");
static final String appClass = "LambdaContainsOldInfApp";
static final String baseArchiveClass = "OldProvider";
public static void main(String[] args) throws Exception {
runTest(LambdaForOldInfInBaseArchive::testCustomBase);
}
static void testCustomBase() throws Exception {
String topArchiveName = getNewArchiveName("top");
doTestCustomBase(topArchiveName);
}
private static void doTestCustomBase(String topArchiveName) throws Exception {
String appJar = ClassFileInstaller.getJarPath("old-inf-base-archive.jar");
// create a custom base archive containing and old interface
OutputAnalyzer output = TestCommon.dump(appJar,
TestCommon.list("OldProvider"), "-Xlog:class+load,cds+class=debug");
TestCommon.checkDump(output);
// Check that the OldProvider is being dumped into the base archive.
output.shouldMatch(".cds,class.*klass.*0x.*app.*OldProvider.*unlinked");
String baseArchiveName = TestCommon.getCurrentArchiveName();
// create a dynamic archive with the custom base archive.
// The old interface is in the base archive and will be
// accessed using a lambda expression of LambdaContainsOldInfApp.
// The lambda proxy class and the app class will be archived in the dynamic archive.
dump2(baseArchiveName, topArchiveName,
"-Xlog:cds,cds+dynamic,class+load,cds+class=debug",
"-cp", appJar,
appClass)
.assertNormalExit(out -> {
out.shouldContain("OldProvider source: shared objects file")
.shouldMatch("Archiving hidden LambdaContainsOldInfApp[$][$]Lambda[$][\\d+]*");
});
// Run with both base and dynamic archives. The OldProvider class
// should be loaded from the base archive. The LambdaContainsOldInfApp
// and its lambda proxy class should be loaded from the dynamic archive.
run2(baseArchiveName, topArchiveName,
"-Xlog:cds,cds+dynamic,class+load",
"-cp", appJar,
appClass)
.assertNormalExit(out -> {
out.shouldContain("OldProvider source: shared objects file")
.shouldContain("LambdaContainsOldInfApp source: shared objects file (top)")
.shouldMatch(".class.load. LambdaContainsOldInfApp[$][$]Lambda[$].*/0x.*source:.*shared.*objects.*file.*(top)");
});
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2021, 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
* @bug 8276184
* @summary Archive an old class in the base archive and an app class which
* uses the old class in the dynamic archive.
* The old class should be loaded from the base archive. The app class
* should be loaded from the dynamic archive.
* @requires vm.cds
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes
* @build OldSuperApp sun.hotspot.WhiteBox OldSuper ChildOldSuper GChild
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar old-class-base-archive.jar OldSuperApp OldSuper ChildOldSuper GChild
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. OldClassInBaseArchive
*/
import java.io.File;
import jdk.test.lib.cds.CDSOptions;
import jdk.test.lib.cds.CDSTestUtils;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.helpers.ClassFileInstaller;
public class OldClassInBaseArchive extends DynamicArchiveTestBase {
static final String classList = CDSTestUtils.getOutputFileName("classlist");
static final String appClass = "OldSuperApp";
static final String baseArchiveClass = "OldSuper";
public static void main(String[] args) throws Exception {
runTest(OldClassInBaseArchive::testCustomBase);
}
static void testCustomBase() throws Exception {
String topArchiveName = getNewArchiveName("top");
doTestCustomBase(topArchiveName);
}
private static void doTestCustomBase(String topArchiveName) throws Exception {
String appJar = ClassFileInstaller.getJarPath("old-class-base-archive.jar");
// create a custom base archive containing and old class
OutputAnalyzer output = TestCommon.dump(appJar,
TestCommon.list("OldSuper"), "-Xlog:class+load,cds+class=debug");
TestCommon.checkDump(output);
// Check the OldSuper is being dumped into the base archive.
output.shouldMatch(".cds.class.*klass.*0x.*app.*OldSuper.*unlinked");
String baseArchiveName = TestCommon.getCurrentArchiveName();
// create a dynamic archive with the custom base archive.
// The old class is in the base archive and will be
// accessed from OldSuperApp.
// The OldSuperApp, ChildOldSuper, and GChild classes will be archived
// in the dynamic archive.
dump2(baseArchiveName, topArchiveName,
"-Xlog:cds,cds+dynamic,class+load,cds+class=debug",
"-cp", appJar,
appClass)
.assertNormalExit(out -> {
out.shouldContain("OldSuper source: shared objects file")
// Check the following classes are being dumped into the dynamic archive.
.shouldMatch(".cds,class.*klass.*0x.*app.*OldSuperApp")
.shouldMatch(".cds,class.*klass.*0x.*app.*ChildOldSuper")
.shouldMatch(".cds,class.*klass.*0x.*app.*GChild");
});
// Run with both base and dynamic archives. The OldSuper class
// should be loaded from the base archive. The OldSuperApp
// and related classes should be loaded from the dynamic archive.
run2(baseArchiveName, topArchiveName,
"-Xlog:cds,cds+dynamic,class+load",
"-cp", appJar,
appClass)
.assertNormalExit(out -> {
out.shouldContain("OldSuper source: shared objects file")
.shouldContain("OldSuperApp source: shared objects file (top)")
.shouldContain("ChildOldSuper source: shared objects file (top)")
.shouldContain("GChild source: shared objects file (top)");
});
}
}

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2021, 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
* @bug 8276184
* @summary If the caller class is redefined during dump time, the caller class
* and its lambda proxy class should not be archived.
* @requires vm.cds
* @requires vm.jvmti
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
* /test/hotspot/jtreg/runtime/cds/appcds/test-classes
* /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes
* @build sun.hotspot.WhiteBox OldProvider
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
* @run driver RedefineClassHelper
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. RedefineCallerClassTest
*/
import jdk.test.lib.helpers.ClassFileInstaller;
public class RedefineCallerClassTest extends DynamicArchiveTestBase {
static String mainClass = RedefineCallerClass.class.getName();
static String providerClass = OldProvider.class.getName();
static String sharedClasses[] = {
mainClass,
"SimpleLambda", // caller class will be redefined in RedefineCallerClass
providerClass, // inteface with class file major version < 50
"jdk/test/lib/compiler/InMemoryJavaCompiler",
"jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper",
"jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper$1",
"jdk/test/lib/compiler/InMemoryJavaCompiler$MemoryJavaFileObject"
};
public static void main(String[] args) throws Exception {
runTest(RedefineCallerClassTest::test);
}
static void test() throws Exception {
String topArchiveName = getNewArchiveName();
String appJar = ClassFileInstaller.writeJar("redefine_caller_class.jar", sharedClasses);
String[] mainArgs = {
"redefineCaller", // redefine caller class only
"useOldInf", // use old interface only
"both" // both of the above
};
for (String mainArg : mainArgs) {
String[] options = {
"-Xlog:class+load,cds",
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+AllowArchivingWithJavaAgent",
"-javaagent:redefineagent.jar",
"-cp", appJar, mainClass, mainArg
};
dump(topArchiveName, options)
.assertNormalExit(output -> {
output.shouldHaveExitValue(0);
if (mainArg.equals("both") || mainArg.equals("useOldInf")) {
output.shouldContain("Skipping OldProvider: Old class has been linked")
.shouldMatch("Skipping.SimpleLambda[$][$]Lambda[$].*0x.*:.*Old.class.has.been.linked");
}
if (mainArg.equals("both") || mainArg.equals("redefineCaller")) {
output.shouldContain("Skipping SimpleLambda: Has been redefined");
}
});
run(topArchiveName, options)
.assertNormalExit(output -> {
output.shouldHaveExitValue(0)
.shouldContain("RedefineCallerClass source: shared objects file (top)")
.shouldMatch(".class.load. SimpleLambda[$][$]Lambda[$].*/0x.*source:.*SimpleLambda");
if (mainArg.equals("both") || mainArg.equals("useOldInf")) {
output.shouldMatch(".class.load. OldProvider.source:.*redefine_caller_class.jar");
}
if (mainArg.equals("both") || mainArg.equals("redefineCaller")) {
output.shouldMatch(".class.load. SimpleLambda.source:.*redefine_caller_class.jar");
}
});
}
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2021, 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.
*
*/
class SimpleLambda {
public Runnable getRunnable() {
return () -> {};
}
public OldProvider getProvider() {
return () -> {
return null;
};
}
}
public class RedefineCallerClass {
public static String newClass =
" class SimpleLambda { " +
" public Runnable getRunnable() { " +
" return () -> {}; " +
" } " +
" public OldProvider getProvider() { " +
" return () -> { " +
" return null; " +
" }; " +
" } " +
" } ";
public static void main(String args[]) throws Exception {
String mode = "both";
if (args.length == 1) {
mode = args[0];
}
SimpleLambda s = new SimpleLambda();
if (mode.equals("both") || mode.equals("useOldInf")) {
System.out.println(s.getProvider());
} else {
System.out.println(s.getRunnable());
}
if (mode.equals("both") || mode.equals("redefineCaller")) {
RedefineClassHelper.redefineClass(s.getClass(), newClass);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2021, 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
@ -25,5 +25,12 @@
public class Hello {
public static void main(String args[]) {
System.out.println("Hello World");
if (args.length > 0 && args[0].equals("testlambda")) {
System.out.println(getRunnable());
}
}
public static Runnable getRunnable() {
return () -> {};
}
}

View File

@ -24,6 +24,9 @@
public class LambdaContainsOldInfApp {
public static void main(final String... args) {
getProvider();
if (args.length == 1 && args[0].equals("addLambda")) {
getProvider2();
}
}
public static OldProvider getProvider() {
@ -31,4 +34,10 @@ public class LambdaContainsOldInfApp {
return null;
};
}
public static OldProvider getProvider2() {
return () -> {
return null;
};
}
}