mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-14 18:03:44 +00:00
8317269: Store old classes in linked state in AOT cache
Reviewed-by: coleenp, matsaave
This commit is contained in:
parent
fdc11a1569
commit
85715e1050
@ -652,6 +652,8 @@ char* VM_PopulateDumpSharedSpace::dump_read_only_tables(AOTClassLocationConfig*&
|
||||
}
|
||||
|
||||
void VM_PopulateDumpSharedSpace::doit() {
|
||||
CDSConfig::set_is_at_aot_safepoint(true);
|
||||
|
||||
if (!CDSConfig::is_dumping_final_static_archive()) {
|
||||
guarantee(!CDSConfig::is_using_archive(), "We should not be using an archive when we dump");
|
||||
}
|
||||
@ -717,6 +719,8 @@ void VM_PopulateDumpSharedSpace::doit() {
|
||||
_map_info->set_serialized_data(serialized_data);
|
||||
_map_info->set_cloned_vtables(CppVtables::vtables_serialized_base());
|
||||
_map_info->header()->set_class_location_config(cl_config);
|
||||
|
||||
CDSConfig::set_is_at_aot_safepoint(false);
|
||||
}
|
||||
|
||||
class CollectClassesForLinking : public KlassClosure {
|
||||
@ -773,12 +777,9 @@ bool AOTMetaspace::may_be_eagerly_linked(InstanceKlass* ik) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void AOTMetaspace::link_shared_classes(TRAPS) {
|
||||
AOTClassLinker::initialize();
|
||||
AOTClassInitializer::init_test_class(CHECK);
|
||||
|
||||
void AOTMetaspace::link_all_loaded_classes(JavaThread* current) {
|
||||
while (true) {
|
||||
ResourceMark rm(THREAD);
|
||||
ResourceMark rm(current);
|
||||
CollectClassesForLinking collect_classes;
|
||||
bool has_linked = false;
|
||||
const GrowableArray<OopHandle>* mirrors = collect_classes.mirrors();
|
||||
@ -786,7 +787,7 @@ void AOTMetaspace::link_shared_classes(TRAPS) {
|
||||
OopHandle mirror = mirrors->at(i);
|
||||
InstanceKlass* ik = InstanceKlass::cast(java_lang_Class::as_Klass(mirror.resolve()));
|
||||
if (may_be_eagerly_linked(ik)) {
|
||||
has_linked |= try_link_class(THREAD, ik);
|
||||
has_linked |= try_link_class(current, ik);
|
||||
}
|
||||
}
|
||||
|
||||
@ -796,6 +797,13 @@ void AOTMetaspace::link_shared_classes(TRAPS) {
|
||||
// Class linking includes verification which may load more classes.
|
||||
// Keep scanning until we have linked no more classes.
|
||||
}
|
||||
}
|
||||
|
||||
void AOTMetaspace::link_shared_classes(TRAPS) {
|
||||
AOTClassLinker::initialize();
|
||||
AOTClassInitializer::init_test_class(CHECK);
|
||||
|
||||
link_all_loaded_classes(THREAD);
|
||||
|
||||
// Eargerly resolve all string constants in constant pools
|
||||
{
|
||||
|
||||
@ -135,6 +135,7 @@ public:
|
||||
}
|
||||
|
||||
static bool try_link_class(JavaThread* current, InstanceKlass* ik);
|
||||
static void link_all_loaded_classes(JavaThread* current);
|
||||
static void link_shared_classes(TRAPS) NOT_CDS_RETURN;
|
||||
static bool may_be_eagerly_linked(InstanceKlass* ik) NOT_CDS_RETURN_(false);
|
||||
|
||||
|
||||
@ -937,7 +937,7 @@ void ArchiveBuilder::make_klasses_shareable() {
|
||||
ADD_COUNT(num_enum_klasses);
|
||||
}
|
||||
|
||||
if (!ik->can_be_verified_at_dumptime()) {
|
||||
if (CDSConfig::is_old_class_for_verifier(ik)) {
|
||||
ADD_COUNT(num_old_klasses);
|
||||
old = " old";
|
||||
}
|
||||
|
||||
@ -56,6 +56,7 @@ bool CDSConfig::_has_temp_aot_config_file = false;
|
||||
bool CDSConfig::_old_cds_flags_used = false;
|
||||
bool CDSConfig::_new_aot_flags_used = false;
|
||||
bool CDSConfig::_disable_heap_dumping = false;
|
||||
bool CDSConfig::_is_at_aot_safepoint = false;
|
||||
|
||||
const char* CDSConfig::_default_archive_path = nullptr;
|
||||
const char* CDSConfig::_input_static_archive_path = nullptr;
|
||||
@ -922,6 +923,35 @@ bool CDSConfig::is_dumping_lambdas_in_legacy_mode() {
|
||||
return !is_dumping_method_handles();
|
||||
}
|
||||
|
||||
bool CDSConfig::is_preserving_verification_constraints() {
|
||||
// Verification dependencies are classes used in assignability checks by the
|
||||
// bytecode verifier. In the following example, the verification dependencies
|
||||
// for X are A and B.
|
||||
//
|
||||
// class X {
|
||||
// A getA() { return new B(); }
|
||||
// }
|
||||
//
|
||||
// With the AOT cache, we can ensure that all the verification dependencies
|
||||
// (A and B in the above example) are unconditionally loaded during the bootstrap
|
||||
// of the production run. This means that if a class was successfully verified
|
||||
// in the assembly phase, all of the verifier's assignability checks will remain
|
||||
// valid in the production run, so we don't need to verify aot-linked classes again.
|
||||
|
||||
if (is_dumping_preimage_static_archive()) { // writing AOT config
|
||||
return AOTClassLinking;
|
||||
} else if (is_dumping_final_static_archive()) { // writing AOT cache
|
||||
return is_dumping_aot_linked_classes();
|
||||
} else {
|
||||
// For simplicity, we don't support this optimization with the old CDS workflow.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CDSConfig::is_old_class_for_verifier(const InstanceKlass* ik) {
|
||||
return ik->major_version() < 50 /*JAVA_6_VERSION*/;
|
||||
}
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
bool CDSConfig::are_vm_options_incompatible_with_dumping_heap() {
|
||||
return check_options_incompatible_with_dumping_heap() != nullptr;
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "utilities/macros.hpp"
|
||||
|
||||
class JavaThread;
|
||||
class InstanceKlass;
|
||||
|
||||
class CDSConfig : public AllStatic {
|
||||
#if INCLUDE_CDS
|
||||
@ -43,6 +44,7 @@ class CDSConfig : public AllStatic {
|
||||
static bool _has_aot_linked_classes;
|
||||
static bool _is_single_command_training;
|
||||
static bool _has_temp_aot_config_file;
|
||||
static bool _is_at_aot_safepoint;
|
||||
|
||||
const static char* _default_archive_path;
|
||||
const static char* _input_static_archive_path;
|
||||
@ -99,6 +101,9 @@ public:
|
||||
static const char* type_of_archive_being_written();
|
||||
static void prepare_for_dumping();
|
||||
|
||||
static bool is_at_aot_safepoint() { return CDS_ONLY(_is_at_aot_safepoint) NOT_CDS(false); }
|
||||
static void set_is_at_aot_safepoint(bool value) { CDS_ONLY(_is_at_aot_safepoint = value); }
|
||||
|
||||
// --- Basic CDS features
|
||||
|
||||
// archive(s) in general
|
||||
@ -161,6 +166,10 @@ public:
|
||||
static bool is_using_aot_linked_classes() NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||
static void set_has_aot_linked_classes(bool has_aot_linked_classes) NOT_CDS_JAVA_HEAP_RETURN;
|
||||
|
||||
// Bytecode verification
|
||||
static bool is_preserving_verification_constraints();
|
||||
static bool is_old_class_for_verifier(const InstanceKlass* ik);
|
||||
|
||||
// archive_path
|
||||
|
||||
// Points to the classes.jsa in $JAVA_HOME (could be input or output)
|
||||
|
||||
@ -47,7 +47,7 @@ size_t DumpTimeClassInfo::runtime_info_bytesize() const {
|
||||
num_enum_klass_static_fields());
|
||||
}
|
||||
|
||||
void DumpTimeClassInfo::add_verification_constraint(InstanceKlass* k, Symbol* name,
|
||||
void DumpTimeClassInfo::add_verification_constraint(Symbol* name,
|
||||
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
|
||||
if (_verifier_constraints == nullptr) {
|
||||
_verifier_constraints = new (mtClass) GrowableArray<DTVerifierConstraint>(4, mtClass);
|
||||
@ -73,9 +73,14 @@ void DumpTimeClassInfo::add_verification_constraint(InstanceKlass* k, Symbol* na
|
||||
|
||||
if (log_is_enabled(Trace, aot, verification)) {
|
||||
ResourceMark rm;
|
||||
log_trace(aot, verification)("add_verification_constraint: %s: %s must be subclass of %s [0x%x] array len %d flags len %d",
|
||||
k->external_name(), from_name->as_klass_external_name(),
|
||||
name->as_klass_external_name(), c, vc_array->length(), vcflags_array->length());
|
||||
if (from_name != nullptr) {
|
||||
log_trace(aot, verification)("add verification constraint: %s: %s must be subclass of %s [0x%x]",
|
||||
_klass->external_name(), from_name->as_klass_external_name(),
|
||||
name->as_klass_external_name(), c);
|
||||
} else {
|
||||
log_trace(aot, verification)("added old verification constraint: %s: %s", _klass->external_name(),
|
||||
name->as_klass_external_name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -88,7 +88,7 @@ class DumpTimeClassInfo: public CHeapObj<mtClass> {
|
||||
Symbol* _from_name;
|
||||
public:
|
||||
DTVerifierConstraint() : _name(nullptr), _from_name(nullptr) {}
|
||||
DTVerifierConstraint(Symbol* n, Symbol* fn) : _name(n), _from_name(fn) {
|
||||
DTVerifierConstraint(Symbol* n, Symbol* fn = nullptr) : _name(n), _from_name(fn) {
|
||||
Symbol::maybe_increment_refcount(_name);
|
||||
Symbol::maybe_increment_refcount(_from_name);
|
||||
}
|
||||
@ -152,8 +152,9 @@ public:
|
||||
DumpTimeClassInfo& operator=(const DumpTimeClassInfo&) = delete;
|
||||
~DumpTimeClassInfo();
|
||||
|
||||
void add_verification_constraint(InstanceKlass* k, Symbol* name,
|
||||
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object);
|
||||
// For old verifier: only name is saved; all other fields are null/false.
|
||||
void add_verification_constraint(Symbol* name,
|
||||
Symbol* from_name = nullptr, bool from_field_is_protected = false, bool from_is_array = false, bool from_is_object = false);
|
||||
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);
|
||||
@ -175,6 +176,14 @@ public:
|
||||
return array_length_or_zero(_verifier_constraint_flags);
|
||||
}
|
||||
|
||||
Symbol* verifier_constraint_name_at(int i) const {
|
||||
return _verifier_constraints->at(i).name();
|
||||
}
|
||||
|
||||
Symbol* verifier_constraint_from_name_at(int i) const {
|
||||
return _verifier_constraints->at(i).from_name();
|
||||
}
|
||||
|
||||
int num_loader_constraints() const {
|
||||
return array_length_or_zero(_loader_constraints);
|
||||
}
|
||||
|
||||
@ -110,6 +110,12 @@ public:
|
||||
}
|
||||
|
||||
void doit() {
|
||||
CDSConfig::set_is_at_aot_safepoint(true);
|
||||
doit_inner();
|
||||
CDSConfig::set_is_at_aot_safepoint(false);
|
||||
}
|
||||
|
||||
void doit_inner() {
|
||||
verify_universe("Before CDS dynamic dump");
|
||||
DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm);
|
||||
|
||||
|
||||
@ -471,12 +471,12 @@ class LambdaProxyClassDictionary::CleanupDumpTimeLambdaProxyClassTable: StackObj
|
||||
|
||||
// If the caller class and/or nest_host are excluded, the associated lambda proxy
|
||||
// must also be excluded.
|
||||
bool always_exclude = SystemDictionaryShared::check_for_exclusion(caller_ik, nullptr) ||
|
||||
SystemDictionaryShared::check_for_exclusion(nest_host, nullptr);
|
||||
bool always_exclude = SystemDictionaryShared::should_be_excluded(caller_ik) ||
|
||||
SystemDictionaryShared::should_be_excluded(nest_host);
|
||||
|
||||
for (int i = info._proxy_klasses->length() - 1; i >= 0; i--) {
|
||||
InstanceKlass* ik = info._proxy_klasses->at(i);
|
||||
if (always_exclude || SystemDictionaryShared::check_for_exclusion(ik, nullptr)) {
|
||||
if (always_exclude || SystemDictionaryShared::should_be_excluded(ik)) {
|
||||
LambdaProxyClassDictionary::reset_registered_lambda_proxy_class(ik);
|
||||
info._proxy_klasses->remove_at(i);
|
||||
}
|
||||
|
||||
@ -40,12 +40,18 @@ void RunTimeClassInfo::init(DumpTimeClassInfo& info) {
|
||||
_num_verifier_constraints = info.num_verifier_constraints();
|
||||
_num_loader_constraints = info.num_loader_constraints();
|
||||
int i;
|
||||
|
||||
if (CDSConfig::is_preserving_verification_constraints() && CDSConfig::is_dumping_final_static_archive()) {
|
||||
// The production run doesn't need the verifier constraints, as we can guarantee that all classes checked by
|
||||
// the verifier during AOT training/assembly phases cannot be replaced in the production run.
|
||||
_num_verifier_constraints = 0;
|
||||
}
|
||||
if (_num_verifier_constraints > 0) {
|
||||
RTVerifierConstraint* vf_constraints = verifier_constraints();
|
||||
char* flags = verifier_constraint_flags();
|
||||
for (i = 0; i < _num_verifier_constraints; i++) {
|
||||
vf_constraints[i]._name = builder->any_to_offset_u4(info._verifier_constraints->at(i).name());
|
||||
vf_constraints[i]._from_name = builder->any_to_offset_u4(info._verifier_constraints->at(i).from_name());
|
||||
vf_constraints[i]._name = builder->any_to_offset_u4(info._verifier_constraints->at(i).name());
|
||||
vf_constraints[i]._from_name = builder->any_or_null_to_offset_u4(info._verifier_constraints->at(i).from_name());
|
||||
}
|
||||
for (i = 0; i < _num_verifier_constraints; i++) {
|
||||
flags[i] = info._verifier_constraint_flags->at(i);
|
||||
|
||||
@ -59,7 +59,9 @@ class RunTimeClassInfo {
|
||||
u4 _name;
|
||||
u4 _from_name;
|
||||
Symbol* name() { return ArchiveUtils::offset_to_archived_address<Symbol*>(_name); }
|
||||
Symbol* from_name() { return ArchiveUtils::offset_to_archived_address<Symbol*>(_from_name); }
|
||||
Symbol* from_name() {
|
||||
return (_from_name == 0) ? nullptr : ArchiveUtils::offset_to_archived_address<Symbol*>(_from_name);
|
||||
}
|
||||
};
|
||||
|
||||
struct RTLoaderConstraint {
|
||||
|
||||
@ -204,28 +204,156 @@ DumpTimeClassInfo* SystemDictionaryShared::get_info_locked(InstanceKlass* k) {
|
||||
return info;
|
||||
}
|
||||
|
||||
bool SystemDictionaryShared::check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) {
|
||||
if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(k)) {
|
||||
// We have reached a super type that's already in the base archive. Treat it
|
||||
// as "not excluded".
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info == nullptr) {
|
||||
info = _dumptime_table->get(k);
|
||||
assert(info != nullptr, "supertypes of any classes in _dumptime_table must either be shared, or must also be in _dumptime_table");
|
||||
}
|
||||
bool SystemDictionaryShared::should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info) {
|
||||
assert_lock_strong(DumpTimeTable_lock);
|
||||
|
||||
if (!info->has_checked_exclusion()) {
|
||||
if (check_for_exclusion_impl(k)) {
|
||||
info->set_excluded();
|
||||
}
|
||||
info->set_has_checked_exclusion();
|
||||
check_exclusion_for_self_and_dependencies(k);
|
||||
assert(info->has_checked_exclusion(), "must be");
|
||||
}
|
||||
|
||||
return info->is_excluded();
|
||||
}
|
||||
|
||||
// <func> returns bool and takes a single parameter of Symbol*
|
||||
// The return value indicates whether we want to keep on iterating or not.
|
||||
template<typename Function>
|
||||
void SystemDictionaryShared::iterate_verification_constraint_names(InstanceKlass* k, DumpTimeClassInfo* info, Function func) {
|
||||
int n = info->num_verifier_constraints();
|
||||
bool cont; // continue iterating?
|
||||
for (int i = 0; i < n; i++) {
|
||||
cont = func(info->verifier_constraint_name_at(i));
|
||||
if (!cont) {
|
||||
return; // early termination
|
||||
}
|
||||
Symbol* from_name = info->verifier_constraint_from_name_at(i);
|
||||
if (from_name != nullptr) {
|
||||
cont = func(from_name);
|
||||
if (!cont) {
|
||||
return; // early termination
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is a table of classes that need to be checked for exclusion.
|
||||
class SystemDictionaryShared::ExclusionCheckCandidates
|
||||
: public HashTable<InstanceKlass*, DumpTimeClassInfo*, 15889> {
|
||||
void add_candidate(InstanceKlass* k) {
|
||||
if (contains(k)) {
|
||||
return;
|
||||
}
|
||||
if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(k)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DumpTimeClassInfo* info = SystemDictionaryShared::get_info_locked(k);
|
||||
if (info->has_checked_exclusion()) {
|
||||
// We have check exclusion of k and all of its dependencies, so there's no need to check again.
|
||||
return;
|
||||
}
|
||||
|
||||
put(k, info);
|
||||
|
||||
if (!k->is_loaded()) {
|
||||
// super types are not yet initialized for k.
|
||||
return;
|
||||
}
|
||||
|
||||
InstanceKlass* super = k->java_super();
|
||||
if (super != nullptr) {
|
||||
add_candidate(super);
|
||||
}
|
||||
|
||||
Array<InstanceKlass*>* interfaces = k->local_interfaces();
|
||||
int len = interfaces->length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
add_candidate(interfaces->at(i));
|
||||
}
|
||||
|
||||
InstanceKlass* nest_host = k->nest_host_or_null();
|
||||
if (nest_host != nullptr && nest_host != k) {
|
||||
add_candidate(nest_host);
|
||||
}
|
||||
|
||||
if (CDSConfig::is_preserving_verification_constraints()) {
|
||||
SystemDictionaryShared::iterate_verification_constraint_names(k, info, [&] (Symbol* constraint_class_name) {
|
||||
Klass* constraint_bottom_class = find_verification_constraint_bottom_class(k, constraint_class_name);
|
||||
if (constraint_bottom_class != nullptr && constraint_bottom_class->is_instance_klass()) {
|
||||
add_candidate(InstanceKlass::cast(constraint_bottom_class));
|
||||
}
|
||||
return true; // Keep iterating.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
ExclusionCheckCandidates(InstanceKlass* k) {
|
||||
add_candidate(k);
|
||||
}
|
||||
};
|
||||
|
||||
// A class X is excluded if check_self_exclusion() returns true for X or any of
|
||||
// X's "exclusion dependency" classes, which include:
|
||||
// - ik's super types
|
||||
// - ik's nest host (if any)
|
||||
//
|
||||
// plus, if CDSConfig::is_preserving_verification_constraints()==true:
|
||||
// - ik's verification constraints. These are the classes used in assignability checks
|
||||
// when verifying ik's bytecodes.
|
||||
//
|
||||
// This method ensure that exclusion check is performed on X and all of its exclusion dependencies.
|
||||
void SystemDictionaryShared::check_exclusion_for_self_and_dependencies(InstanceKlass* ik) {
|
||||
assert_lock_strong(DumpTimeTable_lock);
|
||||
ResourceMark rm;
|
||||
|
||||
// This will recursively find ik and all of its exclusion dependencies that have not yet been checked.
|
||||
ExclusionCheckCandidates candidates(ik);
|
||||
|
||||
// (1) Check each class to see if it should be excluded due to its own problems
|
||||
candidates.iterate_all([&] (InstanceKlass* k, DumpTimeClassInfo* info) {
|
||||
if (check_self_exclusion(k)) {
|
||||
info->set_excluded();
|
||||
}
|
||||
});
|
||||
|
||||
// (2) Check each class to see if it should be excluded because of problems in a depeendency class
|
||||
while (true) {
|
||||
bool found_new_exclusion = false;
|
||||
|
||||
candidates.iterate_all([&] (InstanceKlass* k, DumpTimeClassInfo* info) {
|
||||
if (!info->is_excluded() && check_dependencies_exclusion(k, info)) {
|
||||
info->set_excluded();
|
||||
found_new_exclusion = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Algorithm notes:
|
||||
//
|
||||
// The dependencies form a directed graph, possibly cyclic. Class X is excluded
|
||||
// if it has at least one directed path that reaches class Y, where
|
||||
// check_self_exclusion(Y) returns true.
|
||||
//
|
||||
// Because of the possibility of cycles in the graph, we cannot use simple
|
||||
// recursion. Otherwise we will either never terminate, or will miss some paths.
|
||||
//
|
||||
// Hence, we keep doing a linear scan of the candidates until we stop finding
|
||||
// new exclusions.
|
||||
//
|
||||
// In the worst case, we find one exclusion per iteration of the while loop,
|
||||
// so the while loop gets executed O(N^2) times. However, in reality we have
|
||||
// very few exclusions, so in most cases the while loop executes only once, and we
|
||||
// walk each edge in the dependencies graph exactly once.
|
||||
if (!found_new_exclusion) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
candidates.iterate_all([&] (InstanceKlass* k, DumpTimeClassInfo* info) {
|
||||
// All candidates have been fully checked, so we don't need to check them again.
|
||||
info->set_has_checked_exclusion();
|
||||
});
|
||||
}
|
||||
|
||||
// Returns true so the caller can do: return warn_excluded(".....");
|
||||
bool SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) {
|
||||
ResourceMark rm;
|
||||
@ -248,7 +376,8 @@ bool SystemDictionaryShared::is_early_klass(InstanceKlass* ik) {
|
||||
return (info != nullptr) ? info->is_early_klass() : false;
|
||||
}
|
||||
|
||||
bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
|
||||
bool SystemDictionaryShared::check_self_exclusion(InstanceKlass* k) {
|
||||
assert_lock_strong(DumpTimeTable_lock);
|
||||
if (CDSConfig::is_dumping_final_static_archive() && k->defined_by_other_loaders()
|
||||
&& k->in_aot_cache()) {
|
||||
return false; // Do not exclude: unregistered classes are passed from preimage to final image.
|
||||
@ -301,9 +430,8 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
|
||||
return warn_excluded(k, "Failed verification");
|
||||
} else if (CDSConfig::is_dumping_aot_linked_classes()) {
|
||||
// Most loaded classes should have been speculatively linked by AOTMetaspace::link_class_for_cds().
|
||||
// However, we do not speculatively link old classes, as they are not recorded by
|
||||
// SystemDictionaryShared::record_linking_constraint(). As a result, such an unlinked
|
||||
// class may fail to verify in AOTLinkedClassBulkLoader::init_required_classes_for_loader(),
|
||||
// Old classes may not be linked if CDSConfig::is_preserving_verification_constraints()==false.
|
||||
// An unlinked class may fail to verify in AOTLinkedClassBulkLoader::init_required_classes_for_loader(),
|
||||
// causing the JVM to fail at bootstrap.
|
||||
return warn_excluded(k, "Unlinked class not supported by AOTClassLinking");
|
||||
} else if (CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
@ -329,10 +457,13 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
|
||||
return true;
|
||||
}
|
||||
|
||||
InstanceKlass* super = k->super();
|
||||
if (super != nullptr && check_for_exclusion(super, nullptr)) {
|
||||
ResourceMark rm;
|
||||
aot_log_warning(aot)("Skipping %s: super class %s is excluded", k->name()->as_C_string(), super->name()->as_C_string());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true if DumpTimeClassInfo::is_excluded() is true for at least one of k's exclusion dependencies.
|
||||
bool SystemDictionaryShared::check_dependencies_exclusion(InstanceKlass* k, DumpTimeClassInfo* info) {
|
||||
InstanceKlass* super = k->java_super();
|
||||
if (super != nullptr && is_dependency_excluded(k, super, "super")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -340,21 +471,87 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
|
||||
int len = interfaces->length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
InstanceKlass* intf = interfaces->at(i);
|
||||
if (check_for_exclusion(intf, nullptr)) {
|
||||
ResourceMark rm;
|
||||
aot_log_warning(aot)("Skipping %s: interface %s is excluded", k->name()->as_C_string(), intf->name()->as_C_string());
|
||||
if (is_dependency_excluded(k, intf, "interface")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
InstanceKlass* nest_host = k->nest_host_or_null();
|
||||
if (nest_host != nullptr && nest_host != k && check_for_exclusion(nest_host, nullptr)) {
|
||||
ResourceMark rm;
|
||||
aot_log_warning(aot)("Skipping %s: nest_host class %s is excluded", k->name()->as_C_string(), nest_host->name()->as_C_string());
|
||||
if (nest_host != nullptr && nest_host != k && is_dependency_excluded(k, nest_host, "nest host class")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // false == k should NOT be excluded
|
||||
if (CDSConfig::is_preserving_verification_constraints()) {
|
||||
bool excluded = false;
|
||||
|
||||
iterate_verification_constraint_names(k, info, [&] (Symbol* constraint_class_name) {
|
||||
if (check_verification_constraint_exclusion(k, constraint_class_name)) {
|
||||
// If one of the verification constraint class has been excluded, the assignability checks
|
||||
// by the verifier may no longer be valid in the production run. For safety, exclude this class.
|
||||
excluded = true;
|
||||
return false; // terminate iteration; k will be excluded
|
||||
} else {
|
||||
return true; // keep iterating
|
||||
}
|
||||
});
|
||||
|
||||
if (excluded) {
|
||||
// At least one verification constraint class has been excluded
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SystemDictionaryShared::is_dependency_excluded(InstanceKlass* k, InstanceKlass* dependency, const char* type) {
|
||||
if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(dependency)) {
|
||||
return false;
|
||||
}
|
||||
DumpTimeClassInfo* dependency_info = get_info_locked(dependency);
|
||||
if (dependency_info->is_excluded()) {
|
||||
ResourceMark rm;
|
||||
aot_log_warning(aot)("Skipping %s: %s %s is excluded", k->name()->as_C_string(), type, dependency->name()->as_C_string());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SystemDictionaryShared::check_verification_constraint_exclusion(InstanceKlass* k, Symbol* constraint_class_name) {
|
||||
Klass* constraint_bottom_class = find_verification_constraint_bottom_class(k, constraint_class_name);
|
||||
if (constraint_bottom_class == nullptr) {
|
||||
// We don't have a bottom class (constraint_class_name is a type array), or constraint_class_name
|
||||
// has not been loaded. The latter case happens when the new verifier was checking
|
||||
// if constraint_class_name is assignable to an interface, and found the answer without resolving
|
||||
// constraint_class_name.
|
||||
//
|
||||
// Since this class is not even loaded, it surely cannot be excluded.
|
||||
return false;
|
||||
} else if (constraint_bottom_class->is_instance_klass()) {
|
||||
if (is_dependency_excluded(k, InstanceKlass::cast(constraint_bottom_class), "verification constraint")) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
assert(constraint_bottom_class->is_typeArray_klass(), "must be");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Klass* SystemDictionaryShared::find_verification_constraint_bottom_class(InstanceKlass* k, Symbol* constraint_class_name) {
|
||||
Thread* current = Thread::current();
|
||||
Handle loader(current, k->class_loader());
|
||||
Klass* constraint_class = SystemDictionary::find_instance_or_array_klass(current, constraint_class_name, loader);
|
||||
if (constraint_class == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (constraint_class->is_objArray_klass()) {
|
||||
constraint_class = ObjArrayKlass::cast(constraint_class)->bottom_klass();
|
||||
}
|
||||
|
||||
precond(constraint_class->is_typeArray_klass() || constraint_class->is_instance_klass());
|
||||
return constraint_class;
|
||||
}
|
||||
|
||||
bool SystemDictionaryShared::is_builtin_loader(ClassLoaderData* loader_data) {
|
||||
@ -556,7 +753,7 @@ void SystemDictionaryShared::handle_class_unloading(InstanceKlass* klass) {
|
||||
|
||||
void SystemDictionaryShared::init_dumptime_info_from_preimage(InstanceKlass* k) {
|
||||
init_dumptime_info(k);
|
||||
copy_verification_constraints_from_preimage(k);
|
||||
copy_verification_info_from_preimage(k);
|
||||
copy_linking_constraints_from_preimage(k);
|
||||
|
||||
if (SystemDictionary::is_platform_class_loader(k->class_loader())) {
|
||||
@ -651,16 +848,21 @@ public:
|
||||
// Returns true if the class should be excluded. This can be called by
|
||||
// AOTConstantPoolResolver before or after we enter the CDS safepoint.
|
||||
// When called before the safepoint, we need to link the class so that
|
||||
// it can be checked by check_for_exclusion().
|
||||
// it can be checked by should_be_excluded_impl().
|
||||
bool SystemDictionaryShared::should_be_excluded(Klass* k) {
|
||||
assert(CDSConfig::is_dumping_archive(), "sanity");
|
||||
assert(CDSConfig::current_thread_is_vm_or_dumper(), "sanity");
|
||||
|
||||
if (k->is_objArray_klass()) {
|
||||
return should_be_excluded(ObjArrayKlass::cast(k)->bottom_klass());
|
||||
if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(k)) {
|
||||
// We have reached a super type that's already in the base archive. Treat it
|
||||
// as "not excluded".
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!k->is_instance_klass()) {
|
||||
if (k->is_objArray_klass()) {
|
||||
return should_be_excluded(ObjArrayKlass::cast(k)->bottom_klass());
|
||||
} else if (!k->is_instance_klass()) {
|
||||
assert(k->is_typeArray_klass(), "must be");
|
||||
return false;
|
||||
} else {
|
||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||
@ -672,7 +874,7 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
|
||||
|
||||
if (!SafepointSynchronize::is_at_safepoint()) {
|
||||
if (!ik->is_linked()) {
|
||||
// check_for_exclusion() below doesn't link unlinked classes. We come
|
||||
// should_be_excluded_impl() below doesn't link unlinked classes. We come
|
||||
// here only when we are trying to aot-link constant pool entries, so
|
||||
// we'd better link the class.
|
||||
JavaThread* THREAD = JavaThread::current();
|
||||
@ -681,6 +883,10 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
return true; // linking failed -- let's exclude it
|
||||
}
|
||||
|
||||
// Also link any classes that were loaded for the verification of ik or its supertypes.
|
||||
// Otherwise we might miss the verification constraints of those classes.
|
||||
AOTMetaspace::link_all_loaded_classes(THREAD);
|
||||
}
|
||||
|
||||
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
|
||||
@ -688,8 +894,17 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
|
||||
if (p->is_excluded()) {
|
||||
return true;
|
||||
}
|
||||
return check_for_exclusion(ik, p);
|
||||
return should_be_excluded_impl(ik, p);
|
||||
} else {
|
||||
// When called within the CDS safepoint, the correctness of this function
|
||||
// relies on the call to AOTMetaspace::link_all_loaded_classes()
|
||||
// that happened right before we enter the CDS safepoint.
|
||||
//
|
||||
// Do not call this function in other types of safepoints. For example, if this
|
||||
// is called in a GC safepoint, a klass may be improperly excluded because some
|
||||
// of its verification constraints have not yet been linked.
|
||||
assert(CDSConfig::is_at_aot_safepoint(), "Do not call this function in any other safepoint");
|
||||
|
||||
// No need to check for is_linked() as all eligible classes should have
|
||||
// already been linked in AOTMetaspace::link_class_for_cds().
|
||||
// Can't take the lock as we are in safepoint.
|
||||
@ -697,12 +912,13 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
|
||||
if (p->is_excluded()) {
|
||||
return true;
|
||||
}
|
||||
return check_for_exclusion(ik, p);
|
||||
return should_be_excluded_impl(ik, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::finish_exclusion_checks() {
|
||||
assert_at_safepoint();
|
||||
if (CDSConfig::is_dumping_dynamic_archive() || CDSConfig::is_dumping_preimage_static_archive()) {
|
||||
// Do this first -- if a base class is excluded due to duplication,
|
||||
// all of its subclasses will also be excluded.
|
||||
@ -713,7 +929,7 @@ void SystemDictionaryShared::finish_exclusion_checks() {
|
||||
}
|
||||
|
||||
_dumptime_table->iterate_all_live_classes([&] (InstanceKlass* k, DumpTimeClassInfo& info) {
|
||||
SystemDictionaryShared::check_for_exclusion(k, &info);
|
||||
SystemDictionaryShared::should_be_excluded_impl(k, &info);
|
||||
});
|
||||
|
||||
_dumptime_table->update_counts();
|
||||
@ -793,7 +1009,7 @@ void SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbo
|
||||
bool* skip_assignability_check) {
|
||||
assert(CDSConfig::is_dumping_archive(), "sanity");
|
||||
DumpTimeClassInfo* info = get_info(k);
|
||||
info->add_verification_constraint(k, name, from_name, from_field_is_protected,
|
||||
info->add_verification_constraint(name, from_name, from_field_is_protected,
|
||||
from_is_array, from_is_object);
|
||||
|
||||
if (CDSConfig::is_dumping_classic_static_archive() && !is_builtin(k)) {
|
||||
@ -818,6 +1034,15 @@ void SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbo
|
||||
}
|
||||
}
|
||||
|
||||
// When the old verifier is verifying the class <ik> at dump time, it tries to resolve a
|
||||
// class with the given <name>. For the verification result to be valid at run time, we must
|
||||
// ensure that <name> resolves to the exact same Klass as in dump time.
|
||||
void SystemDictionaryShared::add_old_verification_constraint(Thread* current, InstanceKlass* ik, Symbol* name) {
|
||||
precond(CDSConfig::is_preserving_verification_constraints());
|
||||
DumpTimeClassInfo* info = get_info(ik);
|
||||
info->add_verification_constraint(name);
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::add_enum_klass_static_field(InstanceKlass* ik, int root_index) {
|
||||
assert(CDSConfig::is_dumping_heap(), "sanity");
|
||||
DumpTimeClassInfo* info = get_info_locked(ik);
|
||||
@ -836,6 +1061,13 @@ void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass
|
||||
Symbol* name = vc->name();
|
||||
Symbol* from_name = vc->from_name();
|
||||
|
||||
if (from_name == nullptr) {
|
||||
// This is for old verifier. No need to check, as we can guarantee that all classes checked by
|
||||
// the old verifier during AOT training phase cannot be replaced in the asembly phase.
|
||||
precond(CDSConfig::is_dumping_final_static_archive());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (log_is_enabled(Trace, aot, verification)) {
|
||||
ResourceMark rm(THREAD);
|
||||
log_trace(aot, verification)("check_verification_constraint: %s: %s must be subclass of %s [0x%x]",
|
||||
@ -860,7 +1092,7 @@ void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass
|
||||
}
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::copy_verification_constraints_from_preimage(InstanceKlass* klass) {
|
||||
void SystemDictionaryShared::copy_verification_info_from_preimage(InstanceKlass* klass) {
|
||||
assert(CDSConfig::is_using_archive(), "called at run time with CDS enabled only");
|
||||
DumpTimeClassInfo* dt_info = get_info(klass);
|
||||
RunTimeClassInfo* rt_info = RunTimeClassInfo::get_for(klass); // from preimage
|
||||
@ -872,7 +1104,7 @@ void SystemDictionaryShared::copy_verification_constraints_from_preimage(Instanc
|
||||
Symbol* name = vc->name();
|
||||
Symbol* from_name = vc->from_name();
|
||||
|
||||
dt_info->add_verification_constraint(klass, name, from_name,
|
||||
dt_info->add_verification_constraint(name, from_name,
|
||||
rt_info->from_field_is_protected(i), rt_info->from_is_array(i), rt_info->from_is_object(i));
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,7 +146,7 @@ class SystemDictionaryShared: public SystemDictionary {
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
class ExclusionCheckCandidates;
|
||||
static DumpTimeSharedClassTable* _dumptime_table;
|
||||
|
||||
static ArchiveInfo _static_archive;
|
||||
@ -175,14 +175,27 @@ private:
|
||||
static void write_dictionary(RunTimeSharedDictionary* dictionary,
|
||||
bool is_builtin);
|
||||
static bool is_jfr_event_class(InstanceKlass *k);
|
||||
static bool check_for_exclusion_impl(InstanceKlass* k);
|
||||
static bool should_be_excluded_impl(InstanceKlass* k, DumpTimeClassInfo* info);
|
||||
|
||||
// exclusion checks
|
||||
static void check_exclusion_for_self_and_dependencies(InstanceKlass *k);
|
||||
static bool check_self_exclusion(InstanceKlass* k);
|
||||
static bool check_dependencies_exclusion(InstanceKlass* k, DumpTimeClassInfo* info);
|
||||
static bool check_verification_constraint_exclusion(InstanceKlass* k, Symbol* constraint_class_name);
|
||||
static bool is_dependency_excluded(InstanceKlass* k, InstanceKlass* dependency, const char* type);
|
||||
static bool is_excluded_verification_constraint(InstanceKlass* k, Symbol* constraint_class_name);
|
||||
static Klass* find_verification_constraint_bottom_class(InstanceKlass* k, Symbol* constraint_class_name);
|
||||
|
||||
static void remove_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN;
|
||||
static bool has_been_redefined(InstanceKlass* k);
|
||||
DEBUG_ONLY(static bool _class_loading_may_happen;)
|
||||
|
||||
static void copy_verification_constraints_from_preimage(InstanceKlass* klass);
|
||||
static void copy_verification_info_from_preimage(InstanceKlass* klass);
|
||||
static void copy_linking_constraints_from_preimage(InstanceKlass* klass);
|
||||
|
||||
template<typename Function>
|
||||
static void iterate_verification_constraint_names(InstanceKlass* k, DumpTimeClassInfo* info, Function func);
|
||||
|
||||
public:
|
||||
static bool is_early_klass(InstanceKlass* k); // Was k loaded while JvmtiExport::is_early_phase()==true
|
||||
static bool has_archived_enum_objs(InstanceKlass* ik);
|
||||
@ -239,6 +252,7 @@ public:
|
||||
Symbol* from_name, bool from_field_is_protected,
|
||||
bool from_is_array, bool from_is_object,
|
||||
bool* skip_assignability_check);
|
||||
static void add_old_verification_constraint(Thread* current, InstanceKlass* k, Symbol* name);
|
||||
static void check_verification_constraints(InstanceKlass* klass,
|
||||
TRAPS) NOT_CDS_RETURN;
|
||||
static void add_enum_klass_static_field(InstanceKlass* ik, int root_index);
|
||||
@ -258,7 +272,6 @@ public:
|
||||
static DumpTimeSharedClassTable* dumptime_table() { return _dumptime_table; }
|
||||
|
||||
static bool should_be_excluded(Klass* k);
|
||||
static bool check_for_exclusion(InstanceKlass* k, DumpTimeClassInfo* info);
|
||||
static void validate_before_archiving(InstanceKlass* k);
|
||||
static bool is_excluded_class(InstanceKlass* k);
|
||||
static void set_excluded(InstanceKlass* k);
|
||||
|
||||
@ -2839,18 +2839,20 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
|
||||
DEBUG_ONLY(FieldInfoStream::validate_search_table(_constants, _fieldinfo_stream, _fieldinfo_search_table));
|
||||
}
|
||||
|
||||
// Check if a class or any of its supertypes has a version older than 50.
|
||||
// CDS will not perform verification of old classes during dump time because
|
||||
// without changing the old verifier, the verification constraint cannot be
|
||||
// retrieved during dump time.
|
||||
// Verification of archived old classes will be performed during run time.
|
||||
bool InstanceKlass::can_be_verified_at_dumptime() const {
|
||||
if (AOTMetaspace::in_aot_cache(this)) {
|
||||
// This is a class that was dumped into the base archive, so we know
|
||||
// it was verified at dump time.
|
||||
return true;
|
||||
}
|
||||
if (major_version() < 50 /*JAVA_6_VERSION*/) {
|
||||
|
||||
if (CDSConfig::is_preserving_verification_constraints()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CDSConfig::is_old_class_for_verifier(this)) {
|
||||
// The old verifier does not save verification constraints, so at run time
|
||||
// SystemDictionaryShared::check_verification_constraints() will not work for this class.
|
||||
return false;
|
||||
}
|
||||
if (super() != nullptr && !super()->can_be_verified_at_dumptime()) {
|
||||
|
||||
@ -323,9 +323,8 @@ void VirtualCallTypeData::post_initialize(BytecodeStream* stream, MethodData* md
|
||||
|
||||
static bool is_excluded(Klass* k) {
|
||||
#if INCLUDE_CDS
|
||||
if (SafepointSynchronize::is_at_safepoint() &&
|
||||
CDSConfig::is_dumping_archive() &&
|
||||
CDSConfig::current_thread_is_vm_or_dumper()) {
|
||||
if (CDSConfig::is_at_aot_safepoint()) {
|
||||
// Check for CDS exclusion only at CDS safe point.
|
||||
if (k->is_instance_klass() && !InstanceKlass::cast(k)->is_loaded()) {
|
||||
log_debug(aot, training)("Purged %s from MDO: unloaded class", k->name()->as_C_string());
|
||||
return true;
|
||||
|
||||
@ -554,7 +554,11 @@ void KlassTrainingData::cleanup(Visitor& visitor) {
|
||||
}
|
||||
visitor.visit(this);
|
||||
if (has_holder()) {
|
||||
bool is_excluded = !holder()->is_loaded() || SystemDictionaryShared::check_for_exclusion(holder(), nullptr);
|
||||
bool is_excluded = !holder()->is_loaded();
|
||||
if (CDSConfig::is_at_aot_safepoint()) {
|
||||
// Check for AOT exclusion only at AOT safe point.
|
||||
is_excluded |= SystemDictionaryShared::should_be_excluded(holder());
|
||||
}
|
||||
if (is_excluded) {
|
||||
ResourceMark rm;
|
||||
log_debug(aot, training)("Cleanup KTD %s", name()->as_klass_external_name());
|
||||
@ -573,7 +577,8 @@ void MethodTrainingData::cleanup(Visitor& visitor) {
|
||||
}
|
||||
visitor.visit(this);
|
||||
if (has_holder()) {
|
||||
if (SystemDictionaryShared::check_for_exclusion(holder()->method_holder(), nullptr)) {
|
||||
if (CDSConfig::is_at_aot_safepoint() && SystemDictionaryShared::should_be_excluded(holder()->method_holder())) {
|
||||
// Check for AOT exclusion only at AOT safe point.
|
||||
log_debug(aot, training)("Cleanup MTD %s::%s", name()->as_klass_external_name(), signature()->as_utf8());
|
||||
if (_final_profile != nullptr && _final_profile->method() != _holder) {
|
||||
log_warning(aot, training)("Stale MDO for %s::%s", name()->as_klass_external_name(), signature()->as_utf8());
|
||||
|
||||
@ -850,6 +850,13 @@ JVM_ENTRY(jclass, JVM_FindClassFromClass(JNIEnv *env, const char *name,
|
||||
log_debug(class, resolve)("%s %s (verification)", from_name, to);
|
||||
}
|
||||
|
||||
#if INCLUDE_CDS
|
||||
if (CDSConfig::is_preserving_verification_constraints() && from_class->is_instance_klass()) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(from_class);
|
||||
SystemDictionaryShared::add_old_verification_constraint(THREAD, ik, h_name);
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
JVM_END
|
||||
|
||||
|
||||
@ -303,11 +303,11 @@ void mutex_init() {
|
||||
#endif
|
||||
MUTEX_DEFN(DumpTimeTable_lock , PaddedMutex , nosafepoint);
|
||||
MUTEX_DEFN(CDSLambda_lock , PaddedMutex , nosafepoint);
|
||||
MUTEX_DEFN(DumpRegion_lock , PaddedMutex , nosafepoint);
|
||||
MUTEX_DEFL(DumpRegion_lock , PaddedMutex , DumpTimeTable_lock);
|
||||
MUTEX_DEFN(ClassListFile_lock , PaddedMutex , nosafepoint);
|
||||
MUTEX_DEFN(UnregisteredClassesTable_lock , PaddedMutex , nosafepoint-1);
|
||||
MUTEX_DEFN(LambdaFormInvokers_lock , PaddedMutex , safepoint);
|
||||
MUTEX_DEFN(ScratchObjects_lock , PaddedMutex , nosafepoint-1); // Holds DumpTimeTable_lock
|
||||
MUTEX_DEFL(ScratchObjects_lock , PaddedMutex , DumpTimeTable_lock);
|
||||
MUTEX_DEFN(FinalImageRecipes_lock , PaddedMutex , nosafepoint);
|
||||
#endif // INCLUDE_CDS
|
||||
MUTEX_DEFN(Bootclasspath_lock , PaddedMutex , nosafepoint);
|
||||
|
||||
@ -526,6 +526,7 @@ hotspot_aot_classlinking = \
|
||||
-runtime/cds/appcds/cacheObject/ArchivedIntegerCacheTest.java \
|
||||
-runtime/cds/appcds/cacheObject/ArchivedModuleCompareTest.java \
|
||||
-runtime/cds/appcds/CDSandJFR.java \
|
||||
-runtime/cds/appcds/LambdaContainsOldInf.java \
|
||||
-runtime/cds/appcds/customLoader/CustomClassListDump.java \
|
||||
-runtime/cds/appcds/customLoader/HelloCustom_JFR.java \
|
||||
-runtime/cds/appcds/customLoader/OldClassAndInf.java \
|
||||
@ -533,14 +534,17 @@ hotspot_aot_classlinking = \
|
||||
-runtime/cds/appcds/customLoader/ParallelTestSingleFP.java \
|
||||
-runtime/cds/appcds/customLoader/SameNameInTwoLoadersTest.java \
|
||||
-runtime/cds/appcds/DumpClassListWithLF.java \
|
||||
-runtime/cds/appcds/dynamicArchive/ModulePath.java \
|
||||
-runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java \
|
||||
-runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java \
|
||||
-runtime/cds/appcds/dynamicArchive/LambdaForOldInfInBaseArchive.java \
|
||||
-runtime/cds/appcds/dynamicArchive/LambdaInBaseArchive.java \
|
||||
-runtime/cds/appcds/dynamicArchive/LambdasInTwoArchives.java \
|
||||
-runtime/cds/appcds/dynamicArchive/ModulePath.java \
|
||||
-runtime/cds/appcds/dynamicArchive/NestHostOldInf.java \
|
||||
-runtime/cds/appcds/dynamicArchive/OldClassAndInf.java \
|
||||
-runtime/cds/appcds/dynamicArchive/OldClassInBaseArchive.java \
|
||||
-runtime/cds/appcds/dynamicArchive/OldClassVerifierTrouble.java \
|
||||
-runtime/cds/appcds/dynamicArchive/RedefineCallerClassTest.java \
|
||||
-runtime/cds/appcds/HelloExtTest.java \
|
||||
-runtime/cds/appcds/javaldr/ExceptionDuringDumpAtObjectsInitPhase.java \
|
||||
-runtime/cds/appcds/javaldr/GCDuringDump.java \
|
||||
|
||||
@ -99,7 +99,6 @@ public class ExcludedClasses {
|
||||
if (runMode == RunMode.ASSEMBLY) {
|
||||
out.shouldNotMatch("aot,resolve.*archived field.*TestApp.Foo => TestApp.Foo.ShouldBeExcluded.f:I");
|
||||
} else if (runMode == RunMode.PRODUCTION) {
|
||||
out.shouldContain("check_verification_constraint: TestApp$Foo$Taz: TestApp$Foo$ShouldBeExcludedChild must be subclass of TestApp$Foo$ShouldBeExcluded");
|
||||
out.shouldContain("jdk.jfr.Event source: jrt:/jdk.jfr");
|
||||
out.shouldMatch("TestApp[$]Foo[$]ShouldBeExcluded source: .*/app.jar");
|
||||
out.shouldMatch("TestApp[$]Foo[$]ShouldBeExcludedChild source: .*/app.jar");
|
||||
@ -259,14 +258,9 @@ class TestApp {
|
||||
|
||||
static class Taz {
|
||||
static ShouldBeExcluded m() {
|
||||
// When verifying this method, we need to check the constraint that
|
||||
// ShouldBeExcluded must be a supertype of ShouldBeExcludedChild. This information
|
||||
// is checked by SystemDictionaryShared::check_verification_constraints() when the Taz
|
||||
// class is linked during the production run.
|
||||
//
|
||||
// Because ShouldBeExcluded is excluded from the AOT archive, it must be loaded
|
||||
// dynamically from app.jar inside SystemDictionaryShared::check_verification_constraints().
|
||||
// This must happen after the app class loader has been fully restored from the AOT cache.
|
||||
// Taz should be excluded from the AOT cache because it has a verification constraint that
|
||||
// "ShouldBeExcludedChild must be a subtype of ShouldBeExcluded", but ShouldBeExcluded is
|
||||
// excluded from the AOT cache.
|
||||
return new ShouldBeExcludedChild();
|
||||
}
|
||||
static void hotSpot4() {
|
||||
|
||||
38
test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldA.jasm
Normal file
38
test/hotspot/jtreg/runtime/cds/appcds/aotCache/OldA.jasm
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
super public class OldA
|
||||
version 49:0
|
||||
{
|
||||
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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
|
||||
* @summary Store old classes linked state in AOT cache as long as their verification constraints are not excluded.
|
||||
* @bug 8317269
|
||||
* @requires vm.cds.supports.aot.class.linking
|
||||
* @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
|
||||
* @build OldClass OldA OldClassWithVerifierConstraints OldClassWithExcludedVerifierConstraints
|
||||
* @build OldClassSupport
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
|
||||
* AppUsesOldClass MyIntf OldClass OldA NewB MyEvent MyEvent2
|
||||
* OldClassWithVerifierConstraints
|
||||
* OldClassWithExcludedVerifierConstraints
|
||||
* NewClassWithExcludedVerifierConstraints
|
||||
* @run driver OldClassSupport
|
||||
*/
|
||||
|
||||
import jdk.jfr.Event;
|
||||
import jdk.test.lib.cds.CDSAppTester;
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
public class OldClassSupport {
|
||||
static final String appJar = ClassFileInstaller.getJarPath("app.jar");
|
||||
static final String mainClass = "AppUsesOldClass";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Tester tester = new Tester();
|
||||
tester.run(new String[] {"AOT", "--two-step-training"} );
|
||||
}
|
||||
|
||||
static class Tester extends CDSAppTester {
|
||||
public Tester() {
|
||||
super(mainClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String classpath(RunMode runMode) {
|
||||
return appJar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] vmArgs(RunMode runMode) {
|
||||
return new String[] {
|
||||
"-Xlog:aot+class=debug",
|
||||
"-Xlog:aot+resolve=trace",
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] appCommandLine(RunMode runMode) {
|
||||
return new String[] {"-Xlog:cds+class=debug", mainClass};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkExecution(OutputAnalyzer out, RunMode runMode) {
|
||||
Class[] included = {
|
||||
OldClass.class,
|
||||
OldA.class,
|
||||
NewB.class,
|
||||
OldClassWithVerifierConstraints.class,
|
||||
};
|
||||
|
||||
Class[] excluded = {
|
||||
OldClassWithExcludedVerifierConstraints.class,
|
||||
NewClassWithExcludedVerifierConstraints.class,
|
||||
};
|
||||
|
||||
|
||||
if (runMode == RunMode.TRAINING) {
|
||||
shouldInclude(out, false, included);
|
||||
shouldNotInclude(out, excluded);
|
||||
shouldSkip(out, excluded);
|
||||
} else if (runMode == RunMode.ASSEMBLY) {
|
||||
shouldInclude(out, true, included);
|
||||
shouldNotInclude(out, excluded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shouldInclude(OutputAnalyzer out, boolean linked, Class[] classes) {
|
||||
for (Class c : classes) {
|
||||
out.shouldMatch("aot,class.* = 0x.* app *" + c.getName() + (linked ? " .*aot-linked" : ""));
|
||||
}
|
||||
}
|
||||
|
||||
static void shouldNotInclude(OutputAnalyzer out, Class[] classes) {
|
||||
for (Class c : classes) {
|
||||
out.shouldNotMatch("aot,class.* = 0x.* app *" + c.getName());
|
||||
}
|
||||
}
|
||||
|
||||
static void shouldSkip(OutputAnalyzer out, Class[] classes) {
|
||||
for (Class c : classes) {
|
||||
out.shouldMatch("Skipping " + c.getName() + ": verification constraint .* is excluded");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AppUsesOldClass {
|
||||
public static void main(String args[]) {
|
||||
System.out.println("Old Class Instance: " + new OldClass());
|
||||
|
||||
System.out.println(get_OldA_from_NewB());
|
||||
System.out.println(OldClassWithVerifierConstraints.get_OldA_from_NewB());
|
||||
System.out.println(OldClassWithExcludedVerifierConstraints.get_Event_from_MyEvent());
|
||||
System.out.println(NewClassWithExcludedVerifierConstraints.get_MyEvent_from_MyEvent2());
|
||||
System.out.println(new MyEvent());
|
||||
|
||||
// OldClassWithExcludedVerifierConstraints should still be excluded even it has been used
|
||||
// in a lambda expression during the training run.
|
||||
run((OldClassWithExcludedVerifierConstraints x) -> {
|
||||
System.out.println(x);
|
||||
});
|
||||
}
|
||||
|
||||
static OldA get_OldA_from_NewB() {
|
||||
return new NewB();
|
||||
}
|
||||
|
||||
static void run(MyIntf intf) {
|
||||
intf.function(new OldClassWithExcludedVerifierConstraints());
|
||||
}
|
||||
}
|
||||
|
||||
interface MyIntf {
|
||||
public void function(OldClassWithExcludedVerifierConstraints x);
|
||||
}
|
||||
|
||||
class NewB extends OldA {}
|
||||
|
||||
class MyEvent extends Event {}
|
||||
class MyEvent2 extends MyEvent {}
|
||||
|
||||
class NewClassWithExcludedVerifierConstraints {
|
||||
static MyEvent get_MyEvent_from_MyEvent2() {
|
||||
return new MyEvent2();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// This old class has a verification constraint that "MyEvent must be a subtype of Event". However,
|
||||
// Event and all of its subtypes are excluded from the AOT cache, so this class must also be excluded.
|
||||
|
||||
super public class OldClassWithExcludedVerifierConstraints
|
||||
version 49:0
|
||||
{
|
||||
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
static Method get_Event_from_MyEvent:"()Ljdk/jfr/Event;"
|
||||
stack 2 locals 0
|
||||
{
|
||||
new class MyEvent;
|
||||
dup;
|
||||
invokespecial Method MyEvent."<init>":"()V";
|
||||
areturn;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
// This old class as a verification constraint that "NewB must be a subtype of OldA". Since both
|
||||
// OldA and NewB are not excluded, then this class should be cached in aot-linked state.
|
||||
|
||||
super public class OldClassWithVerifierConstraints
|
||||
version 49:0
|
||||
{
|
||||
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
static Method get_OldA_from_NewB:"()LOldA;"
|
||||
stack 2 locals 0
|
||||
{
|
||||
new class NewB;
|
||||
dup;
|
||||
invokespecial Method NewB."<init>":"()V";
|
||||
areturn;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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
|
||||
* @bug 8317269
|
||||
* @requires vm.cds
|
||||
* @requires vm.cds.supports.aot.class.linking
|
||||
* @summary Test for verification of classes that are aot-linked
|
||||
* @library /test/jdk/lib/testlibrary
|
||||
* /test/lib
|
||||
* /test/hotspot/jtreg/runtime/cds/appcds
|
||||
* /test/hotspot/jtreg/runtime/cds/appcds/test-classes
|
||||
* @build GoodOldClass
|
||||
* BadOldClass BadOldClass2 BadOldClass3 BadOldClass4
|
||||
* BadNewClass BadNewClass2 BadNewClass3 BadNewClass4
|
||||
* @build AOTClassLinkingVerification
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app1.jar
|
||||
* AOTClassLinkingVerificationApp
|
||||
* Unlinked UnlinkedSuper
|
||||
* BadOldClass
|
||||
* BadOldClass2
|
||||
* BadOldClass3
|
||||
* BadOldClass4
|
||||
* BadNewClass
|
||||
* BadNewClass2
|
||||
* BadNewClass3
|
||||
* BadNewClass4
|
||||
* GoodOldClass Vehicle Car
|
||||
* Util
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app2.jar
|
||||
* Foo NotFoo
|
||||
* UnlinkedSub
|
||||
* @run driver AOTClassLinkingVerification
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import jdk.test.lib.cds.CDSAppTester;
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.whitebox.WhiteBox;
|
||||
|
||||
public class AOTClassLinkingVerification {
|
||||
static final String app1Jar = ClassFileInstaller.getJarPath("app1.jar");
|
||||
static final String app2Jar = ClassFileInstaller.getJarPath("app2.jar");
|
||||
static final String wbJar = TestCommon.getTestJar("WhiteBox.jar");
|
||||
static final String bootAppendWhiteBox = "-Xbootclasspath/a:" + wbJar;
|
||||
static final String mainClass = AOTClassLinkingVerificationApp.class.getName();
|
||||
|
||||
static class Tester extends CDSAppTester {
|
||||
public Tester(String testName) {
|
||||
super(testName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] vmArgs(RunMode runMode) {
|
||||
if (runMode == RunMode.TRAINING ||
|
||||
runMode == RunMode.ASSEMBLY) {
|
||||
return new String[] {
|
||||
"-XX:+AOTClassLinking", "-Xlog:cds+class=debug", bootAppendWhiteBox,
|
||||
};
|
||||
} else {
|
||||
return new String[] {
|
||||
"-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", bootAppendWhiteBox,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String classpath(RunMode runMode) {
|
||||
if (runMode == RunMode.TRAINING ||
|
||||
runMode == RunMode.ASSEMBLY) {
|
||||
return app1Jar;
|
||||
} else {
|
||||
return app1Jar + File.pathSeparator + app2Jar;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] appCommandLine(RunMode runMode) {
|
||||
if (runMode == RunMode.TRAINING ||
|
||||
runMode == RunMode.ASSEMBLY) {
|
||||
return new String[] {
|
||||
"AOTClassLinkingVerificationApp", app1Jar, "ASSEMBLY"
|
||||
};
|
||||
} else {
|
||||
return new String[] {
|
||||
"AOTClassLinkingVerificationApp", app1Jar, "PRODUCTION"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
|
||||
if (runMode == RunMode.TRAINING) {
|
||||
out.shouldContain("Preload Warning: Verification failed for BadNewClass");
|
||||
out.shouldContain("Preload Warning: Verification failed for BadNewClass2");
|
||||
out.shouldContain("Preload Warning: Verification failed for BadNewClass3");
|
||||
out.shouldContain("Preload Warning: Verification failed for BadNewClass4");
|
||||
out.shouldContain("Preload Warning: Verification failed for BadOldClass");
|
||||
out.shouldContain("Preload Warning: Verification failed for BadOldClass2");
|
||||
out.shouldContain("Preload Warning: Verification failed for BadOldClass3");
|
||||
out.shouldContain("Preload Warning: Verification failed for BadOldClass4");
|
||||
out.shouldContain("Preload Warning: Verification failed for Unlinked");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Dump without app2.jar so:
|
||||
// - Unlinked can be resolved, but UnlinkedSuper UnlinkedSub cannot be resolved,
|
||||
// so Unlinked cannot be verified at dump time.
|
||||
// - BadOldClass2 can be resolved, but Foo and NotFoo cannot be resolved,
|
||||
// so BadOldClass2 cannot be verified at dump time.
|
||||
// - BadNewClass2 can be resolved, but Foo and NotFoo cannot be resolved,
|
||||
// so BadNewClass2 cannot be verified at dump time.
|
||||
Tester t1 = new Tester("verification-aot-linked-classes");
|
||||
t1.run("AOT");
|
||||
}
|
||||
}
|
||||
|
||||
class AOTClassLinkingVerificationApp {
|
||||
static WhiteBox wb = WhiteBox.getWhiteBox();
|
||||
static ClassLoader classLoader = AOTClassLinkingVerificationApp.class.getClassLoader();
|
||||
static File app1Jar;
|
||||
static boolean isProduction;
|
||||
public static void main(String[] args) throws Exception {
|
||||
app1Jar = new File(args[0]);
|
||||
isProduction = args[1].equals("PRODUCTION");
|
||||
if (isProduction) {
|
||||
assertNotShared(UnlinkedSub.class);
|
||||
assertShared(UnlinkedSuper.class);
|
||||
assertNotShared(Unlinked.class); // failed verification during dump time
|
||||
assertNotShared(Foo.class);
|
||||
assertNotShared(NotFoo.class);
|
||||
}
|
||||
String s = null;
|
||||
try {
|
||||
s = Unlinked.doit();
|
||||
} catch (NoClassDefFoundError ncdfe) {
|
||||
// UnlinkedSub is in app2Jar but only app1Jar is used during training
|
||||
// and assembly phases. So NoClassDefFoundError is expected during
|
||||
// during training and assembly phases.
|
||||
if (isProduction) {
|
||||
throw ncdfe;
|
||||
}
|
||||
}
|
||||
if (isProduction && !s.equals("heyhey")) {
|
||||
throw new RuntimeException("Unlinked.doit() returns wrong result: " + s);
|
||||
}
|
||||
|
||||
// ===============================================================================
|
||||
|
||||
checkSimpleBadClass("BadOldClass");
|
||||
|
||||
Class cls_BadOldClass2 = Class.forName("BadOldClass2", false, classLoader);
|
||||
if (isProduction) {
|
||||
assertNotShared(cls_BadOldClass2); // failed verification during dump time
|
||||
}
|
||||
try {
|
||||
cls_BadOldClass2.newInstance();
|
||||
throw new RuntimeException("BadOldClass2 cannot be verified");
|
||||
} catch (NoClassDefFoundError ncdfe) {
|
||||
// BadOldClass2 loads Foo and NotFoo which is in app2Jar which is used
|
||||
// only in production run.
|
||||
if (isProduction) {
|
||||
throw ncdfe;
|
||||
}
|
||||
} catch (VerifyError expected) {}
|
||||
|
||||
checkSimpleBadClass("BadOldClass3");
|
||||
checkSimpleBadClass("BadOldClass4");
|
||||
|
||||
// ===============================================================================
|
||||
|
||||
checkSimpleBadClass("BadNewClass");
|
||||
|
||||
Class cls_BadNewClass2 = Class.forName("BadNewClass2", false, classLoader);
|
||||
if (isProduction) {
|
||||
assertNotShared(cls_BadNewClass2); // failed verification during dump time
|
||||
}
|
||||
try {
|
||||
cls_BadNewClass2.newInstance();
|
||||
throw new RuntimeException("BadNewClass2 cannot be verified");
|
||||
} catch (NoClassDefFoundError ncdfe) {
|
||||
// BadNewClass2 loads Foo and NotFoo which is in app2Jar which is used
|
||||
// only in production run.
|
||||
if (isProduction) {
|
||||
throw ncdfe;
|
||||
}
|
||||
} catch (VerifyError expected) {}
|
||||
|
||||
checkSimpleBadClass("BadNewClass3");
|
||||
checkSimpleBadClass("BadNewClass4");
|
||||
|
||||
// ===============================================================================
|
||||
|
||||
if (isProduction) {
|
||||
assertAlreadyLoaded("Vehicle");
|
||||
assertAlreadyLoaded("Car");
|
||||
assertAlreadyLoaded("GoodOldClass");
|
||||
|
||||
assertShared(GoodOldClass.class);
|
||||
assertShared(Vehicle.class);
|
||||
assertShared(Car.class);
|
||||
}
|
||||
|
||||
GoodOldClass.doit(); // Should not fail
|
||||
}
|
||||
|
||||
static void checkSimpleBadClass(String className) throws Exception {
|
||||
Class cls = Class.forName(className, false, classLoader);
|
||||
if (isProduction) {
|
||||
assertNotShared(cls); // failed verification during dump time
|
||||
}
|
||||
try {
|
||||
cls.newInstance();
|
||||
throw new RuntimeException(className + " should not pass verification");
|
||||
} catch (VerifyError expected) {}
|
||||
}
|
||||
|
||||
static void assertShared(Class c) {
|
||||
if (!wb.isSharedClass(c)) {
|
||||
throw new RuntimeException("wb.isSharedClass(" + c.getName() + ") should be true");
|
||||
}
|
||||
}
|
||||
|
||||
static void assertNotShared(Class c) {
|
||||
if (wb.isSharedClass(c)) {
|
||||
throw new RuntimeException("wb.isSharedClass(" + c.getName() + ") should be false");
|
||||
}
|
||||
}
|
||||
|
||||
static void assertAlreadyLoaded(String className) throws Exception {
|
||||
byte[] data = Util.getClassFileFromJar(app1Jar, className);
|
||||
try {
|
||||
MethodHandles.lookup().defineClass(data);
|
||||
} catch (LinkageError e) {
|
||||
if (e.getMessage().contains("duplicate class definition for " + className)) {
|
||||
return;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException(className + " must have already been loaded");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Unlinked {
|
||||
static String doit() {
|
||||
UnlinkedSuper sup = new UnlinkedSub();
|
||||
return sup.doit();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class UnlinkedSuper {
|
||||
abstract String doit();
|
||||
}
|
||||
|
||||
class UnlinkedSub extends UnlinkedSuper {
|
||||
String doit() {
|
||||
return "heyhey";
|
||||
}
|
||||
}
|
||||
|
||||
class Foo {}
|
||||
class NotFoo {}
|
||||
|
||||
class Vehicle {}
|
||||
class Car extends Vehicle {}
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
super public class BadNewClass
|
||||
version 52:0
|
||||
{
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following method tries to return an Object as a String.
|
||||
* Verifier should fail.
|
||||
*/
|
||||
public Method doit:"()Ljava/lang/String;"
|
||||
stack 2 locals 1
|
||||
{
|
||||
new class java/lang/Object;
|
||||
dup;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
astore_0;
|
||||
aload_0;
|
||||
areturn; // tries to return an Object as a String
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
super public class BadNewClass2
|
||||
version 52:0
|
||||
{
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following method tries to return a NotFoo as a Foo.
|
||||
* Verifier should fail.
|
||||
*/
|
||||
public Method doit:"()LFoo;"
|
||||
stack 2 locals 1
|
||||
{
|
||||
new class NotFoo;
|
||||
dup;
|
||||
invokespecial Method NotFoo."<init>":"()V";
|
||||
astore_0;
|
||||
aload_0;
|
||||
areturn; // tries to return a NotFoo as a Foo
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
super public class BadNewClass3
|
||||
version 52:0
|
||||
{
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following method tries to return a String[][] as an Integer[].
|
||||
* Verifier should fail.
|
||||
*
|
||||
* Note: the arrays must have different number of dimensions, or else
|
||||
* the new verifier will just check the "bottom" classes. I.e., String and Integer
|
||||
*/
|
||||
public Method doit:"()[Ljava/lang/Integer;"
|
||||
stack 2 locals 1
|
||||
{
|
||||
iconst_1;
|
||||
iconst_1;
|
||||
multianewarray class "[[Ljava/lang/String;", 2;
|
||||
areturn;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
super public class BadNewClass4
|
||||
version 52:0
|
||||
{
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following method tries to return a String[][] as an Integer[][].
|
||||
* Verifier should fail.
|
||||
*
|
||||
* Note: the new verifier looks up the Integer and String types,
|
||||
* not the array types.
|
||||
*/
|
||||
public Method doit:"()[[Ljava/lang/Integer;"
|
||||
stack 2 locals 1
|
||||
{
|
||||
iconst_1;
|
||||
iconst_1;
|
||||
multianewarray class "[[Ljava/lang/String;", 2;
|
||||
areturn;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
super public class BadOldClass
|
||||
version 49:0
|
||||
{
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following method tries to return an Object as a String.
|
||||
* Verifier should fail.
|
||||
*/
|
||||
public Method doit:"()Ljava/lang/String;"
|
||||
stack 2 locals 1
|
||||
{
|
||||
new class java/lang/Object;
|
||||
dup;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
astore_0;
|
||||
aload_0;
|
||||
areturn; // tries to return an Object as a String
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
super public class BadOldClass2
|
||||
version 49:0
|
||||
{
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following method tries to return a NotFoo as a Foo.
|
||||
* Verifier should fail.
|
||||
*/
|
||||
public Method doit:"()LFoo;"
|
||||
stack 2 locals 1
|
||||
{
|
||||
new class NotFoo;
|
||||
dup;
|
||||
invokespecial Method NotFoo."<init>":"()V";
|
||||
astore_0;
|
||||
aload_0;
|
||||
areturn; // tries to return a NotFoo as a Foo
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
super public class BadOldClass3
|
||||
version 49:0
|
||||
{
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following method tries to return a String[][] as an Integer[].
|
||||
* Verifier should fail.
|
||||
*
|
||||
* Note: the arrays have different number of dimensions. The old verifier
|
||||
* rejects this immediately without looking up the String/Integer types.
|
||||
*/
|
||||
public Method doit:"()[Ljava/lang/Integer;"
|
||||
stack 2 locals 1
|
||||
{
|
||||
iconst_1;
|
||||
iconst_1;
|
||||
multianewarray class "[[Ljava/lang/String;", 2;
|
||||
areturn;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
super public class BadOldClass4
|
||||
version 49:0
|
||||
{
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following method tries to return a String[][] as an Integer[][].
|
||||
* Verifier should fail.
|
||||
*
|
||||
* Note: the old verifier looks up the Integer and String types,
|
||||
* not the array types.
|
||||
*/
|
||||
public Method doit:"()[[Ljava/lang/Integer;"
|
||||
stack 2 locals 1
|
||||
{
|
||||
iconst_1;
|
||||
iconst_1;
|
||||
multianewarray class "[[Ljava/lang/String;", 2;
|
||||
areturn;
|
||||
}
|
||||
|
||||
}
|
||||
@ -147,7 +147,7 @@ public class BulkLoaderTest {
|
||||
@Override
|
||||
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
|
||||
if (isAOTWorkflow() && runMode == RunMode.TRAINING) {
|
||||
out.shouldContain("Skipping BadOldClassA: Unlinked class not supported by AOTConfiguration");
|
||||
out.shouldContain("Skipping BadOldClassA: Failed verification");
|
||||
out.shouldContain("Skipping SimpleCusty: Duplicated unregistered class");
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
super public class GoodOldClass
|
||||
version 49:0
|
||||
{
|
||||
|
||||
public Method "<init>":"()V"
|
||||
stack 1 locals 1
|
||||
{
|
||||
aload_0;
|
||||
invokespecial Method java/lang/Object."<init>":"()V";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
public static Method doit:"()LVehicle;"
|
||||
stack 2 locals 1
|
||||
{
|
||||
new class Car;
|
||||
dup;
|
||||
invokespecial Method Car."<init>":"()V";
|
||||
astore_0;
|
||||
aload_0;
|
||||
areturn; // tries to return a Car as a Vehicle
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user