Skip to content

Commit 3e258cb

Browse files
iklamVladimir Ivanov
andcommitted
8356407: Part of class verification is skipped in AOT training run
Co-authored-by: Vladimir Ivanov <[email protected]> Reviewed-by: matsaave, coleenp
1 parent 7642556 commit 3e258cb

File tree

5 files changed

+113
-27
lines changed

5 files changed

+113
-27
lines changed

src/hotspot/share/classfile/systemDictionaryShared.cpp

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -752,29 +752,43 @@ void SystemDictionaryShared::dumptime_classes_do(MetaspaceClosure* it) {
752752
}
753753
}
754754

755-
bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbol* name,
756-
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
755+
// Called from VerificationType::is_reference_assignable_from() before performing the assignability check of
756+
// T1 must be assignable from T2
757+
// Where:
758+
// L is the class loader of <k>
759+
// T1 is the type resolved by L using the name <name>
760+
// T2 is the type resolved by L using the name <from_name>
761+
//
762+
// The meaning of (*skip_assignability_check):
763+
// true: is_reference_assignable_from() should SKIP the assignability check
764+
// false: is_reference_assignable_from() should COMPLETE the assignability check
765+
void SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbol* name,
766+
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object,
767+
bool* skip_assignability_check) {
757768
assert(CDSConfig::is_dumping_archive(), "sanity");
758769
DumpTimeClassInfo* info = get_info(k);
759770
info->add_verification_constraint(k, name, from_name, from_field_is_protected,
760771
from_is_array, from_is_object);
761772

762-
if (CDSConfig::is_dumping_dynamic_archive()) {
763-
// For dynamic dumping, we can resolve all the constraint classes for all class loaders during
764-
// the initial run prior to creating the archive before vm exit. We will also perform verification
765-
// check when running with the archive.
766-
return false;
773+
if (CDSConfig::is_dumping_classic_static_archive() && !is_builtin(k)) {
774+
// This applies ONLY to the "classic" CDS static dump, which reads the list of
775+
// unregistered classes (those intended for custom class loaders) from the classlist
776+
// and loads them using jdk.internal.misc.CDS$UnregisteredClassLoader.
777+
//
778+
// When the classlist contains an unregistered class k, the supertypes of k are also
779+
// recorded in the classlist. However, the classlist does not contain information about
780+
// any class X that's not a supertype of k but is needed in the verification of k.
781+
// As a result, CDS$UnregisteredClassLoader will not know how to resolve X.
782+
//
783+
// Therefore, we tell the verifier to refrain from resolving X. Instead, X is recorded
784+
// (symbolically) in the verification constraints of k. In the production run,
785+
// when k is loaded, we will go through its verification constraints and resolve X to complete
786+
// the is_reference_assignable_from() checks.
787+
*skip_assignability_check = true;
767788
} else {
768-
if (is_builtin(k)) {
769-
// For builtin class loaders, we can try to complete the verification check at dump time,
770-
// because we can resolve all the constraint classes. We will also perform verification check
771-
// when running with the archive.
772-
return false;
773-
} else {
774-
// For non-builtin class loaders, we cannot complete the verification check at dump time,
775-
// because at dump time we don't know how to resolve classes for such loaders.
776-
return true;
777-
}
789+
// In all other cases, we are using an *actual* class loader to load k, so it should be able
790+
// to resolve any types that are needed for the verification of k.
791+
*skip_assignability_check = false;
778792
}
779793
}
780794

src/hotspot/share/classfile/systemDictionaryShared.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,10 @@ class SystemDictionaryShared: public SystemDictionary {
233233
// ensures that you cannot load a shared class if its super type(s) are changed. However,
234234
// we need an additional check to ensure that the verification_constraints did not change
235235
// between dump time and runtime.
236-
static bool add_verification_constraint(InstanceKlass* k, Symbol* name,
236+
static void add_verification_constraint(InstanceKlass* k, Symbol* name,
237237
Symbol* from_name, bool from_field_is_protected,
238-
bool from_is_array, bool from_is_object) NOT_CDS_RETURN_(false);
238+
bool from_is_array, bool from_is_object,
239+
bool* skip_assignability_check);
239240
static void check_verification_constraints(InstanceKlass* klass,
240241
TRAPS) NOT_CDS_RETURN;
241242
static void add_enum_klass_static_field(InstanceKlass* ik, int root_index);

src/hotspot/share/classfile/verificationType.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,19 @@ bool VerificationType::is_reference_assignable_from(
106106
return true;
107107
}
108108

109+
#if INCLUDE_CDS
109110
if (CDSConfig::is_dumping_archive()) {
110-
if (SystemDictionaryShared::add_verification_constraint(klass,
111+
bool skip_assignability_check = false;
112+
SystemDictionaryShared::add_verification_constraint(klass,
111113
name(), from.name(), from_field_is_protected, from.is_array(),
112-
from.is_object())) {
113-
// If add_verification_constraint() returns true, the resolution/check should be
114-
// delayed until runtime.
114+
from.is_object(), &skip_assignability_check);
115+
if (skip_assignability_check) {
116+
// We are not able to resolve name() or from.name(). The actual assignability check
117+
// will be delayed until runtime.
115118
return true;
116119
}
117120
}
118-
121+
#endif
119122
return resolve_and_check_assignability(klass, name(), from.name(),
120123
from_field_is_protected, from.is_array(), from.is_object(), THREAD);
121124
} else if (is_array() && from.is_array()) {

test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCacheSupportForCustomLoaders.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@
2929
* @requires vm.cds.supports.aot.class.linking
3030
* @comment work around JDK-8345635
3131
* @requires !vm.jvmci.enabled
32-
* @library /test/lib
32+
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
33+
* @build ReturnIntegerAsString
3334
* @build AOTCacheSupportForCustomLoaders
3435
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AppWithCustomLoaders AppWithCustomLoaders$MyLoader
35-
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar AppWithCustomLoaders$MyLoadeeA AppWithCustomLoaders$MyLoadeeB
36+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar AppWithCustomLoaders$MyLoadeeA AppWithCustomLoaders$MyLoadeeB ReturnIntegerAsString
3637
* @run driver AOTCacheSupportForCustomLoaders AOT
3738
*/
3839

@@ -50,7 +51,8 @@ public static void main(String... args) throws Exception {
5051
.appCommandLine("AppWithCustomLoaders")
5152
.setAssemblyChecker((OutputAnalyzer out) -> {
5253
out.shouldMatch("cds,class.*unreg AppWithCustomLoaders[$]MyLoadeeA")
53-
.shouldMatch("cds,class.*array \\[LAppWithCustomLoaders[$]MyLoadeeA;");
54+
.shouldMatch("cds,class.*array \\[LAppWithCustomLoaders[$]MyLoadeeA;")
55+
.shouldNotMatch("cds,class.* ReturnIntegerAsString");
5456
})
5557
.setProductionChecker((OutputAnalyzer out) -> {
5658
out.shouldContain("Using AOT-linked classes: true");
@@ -69,6 +71,16 @@ public static void main(String args[]) throws Exception {
6971
Class klass = loader.loadClass("AppWithCustomLoaders$MyLoadeeA");
7072
klass.newInstance();
7173

74+
// Test 2: VerificationType::is_reference_assignable_from() cannot be skipped (JDK-8356407)
75+
try {
76+
Class bad = loader.loadClass("ReturnIntegerAsString");
77+
Object o = bad.newInstance(); // force verification
78+
System.out.println("Expected String but got: " + o.toString().getClass());
79+
throw new RuntimeException("VerifyError expected but not thrown");
80+
} catch (VerifyError ve) {
81+
System.out.println("Expected: " + ve);
82+
}
83+
7284
// TODO: more test cases JDK-8354557
7385
}
7486

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright (c) 2025, 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+
/*
27+
28+
class ReturnIntegerAsString {
29+
String doit() {
30+
return new Integer(1);
31+
}
32+
}
33+
34+
*/
35+
36+
public super class ReturnIntegerAsString
37+
version 60:0
38+
{
39+
public Method "<init>":"()V"
40+
stack 1 locals 1
41+
{
42+
aload_0;
43+
invokespecial Method java/lang/Object."<init>":"()V";
44+
return;
45+
}
46+
public Method toString:"()Ljava/lang/String;"
47+
stack 3 locals 2
48+
{
49+
new class java/lang/Integer;
50+
dup;
51+
iconst_1;
52+
invokespecial Method java/lang/Integer."<init>":"(I)V";
53+
areturn;
54+
}
55+
56+
} // end Class ReturnIntegerAsString

0 commit comments

Comments
 (0)