8275731: CDS archived enums objects are recreated at runtime

Reviewed-by: coleenp, ccheung
This commit is contained in:
Ioi Lam 2022-02-28 20:33:18 +00:00
parent c7cd1487fe
commit d983d108c5
16 changed files with 857 additions and 54 deletions

@ -0,0 +1,305 @@
/*
* Copyright (c) 2022, 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.
*
*/
#include "precompiled.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/cdsHeapVerifier.hpp"
#include "classfile/classLoaderDataGraph.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/resourceArea.hpp"
#include "oops/fieldStreams.inline.hpp"
#include "oops/klass.inline.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#if INCLUDE_CDS_JAVA_HEAP
// CDSHeapVerifier is used to check for problems where an archived object references a
// static field that may be reinitialized at runtime. In the following example,
// Foo.get.test()
// correctly returns true when CDS disabled, but incorrectly returns false when CDS is enabled.
//
// class Foo {
// final Foo archivedFoo; // this field is archived by CDS
// Bar bar;
// static {
// CDS.initializeFromArchive(Foo.class);
// if (archivedFoo == null) {
// archivedFoo = new Foo();
// archivedFoo.bar = Bar.bar;
// }
// }
// static Foo get() { return archivedFoo; }
// boolean test() {
// return bar == Bar.bar;
// }
// }
//
// class Bar {
// // this field is initialized in both CDS dump time and runtime.
// static final Bar bar = new Bar();
// }
//
// The check itself is simple:
// [1] CDSHeapVerifier::do_klass() collects all static fields
// [2] CDSHeapVerifier::do_entry() checks all the archived objects. None of them
// should be in [1]
//
// However, it's legal for *some* static fields to be references. This leads to the
// table of ADD_EXCL below.
//
// [A] In most of the cases, the module bootstrap code will update the static field
// to point to part of the archived module graph. E.g.,
// - java/lang/System::bootLayer
// - jdk/internal/loader/ClassLoaders::BOOT_LOADER
// [B] A final static String that's explicitly initialized inside <clinit>, but
// its value is deterministic and is always the same string literal.
// [C] A non-final static string that is assigned a string literal during class
// initialization; this string is never changed during -Xshare:dump.
// [D] Simple caches whose value doesn't matter.
// [E] Other cases (see comments in-line below).
CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
{
# define ADD_EXCL(...) { static const char* e[] = {__VA_ARGS__, NULL}; add_exclusion(e); }
// Unfortunately this needs to be manually maintained. If
// test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedEnumTest.java fails,
// you might need to fix the core library code, or fix the ADD_EXCL entries below.
//
// class field type
ADD_EXCL("java/lang/ClassLoader", "scl"); // A
ADD_EXCL("java/lang/invoke/InvokerBytecodeGenerator", "DONTINLINE_SIG", // B
"FORCEINLINE_SIG", // B
"HIDDEN_SIG", // B
"INJECTEDPROFILE_SIG", // B
"LF_COMPILED_SIG"); // B
ADD_EXCL("java/lang/Module", "ALL_UNNAMED_MODULE", // A
"ALL_UNNAMED_MODULE_SET", // A
"EVERYONE_MODULE", // A
"EVERYONE_SET"); // A
ADD_EXCL("java/lang/System", "bootLayer"); // A
ADD_EXCL("java/lang/VersionProps", "VENDOR_URL_BUG", // C
"VENDOR_URL_VM_BUG", // C
"VENDOR_VERSION"); // C
ADD_EXCL("java/net/URL$DefaultFactory", "PREFIX"); // B FIXME: JDK-8276561
// A dummy object used by HashSet. The value doesn't matter and it's never
// tested for equality.
ADD_EXCL("java/util/HashSet", "PRESENT"); // E
ADD_EXCL("jdk/internal/loader/BuiltinClassLoader", "packageToModule"); // A
ADD_EXCL("jdk/internal/loader/ClassLoaders", "BOOT_LOADER", // A
"APP_LOADER", // A
"PLATFORM_LOADER"); // A
ADD_EXCL("jdk/internal/loader/URLClassPath", "JAVA_VERSION"); // B
ADD_EXCL("jdk/internal/module/Builder", "cachedVersion"); // D
ADD_EXCL("jdk/internal/module/ModuleLoaderMap$Mapper", "APP_CLASSLOADER", // A
"APP_LOADER_INDEX", // A
"PLATFORM_CLASSLOADER", // A
"PLATFORM_LOADER_INDEX"); // A
ADD_EXCL("jdk/internal/module/ServicesCatalog", "CLV"); // A
// This just points to an empty Map
ADD_EXCL("jdk/internal/reflect/Reflection", "methodFilterMap"); // E
ADD_EXCL("jdk/internal/util/StaticProperty", "FILE_ENCODING"); // C
// Integer for 0 and 1 are in java/lang/Integer$IntegerCache and are archived
ADD_EXCL("sun/invoke/util/ValueConversions", "ONE_INT", // E
"ZERO_INT"); // E
ADD_EXCL("sun/security/util/SecurityConstants", "PROVIDER_VER"); // C
# undef ADD_EXCL
ClassLoaderDataGraph::classes_do(this);
}
CDSHeapVerifier::~CDSHeapVerifier() {
if (_problems > 0) {
log_warning(cds, heap)("Scanned %d objects. Found %d case(s) where "
"an object points to a static field that may be "
"reinitialized at runtime.", _archived_objs, _problems);
}
}
class CDSHeapVerifier::CheckStaticFields : public FieldClosure {
CDSHeapVerifier* _verifier;
InstanceKlass* _ik;
const char** _exclusions;
public:
CheckStaticFields(CDSHeapVerifier* verifier, InstanceKlass* ik)
: _verifier(verifier), _ik(ik) {
_exclusions = _verifier->find_exclusion(_ik);
}
void do_field(fieldDescriptor* fd) {
if (fd->field_type() != T_OBJECT) {
return;
}
oop static_obj_field = _ik->java_mirror()->obj_field(fd->offset());
if (static_obj_field != NULL) {
Klass* klass = static_obj_field->klass();
if (_exclusions != NULL) {
for (const char** p = _exclusions; *p != NULL; p++) {
if (fd->name()->equals(*p)) {
return;
}
}
}
if (fd->is_final() && java_lang_String::is_instance(static_obj_field) && fd->has_initial_value()) {
// This field looks like like this in the Java source:
// static final SOME_STRING = "a string literal";
// This string literal has been stored in the shared string table, so it's OK
// for the archived objects to refer to it.
return;
}
if (fd->is_final() && java_lang_Class::is_instance(static_obj_field)) {
// This field points to an archived mirror.
return;
}
if (klass->has_archived_enum_objs()) {
// This klass is a subclass of java.lang.Enum. If any instance of this klass
// has been archived, we will archive all static fields of this klass.
// See HeapShared::initialize_enum_klass().
return;
}
// This field *may* be initialized to a different value at runtime. Remember it
// and check later if it appears in the archived object graph.
_verifier->add_static_obj_field(_ik, static_obj_field, fd->name());
}
}
};
// Remember all the static object fields of every class that are currently
// loaded.
void CDSHeapVerifier::do_klass(Klass* k) {
if (k->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(k);
if (HeapShared::is_subgraph_root_class(ik)) {
// ik is inside one of the ArchivableStaticFieldInfo tables
// in heapShared.cpp. We assume such classes are programmed to
// update their static fields correctly at runtime.
return;
}
CheckStaticFields csf(this, ik);
ik->do_local_static_fields(&csf);
}
}
void CDSHeapVerifier::add_static_obj_field(InstanceKlass* ik, oop field, Symbol* name) {
StaticFieldInfo info = {ik, name};
_table.put(field, info);
}
inline bool CDSHeapVerifier::do_entry(oop& orig_obj, HeapShared::CachedOopInfo& value) {
_archived_objs++;
StaticFieldInfo* info = _table.get(orig_obj);
if (info != NULL) {
ResourceMark rm;
LogStream ls(Log(cds, heap)::warning());
ls.print_cr("Archive heap points to a static field that may be reinitialized at runtime:");
ls.print_cr("Field: %s::%s", info->_holder->name()->as_C_string(), info->_name->as_C_string());
ls.print("Value: ");
orig_obj->print_on(&ls);
ls.print_cr("--- trace begin ---");
trace_to_root(orig_obj, NULL, &value);
ls.print_cr("--- trace end ---");
ls.cr();
_problems ++;
}
return true; /* keep on iterating */
}
class CDSHeapVerifier::TraceFields : public FieldClosure {
oop _orig_obj;
oop _orig_field;
LogStream* _ls;
public:
TraceFields(oop orig_obj, oop orig_field, LogStream* ls)
: _orig_obj(orig_obj), _orig_field(orig_field), _ls(ls) {}
void do_field(fieldDescriptor* fd) {
if (fd->field_type() == T_OBJECT || fd->field_type() == T_ARRAY) {
oop obj_field = _orig_obj->obj_field(fd->offset());
if (obj_field == _orig_field) {
_ls->print("::%s (offset = %d)", fd->name()->as_C_string(), fd->offset());
}
}
}
};
// Hint: to exercise this function, uncomment out one of the ADD_EXCL lines above.
int CDSHeapVerifier::trace_to_root(oop orig_obj, oop orig_field, HeapShared::CachedOopInfo* p) {
int level = 0;
LogStream ls(Log(cds, heap)::warning());
if (p->_referrer != NULL) {
HeapShared::CachedOopInfo* ref = HeapShared::archived_object_cache()->get(p->_referrer);
assert(ref != NULL, "sanity");
level = trace_to_root(p->_referrer, orig_obj, ref) + 1;
} else if (java_lang_String::is_instance(orig_obj)) {
ls.print_cr("[%2d] (shared string table)", level++);
}
Klass* k = orig_obj->klass();
ResourceMark rm;
ls.print("[%2d] ", level);
orig_obj->print_address_on(&ls);
ls.print(" %s", k->internal_name());
if (orig_field != NULL) {
if (k->is_instance_klass()) {
TraceFields clo(orig_obj, orig_field, &ls);;
InstanceKlass::cast(k)->do_nonstatic_fields(&clo);
} else {
assert(orig_obj->is_objArray(), "must be");
objArrayOop array = (objArrayOop)orig_obj;
for (int i = 0; i < array->length(); i++) {
if (array->obj_at(i) == orig_field) {
ls.print(" @[%d]", i);
break;
}
}
}
}
ls.cr();
return level;
}
#ifdef ASSERT
void CDSHeapVerifier::verify() {
CDSHeapVerifier verf;
HeapShared::archived_object_cache()->iterate(&verf);
}
#endif
#endif // INCLUDE_CDS_JAVA_HEAP

