diff --git a/src/hotspot/share/classfile/loaderConstraints.hpp b/src/hotspot/share/classfile/loaderConstraints.hpp index c074ab3a250..0a6e4da2a5b 100644 --- a/src/hotspot/share/classfile/loaderConstraints.hpp +++ b/src/hotspot/share/classfile/loaderConstraints.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, 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 @@ -61,6 +61,7 @@ public: // Note: The main entry point for this module is via SystemDictionary. // SystemDictionary::check_signature_loaders(Symbol* signature, + // Klass* klass_being_linked, // Handle loader1, Handle loader2, // bool is_method, TRAPS) diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 15a6f0af9b8..bff53b20657 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -2297,8 +2297,8 @@ Klass* SystemDictionary::find_constrained_instance_or_array_klass( return klass; } - bool SystemDictionary::add_loader_constraint(Symbol* class_name, + Klass* klass_being_linked, Handle class_loader1, Handle class_loader2, Thread* THREAD) { @@ -2336,6 +2336,12 @@ bool SystemDictionary::add_loader_constraint(Symbol* class_name, InstanceKlass* klass2 = find_class(d_hash2, constraint_name, dictionary2); bool result = constraints()->add_entry(constraint_name, klass1, class_loader1, klass2, class_loader2); + if (Arguments::is_dumping_archive() && klass_being_linked != NULL && + !klass_being_linked->is_shared()) { + SystemDictionaryShared::record_linking_constraint(constraint_name, + InstanceKlass::cast(klass_being_linked), + class_loader1, class_loader2, THREAD); + } if (Signature::is_array(class_name)) { constraint_name->decrement_refcount(); } @@ -2457,7 +2463,7 @@ const char* SystemDictionary::find_nest_host_error(const constantPoolHandle& poo // called (perhaps via an override) from the supertype. // // -// SystemDictionary::check_signature_loaders(sig, l1, l2) +// SystemDictionary::check_signature_loaders(sig, klass_being_linked, l1, l2) // // Make sure all class components (including arrays) in the given // signature will be resolved to the same class in both loaders. @@ -2465,6 +2471,7 @@ const char* SystemDictionary::find_nest_host_error(const constantPoolHandle& poo // NULL if no constraint failed. No exception except OOME is thrown. // Arrays are not added to the loader constraint table, their elements are. Symbol* SystemDictionary::check_signature_loaders(Symbol* signature, + Klass* klass_being_linked, Handle loader1, Handle loader2, bool is_method, TRAPS) { // Nothing to do if loaders are the same. @@ -2478,7 +2485,7 @@ Symbol* SystemDictionary::check_signature_loaders(Symbol* signature, // Note: In the future, if template-like types can take // arguments, we will want to recognize them and dig out class // names hiding inside the argument lists. - if (!add_loader_constraint(sig, loader1, loader2, THREAD)) { + if (!add_loader_constraint(sig, klass_being_linked, loader1, loader2, THREAD)) { return sig; } } @@ -2486,7 +2493,6 @@ Symbol* SystemDictionary::check_signature_loaders(Symbol* signature, return NULL; } - Method* SystemDictionary::find_method_handle_intrinsic(vmIntrinsics::ID iid, Symbol* signature, TRAPS) { diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp index e3623fa85df..65882854539 100644 --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -494,10 +494,10 @@ public: // Note: java_lang_Class::primitive_type is the inverse of java_mirror // Check class loader constraints - static bool add_loader_constraint(Symbol* name, Handle loader1, + static bool add_loader_constraint(Symbol* name, Klass* klass_being_linked, Handle loader1, Handle loader2, TRAPS); - static Symbol* check_signature_loaders(Symbol* signature, Handle loader1, - Handle loader2, bool is_method, TRAPS); + static Symbol* check_signature_loaders(Symbol* signature, Klass* klass_being_linked, + Handle loader1, Handle loader2, bool is_method, TRAPS); // JSR 292 // find a java.lang.invoke.MethodHandle.invoke* method for a given signature @@ -656,7 +656,12 @@ protected: public: static bool is_system_class_loader(oop class_loader); static bool is_platform_class_loader(oop class_loader); - + static bool is_boot_class_loader(oop class_loader) { return class_loader == NULL; } + static bool is_builtin_class_loader(oop class_loader) { + return is_boot_class_loader(class_loader) || + is_platform_class_loader(class_loader) || + is_system_class_loader(class_loader); + } // Returns TRUE if the method is a non-public member of class java.lang.Object. static bool is_nonpublic_Object_method(Method* m) { assert(m != NULL, "Unexpected NULL Method*"); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index f5078157e4e..3c9cae9f179 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -69,11 +69,29 @@ DEBUG_ONLY(bool SystemDictionaryShared::_no_class_loading_should_happen = false; class DumpTimeSharedClassInfo: public CHeapObj { bool _excluded; public: - struct DTConstraint { + struct DTLoaderConstraint { + Symbol* _name; + char _loader_type1; + char _loader_type2; + DTLoaderConstraint(Symbol* name, char l1, char l2) : _name(name), _loader_type1(l1), _loader_type2(l2) { + _name->increment_refcount(); + } + DTLoaderConstraint() : _name(NULL), _loader_type1('0'), _loader_type2('0') {} + bool equals(const DTLoaderConstraint& t) { + return t._name == _name && + ((t._loader_type1 == _loader_type1 && t._loader_type2 == _loader_type2) || + (t._loader_type2 == _loader_type1 && t._loader_type1 == _loader_type2)); + } + }; + + struct DTVerifierConstraint { Symbol* _name; Symbol* _from_name; - DTConstraint() : _name(NULL), _from_name(NULL) {} - DTConstraint(Symbol* n, Symbol* fn) : _name(n), _from_name(fn) {} + DTVerifierConstraint() : _name(NULL), _from_name(NULL) {} + DTVerifierConstraint(Symbol* n, Symbol* fn) : _name(n), _from_name(fn) { + _name->increment_refcount(); + _from_name->increment_refcount(); + } }; InstanceKlass* _klass; @@ -81,8 +99,9 @@ public: int _id; int _clsfile_size; int _clsfile_crc32; - GrowableArray* _verifier_constraints; - GrowableArray* _verifier_constraint_flags; + GrowableArray* _verifier_constraints; + GrowableArray* _verifier_constraint_flags; + GrowableArray* _loader_constraints; DumpTimeSharedClassInfo() { _klass = NULL; @@ -93,16 +112,18 @@ public: _excluded = false; _verifier_constraints = NULL; _verifier_constraint_flags = NULL; + _loader_constraints = 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); bool is_builtin() { return SystemDictionaryShared::is_builtin(_klass); } - int num_constraints() { + int num_verifier_constraints() { if (_verifier_constraint_flags != NULL) { return _verifier_constraint_flags->length(); } else { @@ -110,15 +131,29 @@ public: } } + int num_loader_constraints() { + if (_loader_constraints != NULL) { + return _loader_constraints->length(); + } else { + return 0; + } + } + void metaspace_pointers_do(MetaspaceClosure* it) { it->push(&_klass); if (_verifier_constraints != NULL) { for (int i = 0; i < _verifier_constraints->length(); i++) { - DTConstraint* cons = _verifier_constraints->adr_at(i); + DTVerifierConstraint* cons = _verifier_constraints->adr_at(i); it->push(&cons->_name); it->push(&cons->_from_name); } } + if (_loader_constraints != NULL) { + for (int i = 0; i < _loader_constraints->length(); i++) { + DTLoaderConstraint* lc = _loader_constraints->adr_at(i); + it->push(&lc->_name); + } + } } void set_excluded() { @@ -202,19 +237,32 @@ public: int _clsfile_crc32; }; - // This is different than DumpTimeSharedClassInfo::DTConstraint. We use + // This is different than DumpTimeSharedClassInfo::DTVerifierConstraint. We use // u4 instead of Symbol* to save space on 64-bit CPU. - struct RTConstraint { + struct RTVerifierConstraint { u4 _name; u4 _from_name; + Symbol* name() { return (Symbol*)(SharedBaseAddress + _name);} + Symbol* from_name() { return (Symbol*)(SharedBaseAddress + _from_name); } + }; + + struct RTLoaderConstraint { + u4 _name; + char _loader_type1; + char _loader_type2; + Symbol* constraint_name() { + return (Symbol*)(SharedBaseAddress + _name); + } }; InstanceKlass* _klass; - int _num_constraints; + int _num_verifier_constraints; + int _num_loader_constraints; - // optional CrcInfo _crc; (only for UNREGISTERED classes) - // optional RTConstraint _verifier_constraints[_num_constraints] - // optional char _verifier_constraint_flags[_num_constraints] + // optional CrcInfo _crc; (only for UNREGISTERED classes) + // optional RTLoaderConstraint _loader_constraint_types[_num_loader_constraints] + // optional RTVerifierConstraint _verifier_constraints[_num_verifier_constraints] + // optional char _verifier_constraint_flags[_num_verifier_constraints] private: static size_t header_size_size() { @@ -227,34 +275,46 @@ private: return 0; } } - static size_t verifier_constraints_size(int num_constraints) { - return sizeof(RTConstraint) * num_constraints; + static size_t verifier_constraints_size(int num_verifier_constraints) { + return sizeof(RTVerifierConstraint) * num_verifier_constraints; } - static size_t verifier_constraint_flags_size(int num_constraints) { - return sizeof(char) * num_constraints; + static size_t verifier_constraint_flags_size(int num_verifier_constraints) { + return sizeof(char) * num_verifier_constraints; + } + static size_t loader_constraints_size(int num_loader_constraints) { + return sizeof(RTLoaderConstraint) * num_loader_constraints; } public: - static size_t byte_size(InstanceKlass* klass, int num_constraints) { + static size_t byte_size(InstanceKlass* klass, int num_verifier_constraints, int num_loader_constraints) { return header_size_size() + crc_size(klass) + - verifier_constraints_size(num_constraints) + - verifier_constraint_flags_size(num_constraints); + loader_constraints_size(num_loader_constraints) + + verifier_constraints_size(num_verifier_constraints) + + verifier_constraint_flags_size(num_verifier_constraints); } private: size_t crc_offset() const { return header_size_size(); } - size_t verifier_constraints_offset() const { + + size_t loader_constraints_offset() const { return crc_offset() + crc_size(_klass); } + size_t verifier_constraints_offset() const { + return loader_constraints_offset() + loader_constraints_size(_num_loader_constraints); + } size_t verifier_constraint_flags_offset() const { - return verifier_constraints_offset() + verifier_constraints_size(_num_constraints); + return verifier_constraints_offset() + verifier_constraints_size(_num_verifier_constraints); } - void check_constraint_offset(int i) const { - assert(0 <= i && i < _num_constraints, "sanity"); + void check_verifier_constraint_offset(int i) const { + assert(0 <= i && i < _num_verifier_constraints, "sanity"); + } + + void check_loader_constraint_offset(int i) const { + assert(0 <= i && i < _num_loader_constraints, "sanity"); } public: @@ -262,20 +322,30 @@ public: assert(crc_size(_klass) > 0, "must be"); return (CrcInfo*)(address(this) + crc_offset()); } - RTConstraint* verifier_constraints() { - assert(_num_constraints > 0, "sanity"); - return (RTConstraint*)(address(this) + verifier_constraints_offset()); + RTVerifierConstraint* verifier_constraints() { + assert(_num_verifier_constraints > 0, "sanity"); + return (RTVerifierConstraint*)(address(this) + verifier_constraints_offset()); } - RTConstraint* verifier_constraint_at(int i) { - check_constraint_offset(i); + RTVerifierConstraint* verifier_constraint_at(int i) { + check_verifier_constraint_offset(i); return verifier_constraints() + i; } char* verifier_constraint_flags() { - assert(_num_constraints > 0, "sanity"); + assert(_num_verifier_constraints > 0, "sanity"); return (char*)(address(this) + verifier_constraint_flags_offset()); } + RTLoaderConstraint* loader_constraints() { + assert(_num_loader_constraints > 0, "sanity"); + return (RTLoaderConstraint*)(address(this) + loader_constraints_offset()); + } + + RTLoaderConstraint* loader_constraint_at(int i) { + check_loader_constraint_offset(i); + return loader_constraints() + i; + } + static u4 object_delta_u4(Symbol* sym) { if (DynamicDumpSharedSpaces) { sym = DynamicArchive::original_to_target(sym); @@ -290,19 +360,29 @@ public: c->_clsfile_size = info._clsfile_size; c->_clsfile_crc32 = info._clsfile_crc32; } - _num_constraints = info.num_constraints(); - if (_num_constraints > 0) { - RTConstraint* constraints = verifier_constraints(); + _num_verifier_constraints = info.num_verifier_constraints(); + _num_loader_constraints = info.num_loader_constraints(); + int i; + if (_num_verifier_constraints > 0) { + RTVerifierConstraint* vf_constraints = verifier_constraints(); char* flags = verifier_constraint_flags(); - int i; - for (i = 0; i < _num_constraints; i++) { - constraints[i]._name = object_delta_u4(info._verifier_constraints->at(i)._name); - constraints[i]._from_name = object_delta_u4(info._verifier_constraints->at(i)._from_name); + for (i = 0; i < _num_verifier_constraints; i++) { + vf_constraints[i]._name = object_delta_u4(info._verifier_constraints->at(i)._name); + vf_constraints[i]._from_name = object_delta_u4(info._verifier_constraints->at(i)._from_name); } - for (i = 0; i < _num_constraints; i++) { + for (i = 0; i < _num_verifier_constraints; i++) { flags[i] = info._verifier_constraint_flags->at(i); } } + + if (_num_loader_constraints > 0) { + RTLoaderConstraint* ld_constraints = loader_constraints(); + for (i = 0; i < _num_loader_constraints; i++) { + ld_constraints[i]._name = object_delta_u4(info._loader_constraints->at(i)._name); + ld_constraints[i]._loader_type1 = info._loader_constraints->at(i)._loader_type1; + ld_constraints[i]._loader_type2 = info._loader_constraints->at(i)._loader_type2; + } + } if (DynamicDumpSharedSpaces) { _klass = DynamicArchive::original_to_target(info._klass); } @@ -314,15 +394,8 @@ public: crc()->_clsfile_crc32 == clsfile_crc32; } - Symbol* get_constraint_name(int i) { - return (Symbol*)(SharedBaseAddress + verifier_constraint_at(i)->_name); - } - Symbol* get_constraint_from_name(int i) { - return (Symbol*)(SharedBaseAddress + verifier_constraint_at(i)->_from_name); - } - - char get_constraint_flag(int i) { - check_constraint_offset(i); + char verifier_constraint_flag(int i) { + check_verifier_constraint_offset(i); return verifier_constraint_flags()[i]; } @@ -386,7 +459,6 @@ oop SystemDictionaryShared::shared_jar_manifest(int index) { return _shared_jar_manifests->obj_at(index); } - Handle SystemDictionaryShared::get_shared_jar_manifest(int shared_path_index, TRAPS) { Handle manifest ; if (shared_jar_manifest(shared_path_index) == NULL) { @@ -1034,7 +1106,7 @@ void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) { } if (p->_verifier_constraints != NULL) { for (int i = 0; i < p->_verifier_constraints->length(); i++) { - DumpTimeSharedClassInfo::DTConstraint constraint = p->_verifier_constraints->at(i); + DumpTimeSharedClassInfo::DTVerifierConstraint constraint = p->_verifier_constraints->at(i); if (constraint._name != NULL ) { constraint._name->decrement_refcount(); } @@ -1042,11 +1114,21 @@ void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) { constraint._from_name->decrement_refcount(); } } - FREE_C_HEAP_ARRAY(DTConstraint, p->_verifier_constraints); + FREE_C_HEAP_ARRAY(DumpTimeSharedClassInfo::DTVerifierConstraint, p->_verifier_constraints); p->_verifier_constraints = NULL; + FREE_C_HEAP_ARRAY(char, p->_verifier_constraint_flags); + p->_verifier_constraint_flags = NULL; + } + if (p->_loader_constraints != NULL) { + for (int i = 0; i < p->_loader_constraints->length(); i++) { + DumpTimeSharedClassInfo::DTLoaderConstraint ld = p->_loader_constraints->at(i); + if (ld._name != NULL) { + ld._name->decrement_refcount(); + } + } + FREE_C_HEAP_ARRAY(DumpTimeSharedClassInfo::DTLoaderConstraint, p->_loader_constraints); + p->_loader_constraints = NULL; } - FREE_C_HEAP_ARRAY(char, p->_verifier_constraint_flags); - p->_verifier_constraint_flags = NULL; _dumptime_table->remove(k); } @@ -1246,19 +1328,19 @@ bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbo void DumpTimeSharedClassInfo::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) { - _verifier_constraints = new(ResourceObj::C_HEAP, mtClass) GrowableArray(4, true, mtClass); + _verifier_constraints = new(ResourceObj::C_HEAP, mtClass) GrowableArray(4, true, mtClass); } if (_verifier_constraint_flags == NULL) { _verifier_constraint_flags = new(ResourceObj::C_HEAP, mtClass) GrowableArray(4, true, mtClass); } - GrowableArray* vc_array = _verifier_constraints; + GrowableArray* vc_array = _verifier_constraints; for (int i = 0; i < vc_array->length(); i++) { - DTConstraint* p = vc_array->adr_at(i); + DTVerifierConstraint* p = vc_array->adr_at(i); if (name == p->_name && from_name == p->_from_name) { return; } } - DTConstraint cons(name, from_name); + DTVerifierConstraint cons(name, from_name); vc_array->append(cons); GrowableArray* vcflags_array = _verifier_constraint_flags; @@ -1276,17 +1358,76 @@ void DumpTimeSharedClassInfo::add_verification_constraint(InstanceKlass* k, Symb } } +static char get_loader_type_by(oop loader) { + assert(SystemDictionary::is_builtin_class_loader(loader), "Must be built-in loader"); + if (SystemDictionary::is_boot_class_loader(loader)) { + return (char)ClassLoader::BOOT_LOADER; + } else if (SystemDictionary::is_platform_class_loader(loader)) { + return (char)ClassLoader::PLATFORM_LOADER; + } else { + assert(SystemDictionary::is_system_class_loader(loader), "Class loader mismatch"); + return (char)ClassLoader::APP_LOADER; + } +} + +static oop get_class_loader_by(char type) { + if (type == (char)ClassLoader::BOOT_LOADER) { + return (oop)NULL; + } else if (type == (char)ClassLoader::PLATFORM_LOADER) { + return SystemDictionary::java_platform_loader(); + } else { + assert (type == (char)ClassLoader::APP_LOADER, "Sanity"); + return SystemDictionary::java_system_loader(); + } +} + +void DumpTimeSharedClassInfo::record_linking_constraint(Symbol* name, Handle loader1, Handle loader2) { + assert(loader1 != loader2, "sanity"); + LogTarget(Info, class, loader, constraints) log; + if (_loader_constraints == NULL) { + _loader_constraints = new (ResourceObj::C_HEAP, mtClass) GrowableArray(4, true, mtClass); + } + char lt1 = get_loader_type_by(loader1()); + char lt2 = get_loader_type_by(loader2()); + DTLoaderConstraint lc(name, lt1, lt2); + for (int i = 0; i < _loader_constraints->length(); i++) { + DTLoaderConstraint dt = _loader_constraints->at(i); + if (lc.equals(dt)) { + if (log.is_enabled()) { + ResourceMark rm; + // Use loader[0]/loader[1] to be consistent with the logs in loaderConstraints.cpp + log.print("[CDS record loader constraint for class: %s constraint_name: %s loader[0]: %s loader[1]: %s already added]", + _klass->external_name(), name->as_C_string(), + ClassLoaderData::class_loader_data(loader1())->loader_name_and_id(), + ClassLoaderData::class_loader_data(loader2())->loader_name_and_id()); + } + return; + } + } + _loader_constraints->append(lc); + if (log.is_enabled()) { + ResourceMark rm; + // Use loader[0]/loader[1] to be consistent with the logs in loaderConstraints.cpp + log.print("[CDS record loader constraint for class: %s constraint_name: %s loader[0]: %s loader[1]: %s total %d]", + _klass->external_name(), name->as_C_string(), + ClassLoaderData::class_loader_data(loader1())->loader_name_and_id(), + ClassLoaderData::class_loader_data(loader2())->loader_name_and_id(), + _loader_constraints->length()); + } +} + void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass, TRAPS) { assert(!DumpSharedSpaces && UseSharedSpaces, "called at run time with CDS enabled only"); RunTimeSharedClassInfo* record = RunTimeSharedClassInfo::get_for(klass); - int length = record->_num_constraints; + int length = record->_num_verifier_constraints; if (length > 0) { for (int i = 0; i < length; i++) { - Symbol* name = record->get_constraint_name(i); - Symbol* from_name = record->get_constraint_from_name(i); - char c = record->get_constraint_flag(i); + RunTimeSharedClassInfo::RTVerifierConstraint* vc = record->verifier_constraint_at(i); + Symbol* name = vc->name(); + Symbol* from_name = vc->from_name(); + char c = record->verifier_constraint_flag(i); if (log_is_enabled(Trace, cds, verification)) { ResourceMark rm(THREAD); @@ -1316,6 +1457,109 @@ void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass } } +// Record class loader constraints that are checked inside +// InstanceKlass::link_class(), so that these can be checked quickly +// at runtime without laying out the vtable/itables. +void SystemDictionaryShared::record_linking_constraint(Symbol* name, InstanceKlass* klass, + Handle loader1, Handle loader2, TRAPS) { + // A linking constraint check is executed when: + // - klass extends or implements type S + // - klass overrides method S.M(...) with X.M + // - If klass defines the method M, X is + // the same as klass. + // - If klass does not define the method M, + // X must be a supertype of klass and X.M is + // a default method defined by X. + // - loader1 = X->class_loader() + // - loader2 = S->class_loader() + // - loader1 != loader2 + // - M's paramater(s) include an object type T + // We require that + // - whenever loader1 and loader2 try to + // resolve the type T, they must always resolve to + // the same InstanceKlass. + // NOTE: type T may or may not be currently resolved in + // either of these two loaders. The check itself does not + // try to resolve T. + oop klass_loader = klass->class_loader(); + assert(klass_loader != NULL, "should not be called for boot loader"); + assert(loader1 != loader2, "must be"); + + if (!is_system_class_loader(klass_loader) && + !is_platform_class_loader(klass_loader)) { + // If klass is loaded by system/platform loaders, we can + // guarantee that klass and S must be loaded by the same + // respective loader between dump time and run time, and + // the exact same check on (name, loader1, loader2) will + // be executed. Hence, we can cache this check and execute + // it at runtime without walking the vtable/itables. + // + // This cannot be guaranteed for classes loaded by other + // loaders, so we bail. + return; + } + + if (THREAD->is_VM_thread()) { + assert(DynamicDumpSharedSpaces, "must be"); + // We are re-laying out the vtable/itables of the *copy* of + // a class during the final stage of dynamic dumping. The + // linking constraints for this class has already been recorded. + return; + } + Arguments::assert_is_dumping_archive(); + DumpTimeSharedClassInfo* info = find_or_allocate_info_for(klass); + info->record_linking_constraint(name, loader1, loader2); +} + +// returns true IFF there's no need to re-initialize the i/v-tables for klass for +// the purpose of checking class loader constraints. +bool SystemDictionaryShared::check_linking_constraints(InstanceKlass* klass, TRAPS) { + assert(!DumpSharedSpaces && UseSharedSpaces, "called at run time with CDS enabled only"); + LogTarget(Info, class, loader, constraints) log; + if (klass->is_shared_boot_class()) { + // No class loader constraint check performed for boot classes. + return true; + } + if (klass->is_shared_platform_class() || klass->is_shared_app_class()) { + RunTimeSharedClassInfo* info = RunTimeSharedClassInfo::get_for(klass); + assert(info != NULL, "Sanity"); + if (info->_num_loader_constraints > 0) { + HandleMark hm; + for (int i = 0; i < info->_num_loader_constraints; i++) { + RunTimeSharedClassInfo::RTLoaderConstraint* lc = info->loader_constraint_at(i); + Symbol* name = lc->constraint_name(); + Handle loader1(THREAD, get_class_loader_by(lc->_loader_type1)); + Handle loader2(THREAD, get_class_loader_by(lc->_loader_type2)); + if (log.is_enabled()) { + ResourceMark rm(THREAD); + log.print("[CDS add loader constraint for class %s symbol %s loader[0] %s loader[1] %s", + klass->external_name(), name->as_C_string(), + ClassLoaderData::class_loader_data(loader1())->loader_name_and_id(), + ClassLoaderData::class_loader_data(loader2())->loader_name_and_id()); + } + if (!SystemDictionary::add_loader_constraint(name, klass, loader1, loader2, THREAD)) { + // Loader constraint violation has been found. The caller + // will re-layout the vtable/itables to produce the correct + // exception. + if (log.is_enabled()) { + log.print(" failed]"); + } + return false; + } + if (log.is_enabled()) { + log.print(" succeeded]"); + } + } + return true; // for all recorded constraints added successully. + } + } + if (log.is_enabled()) { + ResourceMark rm(THREAD); + log.print("[CDS has not recorded loader constraint for class %s]", klass->external_name()); + } + return false; +} + class EstimateSizeForArchive : StackObj { size_t _shared_class_info_size; int _num_builtin_klasses; @@ -1330,7 +1574,7 @@ public: bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { if (!info.is_excluded()) { - size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_constraints()); + size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints()); _shared_class_info_size += align_up(byte_size, BytesPerWord); } return true; // keep on iterating @@ -1360,7 +1604,7 @@ public: bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) { if (!info.is_excluded() && info.is_builtin() == _is_builtin) { - size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_constraints()); + size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints()); RunTimeSharedClassInfo* record; record = (RunTimeSharedClassInfo*)MetaspaceShared::read_only_space_alloc(byte_size); record->init(info); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp index bd0b43d1983..f714104a9aa 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -285,6 +285,9 @@ public: TRAPS) NOT_CDS_RETURN; 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 bool check_linking_constraints(InstanceKlass* klass, TRAPS) NOT_CDS_RETURN_(false); + static void record_linking_constraint(Symbol* name, InstanceKlass* klass, + Handle loader1, Handle loader2, TRAPS) NOT_CDS_RETURN; static bool is_builtin(InstanceKlass* k) { return (k->shared_classpath_index() != UNREGISTERED_INDEX); } diff --git a/src/hotspot/share/interpreter/linkResolver.cpp b/src/hotspot/share/interpreter/linkResolver.cpp index cded39c86cb..d8e01e80d2e 100644 --- a/src/hotspot/share/interpreter/linkResolver.cpp +++ b/src/hotspot/share/interpreter/linkResolver.cpp @@ -664,7 +664,9 @@ void LinkResolver::check_method_loader_constraints(const LinkInfo& link_info, ResourceMark rm(THREAD); Symbol* failed_type_symbol = - SystemDictionary::check_signature_loaders(link_info.signature(), current_loader, + SystemDictionary::check_signature_loaders(link_info.signature(), + /*klass_being_linked*/ NULL, // We are not linking class + current_loader, resolved_loader, true, CHECK); if (failed_type_symbol != NULL) { Klass* current_class = link_info.current_klass(); @@ -700,6 +702,7 @@ void LinkResolver::check_field_loader_constraints(Symbol* field, Symbol* sig, ResourceMark rm(THREAD); // needed for check_signature_loaders Symbol* failed_type_symbol = SystemDictionary::check_signature_loaders(sig, + /*klass_being_linked*/ NULL, // We are not linking class ref_loader, sel_loader, false, CHECK); diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 6625bebfd5c..6fabc30001d 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -919,20 +919,22 @@ bool InstanceKlass::link_class_impl(TRAPS) { // fabricate new Method*s. // also does loader constraint checking // - // initialize_vtable and initialize_itable need to be rerun for - // a shared class if the class is not loaded by the NULL classloader. - ClassLoaderData * loader_data = class_loader_data(); - if (!(is_shared() && - loader_data->is_the_null_class_loader_data())) { + // initialize_vtable and initialize_itable need to be rerun + // for a shared class if + // 1) the class is loaded by custom class loader or + // 2) the class is loaded by built-in class loader but failed to add archived loader constraints + bool need_init_table = true; + if (is_shared() && SystemDictionaryShared::check_linking_constraints(this, THREAD)) { + need_init_table = false; + } + if (need_init_table) { vtable().initialize_vtable(true, CHECK_false); itable().initialize_itable(true, CHECK_false); } #ifdef ASSERT - else { - vtable().verify(tty, true); - // In case itable verification is ever added. - // itable().verify(tty, true); - } + vtable().verify(tty, true); + // In case itable verification is ever added. + // itable().verify(tty, true); #endif set_init_state(linked); if (JvmtiExport::should_post_class_prepare()) { diff --git a/src/hotspot/share/oops/klassVtable.cpp b/src/hotspot/share/oops/klassVtable.cpp index da0aa718e26..042f14056c1 100644 --- a/src/hotspot/share/oops/klassVtable.cpp +++ b/src/hotspot/share/oops/klassVtable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, 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 @@ -491,9 +491,9 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, const methodHand if (target_loader() != super_loader()) { ResourceMark rm(THREAD); Symbol* failed_type_symbol = - SystemDictionary::check_signature_loaders(signature, target_loader, - super_loader, true, - CHECK_(false)); + SystemDictionary::check_signature_loaders(signature, _klass, + target_loader, super_loader, + true, CHECK_(false)); if (failed_type_symbol != NULL) { stringStream ss; ss.print("loader constraint violation for class %s: when selecting " @@ -1230,6 +1230,7 @@ void klassItable::initialize_itable_for_interface(int method_table_offset, Insta ResourceMark rm(THREAD); Symbol* failed_type_symbol = SystemDictionary::check_signature_loaders(m->signature(), + _klass, method_holder_loader, interface_loader, true, CHECK); diff --git a/src/hotspot/share/runtime/handles.hpp b/src/hotspot/share/runtime/handles.hpp index 712d406e470..17d4bedb4f8 100644 --- a/src/hotspot/share/runtime/handles.hpp +++ b/src/hotspot/share/runtime/handles.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, 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 @@ -80,7 +80,9 @@ class Handle { oop operator -> () const { return non_null_obj(); } bool operator == (oop o) const { return obj() == o; } + bool operator != (oop o) const { return obj() != o; } bool operator == (const Handle& h) const { return obj() == h.obj(); } + bool operator != (const Handle& h) const { return obj() != h.obj(); } // Null checks bool is_null() const { return _handle == NULL; } diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index badb5f7e19b..0f084ab4750 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -339,7 +339,7 @@ void mutex_init() { #if INCLUDE_JVMTI def(CDSClassFileStream_lock , PaddedMutex , max_nonleaf, false, _safepoint_check_always); #endif - def(DumpTimeTable_lock , PaddedMutex , leaf, true, _safepoint_check_never); + def(DumpTimeTable_lock , PaddedMutex , leaf - 1, true, _safepoint_check_never); #endif // INCLUDE_CDS #if INCLUDE_JVMCI diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index d9a1319ef43..fe78b3b3bd3 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -323,6 +323,7 @@ hotspot_appcds_dynamic = \ -runtime/cds/appcds/cacheObject \ -runtime/cds/appcds/customLoader \ -runtime/cds/appcds/dynamicArchive \ + -runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java \ -runtime/cds/appcds/javaldr/ArrayTest.java \ -runtime/cds/appcds/javaldr/GCSharedStringsDuringDump.java \ -runtime/cds/appcds/javaldr/HumongousDuringDump.java \ diff --git a/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java new file mode 100644 index 00000000000..c21d03f461f --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2020, 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 + * @requires vm.cds + * @summary Test class loader constraint checks for archived classes (dynamic archive) + * @library /test/lib + * /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive + * @modules java.base/jdk.internal.misc + * jdk.httpserver + * @run driver DynamicLoaderConstraintsTest + */ + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import jdk.test.lib.Asserts; + +public class DynamicLoaderConstraintsTest extends DynamicArchiveTestBase { + static String mainClass = LoaderConstraintsApp.class.getName(); + static String appJar = null; + static String appClasses[] = { + mainClass, + HttpHandler.class.getName(), + HttpExchange.class.getName(), + Asserts.class.getName(), + MyHttpHandler.class.getName(), + MyHttpHandlerB.class.getName(), + MyHttpHandlerC.class.getName(), + MyClassLoader.class.getName() + }; + + public static void main(String[] args) throws Exception { + runTest(DynamicLoaderConstraintsTest::doTest); + } + + static void doTest() throws Exception { + appJar = ClassFileInstaller.writeJar("loader_constraints.jar", appClasses); + doTest(false); + doTest(true); + } + + /* + * errorInDump: + * true: Even when dumping the archive, execute the code that would cause + * LinkageError, to see how the VM can handle such error during + * dump time. + * false: At dump time, simply load all the necessary test classes without + * causing LinkageError. This ensures the test classes will be + * archived so we can test CDS's handling of loader constraints during + * run time. + */ + static void doTest(boolean errorInDump) throws Exception { + for (int i = 1; i <= 3; i++) { + String topArchiveName = getNewArchiveName(); + String testCase = Integer.toString(i); + String cmdLine[] = new String[] { + "-cp", appJar, + "--add-modules", + "java.base,jdk.httpserver", + "--add-exports", + "java.base/jdk.internal.misc=ALL-UNNAMED", + "-Xlog:class+load,class+loader+constraints", + mainClass, testCase + }; + + String[] dumpCmdLine = cmdLine; + if (!errorInDump) { + dumpCmdLine = TestCommon.concat(dumpCmdLine, "loadClassOnly"); + } + + dump(topArchiveName, dumpCmdLine).assertNormalExit(); + run(topArchiveName, cmdLine).assertNormalExit(); + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/LoaderConstraintsApp.java b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/LoaderConstraintsApp.java new file mode 100644 index 00000000000..5b81ca162c8 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/LoaderConstraintsApp.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2020, 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 com.sun.net.httpserver.HttpHandler; +import jdk.internal.misc.Unsafe; +import jdk.test.lib.Asserts; +import java.io.InputStream; +import java.net.URL; + + +// Test cases: +// +// 1. load HttpExchange in apploader, define HttpExchange in parent (platform) loader, +// then load MyHttpHandler => fail. +// 2. define HttpExchange in parent (platform) loader, load MyHttpHandler, +// then try to define HttpExchange in child (app) loader => fail. +// 3. no LinkageError should be throw when linking a class that does not override/implement any +// methods. +class LoaderConstraintsApp { + static void defineHttpExchangeWithAppLoader() throws Exception { + Unsafe unsafe = Unsafe.getUnsafe(); + URL url = new URL("jrt://modules/jdk.httpserver/com/sun/net/httpserver/HttpExchange.class"); + byte[] bytes; + try (InputStream is = url.openStream()) { + bytes = is.readAllBytes(); + } + Class fakeClass = unsafe.defineClass("com/sun/net/httpserver/HttpExchange", bytes, 0, bytes.length, + LoaderConstraintsApp.class.getClassLoader(), + LoaderConstraintsApp.class.getProtectionDomain()); + System.out.println("fake HttpExchange = " + fakeClass.hashCode()); + System.out.println("fake HttpExchange (loader) = " + fakeClass.getClassLoader()); + } + + static void resolveHttpExchangeInParentLoader(ClassLoader loader) throws Exception { + Class realClass = Class.forName("com.sun.net.httpserver.HttpExchange", false, loader); + System.out.println("real HttpExchange = " + realClass.hashCode()); + System.out.println("real HttpExchange (loader) = " + realClass.getClassLoader()); + } + + static void doTest(int k) throws Exception { + ClassLoader appLoader = LoaderConstraintsApp.class.getClassLoader(); + ClassLoader platformLoader = appLoader.getParent(); + if (k == 1) { + defineHttpExchangeWithAppLoader(); + // Resolve HttpExchange in parent loader (platform loader) - should be OK. + resolveHttpExchangeInParentLoader(platformLoader); + try { + // This must fail since the two loaders have resolved different versions of HttpExchange + HttpHandler h1 = new MyHttpHandler(); + throw new RuntimeException("Load HttpExchange with platform loader did not fail as expected"); + } catch (LinkageError e) { + System.out.println("Expected: " + e); + Asserts.assertTrue(e.getMessage().contains("loader constraint violation in interface itable initialization for class MyHttpHandler")); + e.printStackTrace(System.out); + } + } else if (k == 2) { + // Resolve HttpExchange in parent loader (platform loader) - this should succeed + resolveHttpExchangeInParentLoader(platformLoader); + + // Load MyHttpHandler in app loader -- this should succeed, but it should + // create a class loader constraint that app loader must resolve the same HttpExchange + // class as the platform loader + HttpHandler h2 = new MyHttpHandler(); + + // Try to resolve a different HttpExchange class in the app loader. It must fail + try { + defineHttpExchangeWithAppLoader(); + throw new RuntimeException("defineHttpExchangeWithAppLoader() did not fail as expected"); + } catch (LinkageError e) { + System.out.println("Expected: " + e); + e.printStackTrace(System.out); + } + } else if (k == 3) { + // Resolve a different HttpExchange in platform and app loaders + resolveHttpExchangeInParentLoader(platformLoader); + defineHttpExchangeWithAppLoader(); + + // MyHttpHandlerB should still link, as it doesn't override HttpHandler.handle(HttpExchange) + MyHttpHandlerB.touch(); + + MyClassLoader loader = new MyClassLoader(platformLoader, appLoader); + try { + // MyHttpHandlerC should link, as its loader (MyClassLoader) resolves the same + // HttpExchange as the platform loader. + Class C = loader.loadClass("MyHttpHandlerC"); + System.out.println("MyHttpHandlerC = " + C); + System.out.println("MyHttpHandlerC (loader) = " + C.getClassLoader()); + + HttpHandler handlerC = (HttpHandler)C.newInstance(); + try { + // If the following is executed during CDS dynamic dump, a loader constraint is checked when resolving + // the HttpHandler.handle(HttpExchange) method reference inside MyHttpHandlerB.test(). This constraint must + // not be saved into the CDS archive for MyHttpHandlerB, or it would prevent MyHttpHandlerB + // from being linked during runtime. + MyHttpHandlerB.test(handlerC); + throw new RuntimeException("MyHttpHandlerB.test() did not fail as expected"); + } catch (LinkageError e) { + System.out.println("Expected: " + e); + Asserts.assertTrue(e.getMessage().matches(".*constraint violation: when resolving interface method .*.HttpHandler.handle.*")); + e.printStackTrace(System.out); + } + } catch (Exception e) { + throw new RuntimeException("Unexpected exception", e); + } + } else { + // should not be other value + throw new RuntimeException("Wrong option specified k = " + k); + } + } + + public static void main(String... args) throws Throwable { + if (args.length < 1) { + // option of {1, 2} + throw new RuntimeException("Wrong number of arguments"); + } + + if (args.length >= 2 && "loadClassOnly".equals(args[1])) { + System.out.println("Loading: " + MyHttpHandler.class); + System.out.println("Loading: " + MyHttpHandlerB.class); + System.exit(0); + } + + int k = Integer.valueOf(args[0]); + if (k < 1 && k > 3) { + throw new RuntimeException("Arg is out of range [1,3] k = " + k); + } + + doTest(k); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/LoaderConstraintsTest.java b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/LoaderConstraintsTest.java new file mode 100644 index 00000000000..1cbe19dd5f6 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/LoaderConstraintsTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020, 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 + * @requires vm.cds + * @summary Test class loader constraint checks for archived classes + * @library /test/lib + * /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * @modules java.base/jdk.internal.misc + * jdk.httpserver + * @run driver LoaderConstraintsTest + */ + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import jdk.test.lib.Asserts; + +public class LoaderConstraintsTest { + static String mainClass = LoaderConstraintsApp.class.getName(); + static String appJar = null; + static String appClasses[] = { + mainClass, + HttpHandler.class.getName(), + HttpExchange.class.getName(), + Asserts.class.getName(), + MyHttpHandler.class.getName(), + MyHttpHandlerB.class.getName(), + MyHttpHandlerC.class.getName(), + MyClassLoader.class.getName() + }; + + static void doTest() throws Exception { + appJar = ClassFileInstaller.writeJar("loader_constraints.jar", appClasses); + TestCommon.dump(appJar, appClasses, "-Xlog:cds+load"); + String joptsMain[] = TestCommon.concat("-cp", appJar, + "-Xlog:cds", + "-Xlog:class+loader+constraints=debug", + "--add-exports", + "java.base/jdk.internal.misc=ALL-UNNAMED", + mainClass); + runWithArchive(joptsMain, "1"); + runWithArchive(joptsMain, "2"); + runWithArchive(joptsMain, "3"); + } + + static void runWithArchive(String[] optsMain, String arg) throws Exception { + String cmd[] = TestCommon.concat(optsMain, arg); + TestCommon.run(cmd).assertNormalExit(); + } + + public static void main(String... args) throws Exception { + doTest(); + } +} + diff --git a/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/MyClassLoader.java b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/MyClassLoader.java new file mode 100644 index 00000000000..cde9e75bcfc --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/MyClassLoader.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020, 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.io.InputStream; +import java.io.IOException; + +public class MyClassLoader extends ClassLoader { + ClassLoader parent; + ClassLoader appLoader; + public MyClassLoader(ClassLoader parent, ClassLoader appLoader) { + super(parent); + this.parent = parent; + this.appLoader = appLoader; + } + + public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + System.out.println("MyClassLoader: loadClass(\"" + name + "\", " + resolve + ")"); + Class c; + + if (name.equals("MyHttpHandlerC")) { + byte[] bytes; + try (InputStream is = appLoader.getResourceAsStream("MyHttpHandlerC.class")) { + bytes = is.readAllBytes(); + } catch (IOException e) { + throw new RuntimeException("Unexpected", e); + } + c = defineClass(name, bytes, 0, bytes.length); + } else { + c = super.loadClass(name, resolve); + } + System.out.println("MyClassLoader: loaded " + name + " = " + c); + return c; + } +} + diff --git a/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/MyHttpHandler.java b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/MyHttpHandler.java new file mode 100644 index 00000000000..350342aa69a --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/MyHttpHandler.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020, 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 com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; + +public class MyHttpHandler implements HttpHandler { + public void handle(HttpExchange exchange) {} +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/MyHttpHandlerB.java b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/MyHttpHandlerB.java new file mode 100644 index 00000000000..594606879e1 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/MyHttpHandlerB.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020, 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 com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import java.io.IOException; + +public abstract class MyHttpHandlerB implements HttpHandler { + // This class doesn't implement handle(), so it can be linked even + // if the App loader resolves a different HttpExchange than the Platform loader. + + /* public void handle(HttpExchange exchange) {} */ + + static void touch() { + + } + + static public void test(HttpHandler handler) throws IOException { + // This method call must fail to link. + handler.handle(null); + } +} + diff --git a/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/MyHttpHandlerC.java b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/MyHttpHandlerC.java new file mode 100644 index 00000000000..e743f5f52c1 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/MyHttpHandlerC.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020, 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 com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import java.io.IOException; + +public class MyHttpHandlerC implements HttpHandler { + public MyHttpHandlerC() {} + + // This class overrides handle(), but its loader (MyClassLoader) resolves the same + // HttpExchange as the platfom loader (whose handle() method is overidden), so when + // MyHttpHandlerC loaded it should be able to link + public void handle(HttpExchange exchange) { + throw new RuntimeException("MyHttpHandlerB.test() must not be able to invoke this method"); + } + + static public void test(HttpHandler handler) throws IOException { + // This method call must fail to link. + handler.handle(null); + } +} +