Skip to content

Commit 90fb9a0

Browse files
committed
8295102: Always load @lambda-form-invoker lines from default classlist
Reviewed-by: redestad, ccheung
1 parent ac19414 commit 90fb9a0

File tree

7 files changed

+242
-39
lines changed

7 files changed

+242
-39
lines changed

src/hotspot/share/cds/classListParser.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@
5454
volatile Thread* ClassListParser::_parsing_thread = NULL;
5555
ClassListParser* ClassListParser::_instance = NULL;
5656

57-
ClassListParser::ClassListParser(const char* file) : _id2klass_table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {
57+
ClassListParser::ClassListParser(const char* file, ParseMode parse_mode) : _id2klass_table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {
58+
log_info(cds)("Parsing %s%s", file,
59+
(parse_mode == _parse_lambda_forms_invokers_only) ? " (lambda form invokers only)" : "");
5860
_classlist_file = file;
5961
_file = NULL;
6062
// Use os::open() because neither fopen() nor os::fopen()
@@ -73,6 +75,7 @@ ClassListParser::ClassListParser(const char* file) : _id2klass_table(INITIAL_TAB
7375
_line_no = 0;
7476
_interfaces = new (ResourceObj::C_HEAP, mtClass) GrowableArray<int>(10, mtClass);
7577
_indy_items = new (ResourceObj::C_HEAP, mtClass) GrowableArray<const char*>(9, mtClass);
78+
_parse_mode = parse_mode;
7679

7780
// _instance should only be accessed by the thread that created _instance.
7881
assert(_instance == NULL, "must be singleton");
@@ -104,6 +107,10 @@ int ClassListParser::parse(TRAPS) {
104107
continue;
105108
}
106109

110+
if (_parse_mode == _parse_lambda_forms_invokers_only) {
111+
continue;
112+
}
113+
107114
TempNewSymbol class_name_symbol = SymbolTable::new_symbol(_class_name);
108115
if (_indy_items->length() > 0) {
109116
// The current line is "@lambda-proxy class_name". Load the proxy class.

src/hotspot/share/cds/classListParser.hpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -67,6 +67,13 @@ class CDSIndyInfo {
6767
};
6868

6969
class ClassListParser : public StackObj {
70+
public:
71+
enum ParseMode {
72+
_parse_all,
73+
_parse_lambda_forms_invokers_only,
74+
};
75+
76+
private:
7077
// Must be C_HEAP allocated -- we don't want nested resource allocations.
7178
typedef ResizeableResourceHashtable<int, InstanceKlass*,
7279
ResourceObj::C_HEAP, mtClassShared> ID2KlassTable;
@@ -107,6 +114,7 @@ class ClassListParser : public StackObj {
107114
bool _interfaces_specified;
108115
const char* _source;
109116
bool _lambda_form_line;
117+
ParseMode _parse_mode;
110118

111119
bool parse_int_option(const char* option_name, int* value);
112120
bool parse_uint_option(const char* option_name, int* value);
@@ -124,10 +132,15 @@ class ClassListParser : public StackObj {
124132
bool parse_one_line();
125133
Klass* load_current_class(Symbol* class_name_symbol, TRAPS);
126134

127-
public:
128-
ClassListParser(const char* file);
135+
ClassListParser(const char* file, ParseMode _parse_mode);
129136
~ClassListParser();
130137

138+
public:
139+
static int parse_classlist(const char* classlist_path, ParseMode parse_mode, TRAPS) {
140+
ClassListParser parser(classlist_path, parse_mode);
141+
return parser.parse(THREAD); // returns the number of classes loaded.
142+
}
143+
131144
static bool is_parsing_thread();
132145
static ClassListParser* instance() {
133146
assert(is_parsing_thread(), "call this only in the thread that created ClassListParsing::_instance");

src/hotspot/share/cds/metaspaceShared.cpp

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -733,45 +733,59 @@ void MetaspaceShared::adjust_heap_sizes_for_dumping() {
733733
}
734734
#endif // INCLUDE_CDS_JAVA_HEAP && _LP64
735735

736+
void MetaspaceShared::get_default_classlist(char* default_classlist, const size_t buf_size) {
737+
// Construct the path to the class list (in jre/lib)
738+
// Walk up two directories from the location of the VM and
739+
// optionally tack on "lib" (depending on platform)
740+
os::jvm_path(default_classlist, (jint)(buf_size));
741+
for (int i = 0; i < 3; i++) {
742+
char *end = strrchr(default_classlist, *os::file_separator());
743+
if (end != NULL) *end = '\0';
744+
}
745+
size_t classlist_path_len = strlen(default_classlist);
746+
if (classlist_path_len >= 3) {
747+
if (strcmp(default_classlist + classlist_path_len - 3, "lib") != 0) {
748+
if (classlist_path_len < buf_size - 4) {
749+
jio_snprintf(default_classlist + classlist_path_len,
750+
buf_size - classlist_path_len,
751+
"%slib", os::file_separator());
752+
classlist_path_len += 4;
753+
}
754+
}
755+
}
756+
if (classlist_path_len < buf_size - 10) {
757+
jio_snprintf(default_classlist + classlist_path_len,
758+
buf_size - classlist_path_len,
759+
"%sclasslist", os::file_separator());
760+
}
761+
}
762+
736763
void MetaspaceShared::preload_classes(TRAPS) {
737764
char default_classlist[JVM_MAXPATHLEN];
738765
const char* classlist_path;
739766

767+
get_default_classlist(default_classlist, sizeof(default_classlist));
740768
if (SharedClassListFile == NULL) {
741-
// Construct the path to the class list (in jre/lib)
742-
// Walk up two directories from the location of the VM and
743-
// optionally tack on "lib" (depending on platform)
744-
os::jvm_path(default_classlist, sizeof(default_classlist));
745-
for (int i = 0; i < 3; i++) {
746-
char *end = strrchr(default_classlist, *os::file_separator());
747-
if (end != NULL) *end = '\0';
748-
}
749-
int classlist_path_len = (int)strlen(default_classlist);
750-
if (classlist_path_len >= 3) {
751-
if (strcmp(default_classlist + classlist_path_len - 3, "lib") != 0) {
752-
if (classlist_path_len < JVM_MAXPATHLEN - 4) {
753-
jio_snprintf(default_classlist + classlist_path_len,
754-
sizeof(default_classlist) - classlist_path_len,
755-
"%slib", os::file_separator());
756-
classlist_path_len += 4;
757-
}
758-
}
759-
}
760-
if (classlist_path_len < JVM_MAXPATHLEN - 10) {
761-
jio_snprintf(default_classlist + classlist_path_len,
762-
sizeof(default_classlist) - classlist_path_len,
763-
"%sclasslist", os::file_separator());
764-
}
765769
classlist_path = default_classlist;
766770
} else {
767771
classlist_path = SharedClassListFile;
768772
}
769773

770774
log_info(cds)("Loading classes to share ...");
771775
_has_error_classes = false;
772-
int class_count = parse_classlist(classlist_path, CHECK);
776+
int class_count = ClassListParser::parse_classlist(classlist_path,
777+
ClassListParser::_parse_all, CHECK);
773778
if (ExtraSharedClassListFile) {
774-
class_count += parse_classlist(ExtraSharedClassListFile, CHECK);
779+
class_count += ClassListParser::parse_classlist(ExtraSharedClassListFile,
780+
ClassListParser::_parse_all, CHECK);
781+
}
782+
if (classlist_path != default_classlist) {
783+
struct stat statbuf;
784+
if (os::stat(default_classlist, &statbuf) == 0) {
785+
// File exists, let's use it.
786+
class_count += ClassListParser::parse_classlist(default_classlist,
787+
ClassListParser::_parse_lambda_forms_invokers_only, CHECK);
788+
}
775789
}
776790

777791
// Exercise the manifest processing code to ensure classes used by CDS at runtime
@@ -814,12 +828,6 @@ void MetaspaceShared::preload_and_dump_impl(TRAPS) {
814828
VMThread::execute(&op);
815829
}
816830

817-
818-
int MetaspaceShared::parse_classlist(const char* classlist_path, TRAPS) {
819-
ClassListParser parser(classlist_path);
820-
return parser.parse(THREAD); // returns the number of classes loaded.
821-
}
822-
823831
// Returns true if the class's status has changed.
824832
bool MetaspaceShared::try_link_class(JavaThread* current, InstanceKlass* ik) {
825833
ExceptionMark em(current);

src/hotspot/share/cds/metaspaceShared.hpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,6 @@ class MetaspaceShared : AllStatic {
8888
private:
8989
static void preload_and_dump_impl(TRAPS) NOT_CDS_RETURN;
9090
static void preload_classes(TRAPS) NOT_CDS_RETURN;
91-
static int parse_classlist(const char * classlist_path,
92-
TRAPS) NOT_CDS_RETURN_(0);
93-
9491

9592
public:
9693
static Symbol* symbol_rs_base() {
@@ -202,5 +199,6 @@ class MetaspaceShared : AllStatic {
202199
ReservedSpace& class_space_rs);
203200
static MapArchiveResult map_archive(FileMapInfo* mapinfo, char* mapped_base_address, ReservedSpace rs);
204201
static void unmap_archive(FileMapInfo* mapinfo);
202+
static void get_default_classlist(char* default_classlist, const size_t buf_size);
205203
};
206204
#endif // SHARE_CDS_METASPACESHARED_HPP

test/hotspot/jtreg/TEST.groups

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ hotspot_appcds_dynamic = \
423423
-runtime/cds/appcds/jcmd/JCmdTestStaticDump.java \
424424
-runtime/cds/appcds/jcmd/JCmdTestDynamicDump.java \
425425
-runtime/cds/appcds/jcmd/JCmdTestFileSafety.java \
426+
-runtime/cds/appcds/lambdaForm/DefaultClassListLFInvokers.java \
426427
-runtime/cds/appcds/methodHandles \
427428
-runtime/cds/appcds/sharedStrings \
428429
-runtime/cds/appcds/ArchiveRelocationTest.java \

test/hotspot/jtreg/runtime/cds/appcds/TestCommon.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.io.FileInputStream;
3636
import java.io.FileOutputStream;
3737
import java.io.InputStream;
38+
import java.io.IOException;
3839
import java.net.URI;
3940
import java.nio.file.DirectoryStream;
4041
import java.nio.file.Files;
@@ -745,4 +746,37 @@ public static String filterOutLogs(String stdout) {
745746
}
746747
return sb.toString();
747748
}
749+
750+
public static void filesMustMatch(Path a, Path b) throws IOException {
751+
linesMustMatch(Files.readString(a).split("\n"),
752+
Files.readString(b).split("\n"));
753+
}
754+
755+
public static void linesMustMatch(String a[], String b[]) {
756+
int limit = Math.min(a.length, b.length);
757+
758+
// Check the lines that are in both a[] and b[]
759+
for (int i = 0; i < limit; i++) {
760+
if (!a[i].equals(b[i])) {
761+
System.out.println("a:" + i + " " + a[i]);
762+
System.out.println("b:" + i + " " + b[i]);
763+
throw new RuntimeException("Output mismatch on line " + i
764+
+ ": a=" + a[i]
765+
+ ", b=" + b[i]);
766+
}
767+
}
768+
769+
// Report the first line that is in one array but not in the other
770+
if (a.length > b.length) {
771+
throw new RuntimeException("Output mismatch on line " + limit
772+
+ ": a=" + a[limit]
773+
+ ", b=<none>");
774+
775+
}
776+
if (a.length < b.length) {
777+
throw new RuntimeException("Output mismatch on line " + limit
778+
+ ": a=<none>"
779+
+ ", b=" + b[limit]);
780+
}
781+
}
748782
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
/*
26+
* @test
27+
* @bug 8295102
28+
* @summary Always load the lambda-form-invoker lines from default classlist
29+
* @requires vm.cds
30+
* @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds
31+
* @build DefaultClassListLFInvokers
32+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
33+
* DefaultClassListLFInvokersApp DefaultClassListLFInvokersApp$CompMethods
34+
* @run driver DefaultClassListLFInvokers
35+
*/
36+
37+
import java.io.BufferedWriter;
38+
import java.io.File;
39+
import java.io.FileWriter;
40+
import java.lang.reflect.Method;
41+
import java.nio.file.Path;
42+
import java.util.Arrays;
43+
import java.util.Comparator;
44+
45+
import jdk.test.lib.cds.CDSOptions;
46+
import jdk.test.lib.cds.CDSTestUtils;
47+
import jdk.test.lib.helpers.ClassFileInstaller;
48+
49+
public class DefaultClassListLFInvokers {
50+
static final String appClass = DefaultClassListLFInvokersApp.class.getName();
51+
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
52+
53+
static final String[] classlist = {
54+
appClass,
55+
// If we have at least one line of @lambda-form-invoker in the classlist, it triggers
56+
// the regeneration of the 4 XXX$Holder during -Xshare:dump.
57+
"@lambda-form-invoker [LF_RESOLVE] java.lang.invoke.DirectMethodHandle$Holder invokeStatic L_V"
58+
};
59+
60+
public static void main(String[] args) throws Exception {
61+
File classListFile = CDSTestUtils.makeClassList(classlist);
62+
CDSTestUtils.createArchiveAndCheck("-XX:SharedClassListFile=" + classListFile.getPath(),
63+
"-cp", appJar);
64+
65+
// Make sure we still have all the LF invoker methods as when CDS is disabled,
66+
// in which case the XXX$Holder classes are loaded from $JAVA_HOME/lib/modules
67+
Path no_cds_logfile = run(Mode.no_cds);
68+
Path custom_cds_logfile = run(Mode.custom_cds);
69+
System.out.println("\n\n============================== Checking output: custom_cds vs no_cds");
70+
TestCommon.filesMustMatch(custom_cds_logfile, no_cds_logfile);
71+
72+
// We should also have all the LF invoker methods as when the default CDS archive is used
73+
// in which case the XXX$Holder classes are loaded from the default archive,
74+
// e.g., $JAVA_HOME/lib/server/classes.jsa
75+
Path default_cds_logfile = run(Mode.default_cds);
76+
System.out.println("\n\n============================== Checking output: custom_cds vs default_cds");
77+
TestCommon.filesMustMatch(custom_cds_logfile, default_cds_logfile);
78+
}
79+
80+
enum Mode {
81+
no_cds,
82+
default_cds,
83+
custom_cds
84+
};
85+
86+
static Path run(Mode mode) throws Exception {
87+
File f = new File("log_" + mode.name() + ".txt");
88+
CDSOptions opts = (new CDSOptions())
89+
.addSuffix("-showversion", "-cp", appJar, appClass, f.toString())
90+
.setUseVersion(false);
91+
92+
switch (mode) {
93+
case no_cds:
94+
opts.setXShareMode("off");
95+
break;
96+
case custom_cds:
97+
// We will use the archive created by the last CDSTestUtils.createArchiveAndCheck() call
98+
opts.setUseSystemArchive(false);
99+
opts.setXShareMode("auto");
100+
break;
101+
case default_cds:
102+
default:
103+
// We will use the default archive.
104+
opts.setUseSystemArchive(true);
105+
opts.setXShareMode("auto");
106+
break;
107+
}
108+
CDSTestUtils.run(opts).assertNormalExit(DefaultClassListLFInvokersApp.FLAG);
109+
return f.toPath();
110+
}
111+
}
112+
113+
class DefaultClassListLFInvokersApp {
114+
public static final String FLAG = "Test Success!";
115+
static class CompMethods implements Comparator<Method> {
116+
public int compare(Method a, Method b) {
117+
return a.toString().compareTo(b.toString());
118+
}
119+
}
120+
static final CompMethods compMethods = new CompMethods();
121+
122+
public static void main(String[] args) throws Exception {
123+
try (BufferedWriter w = new BufferedWriter(new FileWriter(args[0]))) {
124+
test(w, "java.lang.invoke.Invokers$Holder");
125+
test(w, "java.lang.invoke.DirectMethodHandle$Holder");
126+
test(w, "java.lang.invoke.DelegatingMethodHandle$Holder");
127+
test(w, "java.lang.invoke.LambdaForm$Holder");
128+
System.out.println(FLAG);
129+
}
130+
}
131+
132+
static void test(BufferedWriter w, String className) throws Exception {
133+
Class c = Class.forName(className);
134+
Method[] methods = c.getDeclaredMethods();
135+
w.write("Dumping all methods in " + c + "\n");
136+
Arrays.sort(methods, 0, methods.length, compMethods);
137+
for (Method m : methods) {
138+
w.write(m + "\n");
139+
}
140+
w.write("Found " + methods.length + " methods\n\n\n");
141+
}
142+
}

0 commit comments

Comments
 (0)