--- old/make/hotspot/symbols/symbols-unix 2020-02-26 18:39:57.509617765 -0800 +++ new/make/hotspot/symbols/symbols-unix 2020-02-26 18:39:56.873592878 -0800 @@ -150,6 +150,7 @@ JVM_IsThreadAlive JVM_IsVMGeneratedMethodIx JVM_LatestUserDefinedLoader +JVM_LinkClassesForCDS JVM_LoadLibrary JVM_MaxMemory JVM_MaxObjectInspectionAge --- old/src/hotspot/share/include/jvm.h 2020-02-26 18:39:58.772667186 -0800 +++ new/src/hotspot/share/include/jvm.h 2020-02-26 18:39:58.136642300 -0800 @@ -176,6 +176,9 @@ JNIEXPORT void JNICALL JVM_InitializeFromArchive(JNIEnv* env, jclass cls); +JNIEXPORT void JNICALL +JVM_LinkClassesForCDS(JNIEnv *env); + /* * java.lang.Throwable */ --- old/src/hotspot/share/memory/metaspaceShared.cpp 2020-02-26 18:40:00.043716921 -0800 +++ new/src/hotspot/share/memory/metaspaceShared.cpp 2020-02-26 18:39:59.405691956 -0800 @@ -1688,10 +1688,11 @@ } class LinkSharedClassesClosure : public KlassClosure { + bool _is_static; Thread* THREAD; bool _made_progress; public: - LinkSharedClassesClosure(Thread* thread) : THREAD(thread), _made_progress(false) {} + LinkSharedClassesClosure(bool is_static, Thread* thread) : _is_static(is_static), THREAD(thread), _made_progress(false) {} void reset() { _made_progress = false; } bool made_progress() const { return _made_progress; } @@ -1699,13 +1700,17 @@ void do_klass(Klass* k) { if (k->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(k); - // Link the class to cause the bytecodes to be rewritten and the - // cpcache to be created. Class verification is done according - // to -Xverify setting. - _made_progress |= MetaspaceShared::try_link_class(ik, THREAD); - guarantee(!HAS_PENDING_EXCEPTION, "exception in link_class"); + // For dynamic CDS dump, only link classes loaded by the builtin class loaders. + bool do_linking = _is_static ? true : ik->loader_type() != 0; + if (do_linking) { + // Link the class to cause the bytecodes to be rewritten and the + // cpcache to be created. Class verification is done according + // to -Xverify setting. + _made_progress |= MetaspaceShared::try_link_class(ik, THREAD); + guarantee(!HAS_PENDING_EXCEPTION, "exception in link_class"); - ik->constants()->resolve_class_constants(THREAD); + ik->constants()->resolve_class_constants(THREAD); + } } } }; @@ -1724,10 +1729,10 @@ } }; -void MetaspaceShared::link_and_cleanup_shared_classes(TRAPS) { +void MetaspaceShared::link_and_cleanup_shared_classes(bool is_static, Thread* THREAD) { // We need to iterate because verification may cause additional classes // to be loaded. - LinkSharedClassesClosure link_closure(THREAD); + LinkSharedClassesClosure link_closure(is_static, THREAD); do { link_closure.reset(); ClassLoaderDataGraph::unlocked_loaded_classes_do(&link_closure); @@ -1815,7 +1820,7 @@ // were not explicitly specified in the classlist. E.g., if an interface implemented by class K // fails verification, all other interfaces that were not specified in the classlist but // are implemented by K are not verified. - link_and_cleanup_shared_classes(CATCH); + link_and_cleanup_shared_classes(true, CATCH); log_info(cds)("Rewriting and linking classes: done"); if (HeapShared::is_heap_object_archiving_allowed()) { @@ -1871,7 +1876,7 @@ // Returns true if the class's status has changed bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) { - assert(DumpSharedSpaces, "should only be called during dumping"); + Arguments::assert_is_dumping_archive(); if (ik->init_state() < InstanceKlass::linked) { bool saved = BytecodeVerificationLocal; if (ik->loader_type() == 0 && ik->class_loader() == NULL) { --- old/src/hotspot/share/memory/metaspaceShared.hpp 2020-02-26 18:40:01.327767164 -0800 +++ new/src/hotspot/share/memory/metaspaceShared.hpp 2020-02-26 18:40:00.692742317 -0800 @@ -296,7 +296,7 @@ } static bool try_link_class(InstanceKlass* ik, TRAPS); - static void link_and_cleanup_shared_classes(TRAPS); + static void link_and_cleanup_shared_classes(bool is_static, Thread* THREAD); #if INCLUDE_CDS static ReservedSpace reserve_shared_space(size_t size, char* requested_address = NULL); --- old/src/hotspot/share/oops/constantPool.cpp 2020-02-26 18:40:02.603817094 -0800 +++ new/src/hotspot/share/oops/constantPool.cpp 2020-02-26 18:40:01.949791503 -0800 @@ -304,7 +304,7 @@ } void ConstantPool::resolve_class_constants(TRAPS) { - assert(DumpSharedSpaces, "used during dump time only"); + Arguments::assert_is_dumping_archive(); // The _cache may be NULL if the _pool_holder klass fails verification // at dump time due to missing dependencies. if (cache() == NULL || reference_map() == NULL) { --- old/src/hotspot/share/oops/instanceKlass.cpp 2020-02-26 18:40:03.881867103 -0800 +++ new/src/hotspot/share/oops/instanceKlass.cpp 2020-02-26 18:40:03.240842020 -0800 @@ -2473,7 +2473,7 @@ // returns true IFF is_in_error_state() has been changed as a result of this call. bool InstanceKlass::check_sharing_error_state() { - assert(DumpSharedSpaces, "should only be called during dumping"); + Arguments::assert_is_dumping_archive(); bool old_state = is_in_error_state(); if (!is_in_error_state()) { @@ -3576,7 +3576,7 @@ "this class isn't found in class loader data"); // Verify vtables - if (is_linked()) { + if (is_linked() && !is_in_error_state()) { // $$$ This used to be done only for m/s collections. Doing it // always seemed a valid generalization. (DLD -- 6/00) vtable().verify(st); --- old/src/hotspot/share/oops/instanceKlass.hpp 2020-02-26 18:40:05.192918403 -0800 +++ new/src/hotspot/share/oops/instanceKlass.hpp 2020-02-26 18:40:04.545893085 -0800 @@ -1265,7 +1265,7 @@ public: void set_in_error_state() { - assert(DumpSharedSpaces, "only call this when dumping archive"); + Arguments::assert_is_dumping_archive(); _init_state = initialization_error; } bool check_sharing_error_state(); --- old/src/hotspot/share/prims/jvm.cpp 2020-02-26 18:40:06.487969076 -0800 +++ new/src/hotspot/share/prims/jvm.cpp 2020-02-26 18:40:05.833943485 -0800 @@ -3525,6 +3525,13 @@ HeapShared::initialize_from_archived_subgraph(k); JVM_END +JVM_ENTRY(void, JVM_LinkClassesForCDS(JNIEnv *env)) + JVMWrapper("JVM_LinkClassesForCDS"); + if (DynamicDumpSharedSpaces) { + MetaspaceShared::link_and_cleanup_shared_classes(false, THREAD); + } +JVM_END + // Returns an array of all live Thread objects (VM internal JavaThreads, // jvmti agent threads, and JNI attaching threads are skipped) // See CR 6404306 regarding JNI attaching threads --- old/src/java.base/share/classes/java/lang/Shutdown.java 2020-02-26 18:40:07.808020728 -0800 +++ new/src/java.base/share/classes/java/lang/Shutdown.java 2020-02-26 18:40:07.168995724 -0800 @@ -136,6 +136,10 @@ } } + // Notify the VM to link the classes loaded by the builtin class loaders + // during dynamic CDS dump. + VM.linkClassesForCDS(); + // set shutdown state VM.shutdown(); } --- old/src/java.base/share/classes/jdk/internal/misc/VM.java 2020-02-26 18:40:09.085070698 -0800 +++ new/src/java.base/share/classes/jdk/internal/misc/VM.java 2020-02-26 18:40:08.447045733 -0800 @@ -414,4 +414,6 @@ * object class in the archived graph. */ public static native void initializeFromArchive(Class c); + + public static native void linkClassesForCDS(); } --- old/src/java.base/share/native/libjava/VM.c 2020-02-26 18:40:10.343119923 -0800 +++ new/src/java.base/share/native/libjava/VM.c 2020-02-26 18:40:09.709095115 -0800 @@ -61,3 +61,8 @@ jclass c) { JVM_InitializeFromArchive(env, c); } + +JNIEXPORT void JNICALL +Java_jdk_internal_misc_VM_linkClassesForCDS(JNIEnv *env, jclass ignore) { + JVM_LinkClassesForCDS(env); +} --- /dev/null 2019-10-31 12:50:31.827000000 -0700 +++ new/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LinkClassTest.java 2020-02-26 18:40:10.981144889 -0800 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020, 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 loaded by the builtin class loaders should be linked + * during dynamic CDS dump time. + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @build LinkClassApp + * @run driver ClassFileInstaller -jar link_class_app.jar LinkClassApp Parent Child + * @run driver LinkClassTest + */ + +public class LinkClassTest extends DynamicArchiveTestBase { + public static void main(String[] args) throws Exception { + runTest(LinkClassTest::test); + } + + static void test() throws Exception { + String topArchiveName = getNewArchiveName(); + String appJar = ClassFileInstaller.getJarPath("link_class_app.jar"); + String mainClass = "LinkClassApp"; + + dump(topArchiveName, + "-Xlog:class+load,cds+dynamic=info,cds", + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + output.shouldNotContain("Skipping Parent: Not linked") + .shouldNotContain("Skipping Child: Not linked") + .shouldHaveExitValue(0); + }); + + run(topArchiveName, + "-Xlog:class+load", + "-cp", appJar, mainClass, "run") + .assertNormalExit(output -> { + output.shouldContain("Parent source: shared objects file (top)") + .shouldContain("Child source: shared objects file (top)") + .shouldHaveExitValue(0); + }); + } +} --- /dev/null 2019-10-31 12:50:31.827000000 -0700 +++ new/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/LinkClassApp.java 2020-02-26 18:40:12.256194780 -0800 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020, 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 Parent { + int get() {return 1;} +} + +class Child extends Parent { + int get() {return 2;} +} + +class LinkClassApp { + public static void main(String args[]) { + if (args.length > 0 && args[0].equals("run")) { + System.out.println("test() = " + test()); + } else { + // Executed during dynamic dumping. + System.out.println("Test.class is initialized."); + System.out.println("Parent.class and Child.class are loaded when Test.class is verified,"); + System.out.println("but these two classes are not linked"); + } + } + + static int test() { + // Verification of Test.test() would load Child and Parent, and create a verification constraint that + // Child must be a subtype of Parent. + // + // Child and Parent are not linked until Test.test() is actually executed. + Parent x = new Child(); + return x.get(); + } +}