diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 5915424c4fe..9e8a46e105e 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -236,7 +236,9 @@ void CDSConfig::init_shared_archive_paths() { } void CDSConfig::check_internal_module_property(const char* key, const char* value) { - if (Arguments::is_internal_module_property(key) && !Arguments::is_module_path_property(key)) { + if (Arguments::is_internal_module_property(key) && + !Arguments::is_module_path_property(key) && + !Arguments::is_add_modules_property(key)) { stop_using_optimized_module_handling(); log_info(cds)("optimized module handling: disabled due to incompatible property: %s=%s", key, value); } diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index e9e576a83f6..33a81a81da0 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -934,7 +934,7 @@ void FileMapInfo::extract_module_paths(const char* runtime_path, GrowableArraysort(ClassLoaderExt::compare_module_path_by_name); + module_paths->sort(ClassLoaderExt::compare_module_names); } bool FileMapInfo::check_module_paths() { diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index efd7a906a46..2a43fd2dd4f 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -403,6 +403,7 @@ void MetaspaceShared::serialize(SerializeClosure* soc) { soc->do_tag(--tag); CDS_JAVA_HEAP_ONLY(Modules::serialize(soc);) + CDS_JAVA_HEAP_ONLY(Modules::serialize_addmods_names(soc);) CDS_JAVA_HEAP_ONLY(ClassLoaderDataShared::serialize(soc);) LambdaFormInvokers::serialize(soc); @@ -502,6 +503,8 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables() { LambdaFormInvokers::dump_static_archive_invokers(); // Write module name into archive CDS_JAVA_HEAP_ONLY(Modules::dump_main_module_name();) + // Write module names from --add-modules into archive + CDS_JAVA_HEAP_ONLY(Modules::dump_addmods_names();) // Write the other data to the output array. DumpRegion* ro_region = ArchiveBuilder::current()->ro_region(); char* start = ro_region->top(); diff --git a/src/hotspot/share/classfile/classLoaderExt.cpp b/src/hotspot/share/classfile/classLoaderExt.cpp index 16981669deb..b9e420899c2 100644 --- a/src/hotspot/share/classfile/classLoaderExt.cpp +++ b/src/hotspot/share/classfile/classLoaderExt.cpp @@ -90,7 +90,7 @@ void ClassLoaderExt::setup_app_search_path(JavaThread* current) { os::free(app_class_path); } -int ClassLoaderExt::compare_module_path_by_name(const char** p1, const char** p2) { +int ClassLoaderExt::compare_module_names(const char** p1, const char** p2) { return strcmp(*p1, *p2); } @@ -121,7 +121,7 @@ void ClassLoaderExt::process_module_table(JavaThread* current, ModuleEntryTable* // Sort the module paths before storing into CDS archive for simpler // checking at runtime. - module_paths->sort(compare_module_path_by_name); + module_paths->sort(compare_module_names); for (int i = 0; i < module_paths->length(); i++) { ClassLoader::setup_module_search_path(current, module_paths->at(i)); diff --git a/src/hotspot/share/classfile/classLoaderExt.hpp b/src/hotspot/share/classfile/classLoaderExt.hpp index c3c0b00d55e..1f1b38cd312 100644 --- a/src/hotspot/share/classfile/classLoaderExt.hpp +++ b/src/hotspot/share/classfile/classLoaderExt.hpp @@ -72,7 +72,7 @@ public: static void setup_search_paths(JavaThread* current); static void setup_module_paths(JavaThread* current); static void extract_jar_files_from_path(const char* path, GrowableArray* module_paths); - static int compare_module_path_by_name(const char** p1, const char** p2); + static int compare_module_names(const char** p1, const char** p2); static char* read_manifest(JavaThread* current, ClassPathEntry* entry, jint *manifest_size) { // Remove all the new-line continuations (which wrap long lines at 72 characters, see diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index dee67ce1dde..94e407d045f 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -30,6 +30,7 @@ #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderDataShared.hpp" +#include "classfile/classLoaderExt.hpp" #include "classfile/javaAssertions.hpp" #include "classfile/javaClasses.hpp" #include "classfile/javaClasses.inline.hpp" @@ -560,6 +561,7 @@ void Modules::verify_archived_modules() { } char* Modules::_archived_main_module_name = nullptr; +char* Modules::_archived_addmods_names = nullptr; void Modules::dump_main_module_name() { const char* module_name = Arguments::get_property("jdk.module.main"); @@ -600,6 +602,100 @@ void Modules::serialize(SerializeClosure* soc) { } } +void Modules::dump_addmods_names() { + unsigned int count = Arguments::addmods_count(); + const char* addmods_names = get_addmods_names_as_sorted_string(); + if (addmods_names != nullptr) { + _archived_addmods_names = ArchiveBuilder::current()->ro_strdup(addmods_names); + } + ArchivePtrMarker::mark_pointer(&_archived_addmods_names); +} + +void Modules::serialize_addmods_names(SerializeClosure* soc) { + soc->do_ptr(&_archived_addmods_names); + if (soc->reading()) { + bool disable = false; + if (_archived_addmods_names[0] != '\0') { + if (Arguments::addmods_count() == 0) { + log_info(cds)("--add-modules module name(s) found in archive but not specified during runtime: %s", + _archived_addmods_names); + disable = true; + } else { + const char* addmods_names = get_addmods_names_as_sorted_string(); + if (strcmp((const char*)_archived_addmods_names, addmods_names) != 0) { + log_info(cds)("Mismatched --add-modules module name(s)."); + log_info(cds)(" dump time: %s runtime: %s", _archived_addmods_names, addmods_names); + disable = true; + } + } + } else { + if (Arguments::addmods_count() > 0) { + log_info(cds)("--add-modules module name(s) specified during runtime but not found in archive: %s", + get_addmods_names_as_sorted_string()); + disable = true; + } + } + if (disable) { + log_info(cds)("Disabling optimized module handling"); + CDSConfig::stop_using_optimized_module_handling(); + } + log_info(cds)("optimized module handling: %s", CDSConfig::is_using_optimized_module_handling() ? "enabled" : "disabled"); + log_info(cds)("full module graph: %s", CDSConfig::is_using_full_module_graph() ? "enabled" : "disabled"); + } +} + +const char* Modules::get_addmods_names_as_sorted_string() { + ResourceMark rm; + const int max_digits = 3; + const int extra_symbols_count = 2; // includes '.', '\0' + size_t prop_len = strlen("jdk.module.addmods") + max_digits + extra_symbols_count; + char* prop_name = resource_allocate_bytes(prop_len); + GrowableArray list; + for (unsigned int i = 0; i < Arguments::addmods_count(); i++) { + jio_snprintf(prop_name, prop_len, "jdk.module.addmods.%d", i); + const char* prop_value = Arguments::get_property(prop_name); + char* p = resource_allocate_bytes(strlen(prop_value) + 1); + strcpy(p, prop_value); + while (*p == ',') p++; // skip leading commas + while (*p) { + char* next = strchr(p, ','); + if (next == nullptr) { + // no more commas, p is the last element + list.append(p); + break; + } else { + *next = 0; + list.append(p); + p = next + 1; + } + } + } + + // Example: + // --add-modules=java.compiler --add-modules=java.base,java.base,, + // + // list[0] = "java.compiler" + // list[1] = "java.base" + // list[2] = "java.base" + // list[3] = "" + // list[4] = "" + list.sort(ClassLoaderExt::compare_module_names); + + const char* prefix = ""; + stringStream st; + const char* last_string = ""; // This also filters out all empty strings + for (int i = 0; i < list.length(); i++) { + const char* m = list.at(i); + if (strcmp(m, last_string) != 0) { // filter out duplicates + st.print("%s%s", prefix, m); + last_string = m; + prefix = "\n"; + } + } + + return (const char*)os::strdup(st.as_string()); // Example: "java.base,java.compiler" +} + void Modules::define_archived_modules(Handle h_platform_loader, Handle h_system_loader, TRAPS) { assert(CDSConfig::is_using_full_module_graph(), "must be"); diff --git a/src/hotspot/share/classfile/modules.hpp b/src/hotspot/share/classfile/modules.hpp index 3866f0d6f9b..3ef6f57a57b 100644 --- a/src/hotspot/share/classfile/modules.hpp +++ b/src/hotspot/share/classfile/modules.hpp @@ -61,9 +61,13 @@ public: static void verify_archived_modules() NOT_CDS_JAVA_HEAP_RETURN; static void dump_main_module_name() NOT_CDS_JAVA_HEAP_RETURN; static void serialize(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; + static void dump_addmods_names() NOT_CDS_JAVA_HEAP_RETURN; + static void serialize_addmods_names(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; + static const char* get_addmods_names_as_sorted_string() NOT_CDS_JAVA_HEAP_RETURN_(nullptr); #if INCLUDE_CDS_JAVA_HEAP static char* _archived_main_module_name; + static char* _archived_addmods_names; #endif // Provides the java.lang.Module for the unnamed module defined diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index ce854a258a7..bcb6b919023 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -85,6 +85,7 @@ char** Arguments::_jvm_flags_array = nullptr; int Arguments::_num_jvm_flags = 0; char** Arguments::_jvm_args_array = nullptr; int Arguments::_num_jvm_args = 0; +unsigned int Arguments::_addmods_count = 0; char* Arguments::_java_command = nullptr; SystemProperty* Arguments::_system_properties = nullptr; size_t Arguments::_conservative_max_heap_alignment = 0; @@ -336,6 +337,10 @@ bool Arguments::is_internal_module_property(const char* property) { return false; } +bool Arguments::is_add_modules_property(const char* key) { + return (strcmp(key, MODULE_PROPERTY_PREFIX ADDMODS) == 0); +} + // Return true if the key matches the --module-path property name ("jdk.module.path"). bool Arguments::is_module_path_property(const char* key) { return (strcmp(key, MODULE_PROPERTY_PREFIX PATH) == 0); @@ -1773,7 +1778,6 @@ bool Arguments::sun_java_launcher_is_altjvm() { unsigned int addreads_count = 0; unsigned int addexports_count = 0; unsigned int addopens_count = 0; -unsigned int addmods_count = 0; unsigned int patch_mod_count = 0; unsigned int enable_native_access_count = 0; @@ -1799,7 +1803,7 @@ bool Arguments::check_vm_args_consistency() { PropertyList_unique_add(&_system_properties, "jdk.internal.vm.ci.enabled", "true", AddProperty, UnwriteableProperty, InternalProperty); if (ClassLoader::is_module_observable("jdk.internal.vm.ci")) { - if (!create_numbered_module_property("jdk.module.addmods", "jdk.internal.vm.ci", addmods_count++)) { + if (!create_numbered_module_property("jdk.module.addmods", "jdk.internal.vm.ci", _addmods_count++)) { return false; } } @@ -1808,7 +1812,7 @@ bool Arguments::check_vm_args_consistency() { #if INCLUDE_JFR if (status && (FlightRecorderOptions || StartFlightRecording)) { - if (!create_numbered_module_property("jdk.module.addmods", "jdk.jfr", addmods_count++)) { + if (!create_numbered_module_property("jdk.module.addmods", "jdk.jfr", _addmods_count++)) { return false; } } @@ -2235,7 +2239,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m return JNI_ENOMEM; } } else if (match_option(option, "--add-modules=", &tail)) { - if (!create_numbered_module_property("jdk.module.addmods", tail, addmods_count++)) { + if (!create_numbered_module_property("jdk.module.addmods", tail, _addmods_count++)) { return JNI_ENOMEM; } } else if (match_option(option, "--enable-native-access=", &tail)) { @@ -2322,7 +2326,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m FREE_C_HEAP_ARRAY(char, options); // java agents need module java.instrument - if (!create_numbered_module_property("jdk.module.addmods", "java.instrument", addmods_count++)) { + if (!create_numbered_module_property("jdk.module.addmods", "java.instrument", _addmods_count++)) { return JNI_ENOMEM; } } @@ -2503,7 +2507,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m return JNI_EINVAL; } // management agent in module jdk.management.agent - if (!create_numbered_module_property("jdk.module.addmods", "jdk.management.agent", addmods_count++)) { + if (!create_numbered_module_property("jdk.module.addmods", "jdk.management.agent", _addmods_count++)) { return JNI_ENOMEM; } #else diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index e1bfc0438dc..ac842285fd8 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -196,6 +196,8 @@ class Arguments : AllStatic { static int _num_jvm_args; // string containing all java command (class/jarfile name and app args) static char* _java_command; + // number of unique modules specified in the --add-modules option + static unsigned int _addmods_count; // Property list static SystemProperty* _system_properties; @@ -461,6 +463,8 @@ class Arguments : AllStatic { static int PropertyList_readable_count(SystemProperty* pl); static bool is_internal_module_property(const char* option); + static bool is_add_modules_property(const char* key); + static unsigned int addmods_count() { return _addmods_count; } static bool is_module_path_property(const char* key); // Miscellaneous System property value getter and setters. diff --git a/src/java.base/share/classes/jdk/internal/module/ArchivedModuleGraph.java b/src/java.base/share/classes/jdk/internal/module/ArchivedModuleGraph.java index 8b91ab67d2b..ca5ffb2ff68 100644 --- a/src/java.base/share/classes/jdk/internal/module/ArchivedModuleGraph.java +++ b/src/java.base/share/classes/jdk/internal/module/ArchivedModuleGraph.java @@ -25,6 +25,7 @@ package jdk.internal.module; import java.util.Objects; +import java.util.Set; import java.util.function.Function; import java.lang.module.Configuration; import java.lang.module.ModuleFinder; @@ -43,19 +44,22 @@ class ArchivedModuleGraph { private final Configuration configuration; private final Function classLoaderFunction; private final String mainModule; + private final Set addModules; private ArchivedModuleGraph(boolean hasSplitPackages, boolean hasIncubatorModules, ModuleFinder finder, Configuration configuration, Function classLoaderFunction, - String mainModule) { + String mainModule, + Set addModules) { this.hasSplitPackages = hasSplitPackages; this.hasIncubatorModules = hasIncubatorModules; this.finder = finder; this.configuration = configuration; this.classLoaderFunction = classLoaderFunction; this.mainModule = mainModule; + this.addModules = addModules; } ModuleFinder finder() { @@ -78,12 +82,24 @@ class ArchivedModuleGraph { return hasIncubatorModules; } + static boolean sameAddModules(Set addModules) { + if (archivedModuleGraph.addModules == null || addModules == null) { + return false; + } + + if (archivedModuleGraph.addModules.size() != addModules.size()) { + return false; + } + + return archivedModuleGraph.addModules.containsAll(addModules); + } + /** * Returns the ArchivedModuleGraph for the given initial module. */ - static ArchivedModuleGraph get(String mainModule) { + static ArchivedModuleGraph get(String mainModule, Set addModules) { ArchivedModuleGraph graph = archivedModuleGraph; - if ((graph != null) && Objects.equals(graph.mainModule, mainModule)) { + if ((graph != null) && Objects.equals(graph.mainModule, mainModule) && sameAddModules(addModules)) { return graph; } else { return null; @@ -98,13 +114,15 @@ class ArchivedModuleGraph { ModuleFinder finder, Configuration configuration, Function classLoaderFunction, - String mainModule) { + String mainModule, + Set addModules) { archivedModuleGraph = new ArchivedModuleGraph(hasSplitPackages, hasIncubatorModules, finder, configuration, classLoaderFunction, - mainModule); + mainModule, + addModules); } static { diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java index 656104e2455..f5904915e26 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java +++ b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java @@ -141,7 +141,6 @@ public final class ModuleBootstrap { private static boolean canUseArchivedBootLayer() { return getProperty("jdk.module.upgrade.path") == null && getProperty("jdk.module.patch.0") == null && // --patch-module - getProperty("jdk.module.addmods.0") == null && // --add-modules getProperty("jdk.module.limitmods") == null && // --limit-modules getProperty("jdk.module.addreads.0") == null && // --add-reads getProperty("jdk.module.addexports.0") == null && // --add-exports @@ -212,10 +211,9 @@ public final class ModuleBootstrap { // If the java heap was archived at CDS dump time, and the environment // at dump time matches the current environment, then use the archived // system modules and finder. - ArchivedModuleGraph archivedModuleGraph = ArchivedModuleGraph.get(mainModule); + ArchivedModuleGraph archivedModuleGraph = ArchivedModuleGraph.get(mainModule, addModules); if (archivedModuleGraph != null && !haveModulePath - && addModules.isEmpty() && limitModules.isEmpty() && !isPatched) { systemModuleFinder = archivedModuleGraph.finder(); @@ -466,7 +464,6 @@ public final class ModuleBootstrap { if (CDS.isDumpingStaticArchive() && !haveUpgradeModulePath - && addModules.isEmpty() && allJrtOrModularJar(cf)) { assert !isPatched; @@ -478,7 +475,8 @@ public final class ModuleBootstrap { systemModuleFinder, cf, clf, - mainModule); + mainModule, + addModules); if (!hasSplitPackages && !hasIncubatorModules) { ArchivedBootLayer.archive(bootLayer); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java new file mode 100644 index 00000000000..5bab21dea13 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/addmods/AddmodsOption.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2023, 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 8319343 + * @summary Test handling of the --add-modules option. + * @requires vm.cds.write.archived.java.heap + * @requires vm.flagless + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @run driver AddmodsOption + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class AddmodsOption { + public static void main(String[] args) throws Exception { + final String moduleOption = "jdk.httpserver/sun.net.httpserver.simpleserver.Main"; + final String incubatorModule = "jdk.incubator.vector"; + final String jconsoleModule = "jdk.jconsole"; + final String multiModules = ",,jdk.jconsole,jdk.compiler,,"; + final String allSystem = "ALL-SYSTEM"; + final String allModulePath = "ALL-MODULE-PATH"; + final String loggingOption = "-Xlog:cds=debug,cds+module=debug,cds+heap=info,module=trace"; + final String versionPattern = "java.[0-9][0-9][-].*"; + final String subgraphCannotBeUsed = "subgraph jdk.internal.module.ArchivedBootLayer cannot be used because full module graph is disabled"; + final String warningIncubator = "WARNING: Using incubator modules: jdk.incubator.vector"; + String archiveName = TestCommon.getNewArchiveName("addmods-option"); + TestCommon.setCurrentArchiveName(archiveName); + + // dump a base archive with --add-modules jdk.jconsole -m jdk.httpserver + OutputAnalyzer oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "--add-modules", jconsoleModule, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0); + + // same modules specified during runtime + oa = TestCommon.execCommon( + loggingOption, + "--add-modules", jconsoleModule, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0) + // version of the jdk.httpserver module, e.g. java 22-ea + .shouldMatch(versionPattern) + .shouldMatch("cds,module.*Restored from archive: entry.0x.*name jdk.jconsole") + .shouldMatch("cds,module.*Restored from archive: entry.0x.*name jdk.httpserver"); + + // different --add-modules specified during runtime + oa = TestCommon.execCommon( + loggingOption, + "--add-modules", incubatorModule, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain("Mismatched --add-modules module name(s).") + .shouldContain("dump time: jdk.jconsole runtime: jdk.incubator.vector") + .shouldContain(subgraphCannotBeUsed); + + // no module specified during runtime + oa = TestCommon.execCommon( + loggingOption, + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain("Module jdk.httpserver specified during dump time but not during runtime") + .shouldContain(subgraphCannotBeUsed); + + // dump an archive without the --add-modules option + archiveName = TestCommon.getNewArchiveName("no-addmods-option"); + TestCommon.setCurrentArchiveName(archiveName); + oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0); + + // run with --add-modules option + oa = TestCommon.execCommon( + loggingOption, + "--add-modules", jconsoleModule, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain("--add-modules module name(s) specified during runtime but not found in archive: jdk.jconsole") + // version of the jdk.httpserver module, e.g. java 22-ea + .shouldMatch(versionPattern) + .shouldContain(subgraphCannotBeUsed); + + // dump an archive with an incubator module, -add-modules jdk.incubator.vector + archiveName = TestCommon.getNewArchiveName("incubator-module"); + TestCommon.setCurrentArchiveName(archiveName); + oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "--add-modules", incubatorModule, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0) + // module graph won't be archived with an incubator module + .shouldContain("archivedBootLayer not available, disabling full module graph"); + + // run with the same incubator module + oa = TestCommon.execCommon( + loggingOption, + "--add-modules", incubatorModule, + "-m", moduleOption, + "-version"); + oa.shouldContain("full module graph: disabled") + // module is not restored from archive + .shouldContain("define_module(): creation of module: jdk.incubator.vector") + .shouldContain("WARNING: Using incubator modules: jdk.incubator.vector") + .shouldContain("subgraph jdk.internal.module.ArchivedBootLayer is not recorde") + .shouldHaveExitValue(0); + + // dump an archive with JVMCI option which indirectly adds the + // jdk.internal.vm.ci module using the --add-modules option + archiveName = TestCommon.getNewArchiveName("jvmci-module"); + TestCommon.setCurrentArchiveName(archiveName); + oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "-XX:+UnlockExperimentalVMOptions", + "-XX:+EagerJVMCI", "-XX:+UseJVMCICompiler", + "-version"); + oa.shouldHaveExitValue(0); + + // run with the JVMCI option + oa = TestCommon.execCommon( + loggingOption, + "-XX:+UnlockExperimentalVMOptions", + "-XX:+EagerJVMCI", "-XX:+UseJVMCICompiler", + "-version"); + try { + oa.shouldHaveExitValue(0) + .shouldMatch("cds,module.*Restored from archive: entry.0x.*name jdk.internal.vm.ci"); + } catch (RuntimeException re) { + // JVMCI compile may not be available + oa.shouldHaveExitValue(1) + .shouldContain("Cannot use JVMCI compiler: No JVMCI compiler found"); + } + + // dump an archive with multiple modules in -add-modules + archiveName = TestCommon.getNewArchiveName("muti-modules"); + TestCommon.setCurrentArchiveName(archiveName); + oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "--add-modules", multiModules, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0); + + // run with the same multiple modules with a duplicate module in --add-modules + oa = TestCommon.execCommon( + loggingOption, + "--add-modules", multiModules, + "--add-modules", jconsoleModule, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0) + .shouldMatch("cds,module.*Restored from archive: entry.0x.*name jdk.compiler") + .shouldMatch("cds,module.*Restored from archive: entry.0x.*name jdk.jconsole"); + + // dump an archive with ALL-SYSTEM in -add-modules + archiveName = TestCommon.getNewArchiveName("muti-modules"); + TestCommon.setCurrentArchiveName(archiveName); + oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "--add-modules", allSystem, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain(warningIncubator); + + // run with the same ALL-SYSTEM in --add-modules + oa = TestCommon.execCommon( + loggingOption, + "--add-modules", allSystem, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0) + // the jdk.incubator.vector was specified indirectly via ALL-SYSTEM + .shouldContain(warningIncubator) + .shouldContain("full module graph cannot be loaded: archive was created without full module graph"); + + // dump an archive with ALL-MODULE-PATH in -add-modules + archiveName = TestCommon.getNewArchiveName("muti-modules"); + TestCommon.setCurrentArchiveName(archiveName); + oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "--add-modules", allModulePath, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0); + + // run with the same ALL-MODULE-PATH in --add-modules + oa = TestCommon.execCommon( + loggingOption, + "--add-modules", allModulePath, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0) + .shouldMatch("cds,module.*Restored from archive: entry.0x.*name jdk.httpserver"); + } +}