diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index 5f346e832a8..f85f1e46520 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -145,7 +145,7 @@ void AOTArtifactFinder::find_artifacts() { #if INCLUDE_CDS_JAVA_HEAP // Keep scanning until we discover no more class that need to be AOT-initialized. - if (CDSConfig::is_initing_classes_at_dump_time()) { + if (CDSConfig::is_dumping_aot_linked_classes()) { while (_pending_aot_inited_classes->length() > 0) { InstanceKlass* ik = _pending_aot_inited_classes->pop(); HeapShared::copy_and_rescan_aot_inited_mirror(ik); @@ -188,7 +188,7 @@ void AOTArtifactFinder::end_scanning_for_oops() { } void AOTArtifactFinder::add_aot_inited_class(InstanceKlass* ik) { - if (CDSConfig::is_initing_classes_at_dump_time()) { + if (CDSConfig::is_dumping_aot_linked_classes()) { if (RegeneratedClasses::is_regenerated_object(ik)) { precond(RegeneratedClasses::get_original_object(ik)->is_initialized()); } else { @@ -258,7 +258,7 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) { return; } scan_oops_in_instance_class(ik); - if (ik->is_hidden() && CDSConfig::is_initing_classes_at_dump_time()) { + if (ik->is_hidden() && CDSConfig::is_dumping_aot_linked_classes()) { bool succeed = AOTClassLinker::try_add_candidate(ik); guarantee(succeed, "All cached hidden classes must be aot-linkable"); add_aot_inited_class(ik); diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index 00db747622f..06fc3af6f30 100644 --- a/src/hotspot/share/cds/aotClassInitializer.cpp +++ b/src/hotspot/share/cds/aotClassInitializer.cpp @@ -40,7 +40,7 @@ DEBUG_ONLY(InstanceKlass* _aot_init_class = nullptr;) bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { assert(!ArchiveBuilder::is_active() || !ArchiveBuilder::current()->is_in_buffer_space(ik), "must be source klass"); - if (!CDSConfig::is_initing_classes_at_dump_time()) { + if (!CDSConfig::is_dumping_aot_linked_classes()) { return false; } @@ -64,7 +64,7 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { // Automatic selection for aot-inited classes // ========================================== // - // When CDSConfig::is_initing_classes_at_dump_time is enabled, + // When CDSConfig::is_dumping_aot_linked_classes is enabled, // AOTArtifactFinder::find_artifacts() finds the classes of all // heap objects that are reachable from HeapShared::_run_time_special_subgraph, // and mark these classes as aot-inited. This preserves the initialized @@ -310,7 +310,7 @@ void AOTClassInitializer::init_test_class(TRAPS) { // // -XX:AOTInitTestClass is NOT a general mechanism for including user-defined objects into // the AOT cache. Therefore, this option is NOT available in product JVM. - if (AOTInitTestClass != nullptr && CDSConfig::is_initing_classes_at_dump_time()) { + if (AOTInitTestClass != nullptr && CDSConfig::is_dumping_aot_linked_classes()) { log_info(aot)("Debug build only: force initialization of AOTInitTestClass %s", AOTInitTestClass); TempNewSymbol class_name = SymbolTable::new_symbol(AOTInitTestClass); Handle app_loader(THREAD, SystemDictionary::java_system_loader()); diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 098d3baed58..3824a2be3e2 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -1141,7 +1141,7 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS AOTReferenceObjSupport::initialize(CHECK); AOTReferenceObjSupport::stabilize_cached_reference_objects(CHECK); - if (CDSConfig::is_initing_classes_at_dump_time()) { + if (CDSConfig::is_dumping_aot_linked_classes()) { // java.lang.Class::reflectionFactory cannot be archived yet. We set this field // to null, and it will be initialized again at runtime. log_debug(aot)("Resetting Class::reflectionFactory"); diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 86533e212d8..5f6b568dd6e 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -1026,23 +1026,19 @@ void CDSConfig::set_has_aot_linked_classes(bool has_aot_linked_classes) { _has_aot_linked_classes |= has_aot_linked_classes; } -bool CDSConfig::is_initing_classes_at_dump_time() { - return is_dumping_heap() && is_dumping_aot_linked_classes(); -} - bool CDSConfig::is_dumping_invokedynamic() { // Requires is_dumping_aot_linked_classes(). Otherwise the classes of some archived heap // objects used by the archive indy callsites may be replaced at runtime. return AOTInvokeDynamicLinking && is_dumping_aot_linked_classes() && is_dumping_heap(); } -// When we are dumping aot-linked classes and we are able to write archived heap objects, we automatically -// enable the archiving of MethodHandles. This will in turn enable the archiving of MethodTypes and hidden +// When we are dumping aot-linked classes, we automatically enable the archiving of MethodHandles. +// This will in turn enable the archiving of MethodTypes and hidden // classes that are used in the implementation of MethodHandles. // Archived MethodHandles are required for higher-level optimizations such as AOT resolution of invokedynamic // and dynamic proxies. bool CDSConfig::is_dumping_method_handles() { - return is_initing_classes_at_dump_time(); + return is_dumping_aot_linked_classes(); } #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp index d199e97eefd..202904e8231 100644 --- a/src/hotspot/share/cds/cdsConfig.hpp +++ b/src/hotspot/share/cds/cdsConfig.hpp @@ -187,7 +187,6 @@ public: static void disable_heap_dumping() { CDS_ONLY(_disable_heap_dumping = true); } static bool is_dumping_heap() NOT_CDS_JAVA_HEAP_RETURN_(false); static bool is_loading_heap() NOT_CDS_JAVA_HEAP_RETURN_(false); - static bool is_initing_classes_at_dump_time() NOT_CDS_JAVA_HEAP_RETURN_(false); static bool is_dumping_invokedynamic() NOT_CDS_JAVA_HEAP_RETURN_(false); static bool is_dumping_method_handles() NOT_CDS_JAVA_HEAP_RETURN_(false); diff --git a/src/hotspot/share/cds/cdsEnumKlass.cpp b/src/hotspot/share/cds/cdsEnumKlass.cpp index 1bf6ba4eba8..177d1d6e3ad 100644 --- a/src/hotspot/share/cds/cdsEnumKlass.cpp +++ b/src/hotspot/share/cds/cdsEnumKlass.cpp @@ -40,7 +40,7 @@ bool CDSEnumKlass::is_enum_obj(oop orig_obj) { } // !!! This is legacy support for enum classes before JEP 483. This file is not used when -// !!! CDSConfig::is_initing_classes_at_dump_time()==true. +// !!! CDSConfig::is_dumping_aot_linked_classes()==true. // // Java Enum classes have synthetic methods that look like this // enum MyEnum {FOO, BAR} @@ -63,7 +63,7 @@ bool CDSEnumKlass::is_enum_obj(oop orig_obj) { void CDSEnumKlass::handle_enum_obj(int level, KlassSubGraphInfo* subgraph_info, oop orig_obj) { - assert(!CDSConfig::is_initing_classes_at_dump_time(), "only for legacy support of enums"); + assert(!CDSConfig::is_dumping_aot_linked_classes(), "only for legacy support of enums"); assert(level > 1, "must never be called at the first (outermost) level"); assert(is_enum_obj(orig_obj), "must be"); diff --git a/src/hotspot/share/cds/cdsEnumKlass.hpp b/src/hotspot/share/cds/cdsEnumKlass.hpp index e6019ff705e..a4829368430 100644 --- a/src/hotspot/share/cds/cdsEnumKlass.hpp +++ b/src/hotspot/share/cds/cdsEnumKlass.hpp @@ -35,7 +35,7 @@ class JavaFieldStream; class KlassSubGraphInfo; // This is legacy support for enum classes before JEP 483. This code is not needed when -// CDSConfig::is_initing_classes_at_dump_time()==true. +// CDSConfig::is_dumping_aot_linked_classes()==true. class CDSEnumKlass: AllStatic { public: static bool is_enum_obj(oop orig_obj); diff --git a/src/hotspot/share/cds/cdsHeapVerifier.cpp b/src/hotspot/share/cds/cdsHeapVerifier.cpp index 65063b4b005..3ed0dce1f66 100644 --- a/src/hotspot/share/cds/cdsHeapVerifier.cpp +++ b/src/hotspot/share/cds/cdsHeapVerifier.cpp @@ -156,7 +156,7 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0) # undef ADD_EXCL - if (CDSConfig::is_initing_classes_at_dump_time()) { + if (CDSConfig::is_dumping_aot_linked_classes()) { add_shared_secret_accessors(); } ClassLoaderDataGraph::classes_do(this); diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index bf8a760904c..8ba4514dfed 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -206,6 +206,8 @@ void FinalImageRecipes::load_all_classes(TRAPS) { if (ik->has_aot_safe_initializer() && (flags & WAS_INITED) != 0) { assert(ik->class_loader() == nullptr, "supported only for boot classes for now"); + ResourceMark rm(THREAD); + log_info(aot, init)("Initializing %s", ik->external_name()); ik->initialize(CHECK); } } diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index f2382289c7d..fdc335f3799 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -209,8 +209,14 @@ static bool is_subgraph_root_class_of(ArchivableStaticFieldInfo fields[], Instan } bool HeapShared::is_subgraph_root_class(InstanceKlass* ik) { - return is_subgraph_root_class_of(archive_subgraph_entry_fields, ik) || - is_subgraph_root_class_of(fmg_archive_subgraph_entry_fields, ik); + assert(CDSConfig::is_dumping_heap(), "dump-time only"); + if (!CDSConfig::is_dumping_aot_linked_classes()) { + // Legacy CDS archive support (to be deprecated) + return is_subgraph_root_class_of(archive_subgraph_entry_fields, ik) || + is_subgraph_root_class_of(fmg_archive_subgraph_entry_fields, ik); + } else { + return false; + } } oop HeapShared::CachedOopInfo::orig_referrer() const { @@ -934,12 +940,16 @@ void HeapShared::scan_java_class(Klass* orig_k) { void HeapShared::archive_subgraphs() { assert(CDSConfig::is_dumping_heap(), "must be"); - archive_object_subgraphs(archive_subgraph_entry_fields, - false /* is_full_module_graph */); + if (!CDSConfig::is_dumping_aot_linked_classes()) { + archive_object_subgraphs(archive_subgraph_entry_fields, + false /* is_full_module_graph */); + if (CDSConfig::is_dumping_full_module_graph()) { + archive_object_subgraphs(fmg_archive_subgraph_entry_fields, + true /* is_full_module_graph */); + } + } if (CDSConfig::is_dumping_full_module_graph()) { - archive_object_subgraphs(fmg_archive_subgraph_entry_fields, - true /* is_full_module_graph */); Modules::verify_archived_modules(); } } @@ -1295,8 +1305,10 @@ void HeapShared::resolve_classes(JavaThread* current) { if (!is_archived_heap_in_use()) { return; // nothing to do } - resolve_classes_for_subgraphs(current, archive_subgraph_entry_fields); - resolve_classes_for_subgraphs(current, fmg_archive_subgraph_entry_fields); + if (!CDSConfig::is_using_aot_linked_classes()) { + resolve_classes_for_subgraphs(current, archive_subgraph_entry_fields); + resolve_classes_for_subgraphs(current, fmg_archive_subgraph_entry_fields); + } } void HeapShared::resolve_classes_for_subgraphs(JavaThread* current, ArchivableStaticFieldInfo fields[]) { @@ -1734,13 +1746,13 @@ bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGrap } } - if (CDSConfig::is_initing_classes_at_dump_time()) { + if (CDSConfig::is_dumping_aot_linked_classes()) { if (java_lang_Class::is_instance(orig_obj)) { orig_obj = scratch_java_mirror(orig_obj); assert(orig_obj != nullptr, "must be archived"); } } else if (java_lang_Class::is_instance(orig_obj) && subgraph_info != _dump_time_special_subgraph) { - // Without CDSConfig::is_initing_classes_at_dump_time(), we only allow archived objects to + // Without CDSConfig::is_dumping_aot_linked_classes(), we only allow archived objects to // point to the mirrors of (1) j.l.Object, (2) primitive classes, and (3) box classes. These are initialized // very early by HeapShared::init_box_classes(). if (orig_obj == vmClasses::Object_klass()->java_mirror() @@ -1808,9 +1820,9 @@ bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGrap orig_obj->oop_iterate(&pusher); } - if (CDSConfig::is_initing_classes_at_dump_time()) { - // The classes of all archived enum instances have been marked as aot-init, - // so there's nothing else to be done in the production run. + if (CDSConfig::is_dumping_aot_linked_classes()) { + // The enum klasses are archived with aot-initialized mirror. + // See AOTClassInitializer::can_archive_initialized_mirror(). } else { // This is legacy support for enum classes before JEP 483 -- we cannot rerun // the enum's in the production run, so special handling is needed. @@ -1949,7 +1961,7 @@ void HeapShared::verify_reachable_objects_from(oop obj) { #endif void HeapShared::check_special_subgraph_classes() { - if (CDSConfig::is_initing_classes_at_dump_time()) { + if (CDSConfig::is_dumping_aot_linked_classes()) { // We can have aot-initialized classes (such as Enums) that can reference objects // of arbitrary types. Currently, we trust the JEP 483 implementation to only // aot-initialize classes that are "safe". @@ -2136,9 +2148,11 @@ void HeapShared::init_subgraph_entry_fields(ArchivableStaticFieldInfo fields[], void HeapShared::init_subgraph_entry_fields(TRAPS) { assert(CDSConfig::is_dumping_heap(), "must be"); _dump_time_subgraph_info_table = new (mtClass)DumpTimeKlassSubGraphInfoTable(); - init_subgraph_entry_fields(archive_subgraph_entry_fields, CHECK); - if (CDSConfig::is_dumping_full_module_graph()) { - init_subgraph_entry_fields(fmg_archive_subgraph_entry_fields, CHECK); + if (!CDSConfig::is_dumping_aot_linked_classes()) { + init_subgraph_entry_fields(archive_subgraph_entry_fields, CHECK); + if (CDSConfig::is_dumping_full_module_graph()) { + init_subgraph_entry_fields(fmg_archive_subgraph_entry_fields, CHECK); + } } } diff --git a/src/java.base/share/classes/java/lang/Byte.java b/src/java.base/share/classes/java/lang/Byte.java index d9913e354a4..0f3f7f40d05 100644 --- a/src/java.base/share/classes/java/lang/Byte.java +++ b/src/java.base/share/classes/java/lang/Byte.java @@ -26,6 +26,7 @@ package java.lang; import jdk.internal.misc.CDS; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -103,6 +104,7 @@ public final class Byte extends Number implements Comparable, Constable { return Optional.of(DynamicConstantDesc.ofNamed(BSM_EXPLICIT_CAST, DEFAULT_NAME, CD_byte, intValue())); } + @AOTSafeClassInitializer private static final class ByteCache { private ByteCache() {} diff --git a/src/java.base/share/classes/java/lang/Character.java b/src/java.base/share/classes/java/lang/Character.java index b71849eaee7..ffda729a45a 100644 --- a/src/java.base/share/classes/java/lang/Character.java +++ b/src/java.base/share/classes/java/lang/Character.java @@ -26,6 +26,7 @@ package java.lang; import jdk.internal.misc.CDS; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -9379,6 +9380,7 @@ class Character implements java.io.Serializable, Comparable, Constabl this.value = value; } + @AOTSafeClassInitializer private static final class CharacterCache { private CharacterCache(){} diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 2742ec40abf..a9da1c32490 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -28,6 +28,8 @@ package java.lang; import jdk.internal.misc.CDS; import jdk.internal.misc.VM; import jdk.internal.util.DecimalDigits; +import jdk.internal.vm.annotation.AOTRuntimeSetup; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -891,15 +893,20 @@ public final class Integer extends Number * with new Integer object(s) after initialization. */ + @AOTSafeClassInitializer private static final class IntegerCache { static final int low = -128; - static final int high; + @Stable static int high; - @Stable - static final Integer[] cache; + @Stable static Integer[] cache; static Integer[] archivedCache; static { + runtimeSetup(); + } + + @AOTRuntimeSetup + private static void runtimeSetup() { // high value may be configured by property int h = 127; String integerCacheHighPropValue = @@ -915,34 +922,50 @@ public final class Integer extends Number } high = h; - // Load IntegerCache.archivedCache from archive, if possible - CDS.initializeFromArchive(IntegerCache.class); - int size = (high - low) + 1; - - // Use the archived cache if it exists and is large enough - if (archivedCache == null || size > archivedCache.length) { - Integer[] c = new Integer[size]; - int j = low; - // If archive has Integer cache, we must use all instances from it. - // Otherwise, the identity checks between archived Integers and - // runtime-cached Integers would fail. - int archivedSize = (archivedCache == null) ? 0 : archivedCache.length; - for (int i = 0; i < archivedSize; i++) { - c[i] = archivedCache[i]; - assert j == archivedCache[i]; - j++; - } - // Fill the rest of the cache. - for (int i = archivedSize; i < size; i++) { - c[i] = new Integer(j++); - } - archivedCache = c; + Integer[] precomputed = null; + if (cache != null) { + // IntegerCache has been AOT-initialized. + precomputed = cache; + } else { + // Legacy CDS archive support (to be deprecated): + // Load IntegerCache.archivedCache from archive, if possible + CDS.initializeFromArchive(IntegerCache.class); + precomputed = archivedCache; } - cache = archivedCache; + + cache = loadOrInitializeCache(precomputed); + archivedCache = cache; // Legacy CDS archive support (to be deprecated) // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } + private static Integer[] loadOrInitializeCache(Integer[] precomputed) { + int size = (high - low) + 1; + + // Use the precomputed cache if it exists and is large enough + if (precomputed != null && size <= precomputed.length) { + return precomputed; + } + + Integer[] c = new Integer[size]; + int j = low; + // If we loading a precomputed cache (from AOT cache or CDS archive), + // we must use all instances from it. + // Otherwise, the Integers from the AOT cache (or CDS archive) will not + // have the same object identity as items in IntegerCache.cache[]. + int precomputedSize = (precomputed == null) ? 0 : precomputed.length; + for (int i = 0; i < precomputedSize; i++) { + c[i] = precomputed[i]; + assert j == precomputed[i]; + j++; + } + // Fill the rest of the cache. + for (int i = precomputedSize; i < size; i++) { + c[i] = new Integer(j++); + } + return c; + } + private IntegerCache() {} } diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 3077e7c0a38..c5cd9650f2d 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -35,6 +35,7 @@ import java.util.Optional; import jdk.internal.misc.CDS; import jdk.internal.util.DecimalDigits; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -911,6 +912,7 @@ public final class Long extends Number return Long.valueOf(parseLong(s, 10)); } + @AOTSafeClassInitializer private static final class LongCache { private LongCache() {} diff --git a/src/java.base/share/classes/java/lang/Module.java b/src/java.base/share/classes/java/lang/Module.java index cd2b8095ee4..bd04345554b 100644 --- a/src/java.base/share/classes/java/lang/Module.java +++ b/src/java.base/share/classes/java/lang/Module.java @@ -69,6 +69,7 @@ import jdk.internal.module.ServicesCatalog; import jdk.internal.module.Resources; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; /** @@ -391,6 +392,7 @@ public final class Module implements AnnotatedElement { private static final Module EVERYONE_MODULE; private static final Set EVERYONE_SET; + @AOTSafeClassInitializer private static class ArchivedData { private static ArchivedData archivedData; private final Module allUnnamedModule; diff --git a/src/java.base/share/classes/java/lang/ModuleLayer.java b/src/java.base/share/classes/java/lang/ModuleLayer.java index 9d922f787a6..a073de6b14a 100644 --- a/src/java.base/share/classes/java/lang/ModuleLayer.java +++ b/src/java.base/share/classes/java/lang/ModuleLayer.java @@ -53,6 +53,7 @@ import jdk.internal.module.ServicesCatalog; import jdk.internal.misc.CDS; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; /** @@ -145,6 +146,7 @@ import jdk.internal.vm.annotation.Stable; * @see Module#getLayer() */ +@AOTSafeClassInitializer public final class ModuleLayer { // the empty layer (may be initialized from the CDS archive) diff --git a/src/java.base/share/classes/java/lang/Short.java b/src/java.base/share/classes/java/lang/Short.java index 4c64427b6df..920500a7fa3 100644 --- a/src/java.base/share/classes/java/lang/Short.java +++ b/src/java.base/share/classes/java/lang/Short.java @@ -26,6 +26,7 @@ package java.lang; import jdk.internal.misc.CDS; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -230,6 +231,7 @@ public final class Short extends Number implements Comparable, Constable return Optional.of(DynamicConstantDesc.ofNamed(BSM_EXPLICIT_CAST, DEFAULT_NAME, CD_short, intValue())); } + @AOTSafeClassInitializer private static final class ShortCache { private ShortCache() {} diff --git a/src/java.base/share/classes/java/lang/module/Configuration.java b/src/java.base/share/classes/java/lang/module/Configuration.java index a76a32cfb28..40eeddc3f0b 100644 --- a/src/java.base/share/classes/java/lang/module/Configuration.java +++ b/src/java.base/share/classes/java/lang/module/Configuration.java @@ -44,6 +44,7 @@ import java.util.stream.Stream; import jdk.internal.misc.CDS; import jdk.internal.module.ModuleReferenceImpl; import jdk.internal.module.ModuleTarget; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; /** @@ -155,6 +156,7 @@ import jdk.internal.vm.annotation.Stable; * @since 9 * @see java.lang.ModuleLayer */ +@AOTSafeClassInitializer public final class Configuration { // @see Configuration#empty() diff --git a/src/java.base/share/classes/java/util/ImmutableCollections.java b/src/java.base/share/classes/java/util/ImmutableCollections.java index abc48ff5ed9..e7fe22490da 100644 --- a/src/java.base/share/classes/java/util/ImmutableCollections.java +++ b/src/java.base/share/classes/java/util/ImmutableCollections.java @@ -42,6 +42,9 @@ import java.util.function.UnaryOperator; import jdk.internal.access.JavaUtilCollectionAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.misc.CDS; +import jdk.internal.vm.annotation.AOTRuntimeSetup; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; +import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; /** @@ -52,6 +55,7 @@ import jdk.internal.vm.annotation.Stable; * classes use a serial proxy and thus have no need to declare serialVersionUID. */ @SuppressWarnings("serial") +@AOTSafeClassInitializer class ImmutableCollections { /** * A "salt" value used for randomizing iteration order. This is initialized once @@ -59,14 +63,20 @@ class ImmutableCollections { * it needs to vary sufficiently from one run to the next so that iteration order * will vary between JVM runs. */ - private static final long SALT32L; + @Stable private static long SALT32L; /** * For set and map iteration, we will iterate in "reverse" stochastically, * decided at bootstrap time. */ - private static final boolean REVERSE; + @Stable private static boolean REVERSE; + static { + runtimeSetup(); + } + + @AOTRuntimeSetup + private static void runtimeSetup() { // to generate a reasonably random and well-mixed SALT, use an arbitrary // value (a slice of pi), multiply with a random seed, then pick // the mid 32-bits from the product. By picking a SALT value in the @@ -102,6 +112,7 @@ class ImmutableCollections { static final MapN EMPTY_MAP; static { + // Legacy CDS archive support (to be deprecated) CDS.initializeFromArchive(ImmutableCollections.class); if (archivedObjects == null) { EMPTY = new Object(); diff --git a/src/java.base/share/classes/java/util/jar/Attributes.java b/src/java.base/share/classes/java/util/jar/Attributes.java index 9322bb9acac..20ff81676c9 100644 --- a/src/java.base/share/classes/java/util/jar/Attributes.java +++ b/src/java.base/share/classes/java/util/jar/Attributes.java @@ -36,6 +36,7 @@ import java.util.Objects; import java.util.Set; import jdk.internal.misc.CDS; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; import sun.nio.cs.UTF_8; @@ -60,6 +61,7 @@ import sun.util.logging.PlatformLogger; * @see Manifest * @since 1.2 */ +@AOTSafeClassInitializer public class Attributes implements Map, Cloneable { /** * The attribute name-value mappings. @@ -450,6 +452,7 @@ public class Attributes implements Map, Cloneable { * * @spec jar/jar.html JAR File Specification */ + @AOTSafeClassInitializer public static class Name { private final String name; private final int hashCode; @@ -669,6 +672,7 @@ public class Attributes implements Map, Cloneable { static { + // Legacy CDS archive support (to be deprecated) CDS.initializeFromArchive(Attributes.Name.class); if (KNOWN_NAMES == null) { diff --git a/src/java.base/share/classes/jdk/internal/loader/ArchivedClassLoaders.java b/src/java.base/share/classes/jdk/internal/loader/ArchivedClassLoaders.java index be3425590fc..439772a8789 100644 --- a/src/java.base/share/classes/jdk/internal/loader/ArchivedClassLoaders.java +++ b/src/java.base/share/classes/jdk/internal/loader/ArchivedClassLoaders.java @@ -27,11 +27,13 @@ package jdk.internal.loader; import java.util.Map; import jdk.internal.misc.CDS; import jdk.internal.module.ServicesCatalog; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; /** * Used to archive the built-in class loaders, their services catalogs, and the * package-to-module map used by the built-in class loaders. */ +@AOTSafeClassInitializer class ArchivedClassLoaders { private static ArchivedClassLoaders archivedClassLoaders; diff --git a/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java b/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java index 1ef9dee3a8a..5413226c112 100644 --- a/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java +++ b/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java @@ -26,6 +26,7 @@ package jdk.internal.math; import jdk.internal.misc.CDS; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; import java.util.Arrays; @@ -33,6 +34,7 @@ import java.util.Arrays; /** * A simple big integer class specifically for floating point base conversion. */ +@AOTSafeClassInitializer final class FDBigInteger { @Stable @@ -53,6 +55,7 @@ final class FDBigInteger { // Initialize FDBigInteger cache of powers of 5. static { + // Legacy CDS archive support (to be deprecated) CDS.initializeFromArchive(FDBigInteger.class); Object[] caches = archivedCaches; if (caches == null) { diff --git a/src/java.base/share/classes/jdk/internal/module/ArchivedBootLayer.java b/src/java.base/share/classes/jdk/internal/module/ArchivedBootLayer.java index 5c806f81dcd..425238dd521 100644 --- a/src/java.base/share/classes/jdk/internal/module/ArchivedBootLayer.java +++ b/src/java.base/share/classes/jdk/internal/module/ArchivedBootLayer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -25,10 +25,12 @@ package jdk.internal.module; import jdk.internal.misc.CDS; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; /** * Used by ModuleBootstrap for archiving the boot layer. */ +@AOTSafeClassInitializer class ArchivedBootLayer { private static ArchivedBootLayer archivedBootLayer; 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 4f9223d0171..deb280c878d 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, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, 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 @@ -30,11 +30,13 @@ import java.util.function.Function; import java.lang.module.Configuration; import java.lang.module.ModuleFinder; import jdk.internal.misc.CDS; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; /** * Used by ModuleBootstrap for archiving the configuration for the boot layer, * and the system module finder. */ +@AOTSafeClassInitializer class ArchivedModuleGraph { private static ArchivedModuleGraph archivedModuleGraph; @@ -126,6 +128,7 @@ class ArchivedModuleGraph { } static { + // Legacy CDS archive support (to be deprecated) CDS.initializeFromArchive(ArchivedModuleGraph.class); } } diff --git a/src/java.base/share/classes/sun/util/locale/BaseLocale.java b/src/java.base/share/classes/sun/util/locale/BaseLocale.java index 58ec6d76aa5..31078720ddc 100644 --- a/src/java.base/share/classes/sun/util/locale/BaseLocale.java +++ b/src/java.base/share/classes/sun/util/locale/BaseLocale.java @@ -34,11 +34,14 @@ package sun.util.locale; import jdk.internal.misc.CDS; import jdk.internal.util.ReferencedKeySet; +import jdk.internal.vm.annotation.AOTRuntimeSetup; +import jdk.internal.vm.annotation.AOTSafeClassInitializer; import jdk.internal.vm.annotation.Stable; import java.util.StringJoiner; import java.util.function.Supplier; +@AOTSafeClassInitializer public final class BaseLocale { public static @Stable BaseLocale[] constantBaseLocales; @@ -63,6 +66,7 @@ public final class BaseLocale { CANADA_FRENCH = 18, NUM_CONSTANTS = 19; static { + // Legacy CDS archive support (to be deprecated) CDS.initializeFromArchive(BaseLocale.class); BaseLocale[] baseLocales = constantBaseLocales; if (baseLocales == null) { @@ -91,13 +95,21 @@ public final class BaseLocale { } // Interned BaseLocale cache - private static final LazyConstant> CACHE = + @Stable private static LazyConstant> CACHE; + static { + runtimeSetup(); + } + + @AOTRuntimeSetup + private static void runtimeSetup() { + CACHE = LazyConstant.of(new Supplier<>() { @Override public ReferencedKeySet get() { return ReferencedKeySet.create(true, ReferencedKeySet.concurrentHashMapSupplier()); } }); + } public static final String SEP = "_"; diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index d4f1470aea5..2288e8f8876 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -522,6 +522,7 @@ hotspot_aot_classlinking = \ -runtime/cds/appcds/aotFlags \ -runtime/cds/appcds/aotProfile \ -runtime/cds/appcds/BadBSM.java \ + -runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java \ -runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java \ -runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java \ -runtime/cds/appcds/CDSandJFR.java \ diff --git a/test/hotspot/jtreg/runtime/cds/SharedSymbolTableBucketSize.java b/test/hotspot/jtreg/runtime/cds/SharedSymbolTableBucketSize.java index 2db4ab2df23..070384a2703 100644 --- a/test/hotspot/jtreg/runtime/cds/SharedSymbolTableBucketSize.java +++ b/test/hotspot/jtreg/runtime/cds/SharedSymbolTableBucketSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, 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 @@ -31,6 +31,8 @@ * java.management */ +import java.util.regex.Matcher; +import java.util.regex.Pattern; import jdk.test.lib.cds.CDSTestUtils; import jdk.test.lib.process.OutputAnalyzer; @@ -43,8 +45,44 @@ public class SharedSymbolTableBucketSize { + Integer.valueOf(bucket_size)); CDSTestUtils.checkMappingFailure(output); - String regex = "Average bucket size : ([0-9]+\\.[0-9]+).*"; - String s = output.firstMatch(regex, 1); + /* [1] There may be other table stats that precede the symbol tabble. + Skip all thse until we find this: + + [0.677s][info][aot,hashtables] Shared symbol table stats -------- base: 0x0000000800000000 + [0.677s][info][aot,hashtables] Number of entries : 46244 + [0.677s][info][aot,hashtables] Total bytes used : 393792 + [0.677s][info][aot,hashtables] Average bytes per entry : 8.516 + [0.677s][info][aot,hashtables] Average bucket size : 7.734 + [0.677s][info][aot,hashtables] Variance of bucket size : 7.513 + [0.677s][info][aot,hashtables] Std. dev. of bucket size: 2.741 + [0.677s][info][aot,hashtables] Maximum bucket size : 20 + [0.677s][info][aot,hashtables] Empty buckets : 2 + [0.677s][info][aot,hashtables] Value_Only buckets : 24 + [0.677s][info][aot,hashtables] Other buckets : 5953 + .... + */ + Pattern pattern0 = Pattern.compile("Shared symbol table stats.*", Pattern.DOTALL); + Matcher matcher0 = pattern0.matcher(output.getStdout()); + String stat = null; + if (matcher0.find()) { + stat = matcher0.group(0); + } + if (stat == null) { + throw new Exception("FAILED: pattern \"" + pattern0 + "\" not found"); + } + + /* (2) The first "Average bucket size" line in the remaining output is for the + shared symbol table */ + Pattern pattern = Pattern.compile("Average bucket size *: *([0-9]+\\.[0-9]+).*", Pattern.MULTILINE); + Matcher matcher = pattern.matcher(stat); + String s = null; + if (matcher.find()) { + s = matcher.group(1); + } + if (s == null) { + throw new Exception("FAILED: pattern \"" + pattern + "\" not found"); + } + Float f = Float.parseFloat(s); int size = Math.round(f); if (size != bucket_size) { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTLoggingTag.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTLoggingTag.java index 896df7ca496..4cc6ef81c45 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTLoggingTag.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTLoggingTag.java @@ -83,17 +83,6 @@ public class AOTLoggingTag { out.shouldContain("[aot] Opened AOT cache hello.aot"); out.shouldHaveExitValue(0); - //---------------------------------------------------------------------- - printTestCase("All old -Xlog:cds+heap logs have been changed to -Xlog:aot+heap should alias to -Xlog:cds+heap"); - pb = ProcessTools.createLimitedTestJavaProcessBuilder( - "-XX:AOTCache=" + aotCacheFile, - "-Xlog:aot+heap", - "-cp", appJar, helloClass); - out = CDSTestUtils.executeAndLog(pb, "prod"); - out.shouldNotContain("No tag set matches selection: aot+heap"); - out.shouldContain("[aot,heap] resolve subgraph java.lang.Integer$IntegerCache"); - out.shouldHaveExitValue(0); - //---------------------------------------------------------------------- printTestCase("Production Run: errors should be printed with [aot] decoration"); pb = ProcessTools.createLimitedTestJavaProcessBuilder( diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/HeapObjectIdentity.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/HeapObjectIdentity.java new file mode 100644 index 00000000000..cdd0921627a --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/HeapObjectIdentity.java @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2025, 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 AOT cache should preserve heap object identity when required by JLS. For example, Enums and Integers. + * @requires vm.cds + * @requires vm.cds.supports.aot.class.linking + * @requires vm.debug + * @library /test/lib + * @build HeapObjectIdentity + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar dummy.jar + * Dummy + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boot.jar + * HeapObjectIdentityApp + * MyAOTInitedClass + * MyAOTInitedClass$MyEnum + * MyAOTInitedClass$Wrapper + * @run driver HeapObjectIdentity AOT --two-step-training + */ + +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.helpers.ClassFileInstaller; + +public class HeapObjectIdentity { + static final String appJar = ClassFileInstaller.getJarPath("dummy.jar"); + static final String bootJar = ClassFileInstaller.getJarPath("boot.jar"); + static final String mainClass = "HeapObjectIdentityApp"; // Loaded from boot.jar + + public static void main(String[] args) throws Exception { + Tester t = new Tester(); + t.run(args); + + // Integer$IntegerCache should preserve the object identity of cached Integer objects, + // even when the cache size is different between assembly and production. + t.productionRun(new String[] { + "-XX:AutoBoxCacheMax=2048" + }); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] vmArgs(RunMode runMode) { + String bootcp = "-Xbootclasspath/a:" + bootJar; + if (runMode == RunMode.ASSEMBLY) { + return new String[] { + "-Xlog:aot+class=debug", + "-XX:AOTInitTestClass=MyAOTInitedClass", + bootcp + }; + } else { + return new String[] {bootcp}; + } + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { + mainClass, + runMode.toString(), + }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { + if (runMode == RunMode.ASSEMBLY) { + out.shouldContain("MyAOTInitedClass aot-linked inited"); + } + } + } +} + +class HeapObjectIdentityApp { + public static void main(String... args) { + MyAOTInitedClass.test(); + } +} + +// This class is loaded by the boot loader, as -XX:AOTInitTestClass is not too friendly +// with classes by other loaders. +class MyAOTInitedClass { + static Object[] archivedObjects; + static { + if (archivedObjects == null) { + archivedObjects = new Object[14]; + archivedObjects[0] = Wrapper.BOOLEAN; + archivedObjects[1] = Wrapper.INT.zero(); + archivedObjects[2] = Wrapper.DOUBLE.zero(); + archivedObjects[3] = MyEnum.DUMMY1; + + archivedObjects[4] = Boolean.class; + archivedObjects[5] = Byte.class; + archivedObjects[6] = Character.class; + archivedObjects[7] = Short.class; + archivedObjects[8] = Integer.class; + archivedObjects[9] = Long.class; + archivedObjects[10] = Float.class; + archivedObjects[11] = Double.class; + archivedObjects[12] = Void.class; + + archivedObjects[13] = Integer.valueOf(1); + } else { + System.out.println("Initialized from CDS"); + } + } + + public static void test() { + if (archivedObjects[0] != Wrapper.BOOLEAN) { + throw new RuntimeException("Huh 0"); + } + + if (archivedObjects[1] != Wrapper.INT.zero()) { + throw new RuntimeException("Huh 1"); + } + + if (archivedObjects[2] != Wrapper.DOUBLE.zero()) { + throw new RuntimeException("Huh 2"); + } + + if (archivedObjects[3] != MyEnum.DUMMY1) { + throw new RuntimeException("Huh 3"); + } + + if (MyEnum.BOOLEAN != true) { + throw new RuntimeException("Huh 10.1"); + } + if (MyEnum.BYTE != -128) { + throw new RuntimeException("Huh 10.2"); + } + if (MyEnum.CHAR != 'c') { + throw new RuntimeException("Huh 10.3"); + } + if (MyEnum.SHORT != -12345) { + throw new RuntimeException("Huh 10.4"); + } + if (MyEnum.INT != -123456) { + throw new RuntimeException("Huh 10.5"); + } + if (MyEnum.LONG != 0x1234567890L) { + throw new RuntimeException("Huh 10.6"); + } + if (MyEnum.LONG2 != -0x1234567890L) { + throw new RuntimeException("Huh 10.7"); + } + if (MyEnum.FLOAT != 567891.0f) { + throw new RuntimeException("Huh 10.8"); + } + if (MyEnum.DOUBLE != 12345678905678.890) { + throw new RuntimeException("Huh 10.9"); + } + + checkClass(4, Boolean.class); + checkClass(5, Byte.class); + checkClass(6, Character.class); + checkClass(7, Short.class); + checkClass(8, Integer.class); + checkClass(9, Long.class); + checkClass(10, Float.class); + checkClass(11, Double.class); + checkClass(12, Void.class); + + if (archivedObjects[13] != Integer.valueOf(1)) { + throw new RuntimeException("Integer cache identity test failed"); + } + + System.out.println("Success!"); + } + + static void checkClass(int index, Class c) { + if (archivedObjects[index] != c) { + throw new RuntimeException("archivedObjects[" + index + "] should be " + c); + } + } + + // Simplified version of sun.invoke.util.Wrapper + public enum Wrapper { + // wrapperType simple primitiveType simple char emptyArray + BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0]), + INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0]), + DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0]) + ; + + public static final int COUNT = 10; + private static final Object DOUBLE_ZERO = (Double)(double)0; + + private final Class wrapperType; + private final Class primitiveType; + private final char basicTypeChar; + private final String basicTypeString; + private final Object emptyArray; + + Wrapper(Class wtype, + String wtypeName, + Class ptype, + String ptypeName, + char tchar, + Object emptyArray) { + this.wrapperType = wtype; + this.primitiveType = ptype; + this.basicTypeChar = tchar; + this.basicTypeString = String.valueOf(this.basicTypeChar); + this.emptyArray = emptyArray; + } + + public Object zero() { + return switch (this) { + case BOOLEAN -> Boolean.FALSE; + case INT -> (Integer)0; + case DOUBLE -> DOUBLE_ZERO; + default -> null; + }; + } + } + + enum MyEnum { + DUMMY1, + DUMMY2; + + static final boolean BOOLEAN = true; + static final byte BYTE = -128; + static final short SHORT = -12345; + static final char CHAR = 'c'; + static final int INT = -123456; + static final long LONG = 0x1234567890L; + static final long LONG2 = -0x1234567890L; + static final float FLOAT = 567891.0f; + static final double DOUBLE = 12345678905678.890; + } +} + +class Dummy {} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java index fed56937f2f..3b1ccff1bfa 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchiveHeapTestClass.java @@ -36,7 +36,7 @@ * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar boot.jar * CDSTestClassA CDSTestClassA$XX CDSTestClassA$YY * CDSTestClassB CDSTestClassC CDSTestClassD - * CDSTestClassE CDSTestClassF CDSTestClassG CDSTestClassG$MyEnum CDSTestClassG$Wrapper + * CDSTestClassE CDSTestClassF * pkg.ClassInPackage * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar Hello * @run driver ArchiveHeapTestClass @@ -58,7 +58,6 @@ public class ArchiveHeapTestClass { static final String CDSTestClassD_name = CDSTestClassD.class.getName(); static final String CDSTestClassE_name = CDSTestClassE.class.getName(); static final String CDSTestClassF_name = CDSTestClassF.class.getName(); - static final String CDSTestClassG_name = CDSTestClassG.class.getName(); static final String ClassInPackage_name = pkg.ClassInPackage.class.getName().replace('.', '/'); static final String ARCHIVE_TEST_FIELD_NAME = "archivedObjects"; @@ -162,15 +161,6 @@ public class ArchiveHeapTestClass { output = dumpBootAndHello(CDSTestClassF_name); mustFail(output, "Class java.util.logging.Level not allowed in archive heap"); } - - testCase("Complex enums"); - output = dumpBootAndHello(CDSTestClassG_name, "-XX:+AOTClassLinking", "-Xlog:cds+class=debug"); - mustSucceed(output); - - TestCommon.run("-Xbootclasspath/a:" + bootJar, "-cp", appJar, "-Xlog:aot+heap,cds+init", - CDSTestClassG_name) - .assertNormalExit("init subgraph " + CDSTestClassG_name, - "Initialized from CDS"); } } @@ -287,147 +277,3 @@ class CDSTestClassF { archivedObjects[0] = java.util.logging.Level.OFF; } } - -class CDSTestClassG { - static Object[] archivedObjects; - static { - if (archivedObjects == null) { - archivedObjects = new Object[13]; - archivedObjects[0] = Wrapper.BOOLEAN; - archivedObjects[1] = Wrapper.INT.zero(); - archivedObjects[2] = Wrapper.DOUBLE.zero(); - archivedObjects[3] = MyEnum.DUMMY1; - - archivedObjects[4] = Boolean.class; - archivedObjects[5] = Byte.class; - archivedObjects[6] = Character.class; - archivedObjects[7] = Short.class; - archivedObjects[8] = Integer.class; - archivedObjects[9] = Long.class; - archivedObjects[10] = Float.class; - archivedObjects[11] = Double.class; - archivedObjects[12] = Void.class; - } else { - System.out.println("Initialized from CDS"); - } - } - - public static void main(String args[]) { - if (archivedObjects[0] != Wrapper.BOOLEAN) { - throw new RuntimeException("Huh 0"); - } - - if (archivedObjects[1] != Wrapper.INT.zero()) { - throw new RuntimeException("Huh 1"); - } - - if (archivedObjects[2] != Wrapper.DOUBLE.zero()) { - throw new RuntimeException("Huh 2"); - } - - if (archivedObjects[3] != MyEnum.DUMMY1) { - throw new RuntimeException("Huh 3"); - } - - if (MyEnum.BOOLEAN != true) { - throw new RuntimeException("Huh 10.1"); - } - if (MyEnum.BYTE != -128) { - throw new RuntimeException("Huh 10.2"); - } - if (MyEnum.CHAR != 'c') { - throw new RuntimeException("Huh 10.3"); - } - if (MyEnum.SHORT != -12345) { - throw new RuntimeException("Huh 10.4"); - } - if (MyEnum.INT != -123456) { - throw new RuntimeException("Huh 10.5"); - } - if (MyEnum.LONG != 0x1234567890L) { - throw new RuntimeException("Huh 10.6"); - } - if (MyEnum.LONG2 != -0x1234567890L) { - throw new RuntimeException("Huh 10.7"); - } - if (MyEnum.FLOAT != 567891.0f) { - throw new RuntimeException("Huh 10.8"); - } - if (MyEnum.DOUBLE != 12345678905678.890) { - throw new RuntimeException("Huh 10.9"); - } - - checkClass(4, Boolean.class); - checkClass(5, Byte.class); - checkClass(6, Character.class); - checkClass(7, Short.class); - checkClass(8, Integer.class); - checkClass(9, Long.class); - checkClass(10, Float.class); - checkClass(11, Double.class); - checkClass(12, Void.class); - - System.out.println("Success!"); - } - - static void checkClass(int index, Class c) { - if (archivedObjects[index] != c) { - throw new RuntimeException("archivedObjects[" + index + "] should be " + c); - } - } - - // Simplified version of sun.invoke.util.Wrapper - public enum Wrapper { - // wrapperType simple primitiveType simple char emptyArray - BOOLEAN( Boolean.class, "Boolean", boolean.class, "boolean", 'Z', new boolean[0]), - INT ( Integer.class, "Integer", int.class, "int", 'I', new int[0]), - DOUBLE ( Double.class, "Double", double.class, "double", 'D', new double[0]) - ; - - public static final int COUNT = 10; - private static final Object DOUBLE_ZERO = (Double)(double)0; - - private final Class wrapperType; - private final Class primitiveType; - private final char basicTypeChar; - private final String basicTypeString; - private final Object emptyArray; - - Wrapper(Class wtype, - String wtypeName, - Class ptype, - String ptypeName, - char tchar, - Object emptyArray) { - this.wrapperType = wtype; - this.primitiveType = ptype; - this.basicTypeChar = tchar; - this.basicTypeString = String.valueOf(this.basicTypeChar); - this.emptyArray = emptyArray; - } - - public Object zero() { - return switch (this) { - case BOOLEAN -> Boolean.FALSE; - case INT -> (Integer)0; - case DOUBLE -> DOUBLE_ZERO; - default -> null; - }; - } - } - - enum MyEnum { - DUMMY1, - DUMMY2; - - static final boolean BOOLEAN = true; - static final byte BYTE = -128; - static final short SHORT = -12345; - static final char CHAR = 'c'; - static final int INT = -123456; - static final long LONG = 0x1234567890L; - static final long LONG2 = -0x1234567890L; - static final float FLOAT = 567891.0f; - static final double DOUBLE = 12345678905678.890; - } -}