8295102: Always load @lambda-form-invoker lines from default classlist
Reviewed-by: redestad, ccheung
This commit is contained in:
parent
ac1941425b
commit
90fb9a085b
src/hotspot/share/cds
test/hotspot/jtreg
@ -54,7 +54,9 @@
|
||||
volatile Thread* ClassListParser::_parsing_thread = NULL;
|
||||
ClassListParser* ClassListParser::_instance = NULL;
|
||||
|
||||
ClassListParser::ClassListParser(const char* file) : _id2klass_table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {
|
||||
ClassListParser::ClassListParser(const char* file, ParseMode parse_mode) : _id2klass_table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {
|
||||
log_info(cds)("Parsing %s%s", file,
|
||||
(parse_mode == _parse_lambda_forms_invokers_only) ? " (lambda form invokers only)" : "");
|
||||
_classlist_file = file;
|
||||
_file = NULL;
|
||||
// Use os::open() because neither fopen() nor os::fopen()
|
||||
@ -73,6 +75,7 @@ ClassListParser::ClassListParser(const char* file) : _id2klass_table(INITIAL_TAB
|
||||
_line_no = 0;
|
||||
_interfaces = new (ResourceObj::C_HEAP, mtClass) GrowableArray<int>(10, mtClass);
|
||||
_indy_items = new (ResourceObj::C_HEAP, mtClass) GrowableArray<const char*>(9, mtClass);
|
||||
_parse_mode = parse_mode;
|
||||
|
||||
// _instance should only be accessed by the thread that created _instance.
|
||||
assert(_instance == NULL, "must be singleton");
|
||||
@ -104,6 +107,10 @@ int ClassListParser::parse(TRAPS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_parse_mode == _parse_lambda_forms_invokers_only) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TempNewSymbol class_name_symbol = SymbolTable::new_symbol(_class_name);
|
||||
if (_indy_items->length() > 0) {
|
||||
// The current line is "@lambda-proxy class_name". Load the proxy class.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2022, 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
|
||||
@ -67,6 +67,13 @@ public:
|
||||
};
|
||||
|
||||
class ClassListParser : public StackObj {
|
||||
public:
|
||||
enum ParseMode {
|
||||
_parse_all,
|
||||
_parse_lambda_forms_invokers_only,
|
||||
};
|
||||
|
||||
private:
|
||||
// Must be C_HEAP allocated -- we don't want nested resource allocations.
|
||||
typedef ResizeableResourceHashtable<int, InstanceKlass*,
|
||||
ResourceObj::C_HEAP, mtClassShared> ID2KlassTable;
|
||||
@ -107,6 +114,7 @@ class ClassListParser : public StackObj {
|
||||
bool _interfaces_specified;
|
||||
const char* _source;
|
||||
bool _lambda_form_line;
|
||||
ParseMode _parse_mode;
|
||||
|
||||
bool parse_int_option(const char* option_name, int* value);
|
||||
bool parse_uint_option(const char* option_name, int* value);
|
||||
@ -124,10 +132,15 @@ class ClassListParser : public StackObj {
|
||||
bool parse_one_line();
|
||||
Klass* load_current_class(Symbol* class_name_symbol, TRAPS);
|
||||
|
||||
public:
|
||||
ClassListParser(const char* file);
|
||||
ClassListParser(const char* file, ParseMode _parse_mode);
|
||||
~ClassListParser();
|
||||
|
||||
public:
|
||||
static int parse_classlist(const char* classlist_path, ParseMode parse_mode, TRAPS) {
|
||||
ClassListParser parser(classlist_path, parse_mode);
|
||||
return parser.parse(THREAD); // returns the number of classes loaded.
|
||||
}
|
||||
|
||||
static bool is_parsing_thread();
|
||||
static ClassListParser* instance() {
|
||||
assert(is_parsing_thread(), "call this only in the thread that created ClassListParsing::_instance");
|
||||
|
@ -733,35 +733,39 @@ void MetaspaceShared::adjust_heap_sizes_for_dumping() {
|
||||
}
|
||||
#endif // INCLUDE_CDS_JAVA_HEAP && _LP64
|
||||
|
||||
void MetaspaceShared::get_default_classlist(char* default_classlist, const size_t buf_size) {
|
||||
// Construct the path to the class list (in jre/lib)
|
||||
// Walk up two directories from the location of the VM and
|
||||
// optionally tack on "lib" (depending on platform)
|
||||
os::jvm_path(default_classlist, (jint)(buf_size));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
char *end = strrchr(default_classlist, *os::file_separator());
|
||||
if (end != NULL) *end = '\0';
|
||||
}
|
||||
size_t classlist_path_len = strlen(default_classlist);
|
||||
if (classlist_path_len >= 3) {
|
||||
if (strcmp(default_classlist + classlist_path_len - 3, "lib") != 0) {
|
||||
if (classlist_path_len < buf_size - 4) {
|
||||
jio_snprintf(default_classlist + classlist_path_len,
|
||||
buf_size - classlist_path_len,
|
||||
"%slib", os::file_separator());
|
||||
classlist_path_len += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (classlist_path_len < buf_size - 10) {
|
||||
jio_snprintf(default_classlist + classlist_path_len,
|
||||
buf_size - classlist_path_len,
|
||||
"%sclasslist", os::file_separator());
|
||||
}
|
||||
}
|
||||
|
||||
void MetaspaceShared::preload_classes(TRAPS) {
|
||||
char default_classlist[JVM_MAXPATHLEN];
|
||||
const char* classlist_path;
|
||||
|
||||
get_default_classlist(default_classlist, sizeof(default_classlist));
|
||||
if (SharedClassListFile == NULL) {
|
||||
// Construct the path to the class list (in jre/lib)
|
||||
// Walk up two directories from the location of the VM and
|
||||
// optionally tack on "lib" (depending on platform)
|
||||
os::jvm_path(default_classlist, sizeof(default_classlist));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
char *end = strrchr(default_classlist, *os::file_separator());
|
||||
if (end != NULL) *end = '\0';
|
||||
}
|
||||
int classlist_path_len = (int)strlen(default_classlist);
|
||||
if (classlist_path_len >= 3) {
|
||||
if (strcmp(default_classlist + classlist_path_len - 3, "lib") != 0) {
|
||||
if (classlist_path_len < JVM_MAXPATHLEN - 4) {
|
||||
jio_snprintf(default_classlist + classlist_path_len,
|
||||
sizeof(default_classlist) - classlist_path_len,
|
||||
"%slib", os::file_separator());
|
||||
classlist_path_len += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (classlist_path_len < JVM_MAXPATHLEN - 10) {
|
||||
jio_snprintf(default_classlist + classlist_path_len,
|
||||
sizeof(default_classlist) - classlist_path_len,
|
||||
"%sclasslist", os::file_separator());
|
||||
}
|
||||
classlist_path = default_classlist;
|
||||
} else {
|
||||
classlist_path = SharedClassListFile;
|
||||
@ -769,9 +773,19 @@ void MetaspaceShared::preload_classes(TRAPS) {
|
||||
|
||||
log_info(cds)("Loading classes to share ...");
|
||||
_has_error_classes = false;
|
||||
int class_count = parse_classlist(classlist_path, CHECK);
|
||||
int class_count = ClassListParser::parse_classlist(classlist_path,
|
||||
ClassListParser::_parse_all, CHECK);
|
||||
if (ExtraSharedClassListFile) {
|
||||
class_count += parse_classlist(ExtraSharedClassListFile, CHECK);
|
||||
class_count += ClassListParser::parse_classlist(ExtraSharedClassListFile,
|
||||
ClassListParser::_parse_all, CHECK);
|
||||
}
|
||||
if (classlist_path != default_classlist) {
|
||||
struct stat statbuf;
|
||||
if (os::stat(default_classlist, &statbuf) == 0) {
|
||||
// File exists, let's use it.
|
||||
class_count += ClassListParser::parse_classlist(default_classlist,
|
||||
ClassListParser::_parse_lambda_forms_invokers_only, CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
// Exercise the manifest processing code to ensure classes used by CDS at runtime
|
||||
@ -814,12 +828,6 @@ void MetaspaceShared::preload_and_dump_impl(TRAPS) {
|
||||
VMThread::execute(&op);
|
||||
}
|
||||
|
||||
|
||||
int MetaspaceShared::parse_classlist(const char* classlist_path, TRAPS) {
|
||||
ClassListParser parser(classlist_path);
|
||||
return parser.parse(THREAD); // returns the number of classes loaded.
|
||||
}
|
||||
|
||||
// Returns true if the class's status has changed.
|
||||
bool MetaspaceShared::try_link_class(JavaThread* current, InstanceKlass* ik) {
|
||||
ExceptionMark em(current);
|
||||
|
@ -88,9 +88,6 @@ class MetaspaceShared : AllStatic {
|
||||
private:
|
||||
static void preload_and_dump_impl(TRAPS) NOT_CDS_RETURN;
|
||||
static void preload_classes(TRAPS) NOT_CDS_RETURN;
|
||||
static int parse_classlist(const char * classlist_path,
|
||||
TRAPS) NOT_CDS_RETURN_(0);
|
||||
|
||||
|
||||
public:
|
||||
static Symbol* symbol_rs_base() {
|
||||
@ -202,5 +199,6 @@ private:
|
||||
ReservedSpace& class_space_rs);
|
||||
static MapArchiveResult map_archive(FileMapInfo* mapinfo, char* mapped_base_address, ReservedSpace rs);
|
||||
static void unmap_archive(FileMapInfo* mapinfo);
|
||||
static void get_default_classlist(char* default_classlist, const size_t buf_size);
|
||||
};
|
||||
#endif // SHARE_CDS_METASPACESHARED_HPP
|
||||
|
@ -423,6 +423,7 @@ hotspot_appcds_dynamic = \
|
||||
-runtime/cds/appcds/jcmd/JCmdTestStaticDump.java \
|
||||
-runtime/cds/appcds/jcmd/JCmdTestDynamicDump.java \
|
||||
-runtime/cds/appcds/jcmd/JCmdTestFileSafety.java \
|
||||
-runtime/cds/appcds/lambdaForm/DefaultClassListLFInvokers.java \
|
||||
-runtime/cds/appcds/methodHandles \
|
||||
-runtime/cds/appcds/sharedStrings \
|
||||
-runtime/cds/appcds/ArchiveRelocationTest.java \
|
||||
|
@ -35,6 +35,7 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
@ -745,4 +746,37 @@ public class TestCommon extends CDSTestUtils {
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void filesMustMatch(Path a, Path b) throws IOException {
|
||||
linesMustMatch(Files.readString(a).split("\n"),
|
||||
Files.readString(b).split("\n"));
|
||||
}
|
||||
|
||||
public static void linesMustMatch(String a[], String b[]) {
|
||||
int limit = Math.min(a.length, b.length);
|
||||
|
||||
// Check the lines that are in both a[] and b[]
|
||||
for (int i = 0; i < limit; i++) {
|
||||
if (!a[i].equals(b[i])) {
|
||||
System.out.println("a:" + i + " " + a[i]);
|
||||
System.out.println("b:" + i + " " + b[i]);
|
||||
throw new RuntimeException("Output mismatch on line " + i
|
||||
+ ": a=" + a[i]
|
||||
+ ", b=" + b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Report the first line that is in one array but not in the other
|
||||
if (a.length > b.length) {
|
||||
throw new RuntimeException("Output mismatch on line " + limit
|
||||
+ ": a=" + a[limit]
|
||||
+ ", b=<none>");
|
||||
|
||||
}
|
||||
if (a.length < b.length) {
|
||||
throw new RuntimeException("Output mismatch on line " + limit
|
||||
+ ": a=<none>"
|
||||
+ ", b=" + b[limit]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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 8295102
|
||||
* @summary Always load the lambda-form-invoker lines from default classlist
|
||||
* @requires vm.cds
|
||||
* @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds
|
||||
* @build DefaultClassListLFInvokers
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
|
||||
* DefaultClassListLFInvokersApp DefaultClassListLFInvokersApp$CompMethods
|
||||
* @run driver DefaultClassListLFInvokers
|
||||
*/
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import jdk.test.lib.cds.CDSOptions;
|
||||
import jdk.test.lib.cds.CDSTestUtils;
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
|
||||
public class DefaultClassListLFInvokers {
|
||||
static final String appClass = DefaultClassListLFInvokersApp.class.getName();
|
||||
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
|
||||
|
||||
static final String[] classlist = {
|
||||
appClass,
|
||||
// If we have at least one line of @lambda-form-invoker in the classlist, it triggers
|
||||
// the regeneration of the 4 XXX$Holder during -Xshare:dump.
|
||||
"@lambda-form-invoker [LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeStatic L_V"
|
||||
};
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
File classListFile = CDSTestUtils.makeClassList(classlist);
|
||||
CDSTestUtils.createArchiveAndCheck("-XX:SharedClassListFile=" + classListFile.getPath(),
|
||||
"-cp", appJar);
|
||||
|
||||
// Make sure we still have all the LF invoker methods as when CDS is disabled,
|
||||
// in which case the XXX$Holder classes are loaded from $JAVA_HOME/lib/modules
|
||||
Path no_cds_logfile = run(Mode.no_cds);
|
||||
Path custom_cds_logfile = run(Mode.custom_cds);
|
||||
System.out.println("\n\n============================== Checking output: custom_cds vs no_cds");
|
||||
TestCommon.filesMustMatch(custom_cds_logfile, no_cds_logfile);
|
||||
|
||||
// We should also have all the LF invoker methods as when the default CDS archive is used
|
||||
// in which case the XXX$Holder classes are loaded from the default archive,
|
||||
// e.g., $JAVA_HOME/lib/server/classes.jsa
|
||||
Path default_cds_logfile = run(Mode.default_cds);
|
||||
System.out.println("\n\n============================== Checking output: custom_cds vs default_cds");
|
||||
TestCommon.filesMustMatch(custom_cds_logfile, default_cds_logfile);
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
no_cds,
|
||||
default_cds,
|
||||
custom_cds
|
||||
};
|
||||
|
||||
static Path run(Mode mode) throws Exception {
|
||||
File f = new File("log_" + mode.name() + ".txt");
|
||||
CDSOptions opts = (new CDSOptions())
|
||||
.addSuffix("-showversion", "-cp", appJar, appClass, f.toString())
|
||||
.setUseVersion(false);
|
||||
|
||||
switch (mode) {
|
||||
case no_cds:
|
||||
opts.setXShareMode("off");
|
||||
break;
|
||||
case custom_cds:
|
||||
// We will use the archive created by the last CDSTestUtils.createArchiveAndCheck() call
|
||||
opts.setUseSystemArchive(false);
|
||||
opts.setXShareMode("auto");
|
||||
break;
|
||||
case default_cds:
|
||||
default:
|
||||
// We will use the default archive.
|
||||
opts.setUseSystemArchive(true);
|
||||
opts.setXShareMode("auto");
|
||||
break;
|
||||
}
|
||||
CDSTestUtils.run(opts).assertNormalExit(DefaultClassListLFInvokersApp.FLAG);
|
||||
return f.toPath();
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultClassListLFInvokersApp {
|
||||
public static final String FLAG = "Test Success!";
|
||||
static class CompMethods implements Comparator<Method> {
|
||||
public int compare(Method a, Method b) {
|
||||
return a.toString().compareTo(b.toString());
|
||||
}
|
||||
}
|
||||
static final CompMethods compMethods = new CompMethods();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try (BufferedWriter w = new BufferedWriter(new FileWriter(args[0]))) {
|
||||
test(w, "java.lang.invoke.Invokers$Holder");
|
||||
test(w, "java.lang.invoke.DirectMethodHandle$Holder");
|
||||
test(w, "java.lang.invoke.DelegatingMethodHandle$Holder");
|
||||
test(w, "java.lang.invoke.LambdaForm$Holder");
|
||||
System.out.println(FLAG);
|
||||
}
|
||||
}
|
||||
|
||||
static void test(BufferedWriter w, String className) throws Exception {
|
||||
Class c = Class.forName(className);
|
||||
Method[] methods = c.getDeclaredMethods();
|
||||
w.write("Dumping all methods in " + c + "\n");
|
||||
Arrays.sort(methods, 0, methods.length, compMethods);
|
||||
for (Method m : methods) {
|
||||
w.write(m + "\n");
|
||||
}
|
||||
w.write("Found " + methods.length + " methods\n\n\n");
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user