@ -0,0 +1,89 @@
/*
* Copyright (c) 2022, 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.
*
*/
#ifndef SHARED_CDS_CDSHEAPVERIFIER_HPP
#define SHARED_CDS_CDSHEAPVERIFIER_HPP
#include "cds/heapShared.hpp"
#include "memory/iterator.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/resourceHash.hpp"
class InstanceKlass;
class Symbol;
#if INCLUDE_CDS_JAVA_HEAP
class CDSHeapVerifier : public KlassClosure {
class CheckStaticFields;
class TraceFields;
int _archived_objs;
int _problems;
struct StaticFieldInfo {
InstanceKlass* _holder;
Symbol* _name;
};
ResourceHashtable<oop, StaticFieldInfo,
15889, // prime number
ResourceObj::C_HEAP,
mtClassShared,
HeapShared::oop_hash> _table;
GrowableArray<const char**> _exclusions;
void add_exclusion(const char** excl) {
_exclusions.append(excl);
}
void add_static_obj_field(InstanceKlass* ik, oop field, Symbol* name);
const char** find_exclusion(InstanceKlass* ik) {
for (int i = 0; i < _exclusions.length(); i++) {
const char** excl = _exclusions.at(i);
if (ik->name()->equals(excl[0])) {
return &excl[1];
}
}
return NULL;
}
int trace_to_root(oop orig_obj, oop orig_field, HeapShared::CachedOopInfo* p);
CDSHeapVerifier();
~CDSHeapVerifier();
public:
// Overrides KlassClosure::do_klass()
virtual void do_klass(Klass* k);
// For ResourceHashtable::iterate()
inline bool do_entry(oop& orig_obj, HeapShared::CachedOopInfo& value);
static void verify() NOT_DEBUG_RETURN;
};
#endif // INCLUDE_CDS_JAVA_HEAP
#endif // SHARED_CDS_CDSHEAPVERIFIER_HPP

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022, 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,6 +25,7 @@
#include "precompiled.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/dumpTimeClassInfo.inline.hpp"
#include "cds/runTimeClassInfo.hpp"
#include "classfile/classLoader.hpp"
#include "classfile/classLoaderData.inline.hpp"
#include "classfile/systemDictionaryShared.hpp"
@ -45,6 +46,7 @@ DumpTimeClassInfo DumpTimeClassInfo::clone() {
clone._verifier_constraints = NULL;
clone._verifier_constraint_flags = NULL;
clone._loader_constraints = NULL;
clone._enum_klass_static_fields = NULL;
int clone_num_verifier_constraints = num_verifier_constraints();
if (clone_num_verifier_constraints > 0) {
clone._verifier_constraints = new (ResourceObj::C_HEAP, mtClass) GrowableArray<DTVerifierConstraint>(clone_num_verifier_constraints, mtClass);
@ -61,9 +63,16 @@ DumpTimeClassInfo DumpTimeClassInfo::clone() {
clone._loader_constraints->append(_loader_constraints->at(i));
}
}
assert(_enum_klass_static_fields == NULL, "This should not happen with jcmd VM.cds dumping");
return clone;
}
size_t DumpTimeClassInfo::runtime_info_bytesize() const {
return RunTimeClassInfo::byte_size(_klass, num_verifier_constraints(),
num_loader_constraints(),
num_enum_klass_static_fields());
}
void DumpTimeClassInfo::add_verification_constraint(InstanceKlass* k, Symbol* name,
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
if (_verifier_constraints == NULL) {
@ -144,6 +153,18 @@ void DumpTimeClassInfo::record_linking_constraint(Symbol* name, Handle loader1,
}
}
void DumpTimeClassInfo::add_enum_klass_static_field(int archived_heap_root_index) {
if (_enum_klass_static_fields == NULL) {
_enum_klass_static_fields = new (ResourceObj::C_HEAP, mtClass) GrowableArray<int>(20, mtClass);
}
_enum_klass_static_fields->append(archived_heap_root_index);
}
int DumpTimeClassInfo::enum_klass_static_field(int which_field) {
assert(_enum_klass_static_fields != NULL, "must be");
return _enum_klass_static_fields->at(which_field);
}
bool DumpTimeClassInfo::is_builtin() {
return SystemDictionaryShared::is_builtin(_klass);
}

@ -77,6 +77,7 @@ public:
GrowableArray<DTVerifierConstraint>* _verifier_constraints;
GrowableArray<char>* _verifier_constraint_flags;
GrowableArray<DTLoaderConstraint>* _loader_constraints;
GrowableArray<int>* _enum_klass_static_fields;
DumpTimeClassInfo() {
_klass = NULL;
@ -92,28 +93,38 @@ public:
_verifier_constraints = NULL;
_verifier_constraint_flags = NULL;
_loader_constraints = NULL;
_enum_klass_static_fields = NULL;
}
void add_verification_constraint(InstanceKlass* k, Symbol* name,
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object);
void record_linking_constraint(Symbol* name, Handle loader1, Handle loader2);
void add_enum_klass_static_field(int archived_heap_root_index);
int enum_klass_static_field(int which_field);
bool is_builtin();
int num_verifier_constraints() {
if (_verifier_constraint_flags != NULL) {
return _verifier_constraint_flags->length();
} else {
private:
template <typename T>
static int array_length_or_zero(GrowableArray<T>* array) {
if (array == NULL) {
return 0;
} else {
return array->length();
}
}
int num_loader_constraints() {
if (_loader_constraints != NULL) {
return _loader_constraints->length();
} else {
return 0;
}
public:
int num_verifier_constraints() const {
return array_length_or_zero(_verifier_constraint_flags);
}
int num_loader_constraints() const {
return array_length_or_zero(_loader_constraints);
}
int num_enum_klass_static_fields() const {
return array_length_or_zero(_enum_klass_static_fields);
}
void metaspace_pointers_do(MetaspaceClosure* it) {
@ -151,9 +162,10 @@ public:
void set_failed_verification() { _failed_verification = true; }
InstanceKlass* nest_host() const { return _nest_host; }
void set_nest_host(InstanceKlass* nest_host) { _nest_host = nest_host; }
DumpTimeClassInfo clone();
};
DumpTimeClassInfo clone();
size_t runtime_info_bytesize() const;
};
inline unsigned DumpTimeSharedClassTable_hash(InstanceKlass* const& k) {
if (DumpSharedSpaces) {

@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/archiveUtils.hpp"
#include "cds/cdsHeapVerifier.hpp"
#include "cds/filemap.hpp"
#include "cds/heapShared.inline.hpp"
#include "cds/metaspaceShared.hpp"
@ -42,7 +43,6 @@
#include "gc/shared/gcLocker.hpp"
#include "gc/shared/gcVMOperations.hpp"
#include "logging/log.hpp"
#include "logging/logMessage.hpp"
#include "logging/logStream.hpp"
#include "memory/iterator.inline.hpp"
#include "memory/metadataFactory.hpp"
@ -143,11 +143,24 @@ bool HeapShared::is_archived_object_during_dumptime(oop p) {
}
#endif
////////////////////////////////////////////////////////////////
//
// Java heap object archiving support
//
////////////////////////////////////////////////////////////////
static bool is_subgraph_root_class_of(ArchivableStaticFieldInfo fields[], int num, InstanceKlass* ik) {
for (int i = 0; i < num; i++) {
if (fields[i].klass == ik) {
return true;
}
}
return false;
}
bool HeapShared::is_subgraph_root_class(InstanceKlass* ik) {
return is_subgraph_root_class_of(closed_archive_subgraph_entry_fields,
num_closed_archive_subgraph_entry_fields, ik) ||
is_subgraph_root_class_of(open_archive_subgraph_entry_fields,
num_open_archive_subgraph_entry_fields, ik) ||
is_subgraph_root_class_of(fmg_open_archive_subgraph_entry_fields,
num_fmg_open_archive_subgraph_entry_fields, ik);
}
void HeapShared::fixup_regions() {
FileMapInfo* mapinfo = FileMapInfo::current_info();
if (is_mapped()) {
@ -203,9 +216,9 @@ HeapShared::ArchivedObjectCache* HeapShared::_archived_object_cache = NULL;
oop HeapShared::find_archived_heap_object(oop obj) {
assert(DumpSharedSpaces, "dump-time only");
ArchivedObjectCache* cache = archived_object_cache();
oop* p = cache->get(obj);
CachedOopInfo* p = cache->get(obj);
if (p != NULL) {
return *p;
return p->_obj;
} else {
return NULL;
}
@ -302,7 +315,8 @@ oop HeapShared::archive_object(oop obj) {
assert(hash_original == hash_archived, "Different hash codes: original %x, archived %x", hash_original, hash_archived);
ArchivedObjectCache* cache = archived_object_cache();
cache->put(obj, archived_oop);
CachedOopInfo info = make_cached_oop_info(archived_oop);
cache->put(obj, info);
if (log_is_enabled(Debug, cds, heap)) {
ResourceMark rm;
log_debug(cds, heap)("Archived heap object " PTR_FORMAT " ==> " PTR_FORMAT " : %s",
@ -336,6 +350,94 @@ void HeapShared::archive_klass_objects() {
}
}
// -- Handling of Enum objects
// Java Enum classes have synthetic <clinit> methods that look like this
// enum MyEnum {FOO, BAR}
// MyEnum::<clinint> {
// /*static final MyEnum*/ MyEnum::FOO = new MyEnum("FOO");
// /*static final MyEnum*/ MyEnum::BAR = new MyEnum("BAR");
// }
//
// If MyEnum::FOO object is referenced by any of the archived subgraphs, we must
// ensure the archived value equals (in object address) to the runtime value of
// MyEnum::FOO.
//
// However, since MyEnum::<clinint> is synthetically generated by javac, there's
// no way of programatically handling this inside the Java code (as you would handle
// ModuleLayer::EMPTY_LAYER, for example).
//
// Instead, we archive all static field of such Enum classes. At runtime,
// HeapShared::initialize_enum_klass() will skip the <clinit> method and pull
// the static fields out of the archived heap.
void HeapShared::check_enum_obj(int level,
KlassSubGraphInfo* subgraph_info,
oop orig_obj,
bool is_closed_archive) {
Klass* k = orig_obj->klass();
Klass* relocated_k = ArchiveBuilder::get_relocated_klass(k);
if (!k->is_instance_klass()) {
return;
}
InstanceKlass* ik = InstanceKlass::cast(k);
if (ik->java_super() == vmClasses::Enum_klass() && !ik->has_archived_enum_objs()) {
ResourceMark rm;
ik->set_has_archived_enum_objs();
relocated_k->set_has_archived_enum_objs();
oop mirror = ik->java_mirror();
for (JavaFieldStream fs(ik); !fs.done(); fs.next()) {
if (fs.access_flags().is_static()) {
fieldDescriptor& fd = fs.field_descriptor();
if (fd.field_type() != T_OBJECT && fd.field_type() != T_ARRAY) {
guarantee(false, "static field %s::%s must be T_OBJECT or T_ARRAY",
ik->external_name(), fd.name()->as_C_string());
}
oop oop_field = mirror->obj_field(fd.offset());
if (oop_field == NULL) {
guarantee(false, "static field %s::%s must not be null",
ik->external_name(), fd.name()->as_C_string());
} else if (oop_field->klass() != ik && oop_field->klass() != ik->array_klass_or_null()) {
guarantee(false, "static field %s::%s is of the wrong type",
ik->external_name(), fd.name()->as_C_string());
}
oop archived_oop_field = archive_reachable_objects_from(level, subgraph_info, oop_field, is_closed_archive);
int root_index = append_root(archived_oop_field);
log_info(cds, heap)("Archived enum obj @%d %s::%s (" INTPTR_FORMAT " -> " INTPTR_FORMAT ")",
root_index, ik->external_name(), fd.name()->as_C_string(),
p2i((oopDesc*)oop_field), p2i((oopDesc*)archived_oop_field));
SystemDictionaryShared::add_enum_klass_static_field(ik, root_index);
}
}
}
}
// See comments in HeapShared::check_enum_obj()
bool HeapShared::initialize_enum_klass(InstanceKlass* k, TRAPS) {
if (!is_fully_available()) {
return false;
}
RunTimeClassInfo* info = RunTimeClassInfo::get_for(k);
assert(info != NULL, "sanity");
if (log_is_enabled(Info, cds, heap)) {
ResourceMark rm;
log_info(cds, heap)("Initializing Enum class: %s", k->external_name());
}
oop mirror = k->java_mirror();
int i = 0;
for (JavaFieldStream fs(k); !fs.done(); fs.next()) {
if (fs.access_flags().is_static()) {
int root_index = info->enum_klass_static_field_root_index_at(i++);
fieldDescriptor& fd = fs.field_descriptor();
assert(fd.field_type() == T_OBJECT || fd.field_type() == T_ARRAY, "must be");
mirror->obj_field_put(fd.offset(), get_root(root_index, /*clear=*/true));
}
}
return true;
}
void HeapShared::run_full_gc_in_vm_thread() {
if (HeapShared::can_write()) {
// Avoid fragmentation while archiving heap objects.
@ -377,6 +479,7 @@ void HeapShared::archive_objects(GrowableArray<MemRegion>* closed_regions,
log_info(cds)("Dumping objects to open archive heap region ...");
copy_open_objects(open_regions);
CDSHeapVerifier::verify();
destroy_archived_object_cache();
}
@ -903,6 +1006,11 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
KlassSubGraphInfo* _subgraph_info;
oop _orig_referencing_obj;
oop _archived_referencing_obj;
// The following are for maintaining a stack for determining
// CachedOopInfo::_referrer
static WalkOopAndArchiveClosure* _current;
WalkOopAndArchiveClosure* _last;
public:
WalkOopAndArchiveClosure(int level,
bool is_closed_archive,
@ -912,7 +1020,13 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
_level(level), _is_closed_archive(is_closed_archive),
_record_klasses_only(record_klasses_only),
_subgraph_info(subgraph_info),
_orig_referencing_obj(orig), _archived_referencing_obj(archived) {}
_orig_referencing_obj(orig), _archived_referencing_obj(archived) {
_last = _current;
_current = this;
}
~WalkOopAndArchiveClosure() {
_current = _last;
}
void do_oop(narrowOop *p) { WalkOopAndArchiveClosure::do_oop_work(p); }
void do_oop( oop *p) { WalkOopAndArchiveClosure::do_oop_work(p); }
@ -949,8 +1063,26 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
}
}
}
public:
static WalkOopAndArchiveClosure* current() { return _current; }
oop orig_referencing_obj() { return _orig_referencing_obj; }
KlassSubGraphInfo* subgraph_info() { return _subgraph_info; }
};
WalkOopAndArchiveClosure* WalkOopAndArchiveClosure::_current = NULL;
HeapShared::CachedOopInfo HeapShared::make_cached_oop_info(oop orig_obj) {
CachedOopInfo info;
WalkOopAndArchiveClosure* walker = WalkOopAndArchiveClosure::current();
info._subgraph_info = (walker == NULL) ? NULL : walker->subgraph_info();
info._referrer = (walker == NULL) ? NULL : walker->orig_referencing_obj();
info._obj = orig_obj;
return info;
}
void HeapShared::check_closed_region_object(InstanceKlass* k) {
// Check fields in the object
for (JavaFieldStream fs(k); !fs.done(); fs.next()) {
@ -1076,6 +1208,8 @@ oop HeapShared::archive_reachable_objects_from(int level,
if (is_closed_archive && orig_k->is_instance_klass()) {
check_closed_region_object(InstanceKlass::cast(orig_k));
}
check_enum_obj(level + 1, subgraph_info, orig_obj, is_closed_archive);
return archived_obj;
}

@ -43,6 +43,7 @@
#if INCLUDE_CDS_JAVA_HEAP
class DumpedInternedStrings;
class FileMapInfo;
class KlassSubGraphInfo;
struct ArchivableStaticFieldInfo {
const char* klass_name;
@ -193,7 +194,7 @@ public:
static bool is_fully_available() {
return is_loaded() || is_mapped();
}
static bool is_subgraph_root_class(InstanceKlass* ik);
private:
#if INCLUDE_CDS_JAVA_HEAP
static bool _disable_writing;
@ -228,12 +229,23 @@ public:
assert(is_in_loaded_heap(o), "must be");
}
struct CachedOopInfo {
KlassSubGraphInfo* _subgraph_info;
oop _referrer;
oop _obj;
CachedOopInfo() :_subgraph_info(), _referrer(), _obj() {}
};
private:
static void check_enum_obj(int level,
KlassSubGraphInfo* subgraph_info,
oop orig_obj,
bool is_closed_archive);
static bool is_in_loaded_heap(uintptr_t o) {
return (_loaded_heap_bottom <= o && o < _loaded_heap_top);
}
typedef ResourceHashtable<oop, oop,
typedef ResourceHashtable<oop, CachedOopInfo,
15889, // prime number
ResourceObj::C_HEAP,
mtClassShared,
@ -272,7 +284,7 @@ private:
static RunTimeKlassSubGraphInfoTable _run_time_subgraph_info_table;
static void check_closed_region_object(InstanceKlass* k);
static CachedOopInfo make_cached_oop_info(oop orig_obj);
static void archive_object_subgraphs(ArchivableStaticFieldInfo fields[],
int num,
bool is_closed_archive,
@ -482,6 +494,7 @@ private:
static void init_for_dumping(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
static void write_subgraph_info_table() NOT_CDS_JAVA_HEAP_RETURN;
static void serialize(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;
static bool initialize_enum_klass(InstanceKlass* k, TRAPS) NOT_CDS_JAVA_HEAP_RETURN_(false);
};
#if INCLUDE_CDS_JAVA_HEAP

@ -1,6 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022, 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
@ -64,6 +63,15 @@ void RunTimeClassInfo::init(DumpTimeClassInfo& info) {
InstanceKlass* n_h = info.nest_host();
set_nest_host(n_h);
}
if (_klass->has_archived_enum_objs()) {
int num = info.num_enum_klass_static_fields();
set_num_enum_klass_static_fields(num);
for (int i = 0; i < num; i++) {
int root_index = info.enum_klass_static_field(i);
set_enum_klass_static_field_root_index_at(i, root_index);
}
}
ArchivePtrMarker::mark_pointer(&_klass);
}

@ -1,6 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022, 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
@ -64,30 +63,40 @@ public:
return (Symbol*)(SharedBaseAddress + _name);
}
};
struct RTEnumKlassStaticFields {
int _num;
int _root_indices[1];
};
InstanceKlass* _klass;
int _num_verifier_constraints;
int _num_loader_constraints;
// optional CrcInfo _crc; (only for UNREGISTERED classes)
// optional InstanceKlass* _nest_host
// optional RTLoaderConstraint _loader_constraint_types[_num_loader_constraints]
// optional RTVerifierConstraint _verifier_constraints[_num_verifier_constraints]
// optional char _verifier_constraint_flags[_num_verifier_constraints]
// optional CrcInfo _crc; (only for UNREGISTERED classes)
// optional InstanceKlass* _nest_host
// optional RTLoaderConstraint _loader_constraint_types[_num_loader_constraints]
// optional RTVerifierConstraint _verifier_constraints[_num_verifier_constraints]
// optional char _verifier_constraint_flags[_num_verifier_constraints]
// optional RTEnumKlassStaticFields _enum_klass_static_fields;
private:
static size_t header_size_size() {
return sizeof(RunTimeClassInfo);
return align_up(sizeof(RunTimeClassInfo), wordSize);
}
static size_t verifier_constraints_size(int num_verifier_constraints) {
return sizeof(RTVerifierConstraint) * num_verifier_constraints;
return align_up(sizeof(RTVerifierConstraint) * num_verifier_constraints, wordSize);
}
static size_t verifier_constraint_flags_size(int num_verifier_constraints) {
return sizeof(char) * num_verifier_constraints;
return align_up(sizeof(char) * num_verifier_constraints, wordSize);
}
static size_t loader_constraints_size(int num_loader_constraints) {
return sizeof(RTLoaderConstraint) * num_loader_constraints;
return align_up(sizeof(RTLoaderConstraint) * num_loader_constraints, wordSize);
}
static size_t enum_klass_static_fields_size(int num_fields) {
size_t size = num_fields <= 0 ? 0 : sizeof(RTEnumKlassStaticFields) + (num_fields - 1) * sizeof(int);
return align_up(size, wordSize);
}
static size_t nest_host_size(InstanceKlass* klass) {
if (klass->is_hidden()) {
return sizeof(InstanceKlass*);
@ -98,13 +107,15 @@ private:
static size_t crc_size(InstanceKlass* klass);
public:
static size_t byte_size(InstanceKlass* klass, int num_verifier_constraints, int num_loader_constraints) {
static size_t byte_size(InstanceKlass* klass, int num_verifier_constraints, int num_loader_constraints,
int num_enum_klass_static_fields) {
return header_size_size() +
crc_size(klass) +
nest_host_size(klass) +
loader_constraints_size(num_loader_constraints) +
verifier_constraints_size(num_verifier_constraints) +
verifier_constraint_flags_size(num_verifier_constraints);
verifier_constraint_flags_size(num_verifier_constraints) +
enum_klass_static_fields_size(num_enum_klass_static_fields);
}
private:
@ -113,7 +124,7 @@ private:
}
size_t nest_host_offset() const {
return crc_offset() + crc_size(_klass);
return crc_offset() + crc_size(_klass);
}
size_t loader_constraints_offset() const {
@ -125,6 +136,9 @@ private:
size_t verifier_constraint_flags_offset() const {
return verifier_constraints_offset() + verifier_constraints_size(_num_verifier_constraints);
}
size_t enum_klass_static_fields_offset() const {
return verifier_constraint_flags_offset() + verifier_constraint_flags_size(_num_verifier_constraints);
}
void check_verifier_constraint_offset(int i) const {
assert(0 <= i && i < _num_verifier_constraints, "sanity");
@ -134,6 +148,11 @@ private:
assert(0 <= i && i < _num_loader_constraints, "sanity");
}
RTEnumKlassStaticFields* enum_klass_static_fields_addr() const {
assert(_klass->has_archived_enum_objs(), "sanity");
return (RTEnumKlassStaticFields*)(address(this) + enum_klass_static_fields_offset());
}
public:
CrcInfo* crc() const {
assert(crc_size(_klass) > 0, "must be");
@ -187,6 +206,23 @@ public:
return verifier_constraint_flags()[i];
}
int num_enum_klass_static_fields(int i) const {
return enum_klass_static_fields_addr()->_num;
}
void set_num_enum_klass_static_fields(int num) {
enum_klass_static_fields_addr()->_num = num;
}
int enum_klass_static_field_root_index_at(int i) const {
assert(0 <= i && i < enum_klass_static_fields_addr()->_num, "must be");
return enum_klass_static_fields_addr()->_root_indices[i];
}
void set_enum_klass_static_field_root_index_at(int i, int root_index) {
assert(0 <= i && i < enum_klass_static_fields_addr()->_num, "must be");
enum_klass_static_fields_addr()->_root_indices[i] = root_index;
}
private:
// ArchiveBuilder::make_shallow_copy() has reserved a pointer immediately
// before archived InstanceKlasses. We can use this slot to do a quick

@ -793,6 +793,13 @@ bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbo
}
}
void SystemDictionaryShared::add_enum_klass_static_field(InstanceKlass* ik, int root_index) {
assert(DumpSharedSpaces, "static dump only");
DumpTimeClassInfo* info = SystemDictionaryShared::find_or_allocate_info_for_locked(ik);
assert(info != NULL, "must be");
info->add_enum_klass_static_field(root_index);
}
void SystemDictionaryShared::add_to_dump_time_lambda_proxy_class_dictionary(LambdaProxyClassKey& key,
InstanceKlass* proxy_klass) {
assert_lock_strong(DumpTimeTable_lock);
@ -1174,7 +1181,7 @@ public:
bool do_entry(InstanceKlass* k, DumpTimeClassInfo& info) {
if (!info.is_excluded()) {
size_t byte_size = RunTimeClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints());
size_t byte_size = info.runtime_info_bytesize();
_shared_class_info_size += align_up(byte_size, SharedSpaceObjectAlignment);
}
return true; // keep on iterating
@ -1283,7 +1290,7 @@ public:
bool do_entry(InstanceKlass* k, DumpTimeClassInfo& info) {
if (!info.is_excluded() && info.is_builtin() == _is_builtin) {
size_t byte_size = RunTimeClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints());
size_t byte_size = info.runtime_info_bytesize();
RunTimeClassInfo* record;
record = (RunTimeClassInfo*)ArchiveBuilder::ro_region_alloc(byte_size);
record->init(info);

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2022, 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
@ -193,6 +193,9 @@ private:
public:
static bool is_hidden_lambda_proxy(InstanceKlass* ik);
static bool is_early_klass(InstanceKlass* k); // Was k loaded while JvmtiExport::is_early_phase()==true
static bool has_archived_enum_objs(InstanceKlass* ik);
static void set_has_archived_enum_objs(InstanceKlass* ik);
static InstanceKlass* find_builtin_class(Symbol* class_name);
static const RunTimeClassInfo* find_record(RunTimeSharedDictionary* static_dict,
@ -243,6 +246,7 @@ public:
bool from_is_array, bool from_is_object) NOT_CDS_RETURN_(false);
static void check_verification_constraints(InstanceKlass* klass,
TRAPS) NOT_CDS_RETURN;
static void add_enum_klass_static_field(InstanceKlass* ik, int root_index);
static void set_class_has_failed_verification(InstanceKlass* ik) NOT_CDS_RETURN;
static bool has_class_failed_verification(InstanceKlass* ik) NOT_CDS_RETURN_(false);
static void add_lambda_proxy_class(InstanceKlass* caller_ik,

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2022, 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
@ -136,6 +136,7 @@
do_klass(ByteArrayInputStream_klass, java_io_ByteArrayInputStream ) \
do_klass(URL_klass, java_net_URL ) \
do_klass(URLClassLoader_klass, java_net_URLClassLoader ) \
do_klass(Enum_klass, java_lang_Enum ) \
do_klass(Jar_Manifest_klass, java_util_jar_Manifest ) \
do_klass(jdk_internal_loader_BuiltinClassLoader_klass,jdk_internal_loader_BuiltinClassLoader ) \
do_klass(jdk_internal_loader_ClassLoaders_klass, jdk_internal_loader_ClassLoaders ) \

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2022, 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
@ -700,6 +700,7 @@
template(dumpSharedArchive_signature, "(ZLjava/lang/String;)Ljava/lang/String;") \
template(generateLambdaFormHolderClasses, "generateLambdaFormHolderClasses") \
template(generateLambdaFormHolderClasses_signature, "([Ljava/lang/String;)[Ljava/lang/Object;") \
template(java_lang_Enum, "java/lang/Enum") \
template(java_lang_invoke_Invokers_Holder, "java/lang/invoke/Invokers$Holder") \
template(java_lang_invoke_DirectMethodHandle_Holder, "java/lang/invoke/DirectMethodHandle$Holder") \
template(java_lang_invoke_LambdaForm_Holder, "java/lang/invoke/LambdaForm$Holder") \

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2022, 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
@ -26,6 +26,7 @@
#include "jvm.h"
#include "cds/archiveUtils.hpp"
#include "cds/classListWriter.hpp"
#include "cds/heapShared.hpp"
#include "cds/metaspaceShared.hpp"
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
@ -1503,6 +1504,17 @@ void InstanceKlass::call_class_initializer(TRAPS) {
return;
}
#if INCLUDE_CDS
// This is needed to ensure the consistency of the archived heap objects.
if (has_archived_enum_objs()) {
assert(is_shared(), "must be");
bool initialized = HeapShared::initialize_enum_klass(this, CHECK);
if (initialized) {
return;
}
}
#endif
methodHandle h_method(THREAD, class_initializer());
assert(!is_initialized(), "we cannot initialize twice");
LogTarget(Info, class, init) lt;

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2022, 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
@ -173,10 +173,11 @@ private:
// Flags of the current shared class.
u2 _shared_class_flags;
enum {
_archived_lambda_proxy_is_available = 2,
_has_value_based_class_annotation = 4,
_verified_at_dump_time = 8,
_regenerated = 16
_archived_lambda_proxy_is_available = 1 << 1,
_has_value_based_class_annotation = 1 << 2,
_verified_at_dump_time = 1 << 3,
_has_archived_enum_objs = 1 << 4,
_regenerated = 1 << 5
};
#endif
@ -340,6 +341,14 @@ protected:
NOT_CDS(return false;)
}
void set_has_archived_enum_objs() {
CDS_ONLY(_shared_class_flags |= _has_archived_enum_objs;)
}
bool has_archived_enum_objs() const {
CDS_ONLY(return (_shared_class_flags & _has_archived_enum_objs) != 0;)
NOT_CDS(return false;)
}
void set_regenerated() {
CDS_ONLY(_shared_class_flags |= _regenerated;)
}

@ -0,0 +1,95 @@
/*
* Copyright (c) 2022, 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.
*
*/
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Requires;
import java.lang.module.ModuleDescriptor.Requires.Modifier;
import java.util.Optional;
public class ArchivedEnumApp {
public static void main(final String[] args) throws Exception {
// Validate the archiving of the synthetic Modifier.$VALUES field:
for (Modifier mod : Modifier.values()) {
check(mod);
}
if (Modifier.values().length != 4) {
throw new RuntimeException("Modifier.$VALUES.length expeced: 4, actual: " + Modifier.values().length);
}
// All 4 enums must exist in synthetic Modifier.$VALUES
check_in_array(Modifier.MANDATED);
check_in_array(Modifier.STATIC);
check_in_array(Modifier.SYNTHETIC);
check_in_array(Modifier.TRANSITIVE);
// Find this module from (archived) boot layer
String moduleName = "java.management";
Optional<Module> module = ModuleLayer.boot().findModule(moduleName);
if (module.isEmpty()) {
throw new RuntimeException(moduleName + " module is missing in boot layer");
}
ModuleDescriptor md = module.get().getDescriptor();
System.out.println("Module: " + md);
for (Requires r : md.requires()) {
System.out.println("Requires: " + r);
for (Modifier mod : r.modifiers()) {
System.out.println(" modifier: " + mod);
check(mod);
}
}
System.out.println("Success");
}
static void check(Modifier mod) {
// The archived Enum object must equal to one of the following
// four values.
if (mod != Modifier.MANDATED &&
mod != Modifier.STATIC &&
mod != Modifier.SYNTHETIC &&
mod != Modifier.TRANSITIVE) {
System.out.println("mod = " + info(mod));
System.out.println("Modifier.MANDATED = " + info(Modifier.MANDATED));
System.out.println("Modifier.STATIC = " + info(Modifier.STATIC));
System.out.println("Modifier.SYNTHETIC = " + info(Modifier.SYNTHETIC));
System.out.println("Modifier.TRANSITIVE = " + info(Modifier.TRANSITIVE));
throw new RuntimeException("Archived enum object does not match static fields in enum class: " + info(mod));
}
}
static void check_in_array(Modifier mod) {
for (Modifier m : Modifier.values()) {
if (mod == m) {
return;
}
}
throw new RuntimeException("Enum object is not in $VALUES array: " + info(mod));
}
static String info(Object o) {
return "@0x" + Integer.toHexString(System.identityHashCode(o)) + " " + o;
}
}

@ -0,0 +1,56 @@
/*
* Copyright (c) 2022, 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 8275731
* @summary Enum objects that are stored in the archived module graph should match
* the static final fields in the Enum class.
* @modules java.management
* @requires vm.cds.write.archived.java.heap
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
* @build ArchivedEnumApp
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar ArchivedEnumApp.jar ArchivedEnumApp
* @run driver ArchivedEnumTest
*/
import jdk.test.lib.helpers.ClassFileInstaller;
import jdk.test.lib.process.OutputAnalyzer;
public class ArchivedEnumTest {
public static void main(String[] args) throws Exception {
String appJar = ClassFileInstaller.getJarPath("ArchivedEnumApp.jar");
OutputAnalyzer out = TestCommon.testDump(appJar,
TestCommon.list("ArchivedEnumApp"));
// Note: You can get the following line to fail by commenting out
// the ADD_EXCL(...) lines in cdsHeapVerifier.cpp
out.shouldNotContain("object points to a static field that may be reinitialized at runtime");
TestCommon.run("-cp", appJar,
"-Xlog:cds=debug",
"-Xlog:cds+heap",
"ArchivedEnumApp").assertNormalExit("Success");
}
}