diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index b2af8ca8bcb..0d0ffa69aeb 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -573,6 +573,12 @@ void ArchiveBuilder::verify_estimate_size(size_t estimate, const char* which) { _other_region_used_bytes = 0; } +char* ArchiveBuilder::ro_strdup(const char* s) { + char* archived_str = ro_region_alloc((int)strlen(s) + 1); + strcpy(archived_str, s); + return archived_str; +} + void ArchiveBuilder::dump_rw_metadata() { ResourceMark rm; log_info(cds)("Allocating RW objects ... "); diff --git a/src/hotspot/share/cds/archiveBuilder.hpp b/src/hotspot/share/cds/archiveBuilder.hpp index aa88222daeb..fdc387190eb 100644 --- a/src/hotspot/share/cds/archiveBuilder.hpp +++ b/src/hotspot/share/cds/archiveBuilder.hpp @@ -374,6 +374,8 @@ public: return align_up(byte_size, SharedSpaceObjectAlignment); } + char* ro_strdup(const char* s); + void dump_rw_metadata(); void dump_ro_metadata(); void relocate_metaspaceobj_embedded_pointers(); diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 5f5af09e65b..35fff4a04f6 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -125,7 +125,7 @@ static ArchivableStaticFieldInfo archive_subgraph_entry_fields[] = { // full module graph static ArchivableStaticFieldInfo fmg_archive_subgraph_entry_fields[] = { {"jdk/internal/loader/ArchivedClassLoaders", "archivedClassLoaders"}, - {"jdk/internal/module/ArchivedBootLayer", "archivedBootLayer"}, + {ARCHIVED_BOOT_LAYER_CLASS, ARCHIVED_BOOT_LAYER_FIELD}, {"java/lang/Module$ArchivedData", "archivedData"}, {nullptr, nullptr}, }; @@ -964,7 +964,14 @@ HeapShared::resolve_or_init_classes_for_subgraph_of(Klass* k, bool do_init, TRAP // Initialize from archived data. Currently this is done only // during VM initialization time. No lock is needed. - if (record != nullptr) { + if (record == nullptr) { + if (log_is_enabled(Info, cds, heap)) { + ResourceMark rm(THREAD); + log_info(cds, heap)("subgraph %s is not recorded", + k->external_name()); + } + return nullptr; + } else { if (record->is_full_module_graph() && !MetaspaceShared::use_full_module_graph()) { if (log_is_enabled(Info, cds, heap)) { ResourceMark rm(THREAD); @@ -1751,4 +1758,26 @@ void HeapShared::print_stats() { avg_size(_total_obj_size, _total_obj_count)); } +bool HeapShared::is_archived_boot_layer_available(JavaThread* current) { + TempNewSymbol klass_name = SymbolTable::new_symbol(ARCHIVED_BOOT_LAYER_CLASS); + InstanceKlass* k = SystemDictionary::find_instance_klass(current, klass_name, Handle(), Handle()); + if (k == nullptr) { + return false; + } else { + TempNewSymbol field_name = SymbolTable::new_symbol(ARCHIVED_BOOT_LAYER_FIELD); + TempNewSymbol field_signature = SymbolTable::new_symbol("Ljdk/internal/module/ArchivedBootLayer;"); + fieldDescriptor fd; + if (k->find_field(field_name, field_signature, true, &fd) != nullptr) { + oop m = k->java_mirror(); + oop f = m->obj_field(fd.offset()); + if (CompressedOops::is_null(f)) { + return false; + } + } else { + return false; + } + } + return true; +} + #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index b85d3fdbc45..c41339173cd 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -49,6 +49,9 @@ class ResourceBitMap; struct ArchivableStaticFieldInfo; class ArchiveHeapInfo; +#define ARCHIVED_BOOT_LAYER_CLASS "jdk/internal/module/ArchivedBootLayer" +#define ARCHIVED_BOOT_LAYER_FIELD "archivedBootLayer" + // A dump time sub-graph info for Klass _k. It includes the entry points // (static fields in _k's mirror) of the archived sub-graphs reachable // from _k's mirror. It also contains a list of Klasses of the objects @@ -160,6 +163,7 @@ public: // Scratch objects for archiving Klass::java_mirror() static oop scratch_java_mirror(BasicType t) NOT_CDS_JAVA_HEAP_RETURN_(nullptr); static oop scratch_java_mirror(Klass* k) NOT_CDS_JAVA_HEAP_RETURN_(nullptr); + static bool is_archived_boot_layer_available(JavaThread* current) NOT_CDS_JAVA_HEAP_RETURN_(false); private: #if INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 9f3a554bd11..cc549705c57 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -45,6 +45,7 @@ #include "classfile/classLoaderExt.hpp" #include "classfile/javaClasses.inline.hpp" #include "classfile/loaderConstraints.hpp" +#include "classfile/modules.hpp" #include "classfile/placeholders.hpp" #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" @@ -387,6 +388,7 @@ void MetaspaceShared::serialize(SerializeClosure* soc) { SystemDictionaryShared::serialize_vm_classes(soc); soc->do_tag(--tag); + CDS_JAVA_HEAP_ONLY(Modules::serialize(soc);) CDS_JAVA_HEAP_ONLY(ClassLoaderDataShared::serialize(soc);) LambdaFormInvokers::serialize(soc); @@ -479,6 +481,8 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables() { // Write lambform lines into archive LambdaFormInvokers::dump_static_archive_invokers(); + // Write module name into archive + CDS_JAVA_HEAP_ONLY(Modules::dump_main_module_name();) // Write the other data to the output array. DumpRegion* ro_region = ArchiveBuilder::current()->ro_region(); char* start = ro_region->top(); @@ -763,8 +767,6 @@ void MetaspaceShared::preload_and_dump_impl(TRAPS) { log_info(cds)("Reading extra data: done."); } - HeapShared::init_for_dumping(CHECK); - // Rewrite and link classes log_info(cds)("Rewriting and linking classes ..."); @@ -778,6 +780,11 @@ void MetaspaceShared::preload_and_dump_impl(TRAPS) { #if INCLUDE_CDS_JAVA_HEAP if (CDSConfig::is_dumping_heap()) { StringTable::allocate_shared_strings_array(CHECK); + if (!HeapShared::is_archived_boot_layer_available(THREAD)) { + log_info(cds)("archivedBootLayer not available, disabling full module graph"); + disable_full_module_graph(); + } + HeapShared::init_for_dumping(CHECK); ArchiveHeapWriter::init(); if (use_full_module_graph()) { HeapShared::reset_archived_object_states(CHECK); @@ -1163,8 +1170,8 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File static_mapinfo->map_or_load_heap_region(); } #endif // _LP64 - log_info(cds)("optimized module handling: %s", MetaspaceShared::use_optimized_module_handling() ? "enabled" : "disabled"); - log_info(cds)("full module graph: %s", MetaspaceShared::use_full_module_graph() ? "enabled" : "disabled"); + log_info(cds)("initial optimized module handling: %s", MetaspaceShared::use_optimized_module_handling() ? "enabled" : "disabled"); + log_info(cds)("initial full module graph: %s", MetaspaceShared::use_full_module_graph() ? "enabled" : "disabled"); } else { unmap_archive(static_mapinfo); unmap_archive(dynamic_mapinfo); diff --git a/src/hotspot/share/classfile/modules.cpp b/src/hotspot/share/classfile/modules.cpp index 0f05efcf4d7..0960a999b77 100644 --- a/src/hotspot/share/classfile/modules.cpp +++ b/src/hotspot/share/classfile/modules.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "cds/archiveBuilder.hpp" #include "cds/metaspaceShared.hpp" #include "classfile/classFileParser.hpp" #include "classfile/classLoader.hpp" @@ -559,6 +560,49 @@ void Modules::verify_archived_modules() { ModuleEntry::verify_archived_module_entries(); } +#if INCLUDE_CDS_JAVA_HEAP +char* Modules::_archived_main_module_name = nullptr; +#endif + +void Modules::dump_main_module_name() { + const char* module_name = Arguments::get_property("jdk.module.main"); + if (module_name != nullptr) { + _archived_main_module_name = ArchiveBuilder::current()->ro_strdup(module_name); + } + ArchivePtrMarker::mark_pointer(&_archived_main_module_name); +} + +void Modules::serialize(SerializeClosure* soc) { + soc->do_ptr(&_archived_main_module_name); + if (soc->reading()) { + const char* runtime_main_module = Arguments::get_property("jdk.module.main"); + log_info(cds)("_archived_main_module_name %s", + _archived_main_module_name != nullptr ? _archived_main_module_name : "(null)"); + bool disable = false; + if (runtime_main_module == nullptr) { + if (_archived_main_module_name != nullptr) { + log_info(cds)("Module %s specified during dump time but not during runtime", _archived_main_module_name); + disable = true; + } + } else { + if (_archived_main_module_name == nullptr) { + log_info(cds)("Module %s specified during runtime but not during dump time", runtime_main_module); + disable = true; + } else if (strcmp(runtime_main_module, _archived_main_module_name) != 0) { + log_info(cds)("Mismatched modules: runtime %s dump time %s", runtime_main_module, _archived_main_module_name); + disable = true; + } + } + + if (disable) { + log_info(cds)("Disabling optimized module handling"); + MetaspaceShared::disable_optimized_module_handling(); + } + log_info(cds)("optimized module handling: %s", MetaspaceShared::use_optimized_module_handling() ? "enabled" : "disabled"); + log_info(cds)("full module graph: %s", MetaspaceShared::use_full_module_graph() ? "enabled" : "disabled"); + } +} + void Modules::define_archived_modules(Handle h_platform_loader, Handle h_system_loader, TRAPS) { assert(UseSharedSpaces && MetaspaceShared::use_full_module_graph(), "must be"); diff --git a/src/hotspot/share/classfile/modules.hpp b/src/hotspot/share/classfile/modules.hpp index 2a158621b0e..3866f0d6f9b 100644 --- a/src/hotspot/share/classfile/modules.hpp +++ b/src/hotspot/share/classfile/modules.hpp @@ -59,6 +59,12 @@ public: static void define_archived_modules(Handle h_platform_loader, Handle h_system_loader, TRAPS) NOT_CDS_JAVA_HEAP_RETURN; 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; + +#if INCLUDE_CDS_JAVA_HEAP + static char* _archived_main_module_name; +#endif // Provides the java.lang.Module for the unnamed module defined // to the boot loader. diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 9b229c171c0..dc1e15121b2 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -1262,8 +1262,7 @@ bool Arguments::add_property(const char* prop, PropertyWriteable writeable, Prop } #if INCLUDE_CDS - if (is_internal_module_property(key) || - strcmp(key, "jdk.module.main") == 0) { + if (is_internal_module_property(key)) { MetaspaceShared::disable_optimized_module_handling(); log_info(cds)("optimized module handling: disabled due to incompatible property: %s=%s", key, value); } diff --git a/src/java.base/share/classes/jdk/internal/misc/CDS.java b/src/java.base/share/classes/jdk/internal/misc/CDS.java index c41adcf9c4c..e3365441548 100644 --- a/src/java.base/share/classes/jdk/internal/misc/CDS.java +++ b/src/java.base/share/classes/jdk/internal/misc/CDS.java @@ -44,10 +44,12 @@ public class CDS { private static final boolean isDumpingClassList; private static final boolean isDumpingArchive; private static final boolean isSharingEnabled; + private static final boolean isDumpingStaticArchive; static { isDumpingClassList = isDumpingClassList0(); isDumpingArchive = isDumpingArchive0(); isSharingEnabled = isSharingEnabled0(); + isDumpingStaticArchive = isDumpingArchive && !isSharingEnabled; } /** @@ -71,6 +73,13 @@ public class CDS { return isSharingEnabled; } + /** + * Is dumping static archive. + */ + public static boolean isDumpingStaticArchive() { + return isDumpingStaticArchive; + } + private static native boolean isDumpingClassList0(); private static native boolean isDumpingArchive0(); private static native boolean isSharingEnabled0(); 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 d3e5dff08c2..8b91ab67d2b 100644 --- a/src/java.base/share/classes/jdk/internal/module/ArchivedModuleGraph.java +++ b/src/java.base/share/classes/jdk/internal/module/ArchivedModuleGraph.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -24,6 +24,7 @@ */ package jdk.internal.module; +import java.util.Objects; import java.util.function.Function; import java.lang.module.Configuration; import java.lang.module.ModuleFinder; @@ -41,17 +42,20 @@ class ArchivedModuleGraph { private final ModuleFinder finder; private final Configuration configuration; private final Function classLoaderFunction; + private final String mainModule; private ArchivedModuleGraph(boolean hasSplitPackages, boolean hasIncubatorModules, ModuleFinder finder, Configuration configuration, - Function classLoaderFunction) { + Function classLoaderFunction, + String mainModule) { this.hasSplitPackages = hasSplitPackages; this.hasIncubatorModules = hasIncubatorModules; this.finder = finder; this.configuration = configuration; this.classLoaderFunction = classLoaderFunction; + this.mainModule = mainModule; } ModuleFinder finder() { @@ -79,8 +83,7 @@ class ArchivedModuleGraph { */ static ArchivedModuleGraph get(String mainModule) { ArchivedModuleGraph graph = archivedModuleGraph; - // We only allow the unnamed module (default) case for now - if (mainModule == null) { + if ((graph != null) && Objects.equals(graph.mainModule, mainModule)) { return graph; } else { return null; @@ -94,12 +97,14 @@ class ArchivedModuleGraph { boolean hasIncubatorModules, ModuleFinder finder, Configuration configuration, - Function classLoaderFunction) { + Function classLoaderFunction, + String mainModule) { archivedModuleGraph = new ArchivedModuleGraph(hasSplitPackages, hasIncubatorModules, finder, configuration, - classLoaderFunction); + classLoaderFunction, + mainModule); } 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 80faf5ecf9a..0279c8438a0 100644 --- a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java +++ b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 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 @@ -141,7 +141,6 @@ public final class ModuleBootstrap { return getProperty("jdk.module.upgrade.path") == null && getProperty("jdk.module.path") == null && getProperty("jdk.module.patch.0") == null && // --patch-module - getProperty("jdk.module.main") == null && // --module getProperty("jdk.module.addmods.0") == null && // --add-modules getProperty("jdk.module.limitmods") == null && // --limit-modules getProperty("jdk.module.addreads.0") == null && // --add-reads @@ -228,7 +227,8 @@ public final class ModuleBootstrap { systemModules = SystemModuleFinders.systemModules(mainModule); if (systemModules != null && !isPatched) { needResolution = (traceOutput != null); - canArchive = true; + if (CDS.isDumpingStaticArchive()) + canArchive = true; } } if (systemModules == null) { @@ -469,14 +469,30 @@ public final class ModuleBootstrap { limitedFinder = new SafeModuleFinder(finder); } + // If -Xshare:dump and mainModule are specified, check if the mainModule + // is in the runtime image and not on the upgrade module path. If so, + // set canArchive to true so that the module graph can be archived. + if (CDS.isDumpingStaticArchive() && mainModule != null) { + String scheme = systemModuleFinder.find(mainModule) + .stream() + .map(ModuleReference::location) + .flatMap(Optional::stream) + .findAny() + .map(URI::getScheme) + .orElse(null); + if ("jrt".equalsIgnoreCase(scheme)) { + canArchive = true; + } + } + // Archive module graph and boot layer can be archived at CDS dump time. - // Only allow the unnamed module case for now. - if (canArchive && (mainModule == null)) { + if (canArchive) { ArchivedModuleGraph.archive(hasSplitPackages, hasIncubatorModules, systemModuleFinder, cf, - clf); + clf, + mainModule); if (!hasSplitPackages && !hasIncubatorModules) { ArchivedBootLayer.archive(bootLayer); } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java index d66ba685290..195dc1a6fac 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -78,5 +78,27 @@ public class ArchivedModuleCompareTest { TestCommon.checkOutputStrings( moduleResolutionOut1, moduleResolutionOut2, "\n"); } + + // Test case 3) + // dump an archive with the -m jdk.compiler option + // run with -Xshare:off without -m option + // run with archive without -m option + // the list of modules from both runs should be the same + System.out.println("---------------- Test case 3 -----------------"); + output = TestCommon.dump(appJar, + TestCommon.list("PrintSystemModulesApp"), + "-m", "jdk.compiler"); + TestCommon.checkDump(output); + + output = TestCommon.execOff("-cp", appJar, "PrintSystemModulesApp"); + output.shouldHaveExitValue(0); + bootModules1 = TestCommon.filterOutLogs(output.getStdout()); + + output = TestCommon.exec(appJar, "PrintSystemModulesApp"); + TestCommon.checkExec(output); + if (output.getStderr().contains("sharing")) { + String bootModules2 = TestCommon.filterOutLogs(output.getStdout()); + TestCommon.checkOutputStrings(bootModules1, bootModules2, ", "); + } } } diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java new file mode 100644 index 00000000000..b7b0e8778c8 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/jigsaw/module/ModuleOption.java @@ -0,0 +1,126 @@ +/* + * 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 8316969 + * @summary Test handling of module option (-m). + * @requires vm.cds.write.archived.java.heap + * @requires vm.flagless + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * @run driver ModuleOption + */ + +import jdk.test.lib.process.OutputAnalyzer; + +public class ModuleOption { + 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 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"; + String archiveName = TestCommon.getNewArchiveName("module-option"); + TestCommon.setCurrentArchiveName(archiveName); + + // dump a base archive with -m jdk.httpserver + OutputAnalyzer oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0); + + // same module specified during runtime + oa = TestCommon.execCommon( + loggingOption, + "-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.httpserver"); + + // different module specified during runtime + oa = TestCommon.execCommon( + loggingOption, + "-m", "jdk.compiler/com.sun.tools.javac.Main", + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain("Mismatched modules: runtime jdk.compiler dump time jdk.httpserver") + .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 module option + archiveName = TestCommon.getNewArchiveName("no-module-option"); + TestCommon.setCurrentArchiveName(archiveName); + oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "-version"); + oa.shouldHaveExitValue(0); + + // run with module option + oa = TestCommon.execCommon( + loggingOption, + "-m", moduleOption, + "-version"); + oa.shouldHaveExitValue(0) + .shouldContain("Module jdk.httpserver specified during runtime but not during dump time") + // version of the jdk.httpserver module, e.g. java 22-ea + .shouldMatch(versionPattern) + .shouldContain(subgraphCannotBeUsed); + + // dump an archive with an incubator module, -m jdk.incubator.vector + archiveName = TestCommon.getNewArchiveName("incubator-module"); + TestCommon.setCurrentArchiveName(archiveName); + oa = TestCommon.dumpBaseArchive( + archiveName, + loggingOption, + "-m", incubatorModule, + "-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, + "-m", incubatorModule, + "-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") + .shouldContain("module jdk.incubator.vector does not have a ModuleMainClass attribute, use -m /") + .shouldHaveExitValue(1); + } +}