# HG changeset patch # User ccheung # Date 1590556643 0 # Wed May 27 05:17:23 2020 +0000 # Node ID 60e522647b99c659385d49cb2269953567eb131f # Parent 90b266a84c06f1b3dc0ed8767856793e8c1c357e [mq]: cds_lambda diff --git a/make/hotspot/symbols/symbols-unix b/make/hotspot/symbols/symbols-unix --- a/make/hotspot/symbols/symbols-unix +++ b/make/hotspot/symbols/symbols-unix @@ -142,6 +142,8 @@ JVM_Interrupt JVM_InvokeMethod JVM_IsArrayClass +JVM_IsCDSDumpingEnabled +JVM_IsCDSSharingEnabled JVM_IsConstructorIx JVM_IsHiddenClass JVM_IsInterface @@ -154,6 +156,7 @@ JVM_LatestUserDefinedLoader JVM_LoadLibrary JVM_LookupDefineClass +JVM_LookupLambdaProxyClassFromArchive JVM_MaxMemory JVM_MaxObjectInspectionAge JVM_MonitorNotify @@ -170,6 +173,7 @@ JVM_RawMonitorDestroy JVM_RawMonitorEnter JVM_RawMonitorExit +JVM_RegisterLambdaProxyClassForArchiving JVM_RegisterSignal JVM_ReleaseUTF JVM_ResumeThread diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -1251,10 +1251,16 @@ int path_index = ik->shared_classpath_index(); ClassLoaderData* loader_data = class_loader_data(class_loader); if (path_index < 0) { - // path_index < 0 indicates that the class is intended for a custom loader - // and should not be loaded by boot/platform/app loaders + // path_index < 0 indicates that + // 1) the class is intended for a custom loader and should not be loaded by boot/platform/app loaders + // or + // 2) the class is a hidden lambda proxy class which would be loaded by boot/platform/app loaders if (loader_data->is_builtin_class_loader_data()) { - return false; + if (!SystemDictionaryShared::is_hidden_lambda_proxy(ik)) { + return false; + } else { + return true; + } } else { return true; } @@ -1365,6 +1371,60 @@ return true; } +InstanceKlass* SystemDictionary::load_shared_lambda_proxy_class(InstanceKlass* ik, + Handle class_loader, + Handle protection_domain, + PackageEntry* pkg_entry, + bool initialize, + TRAPS) { + InstanceKlass* shared_nest_host = SystemDictionaryShared::get_shared_nest_host(ik); + assert(shared_nest_host->is_shared(), "nest host must be in CDS archive"); + Symbol* cn = shared_nest_host->name(); + Klass *s = resolve_or_fail(cn, class_loader, protection_domain, true, CHECK_NULL); + if (s != shared_nest_host) { + // The dynamically resolved nest_host is not the same as the one we used during dump time, + // so we cannot use ik. + return NULL; + } else { + assert(s->is_shared(), "must be"); + } + + // The lambda proxy class and its nest host have the same class loader and class loader data, + // as verified in SystemDictionaryShared::add_lambda_proxy_class() + assert(shared_nest_host->class_loader() == class_loader(), "mismatched class loader"); + assert(shared_nest_host->class_loader_data() == ClassLoaderData::class_loader_data(class_loader()), "mismatched class loader data"); + ik->set_nest_host(shared_nest_host, THREAD); + + InstanceKlass* loaded_ik = load_shared_class(ik, class_loader, protection_domain, NULL, pkg_entry, CHECK_NULL); + + assert(shared_nest_host->is_same_class_package(ik), + "lambda proxy class and its nest host must be in the same package"); + + EventClassLoad class_load_start_event; + { + MutexLocker mu_r(THREAD, Compile_lock); + + // Add to class hierarchy, initialize vtables, and do possible + // deoptimizations. + SystemDictionary::add_to_hierarchy(loaded_ik, CHECK_NULL); // No exception, but can block + // But, do not add to dictionary. + } + loaded_ik->link_class(CHECK_NULL); + // notify jvmti + if (JvmtiExport::should_post_class_load()) { + assert(THREAD->is_Java_thread(), "thread->is_Java_thread()"); + JvmtiExport::post_class_load((JavaThread *) THREAD, loaded_ik); + } + if (class_load_start_event.should_commit()) { + post_class_load_event(&class_load_start_event, loaded_ik, ClassLoaderData::class_loader_data(class_loader())); + } + + if (initialize) { + loaded_ik->initialize(CHECK_NULL); + } + return loaded_ik; +} + InstanceKlass* SystemDictionary::load_shared_class(InstanceKlass* ik, Handle class_loader, Handle protection_domain, @@ -1385,8 +1445,11 @@ return NULL; } - InstanceKlass* new_ik = KlassFactory::check_shared_class_file_load_hook( + InstanceKlass* new_ik = NULL; + if (!SystemDictionaryShared::is_hidden_lambda_proxy(ik)) { + new_ik = KlassFactory::check_shared_class_file_load_hook( ik, class_name, class_loader, protection_domain, cfs, CHECK_NULL); + } if (new_ik != NULL) { // The class is changed by CFLH. Return the new class. The shared class is // not used. @@ -1426,6 +1489,12 @@ // package was loaded. if (loader_data->is_the_null_class_loader_data()) { int path_index = ik->shared_classpath_index(); + if (path_index < 0) { + if (SystemDictionaryShared::is_hidden_lambda_proxy(ik)) { + InstanceKlass* nest_host = SystemDictionaryShared::get_shared_nest_host(ik); + path_index = nest_host->shared_classpath_index(); + } + } ik->set_classpath_index(path_index, THREAD); } diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -636,6 +636,12 @@ bool is_superclass, TRAPS); static bool check_shared_class_super_types(InstanceKlass* ik, Handle class_loader, Handle protection_domain, TRAPS); + static InstanceKlass* load_shared_lambda_proxy_class(InstanceKlass* ik, + Handle class_loader, + Handle protection_domain, + PackageEntry* pkg_entry, + bool initialize, + TRAPS); static InstanceKlass* load_shared_class(InstanceKlass* ik, Handle class_loader, Handle protection_domain, diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -39,6 +39,7 @@ #include "logging/log.hpp" #include "memory/allocation.hpp" #include "memory/archiveUtils.hpp" +#include "memory/dynamicArchive.hpp" #include "memory/filemap.hpp" #include "memory/heapShared.hpp" #include "memory/metadataFactory.hpp" @@ -46,7 +47,6 @@ #include "memory/oopFactory.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" -#include "memory/dynamicArchive.hpp" #include "oops/instanceKlass.hpp" #include "oops/klass.inline.hpp" #include "oops/objArrayOop.inline.hpp" @@ -101,6 +101,7 @@ int _clsfile_crc32; GrowableArray* _verifier_constraints; GrowableArray* _verifier_constraint_flags; + bool _is_archived_lambda_proxy; GrowableArray* _loader_constraints; DumpTimeSharedClassInfo() { @@ -112,6 +113,7 @@ _excluded = false; _verifier_constraints = NULL; _verifier_constraint_flags = NULL; + _is_archived_lambda_proxy = false; _loader_constraints = NULL; } @@ -242,6 +244,158 @@ } }; +class LambdaProxyClassKey { + template static void original_to_target(T& field) { + if (field != NULL) { + field = DynamicArchive::original_to_target(field); + ArchivePtrMarker::mark_pointer(&field); + } + } + + InstanceKlass* _caller_ik; + Symbol* _invoked_name; + Symbol* _invoked_type; + Symbol* _method_type; + Method* _member_method; + Symbol* _instantiated_method_type; + +public: + LambdaProxyClassKey(InstanceKlass* caller_ik, + Symbol* invoked_name, + Symbol* invoked_type, + Symbol* method_type, + Method* member_method, + Symbol* instantiated_method_type) : + _caller_ik(caller_ik), + _invoked_name(invoked_name), + _invoked_type(invoked_type), + _method_type(method_type), + _member_method(member_method), + _instantiated_method_type(instantiated_method_type) {} + + void original_to_target() { + original_to_target(_caller_ik); + original_to_target(_instantiated_method_type); + original_to_target(_invoked_name); + original_to_target(_invoked_type); + original_to_target(_member_method); + original_to_target(_method_type); + } + + bool equals(LambdaProxyClassKey const& other) const { + return _caller_ik == other._caller_ik && + _invoked_name == other._invoked_name && + _invoked_type == other._invoked_type && + _method_type == other._method_type && + _member_method == other._member_method && + _instantiated_method_type == other._instantiated_method_type; + } + + unsigned int hash() const { + return SystemDictionaryShared::hash_for_shared_dictionary(_caller_ik) + + SystemDictionaryShared::hash_for_shared_dictionary(_invoked_name) + + SystemDictionaryShared::hash_for_shared_dictionary(_invoked_type) + + SystemDictionaryShared::hash_for_shared_dictionary(_method_type) + + SystemDictionaryShared::hash_for_shared_dictionary(_instantiated_method_type); + } + + unsigned int dumptime_hash() const { + return primitive_hash(_caller_ik) + + primitive_hash(_invoked_name) + + primitive_hash(_invoked_type) + + primitive_hash(_method_type) + + primitive_hash(_instantiated_method_type); + } + + static inline unsigned int DUMPTIME_HASH(LambdaProxyClassKey const& key) { + return (key.dumptime_hash()); + } + + static inline bool DUMPTIME_EQUALS( + LambdaProxyClassKey const& k1, LambdaProxyClassKey const& k2) { + return (k1.equals(k2)); + } +}; + + +class DumpTimeLambdaProxyClassInfo { +public: + GrowableArray* _proxy_klass; + DumpTimeLambdaProxyClassInfo() : _proxy_klass(NULL) {} + void add_proxy_klass(InstanceKlass* proxy_klass) { + if (_proxy_klass == NULL) { + _proxy_klass = new (ResourceObj::C_HEAP, mtInternal)GrowableArray(5, true); + } + assert(_proxy_klass != NULL, "sanity"); + _proxy_klass->append(proxy_klass); + } +}; + +class RunTimeLambdaProxyClassInfo { + LambdaProxyClassKey _key; + InstanceKlass* _proxy_klass; +public: + RunTimeLambdaProxyClassInfo(LambdaProxyClassKey key, InstanceKlass* proxy_klass) : + _key(key), _proxy_klass(proxy_klass) {} + + InstanceKlass* proxy_klass() const { return _proxy_klass; } + + // Used by LambdaProxyClassDictionary to implement OffsetCompactHashtable::EQUALS + static inline bool EQUALS( + const RunTimeLambdaProxyClassInfo* value, LambdaProxyClassKey* key, int len_unused) { + return (value->_key.equals(*key)); + } + void init(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) { + _key = key; + _key.original_to_target(); + _proxy_klass = DynamicArchive::original_to_target(info._proxy_klass->at(0)); + ArchivePtrMarker::mark_pointer(&_proxy_klass); + } + + unsigned int hash() const { + return _key.hash(); + } +}; + +class LambdaProxyClassDictionary : public OffsetCompactHashtable< + LambdaProxyClassKey*, + const RunTimeLambdaProxyClassInfo*, + RunTimeLambdaProxyClassInfo::EQUALS> {}; + +LambdaProxyClassDictionary _lambda_proxy_class_dictionary; + +class DumpTimeLambdaProxyClassDictionary + : public ResourceHashtable { +public: + int _count; +}; + +DumpTimeLambdaProxyClassDictionary* _dumptime_lambda_proxy_class_dictionary = NULL; + +static void add_to_dump_time_lambda_proxy_class_dictionary(LambdaProxyClassKey key, + InstanceKlass* proxy_klass) { + if (_dumptime_lambda_proxy_class_dictionary == NULL) { + _dumptime_lambda_proxy_class_dictionary = + new (ResourceObj::C_HEAP, mtClass)DumpTimeLambdaProxyClassDictionary(); + } + DumpTimeLambdaProxyClassInfo* lambda_info = _dumptime_lambda_proxy_class_dictionary->get(key); + if (lambda_info == NULL) { + DumpTimeLambdaProxyClassInfo info; + info.add_proxy_klass(proxy_klass); + _dumptime_lambda_proxy_class_dictionary->put(key, info); + //lambda_info = _dumptime_lambda_proxy_class_dictionary->get(key); + //assert(lambda_info->_proxy_klass == proxy_klass, "must be"); // debug only -- remove + ++_dumptime_lambda_proxy_class_dictionary->_count; + } else { + lambda_info->add_proxy_klass(proxy_klass); + } +} + class RunTimeSharedClassInfo { public: struct CrcInfo { @@ -272,6 +426,7 @@ int _num_loader_constraints; // optional CrcInfo _crc; (only for UNREGISTERED classes) + // optional InstanceKlass* _nest_host // optional RTLoaderConstraint _loader_constraint_types[_num_loader_constraints] // optional RTVerifierConstraint _verifier_constraints[_num_verifier_constraints] // optional char _verifier_constraint_flags[_num_verifier_constraints] @@ -296,11 +451,19 @@ static size_t loader_constraints_size(int num_loader_constraints) { return sizeof(RTLoaderConstraint) * num_loader_constraints; } + static size_t nest_host_size(InstanceKlass* klass) { + if (klass->is_hidden()) { + return sizeof(InstanceKlass*); + } else { + return 0; + } + } public: static size_t byte_size(InstanceKlass* klass, int num_verifier_constraints, int num_loader_constraints) { return header_size_size() + crc_size(klass) + + nest_host_size(klass) + loader_constraints_size(num_loader_constraints) + verifier_constraints_size(num_verifier_constraints) + verifier_constraint_flags_size(num_verifier_constraints); @@ -311,8 +474,12 @@ return header_size_size(); } + size_t nest_host_offset() const { + return crc_offset() + crc_size(_klass); + } + size_t loader_constraints_offset() const { - return crc_offset() + crc_size(_klass); + return nest_host_offset() + nest_host_size(_klass); } size_t verifier_constraints_offset() const { return loader_constraints_offset() + loader_constraints_size(_num_loader_constraints); @@ -348,6 +515,18 @@ return (char*)(address(this) + verifier_constraint_flags_offset()); } + InstanceKlass** nest_host_addr() { + assert(_klass->is_hidden(), "sanity"); + return (InstanceKlass**)(address(this) + nest_host_offset()); + } + InstanceKlass* nest_host() { + return *nest_host_addr(); + } + void set_nest_host(InstanceKlass* k) { + *nest_host_addr() = k; + ArchivePtrMarker::mark_pointer((address*)nest_host_addr()); + } + RTLoaderConstraint* loader_constraints() { assert(_num_loader_constraints > 0, "sanity"); return (RTLoaderConstraint*)(address(this) + loader_constraints_offset()); @@ -396,6 +575,13 @@ } } if (DynamicDumpSharedSpaces) { + if (_klass->is_hidden()) { + Thread* THREAD = Thread::current(); + InstanceKlass* n_h = _klass->nest_host(THREAD); + n_h = DynamicArchive::original_to_target(n_h); + set_nest_host(n_h); + ArchivePtrMarker::mark_pointer(&n_h); + } _klass = DynamicArchive::original_to_target(info._klass); } ArchivePtrMarker::mark_pointer(&_klass); @@ -426,6 +612,7 @@ public: static RunTimeSharedClassInfo* get_for(InstanceKlass* klass) { + assert(klass->is_shared(), "don't call for non-shared class"); return *info_pointer_addr(klass); } static void set_for(InstanceKlass* klass, RunTimeSharedClassInfo* record) { @@ -1151,16 +1338,43 @@ return false; } +bool SystemDictionaryShared::is_registered_lambda_proxy_class(InstanceKlass* ik) { + DumpTimeSharedClassInfo* info = _dumptime_table->get(ik); + return (info != NULL) ? info->_is_archived_lambda_proxy && !ik->is_non_strong_hidden() : false; +} + +bool SystemDictionaryShared::is_in_shared_lambda_proxy_table(InstanceKlass* ik) { + assert(!DumpSharedSpaces && UseSharedSpaces, "called at run time with CDS enabled only"); + RunTimeSharedClassInfo* record = RunTimeSharedClassInfo::get_for(ik); + if (record != NULL && record->nest_host() != NULL) { + return true; + } else { + return false; + } +} + +bool SystemDictionaryShared::is_hidden_lambda_proxy(InstanceKlass* ik) { + assert(ik->is_shared(), "applicable to only a shared class"); + if (ik->is_hidden()) { + assert(is_in_shared_lambda_proxy_table(ik), "we don't archive other hidden classes"); + return true; + } else { + return false; + } +} + void SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) { ResourceMark rm; log_warning(cds)("Skipping %s: %s", k->name()->as_C_string(), reason); } bool SystemDictionaryShared::should_be_excluded(InstanceKlass* k) { - if (k->is_hidden() || k->is_unsafe_anonymous()) { - warn_excluded(k, "Hidden or Unsafe anonymous class"); - return true; // hidden and unsafe anonymous classes are not archived, skip - } + + if (k->is_unsafe_anonymous()) { + warn_excluded(k, "Unsafe anonymous class"); + return true; // unsafe anonymous classes are not archived, skip + } + if (k->is_in_error_state()) { warn_excluded(k, "In error state"); return true; @@ -1169,7 +1383,7 @@ warn_excluded(k, "Has been redefined"); return true; } - if (k->shared_classpath_index() < 0 && is_builtin(k)) { + if (!k->is_hidden() && k->shared_classpath_index() < 0 && is_builtin(k)) { // These are classes loaded from unsupported locations (such as those loaded by JVMTI native // agent during dump time). warn_excluded(k, "Unsupported location"); @@ -1222,6 +1436,11 @@ return true; } + if (k->is_hidden() && !is_registered_lambda_proxy_class(k)) { + warn_excluded(k, "Hidden class"); + return true; + } + Array* interfaces = k->local_interfaces(); int len = interfaces->length(); for (int i = 0; i < len; i++) { @@ -1244,8 +1463,12 @@ guarantee(info != NULL, "Class %s must be entered into _dumptime_table", name); guarantee(!info->is_excluded(), "Should not attempt to archive excluded class %s", name); if (is_builtin(k)) { + if (k->is_hidden()) { + assert(is_registered_lambda_proxy_class(k), "unexpected hidden class %s", name); + } guarantee(!k->is_shared_unregistered_class(), "Class loader type must be set for BUILTIN class %s", name); + } else { guarantee(k->is_shared_unregistered_class(), "Class loader type must not be set for UNREGISTERED class %s", name); @@ -1361,10 +1584,113 @@ if (log_is_enabled(Trace, cds, verification)) { ResourceMark rm; - log_trace(cds, verification)("add_verification_constraint: %s: %s must be subclass of %s [0x%x]", + log_trace(cds, 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); - } + name->as_klass_external_name(), c, vc_array->length(), vcflags_array->length()); + } +} + +void SystemDictionaryShared::add_lambda_proxy_class(InstanceKlass* caller_ik, + InstanceKlass* lambda_ik, + Symbol* invoked_name, + Symbol* invoked_type, + Symbol* method_type, + Method* member_method, + Symbol* instantiated_method_type) { + + assert(caller_ik->class_loader() == lambda_ik->class_loader(), "mismatched class loader"); + assert(caller_ik->class_loader_data() == lambda_ik->class_loader_data(), "mismatched class loader data"); + + MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); + + lambda_ik->assign_class_loader_type(); + + DumpTimeSharedClassInfo* info = _dumptime_table->get(lambda_ik); + if (info != NULL) { + // Set _is_archived_lambda_proxy in DumpTimeSharedClassInfo so that the lambda_ik + // won't be excluded during dumping of shared archive. See ExcludeDumpTimeSharedClasses. + info->_is_archived_lambda_proxy = true; + } + + LambdaProxyClassKey key(caller_ik, + invoked_name, + invoked_type, + method_type, + member_method, + instantiated_method_type); + add_to_dump_time_lambda_proxy_class_dictionary(key, lambda_ik); +} + +InstanceKlass* SystemDictionaryShared::get_shared_lambda_proxy_class(InstanceKlass* caller_ik, + Symbol* invoked_name, + Symbol* invoked_type, + Symbol* method_type, + Method* member_method, + Symbol* instantiated_method_type) { + MutexLocker ml(CDSLambda_lock, Mutex::_no_safepoint_check_flag); + LambdaProxyClassKey key(caller_ik, invoked_name, invoked_type, + method_type, member_method, instantiated_method_type); + const RunTimeLambdaProxyClassInfo* info = _lambda_proxy_class_dictionary.lookup(&key, key.hash(), 0); + InstanceKlass* proxy_klass = NULL; + if (info != NULL) { + InstanceKlass* curr_klass = info->proxy_klass(); + InstanceKlass* prev_klass = curr_klass; + if (curr_klass->lambda_proxy_is_available()) { + while (curr_klass->next_link() != NULL) { + prev_klass = curr_klass; + curr_klass = InstanceKlass::cast(curr_klass->next_link()); + } + assert(curr_klass->is_hidden(), "must be"); + assert(curr_klass->lambda_proxy_is_available(), "must be"); + + prev_klass->set_next_link(NULL); + proxy_klass = curr_klass; + proxy_klass->clear_lambda_proxy_is_available(); + if (log_is_enabled(Debug, cds)) { + ResourceMark rm; + log_debug(cds)("Loaded lambda proxy: %s", proxy_klass->external_name()); + } + } else { + if (log_is_enabled(Debug, cds)) { + ResourceMark rm; + log_debug(cds)("Used all archived lambda proxy classes for: %s %s%s", + caller_ik->external_name(), invoked_name->as_C_string(), invoked_type->as_C_string()); + } + } + } + return proxy_klass; +} + +InstanceKlass* SystemDictionaryShared::get_shared_nest_host(InstanceKlass* lambda_ik) { + assert(!DumpSharedSpaces && UseSharedSpaces, "called at run time with CDS enabled only"); + RunTimeSharedClassInfo* record = RunTimeSharedClassInfo::get_for(lambda_ik); + return record->nest_host(); +} + +InstanceKlass* SystemDictionaryShared::load_shared_lambda_proxy_class(InstanceKlass* lambda_ik, + InstanceKlass* caller_ik, + bool initialize, TRAPS) { + Handle class_loader(THREAD, caller_ik->class_loader()); + Handle protection_domain; + PackageEntry* pkg_entry = get_package_entry_from_class_name(class_loader, caller_ik->name()); + if (caller_ik->class_loader() != NULL) { + protection_domain = SystemDictionaryShared::init_security_info(class_loader, caller_ik, pkg_entry, CHECK_NULL); + } + + InstanceKlass* shared_nest_host = get_shared_nest_host(lambda_ik); + assert(shared_nest_host != NULL, "unexpected NULL _nest_host"); + + InstanceKlass* loaded_lambda = + SystemDictionary::load_shared_lambda_proxy_class(lambda_ik, class_loader, protection_domain, pkg_entry, initialize, CHECK_NULL); + + InstanceKlass* nest_host = caller_ik->nest_host(THREAD); + // The following ensures that the caller's nest host is the same as the lambda proxy's + // nest host recorded at dump time. + assert(nest_host->has_nest_member(caller_ik, THREAD) || + nest_host == caller_ik, "caller_ik failed nest member check"); + assert(nest_host == shared_nest_host, "mismatched nest host"); + + return loaded_lambda; } static char get_loader_type_by(oop loader) { @@ -1597,10 +1923,66 @@ size_t SystemDictionaryShared::estimate_size_for_archive() { EstimateSizeForArchive est; _dumptime_table->iterate(&est); - return est.total() + + size_t total_size = est.total() + CompactHashtableWriter::estimate_size(_dumptime_table->count_of(true)) + CompactHashtableWriter::estimate_size(_dumptime_table->count_of(false)); -} + if (_dumptime_lambda_proxy_class_dictionary != NULL) { + total_size += + (sizeof(RunTimeLambdaProxyClassInfo) * _dumptime_lambda_proxy_class_dictionary->_count) + + CompactHashtableWriter::estimate_size(_dumptime_lambda_proxy_class_dictionary->_count); + } else { + total_size += CompactHashtableWriter::estimate_size(0); + } + return total_size; +} + +class CopyLambdaProxyClassInfoToArchive : StackObj { + CompactHashtableWriter* _writer; +public: + CopyLambdaProxyClassInfoToArchive(CompactHashtableWriter* writer) + : _writer(writer) {} + bool do_entry(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) { + if (SystemDictionaryShared::is_excluded_class(info._proxy_klass->at(0))) { + return true; + } + ResourceMark rm; + log_info(cds,dynamic)("Archiving hidden %s", info._proxy_klass->at(0)->external_name()); + size_t byte_size = sizeof(RunTimeLambdaProxyClassInfo); + RunTimeLambdaProxyClassInfo* runtime_info = + (RunTimeLambdaProxyClassInfo*)MetaspaceShared::read_only_space_alloc(byte_size); + runtime_info->init(key, info); + unsigned int hash = runtime_info->hash(); // Fields in runtime_info->_key already point to target space. + u4 delta = MetaspaceShared::object_delta_u4(DynamicArchive::buffer_to_target(runtime_info)); + _writer->add(hash, delta); + return true; + } +}; + +class AdjustLambdaProxyClassInfo : StackObj { +public: + AdjustLambdaProxyClassInfo() {} + bool do_entry(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) { + if (SystemDictionaryShared::is_excluded_class(info._proxy_klass->at(0))) { + return true; + } + int len = info._proxy_klass->length(); + if (len > 1) { + for (int i = 0; i < len-1; i++) { + InstanceKlass* ok0 = info._proxy_klass->at(i+0); // this is original klass + InstanceKlass* ok1 = info._proxy_klass->at(i+1); // this is original klass + InstanceKlass* bk0 = DynamicArchive::original_to_buffer(ok0); + InstanceKlass* bk1 = DynamicArchive::original_to_buffer(ok1); + assert(bk0->next_link() == 0, "must be called after Klass::remove_unshareable_info()"); + assert(bk1->next_link() == 0, "must be called after Klass::remove_unshareable_info()"); + bk0->set_next_link(bk1); + bk1->set_lambda_proxy_is_available(); + ArchivePtrMarker::mark_pointer(bk0->next_link_addr()); + } + } + DynamicArchive::original_to_buffer(info._proxy_klass->at(0))->set_lambda_proxy_is_available(); + return true; + } +}; class CopySharedClassInfoToArchive : StackObj { CompactHashtableWriter* _writer; @@ -1630,7 +2012,11 @@ } else { delta = MetaspaceShared::object_delta_u4(record); } - _writer->add(hash, delta); + if (_is_builtin && info._klass->is_hidden()) { + // skip + } else { + _writer->add(hash, delta); + } if (log_is_enabled(Trace, cds, hashtables)) { ResourceMark rm; log_trace(cds,hashtables)("%s dictionary: %s", (_is_builtin ? "builtin" : "unregistered"), info._klass->external_name()); @@ -1643,6 +2029,15 @@ } }; +void SystemDictionaryShared::write_lambda_proxy_class_dictionary(LambdaProxyClassDictionary *dictionary) { + CompactHashtableStats stats; + dictionary->reset(); + CompactHashtableWriter writer(_dumptime_lambda_proxy_class_dictionary->_count, &stats); + CopyLambdaProxyClassInfoToArchive copy(&writer); + _dumptime_lambda_proxy_class_dictionary->iterate(©); + writer.dump(dictionary, "lambda proxy class dictionary"); +} + void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin, bool is_static_archive) { @@ -1662,6 +2057,16 @@ write_dictionary(&_dynamic_builtin_dictionary, true); write_dictionary(&_dynamic_unregistered_dictionary, false); } + if (_dumptime_lambda_proxy_class_dictionary != NULL) { + write_lambda_proxy_class_dictionary(&_lambda_proxy_class_dictionary); + } +} + +void SystemDictionaryShared::adjust_lambda_proxy_class_dictionary() { + if (_dumptime_lambda_proxy_class_dictionary != NULL) { + AdjustLambdaProxyClassInfo adjuster; + _dumptime_lambda_proxy_class_dictionary->iterate(&adjuster); + } } void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc, @@ -1672,6 +2077,7 @@ } else { _dynamic_builtin_dictionary.serialize_header(soc); _dynamic_unregistered_dictionary.serialize_header(soc); + _lambda_proxy_class_dictionary.serialize_header(soc); } } @@ -1706,6 +2112,7 @@ InstanceKlass* SystemDictionaryShared::find_builtin_class(Symbol* name) { const RunTimeSharedClassInfo* record = find_record(&_builtin_dictionary, &_dynamic_builtin_dictionary, name); if (record != NULL) { + assert(!record->_klass->is_hidden(), "hidden class cannot be looked up by name"); return record->_klass; } else { return NULL; @@ -1730,6 +2137,23 @@ } }; +class SharedLambdaDictionaryPrinter : StackObj { + outputStream* _st; + int _index; +public: + SharedLambdaDictionaryPrinter(outputStream* st) : _st(st), _index(0) {} + + void do_value(const RunTimeLambdaProxyClassInfo* record) { + ResourceMark rm; + _st->print_cr("%4d: %s", (_index++), record->proxy_klass()->external_name()); + Klass* k = record->proxy_klass()->next_link(); + while (k != NULL) { + _st->print_cr("%4d: %s", (_index++), k->external_name()); + k = k->next_link(); + } + } +}; + void SystemDictionaryShared::print_on(outputStream* st) { if (UseSharedSpaces) { st->print_cr("Shared Dictionary"); @@ -1739,6 +2163,11 @@ if (DynamicArchive::is_mapped()) { _dynamic_builtin_dictionary.iterate(&p); _unregistered_dictionary.iterate(&p); + if (!_lambda_proxy_class_dictionary.empty()) { + st->print_cr("Shared Lambda Dictionary"); + SharedLambdaDictionaryPrinter ldp(st); + _lambda_proxy_class_dictionary.iterate(&ldp); + } } } } @@ -1750,6 +2179,7 @@ if (DynamicArchive::is_mapped()) { _dynamic_builtin_dictionary.print_table_statistics(st, "Dynamic Builtin Shared Dictionary"); _dynamic_unregistered_dictionary.print_table_statistics(st, "Unregistered Shared Dictionary"); + _lambda_proxy_class_dictionary.print_table_statistics(st, "Lambda Shared Dictionary"); } } } diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp @@ -105,6 +105,7 @@ class ClassFileStream; class DumpTimeSharedClassInfo; class DumpTimeSharedClassTable; +class LambdaProxyClassDictionary; class RunTimeSharedClassInfo; class RunTimeSharedDictionary; @@ -178,7 +179,6 @@ TRAPS); static Handle get_shared_protection_domain(Handle class_loader, ModuleEntry* mod, TRAPS); - static Handle init_security_info(Handle class_loader, InstanceKlass* ik, PackageEntry* pkg_entry, TRAPS); static void atomic_set_array_index(objArrayOop array, int index, oop o) { // Benign race condition: array.obj_at(index) may already be filled in. @@ -213,13 +213,18 @@ static void write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin, bool is_static_archive = true); + static void write_lambda_proxy_class_dictionary(LambdaProxyClassDictionary* dictionary); static bool is_jfr_event_class(InstanceKlass *k); + static bool is_registered_lambda_proxy_class(InstanceKlass* ik); + static bool is_in_shared_lambda_proxy_table(InstanceKlass* ik); static void warn_excluded(InstanceKlass* k, const char* reason); static bool should_be_excluded(InstanceKlass* k); DEBUG_ONLY(static bool _no_class_loading_should_happen;) public: + static bool is_hidden_lambda_proxy(InstanceKlass* ik); + static Handle init_security_info(Handle class_loader, InstanceKlass* ik, PackageEntry* pkg_entry, TRAPS); static InstanceKlass* find_builtin_class(Symbol* class_name); static const RunTimeSharedClassInfo* find_record(RunTimeSharedDictionary* static_dict, @@ -285,6 +290,23 @@ 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 void add_lambda_proxy_class(InstanceKlass* caller_ik, + InstanceKlass* lambda_ik, + Symbol* invoked_name, + Symbol* invoked_type, + Symbol* method_type, + Method* member_method, + Symbol* instantiated_method_type) NOT_CDS_RETURN; + static InstanceKlass* get_shared_lambda_proxy_class(InstanceKlass* caller_ik, + Symbol* invoked_name, + Symbol* invoked_type, + Symbol* method_type, + Method* member_method, + Symbol* instantiated_method_type) NOT_CDS_RETURN_(NULL); + static InstanceKlass* get_shared_nest_host(InstanceKlass* lambda_ik) NOT_CDS_RETURN_(NULL); + static InstanceKlass* load_shared_lambda_proxy_class(InstanceKlass* lambda_ik, + InstanceKlass* caller_ik, + bool initialize, TRAPS) NOT_CDS_RETURN_(NULL); 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; @@ -297,6 +319,7 @@ static void dumptime_classes_do(class MetaspaceClosure* it); static size_t estimate_size_for_archive(); static void write_to_archive(bool is_static_archive = true); + static void adjust_lambda_proxy_class_dictionary(); static void serialize_dictionary_headers(class SerializeClosure* soc, bool is_static_archive = true); static void serialize_well_known_klasses(class SerializeClosure* soc); diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -176,6 +176,30 @@ JNIEXPORT void JNICALL JVM_InitializeFromArchive(JNIEnv* env, jclass cls); +JNIEXPORT void JNICALL +JVM_RegisterLambdaProxyClassForArchiving(JNIEnv* env, jclass caller, + jstring invokedName, + jobject invokedType, + jobject methodType, + jobject implMethodMember, + jobject instantiatedMethodType, + jclass lambdaProxyClass); + +JNIEXPORT jclass JNICALL +JVM_LookupLambdaProxyClassFromArchive(JNIEnv* env, jclass caller, + jstring invokedName, + jobject invokedType, + jobject methodType, + jobject implMethodMember, + jobject instantiatedMethodType, + jboolean initialize); + +JNIEXPORT jboolean JNICALL +JVM_IsCDSDumpingEnabled(JNIEnv* env); + +JNIEXPORT jboolean JNICALL +JVM_IsCDSSharingEnabled(JNIEnv* env); + JNIEXPORT jlong JNICALL JVM_GetRandomSeedForCDSDump(); diff --git a/src/hotspot/share/memory/dynamicArchive.cpp b/src/hotspot/share/memory/dynamicArchive.cpp --- a/src/hotspot/share/memory/dynamicArchive.cpp +++ b/src/hotspot/share/memory/dynamicArchive.cpp @@ -660,6 +660,11 @@ make_klasses_shareable(); { + log_info(cds)("Adjust lambda proxy class dictionary"); + SystemDictionaryShared::adjust_lambda_proxy_class_dictionary(); + } + + { log_info(cds)("Final relocation of pointers ... "); ResourceMark rm; PointerMarker marker(this); @@ -838,16 +843,7 @@ for (i = 0; i < count; i++) { InstanceKlass* ik = _klasses->at(i); - ClassLoaderData *cld = ik->class_loader_data(); - if (cld->is_boot_class_loader_data()) { - ik->set_shared_class_loader_type(ClassLoader::BOOT_LOADER); - } - else if (cld->is_platform_class_loader_data()) { - ik->set_shared_class_loader_type(ClassLoader::PLATFORM_LOADER); - } - else if (cld->is_system_class_loader_data()) { - ik->set_shared_class_loader_type(ClassLoader::APP_LOADER); - } + ik->assign_class_loader_type(); MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread::current(), ik); ik->remove_unshareable_info(); diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -414,13 +414,21 @@ // during dump time. We need to restore it back to an UnresolvedClass, // so that the proper class loading and initialization can happen // at runtime. - CPKlassSlot kslot = klass_slot_at(index); - int resolved_klass_index = kslot.resolved_klass_index(); - int name_index = kslot.name_index(); - assert(tag_at(name_index).is_symbol(), "sanity"); - resolved_klasses()->at_put(resolved_klass_index, NULL); - tag_at_put(index, JVM_CONSTANT_UnresolvedClass); - assert(klass_name_at(index) == symbol_at(name_index), "sanity"); + bool clear_it = true; + if (pool_holder()->is_hidden() && index == pool_holder()->this_class_index()) { + // All references to a hidden class's own field/methods are through this + // index. We cannot clear it. See comments in ClassFileParser::fill_instance_klass. + clear_it = false; + } + if (clear_it) { + CPKlassSlot kslot = klass_slot_at(index); + int resolved_klass_index = kslot.resolved_klass_index(); + int name_index = kslot.name_index(); + assert(tag_at(name_index).is_symbol(), "sanity"); + resolved_klasses()->at_put(resolved_klass_index, NULL); + tag_at_put(index, JVM_CONSTANT_UnresolvedClass); + assert(klass_name_at(index) == symbol_at(name_index), "sanity"); + } } } if (cache() != NULL) { diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -2536,6 +2536,19 @@ } } +void InstanceKlass::assign_class_loader_type() { + ClassLoaderData *cld = class_loader_data(); + if (cld->is_boot_class_loader_data()) { + set_shared_class_loader_type(ClassLoader::BOOT_LOADER); + } + else if (cld->is_platform_class_loader_data()) { + set_shared_class_loader_type(ClassLoader::PLATFORM_LOADER); + } + else if (cld->is_system_class_loader_data()) { + set_shared_class_loader_type(ClassLoader::APP_LOADER); + } +} + #if INCLUDE_JVMTI static void clear_all_breakpoints(Method* m) { m->clear_all_breakpoints(); diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp --- a/src/hotspot/share/oops/instanceKlass.hpp +++ b/src/hotspot/share/oops/instanceKlass.hpp @@ -364,6 +364,8 @@ void set_shared_class_loader_type(s2 loader_type); + void assign_class_loader_type(); + bool has_nonstatic_fields() const { return (_misc_flags & _misc_has_nonstatic_fields) != 0; } @@ -469,11 +471,9 @@ } bool is_record() const { return _record_components != NULL; } -private: // Called to verify that k is a member of this nest - does not look at k's nest-host bool has_nest_member(InstanceKlass* k, TRAPS) const; -public: // Used to construct informative IllegalAccessError messages at a higher level, // if there was an issue resolving or validating the nest host. // Returns NULL if there was no error. diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -176,7 +176,8 @@ // Flags of the current shared class. u2 _shared_class_flags; enum { - _has_raw_archived_mirror = 1 + _has_raw_archived_mirror = 1, + _archived_lambda_proxy_is_available = 2 }; #endif @@ -293,6 +294,7 @@ void set_next_link(Klass* k) { _next_link = k; } Klass* next_link() const { return _next_link; } // The next klass defined by the class loader. + Klass** next_link_addr() { return &_next_link; } // class loader data ClassLoaderData* class_loader_data() const { return _class_loader_data; } @@ -317,6 +319,17 @@ NOT_CDS(return false;) } + void set_lambda_proxy_is_available() { + CDS_ONLY(_shared_class_flags |= _archived_lambda_proxy_is_available;) + } + void clear_lambda_proxy_is_available() { + CDS_ONLY(_shared_class_flags &= ~_archived_lambda_proxy_is_available;) + } + bool lambda_proxy_is_available() const { + CDS_ONLY(return (_shared_class_flags & _archived_lambda_proxy_is_available) != 0;) + NOT_CDS(return false;) + } + // Obtain the module or package for this class virtual ModuleEntry* module() const = 0; virtual PackageEntry* package() const = 0; diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -26,6 +26,7 @@ #include "jvm.h" #include "classfile/classFileStream.hpp" #include "classfile/classLoader.hpp" +#include "classfile/classLoaderData.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/javaAssertions.hpp" #include "classfile/javaClasses.inline.hpp" @@ -41,6 +42,7 @@ #include "interpreter/bytecodeUtils.hpp" #include "jfr/jfrEvents.hpp" #include "logging/log.hpp" +#include "memory/dynamicArchive.hpp" #include "memory/heapShared.hpp" #include "memory/oopFactory.hpp" #include "memory/referenceType.hpp" @@ -3692,6 +3694,107 @@ HeapShared::initialize_from_archived_subgraph(k); JVM_END +JVM_ENTRY(void, JVM_RegisterLambdaProxyClassForArchiving(JNIEnv* env, + jclass caller, + jstring invokedName, + jobject invokedType, + jobject methodType, + jobject implMethodMember, + jobject instantiatedMethodType, + jclass lambdaProxyClass)) + JVMWrapper("JVM_RegisterLambdaProxyClassForArchiving"); + if (!DynamicDumpSharedSpaces) { + return; + } + + Klass* caller_k = java_lang_Class::as_Klass(JNIHandles::resolve(caller)); + InstanceKlass* caller_ik = InstanceKlass::cast(caller_k); + Klass* lambda_k = java_lang_Class::as_Klass(JNIHandles::resolve(lambdaProxyClass)); + InstanceKlass* lambda_ik = InstanceKlass::cast(lambda_k); + assert(lambda_ik->is_hidden(), "must be a hidden class"); + assert(!lambda_ik->is_non_strong_hidden(), "expected a strong hidden class"); + + Symbol* invoked_name = NULL; + if (invokedName != NULL) { + invoked_name = java_lang_String::as_symbol(JNIHandles::resolve_non_null(invokedName)); + } + Handle invoked_type_oop(THREAD, JNIHandles::resolve_non_null(invokedType)); + Symbol* invoked_type = java_lang_invoke_MethodType::as_signature(invoked_type_oop(), true); + + Handle method_type_oop(THREAD, JNIHandles::resolve_non_null(methodType)); + Symbol* method_type = java_lang_invoke_MethodType::as_signature(method_type_oop(), true); + + Handle impl_method_member_oop(THREAD, JNIHandles::resolve_non_null(implMethodMember)); + assert(java_lang_invoke_MemberName::is_method(impl_method_member_oop()), "must be"); + Method* m = java_lang_invoke_MemberName::vmtarget(impl_method_member_oop()); + + Handle instantiated_method_type_oop(THREAD, JNIHandles::resolve_non_null(instantiatedMethodType)); + Symbol* instantiated_method_type = java_lang_invoke_MethodType::as_signature(instantiated_method_type_oop(), true); + + SystemDictionaryShared::add_lambda_proxy_class(caller_ik, lambda_ik, invoked_name, invoked_type, + method_type, m, instantiated_method_type); + +JVM_END + +JVM_ENTRY(jclass, JVM_LookupLambdaProxyClassFromArchive(JNIEnv* env, + jclass caller, + jstring invokedName, + jobject invokedType, + jobject methodType, + jobject implMethodMember, + jobject instantiatedMethodType, + jboolean initialize)) + JVMWrapper("JVM_LookupLambdaProxyClassFromArchive"); + if (!DynamicArchive::is_mapped()) { + return NULL; + } + + if (invokedName == NULL || invokedType == NULL || methodType == NULL || + implMethodMember == NULL || instantiatedMethodType == NULL) { + return NULL; + } + + Klass* caller_k = java_lang_Class::as_Klass(JNIHandles::resolve(caller)); + InstanceKlass* caller_ik = InstanceKlass::cast(caller_k); + if (!caller_ik->is_shared()) { + // there won't be a shared lambda class if the caller_ik is not in the shared archive. + return NULL; + } + + Symbol* invoked_name = java_lang_String::as_symbol(JNIHandles::resolve_non_null(invokedName)); + Handle invoked_type_oop(THREAD, JNIHandles::resolve_non_null(invokedType)); + Symbol* invoked_type = java_lang_invoke_MethodType::as_signature(invoked_type_oop(), true); + + Handle method_type_oop(THREAD, JNIHandles::resolve_non_null(methodType)); + Symbol* method_type = java_lang_invoke_MethodType::as_signature(method_type_oop(), true); + + Handle impl_method_member_oop(THREAD, JNIHandles::resolve_non_null(implMethodMember)); + assert(java_lang_invoke_MemberName::is_method(impl_method_member_oop()), "must be"); + Method* m = java_lang_invoke_MemberName::vmtarget(impl_method_member_oop()); + + Handle instantiated_method_type_oop(THREAD, JNIHandles::resolve_non_null(instantiatedMethodType)); + Symbol* instantiated_method_type = java_lang_invoke_MethodType::as_signature(instantiated_method_type_oop(), true); + + InstanceKlass* lambda_ik = SystemDictionaryShared::get_shared_lambda_proxy_class(caller_ik, invoked_name, invoked_type, + method_type, m, instantiated_method_type); + jclass jcls = NULL; + if (lambda_ik != NULL) { + InstanceKlass* loaded_lambda = SystemDictionaryShared::load_shared_lambda_proxy_class(lambda_ik, caller_ik, initialize, THREAD); + jcls = loaded_lambda == NULL ? NULL : (jclass) JNIHandles::make_local(env, loaded_lambda->java_mirror()); + } + return jcls; +JVM_END + +JVM_ENTRY(jboolean, JVM_IsCDSDumpingEnabled(JNIEnv* env)) + JVMWrapper("JVM_IsCDSDumpingEnable"); + return DynamicDumpSharedSpaces; +JVM_END + +JVM_ENTRY(jboolean, JVM_IsCDSSharingEnabled(JNIEnv* env)) + JVMWrapper("JVM_IsCDSSharingEnable"); + return UseSharedSpaces; +JVM_END + JVM_ENTRY_NO_ENV(jlong, JVM_GetRandomSeedForCDSDump()) JVMWrapper("JVM_GetRandomSeedForCDSDump"); if (DumpSharedSpaces) { diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -149,6 +149,7 @@ Mutex* CDSClassFileStream_lock = NULL; #endif Mutex* DumpTimeTable_lock = NULL; +Mutex* CDSLambda_lock = NULL; #endif // INCLUDE_CDS #if INCLUDE_JVMCI @@ -342,6 +343,7 @@ def(CDSClassFileStream_lock , PaddedMutex , max_nonleaf, false, _safepoint_check_always); #endif def(DumpTimeTable_lock , PaddedMutex , leaf - 1, true, _safepoint_check_never); + def(CDSLambda_lock , PaddedMutex , leaf, true, _safepoint_check_never); #endif // INCLUDE_CDS #if INCLUDE_JVMCI diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -128,6 +128,7 @@ extern Mutex* CDSClassFileStream_lock; // FileMapInfo::open_stream_for_jvmti #endif extern Mutex* DumpTimeTable_lock; // SystemDictionaryShared::find_or_allocate_info_for +extern Mutex* CDSLambda_lock; // SystemDictionaryShared::get_shared_lambda_proxy_class #endif // INCLUDE_CDS #if INCLUDE_JFR extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table diff --git a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java --- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -244,6 +244,46 @@ } } + /** Spins the lambda proxy class. + * + * This first checks if a lambda proxy class can be loaded from CDS archive. + * Otherwise, generate the lambda proxy class. If CDS dumping is enabled, it + * registers the lambda proxy class for including into the CDS archive. + */ + private Class spinInnerClass() throws LambdaConversionException { + // include lambda proxy class in CDS archive at dump time + if (LambdaProxyClassArchive.isDumpArchive()) { + Class innerClass = generateInnerClass(); + LambdaProxyClassArchive.register(targetClass, + samMethodName, + invokedType, + samMethodType, + implMethod, + instantiatedMethodType, + isSerializable, + markerInterfaces, + additionalBridges, + innerClass); + return innerClass; + } + + // load from CDS archive if present + Class innerClass = LambdaProxyClassArchive.find(targetClass, + samMethodName, + invokedType, + samMethodType, + implMethod, + instantiatedMethodType, + isSerializable, + markerInterfaces, + additionalBridges, + !disableEagerInitialization); + if (innerClass == null) { + innerClass = generateInnerClass(); + } + return innerClass; + } + /** * Generate a class file which implements the functional * interface, define and return the class. @@ -259,7 +299,7 @@ * @throws LambdaConversionException If properly formed functional interface * is not found */ - private Class spinInnerClass() throws LambdaConversionException { + private Class generateInnerClass() throws LambdaConversionException { String[] interfaces; String samIntf = samBase.getName().replace('.', '/'); boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase); diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaProxyClassArchive.java b/src/java.base/share/classes/java/lang/invoke/LambdaProxyClassArchive.java new file mode 100644 --- /dev/null +++ b/src/java.base/share/classes/java/lang/invoke/LambdaProxyClassArchive.java @@ -0,0 +1,136 @@ +/* + * 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. + * + */ +package java.lang.invoke; + +import jdk.internal.loader.BuiltinClassLoader; +import jdk.internal.misc.VM; + +final class LambdaProxyClassArchive { + private static final boolean dumpArchive; + private static final boolean sharingEnabled; + + static { + dumpArchive = VM.isCDSDumpingEnabled(); + sharingEnabled = VM.isCDSSharingEnabled(); + } + + /** + * Check if CDS dynamic dump is enabled. + */ + static boolean isDumpArchive() { + return dumpArchive; + } + + /** + * Check if CDS sharing is enabled. + */ + static boolean isSharingEnabled() { + return sharingEnabled; + } + + /** + * Check if the class is loaded by a built-in class loader. + */ + static boolean loadedByBuiltinLoader(Class cls) { + ClassLoader cl = cls.getClassLoader(); + return (cl == null || (cl instanceof BuiltinClassLoader)) ? true : false; + } + + private static native void addToArchive(Class caller, + String invokedName, + MethodType invokedType, + MethodType samMethodType, + MemberName implMethod, + MethodType instantiatedMethodType, + Class lambdaProxyClass); + + private static native Class findFromArchive(Class caller, + String invokedName, + MethodType invokedType, + MethodType samMethodType, + MemberName implMethod, + MethodType instantiatedMethodType, + boolean initialize); + + /** + * Registers the lambdaProxyClass into CDS archive. + * The VM will store the lambdaProxyClass into a hash table + * using the first six argumennts as the key. + * + * CDS only archives lambda proxy class if it's not serializable + * and no marker interfaces and no additional bridges, and if it is + * loaded by a built-in class loader. + */ + static boolean register(Class caller, + String invokedName, + MethodType invokedType, + MethodType samMethodType, + MethodHandle implMethod, + MethodType instantiatedMethodType, + boolean isSerializable, + Class[] markerInterfaces, + MethodType[] additionalBridges, + Class lambdaProxyClass) { + if (!isDumpArchive()) + throw new IllegalStateException("should only register lambda proxy class at dump time"); + + if (loadedByBuiltinLoader(caller) && + !isSerializable && markerInterfaces.length == 0 && additionalBridges.length == 0) { + addToArchive(caller, invokedName, invokedType, samMethodType, + implMethod.internalMemberName(), instantiatedMethodType, + lambdaProxyClass); + return true; + } + return false; + } + + /** + * Lookup a lambda proxy class from the CDS archive using the first + * six arguments as the key. + * + * CDS only archives lambda proxy class if it's not serializable + * and no marker interfaces and no additional bridges, and if it is + * loaded by a built-in class loader. + */ + static Class find(Class caller, + String invokedName, + MethodType invokedType, + MethodType samMethodType, + MethodHandle implMethod, + MethodType instantiatedMethodType, + boolean isSerializable, + Class[] markerInterfaces, + MethodType[] additionalBridges, + boolean initialize) { + if (isDumpArchive()) + throw new IllegalStateException("cannot load class from CDS archive at dump time"); + + if (!loadedByBuiltinLoader(caller) || + !isSharingEnabled() || isSerializable || markerInterfaces.length > 0 || additionalBridges.length > 0) + return null; + + return findFromArchive(caller, invokedName, invokedType, samMethodType, + implMethod.internalMemberName(), instantiatedMethodType, initialize); + } +} diff --git a/src/java.base/share/classes/jdk/internal/misc/VM.java b/src/java.base/share/classes/jdk/internal/misc/VM.java --- a/src/java.base/share/classes/jdk/internal/misc/VM.java +++ b/src/java.base/share/classes/jdk/internal/misc/VM.java @@ -424,6 +424,16 @@ public static native long getRandomSeedForCDSDump(); /** + * Check if CDS dynamic dumping is enabled via the DynamicDumpSharedSpaces flag. + */ + public static native boolean isCDSDumpingEnabled(); + + /** + * Check if CDS sharing is enabled by via the UseSharedSpaces flag. + */ + public static native boolean isCDSSharingEnabled(); + + /** * Provides access to information on buffer usage. */ public interface BufferPool { diff --git a/src/java.base/share/native/libjava/LambdaProxyClassArchive.c b/src/java.base/share/native/libjava/LambdaProxyClassArchive.c new file mode 100644 --- /dev/null +++ b/src/java.base/share/native/libjava/LambdaProxyClassArchive.c @@ -0,0 +1,59 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jlong.h" +#include "jvm.h" + +#include "java_lang_invoke_LambdaProxyClassArchive.h" + +JNIEXPORT void JNICALL +Java_java_lang_invoke_LambdaProxyClassArchive_addToArchive(JNIEnv *env, jclass ignore, + jclass caller, + jstring invokedName, + jobject invokedType, + jobject methodType, + jobject implMethodMember, + jobject instantiatedMethodType, + jclass lambdaProxyClass) { + JVM_RegisterLambdaProxyClassForArchiving(env, caller, invokedName, invokedType, + methodType, implMethodMember, + instantiatedMethodType, lambdaProxyClass); +} + +JNIEXPORT jclass JNICALL +Java_java_lang_invoke_LambdaProxyClassArchive_findFromArchive(JNIEnv *env, jclass ignore, + jclass caller, + jstring invokedName, + jobject invokedType, + jobject methodType, + jobject implMethodMember, + jobject instantiatedMethodType, + jboolean initialize) { + return JVM_LookupLambdaProxyClassFromArchive(env, caller, invokedName, invokedType, + methodType, implMethodMember, + instantiatedMethodType, initialize); +} diff --git a/src/java.base/share/native/libjava/VM.c b/src/java.base/share/native/libjava/VM.c --- a/src/java.base/share/native/libjava/VM.c +++ b/src/java.base/share/native/libjava/VM.c @@ -66,3 +66,13 @@ Java_jdk_internal_misc_VM_getRandomSeedForCDSDump(JNIEnv *env, jclass ignore) { return JVM_GetRandomSeedForCDSDump(); } + +JNIEXPORT jboolean JNICALL +Java_jdk_internal_misc_VM_isCDSDumpingEnabled(JNIEnv *env, jclass jcls) { + return JVM_IsCDSDumpingEnabled(env); +} + +JNIEXPORT jboolean JNICALL +Java_jdk_internal_misc_VM_isCDSSharingEnabled(JNIEnv *env, jclass jcls) { + return JVM_IsCDSSharingEnabled(env); +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/BasicLambdaTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/BasicLambdaTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/BasicLambdaTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019, 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 Basic operations of Lambda class archiving. + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @build BasicLambdaApp sun.hotspot.WhiteBox LambdaVerification + * @run driver ClassFileInstaller -jar basic_lambda_app.jar BasicLambdaApp LambdaVerification + * @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. BasicLambdaTest + */ + +public class BasicLambdaTest extends DynamicArchiveTestBase { + public static void main(String[] args) throws Exception { + runTest(BasicLambdaTest::test); + } + + static void test() throws Exception { + String topArchiveName = getNewArchiveName(); + String appJar = ClassFileInstaller.getJarPath("basic_lambda_app.jar"); + String mainClass = "BasicLambdaApp"; + String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + dump(topArchiveName, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-Xlog:class+load=debug,class+unload=info,cds+dynamic=info", + use_whitebox_jar, + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + output.shouldContain("Archiving hidden BasicLambdaApp$$Lambda$") + .shouldHaveExitValue(0); + }); + + run(topArchiveName, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + use_whitebox_jar, + "-Xlog:class+load=debug,class+resolve=debug,class+unload=info", + "-cp", appJar, mainClass, "run") + .assertNormalExit(output -> { + output.shouldHaveExitValue(0); + }); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/CDSStreamTestDriver.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/CDSStreamTestDriver.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/CDSStreamTestDriver.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2019, 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 Run the CustomFJPoolTest in dynamic CDSarchive mode. + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @compile ../../../../../../jdk/java/util/stream/CustomFJPoolTest.java + * test-classes/TestStreamApp.java + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. CDSStreamTestDriver + */ + +import org.testng.annotations.Test; + +import java.io.File; + +import jtreg.SkippedException; + +import sun.hotspot.gc.GC; + +@Test +public class CDSStreamTestDriver extends DynamicArchiveTestBase { + @Test + public void testMain() throws Exception { + runTest(CDSStreamTestDriver::doTest); + } + + private static final String classDir = System.getProperty("test.classes"); + private static final String mainClass = "TestStreamApp"; + private static final String javaClassPath = System.getProperty("java.class.path"); + private static final String ps = System.getProperty("path.separator"); + private static final String skippedException = "jtreg.SkippedException: Unable to map shared archive: test did not complete"; + + static void doTest() throws Exception { + String topArchiveName = getNewArchiveName(); + JarBuilder.build("streamapp", new File(classDir), null); + String appJar = classDir + File.separator + "streamapp.jar"; + + String[] classPaths = javaClassPath.split(File.pathSeparator); + String testngJar = null; + for (String path : classPaths) { + if (path.endsWith("testng.jar")) { + testngJar = path; + break; + } + } + + String[] testClassNames = { "CustomFJPoolTest" }; + + for (String className : testClassNames) { + try { + dumpAndRun(topArchiveName, "-Xlog:cds,cds+dynamic=debug,class+load=trace", + "-cp", appJar + ps + testngJar, + mainClass, className); + } catch (SkippedException s) { + if (GC.Z.isSelected() && s.toString().equals(skippedException)) { + System.out.println("Got " + s.toString() + " as expected."); + System.out.println("Because the test was run with ZGC with UseCompressedOops and UseCompressedClassPointers disabled,"); + System.out.println("but the base archive was created with the options enabled"); + } else { + throw new RuntimeException("Archive mapping should always succeed after JDK-8231610 (did the machine run out of memory?)"); + } + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DoubleSumAverageTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DoubleSumAverageTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DoubleSumAverageTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019, 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 + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @compile ../../../../../../jdk/java/util/stream/TestDoubleSumAverage.java + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. DoubleSumAverageTest + */ + +import java.io.File; + +public class DoubleSumAverageTest extends DynamicArchiveTestBase { + + public static void main(String[] args) throws Exception { + runTest(DoubleSumAverageTest::testImpl); + } + + private static final String classDir = System.getProperty("test.classes"); + private static final String mainClass = "TestDoubleSumAverage"; + + static void testImpl() throws Exception { + String topArchiveName = getNewArchiveName(); + JarBuilder.build("stream", new File(classDir), null); + String appJar = classDir + File.separator + "stream.jar"; + + dumpAndRun(topArchiveName, "-Xlog:cds,cds+dynamic=debug,class+load=trace", + "-cp", appJar, mainClass); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java @@ -0,0 +1,63 @@ +/* + * 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 + * @summary Lambda proxy class loaded by a custom class loader will not be archived. + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @build CustomLoaderApp LambHello sun.hotspot.WhiteBox + * @run driver ClassFileInstaller -jar custom_loader_app.jar CustomLoaderApp LambHello + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. LambdaCustomLoader + */ + +public class LambdaCustomLoader extends DynamicArchiveTestBase { + public static void main(String[] args) throws Exception { + runTest(LambdaCustomLoader::test); + } + + static void test() throws Exception { + String topArchiveName = getNewArchiveName(); + String appJar = ClassFileInstaller.getJarPath("custom_loader_app.jar"); + String mainClass = "CustomLoaderApp"; + + dump(topArchiveName, + "-Xlog:class+load,cds,cds+dynamic", + "-cp", appJar, mainClass, appJar) + .assertNormalExit(output -> { + output.shouldMatch("Skipping.LambHello[$][$]Lambda[$].*0x.*:.Hidden.class") + .shouldHaveExitValue(0); + }); + + run(topArchiveName, + "-Xlog:class+load,class+unload=info", + "-cp", appJar, mainClass, appJar) + .assertNormalExit(output -> { + output.shouldMatch("class.load.*LambHello[$][$]Lambda[$].*0x.*source:.LambHello") + .shouldHaveExitValue(0); + }); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaForClassInBaseArchive.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaForClassInBaseArchive.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaForClassInBaseArchive.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019, 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 Archive an base app class in a base archive and its lambda proxy + * class in a dynamic archive. During runtime, the base app class + * should be loaded from the base archive and the lambda proxy class + * should be loaded from the dynamic archive. + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @build SimpleApp sun.hotspot.WhiteBox + * @run driver ClassFileInstaller -jar simpleApp.jar SimpleApp MyClass MyInterface + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. LambdaForClassInBaseArchive + */ + +import java.io.File; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class LambdaForClassInBaseArchive extends DynamicArchiveTestBase { + static final String classList = System.getProperty("test.classes") + + File.separator + "LambdaForClassInBaseArchive.list"; + static final String appClass = "SimpleApp"; + + public static void main(String[] args) throws Exception { + runTest(LambdaForClassInBaseArchive::testCustomBase); + } + + static void testCustomBase() throws Exception { + String topArchiveName = getNewArchiveName("top"); + String baseArchiveName = getNewArchiveName("base"); + doTestCustomBase(baseArchiveName, topArchiveName); + } + + private static void doTestCustomBase(String baseArchiveName, String topArchiveName) throws Exception { + String appJar = ClassFileInstaller.getJarPath("simpleApp.jar"); + // dump class list by running the SimpleApp + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:DumpLoadedClassList=" + classList, + "-cp", + appJar, + appClass); + OutputAnalyzer output = TestCommon.executeAndLog(pb, "dumpClassList"); + TestCommon.checkExecReturn(output, 0, true); + + // create a custom base archive based on the class list + TestCommon.dumpBaseArchive(baseArchiveName, + "-XX:SharedClassListFile=" + classList, + "-cp", appJar, appClass); + + // create a dynamic archive with the custom base archive. + // The SimpleApp class is in the base archive. Its interface + // will be accessed using a lambda expression and the lambda + // proxy class will be archived in the dynamic archive. + dump2(baseArchiveName, topArchiveName, + "-Xlog:cds,cds+dynamic", + "-cp", appJar, + appClass, "lambda") + .assertNormalExit(out -> { + out.shouldHaveExitValue(0) + .shouldContain("Archiving hidden SimpleApp$$Lambda$1"); + }); + + // Run with both base and dynamic archives. The SimpleApp class + // should be loaded from the base archive. Its lambda proxy class + // should be loaded from the dynamic archive. + run2(baseArchiveName, topArchiveName, + "-Xlog:cds,cds+dynamic", + "-Xlog:class+load=trace", + "-cp", appJar, + appClass, "lambda") + .assertNormalExit(out -> { + out.shouldHaveExitValue(0) + .shouldContain("SimpleApp source: shared objects file") + .shouldMatch(".class.load. SimpleApp[$][$]Lambda[$]1/0x.*source:.*shared.*objects.*file.*(top)"); + }); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/NestTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/NestTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/NestTest.java @@ -0,0 +1,74 @@ +/* + * 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 + * @summary Archive a lambda proxy class created from a nested class. This + * tests the scenario when the caller class is different from the + * nest host. + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @build NestApp sun.hotspot.WhiteBox LambdaVerification + * @run driver ClassFileInstaller -jar nest_app.jar NestApp NestApp$InnerA NestApp$InnerB NestApp$InnerA$InnerInnerA + * @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. NestTest + */ + +public class NestTest extends DynamicArchiveTestBase { + public static void main(String[] args) throws Exception { + runTest(NestTest::test); + } + + static void test() throws Exception { + String topArchiveName = getNewArchiveName(); + String appJar = ClassFileInstaller.getJarPath("nest_app.jar"); + String mainClass = "NestApp"; + String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + dump(topArchiveName, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-Xlog:class+load=debug,class+unload=info,cds+dynamic=info", + use_whitebox_jar, + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + output.shouldContain("Archiving hidden NestApp$InnerA$InnerInnerA$$Lambda$") + .shouldHaveExitValue(0); + }); + + run(topArchiveName, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + use_whitebox_jar, + "-Xlog:class+load=debug,class+resolve=debug,class+unload=info", + "-cp", appJar, mainClass, "run") + .assertNormalExit(output -> { + output.shouldMatch(".class.load.* NestApp[$]InnerA[$]InnerInnerA[$][$]Lambda[$]1/0x.*source:.*shared.*objects.*file.*(top)") + .shouldHaveExitValue(0); + }); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ParallelLambdaLoadTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ParallelLambdaLoadTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/ParallelLambdaLoadTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019, 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 Test parallel loading of lambda proxy classes. + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @build ParallelLambdaLoad sun.hotspot.WhiteBox LambdaVerification + * @run driver ClassFileInstaller -jar parallel_lambda.jar ParallelLambdaLoad ThreadUtil DoSomething LambdaVerification + * @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. ParallelLambdaLoadTest + */ + +public class ParallelLambdaLoadTest extends DynamicArchiveTestBase { + public static void main(String[] args) throws Exception { + runTest(ParallelLambdaLoadTest::test); + } + + static void test() throws Exception { + String topArchiveName = getNewArchiveName(); + String appJar = ClassFileInstaller.getJarPath("parallel_lambda.jar"); + String mainClass = "ParallelLambdaLoad"; + String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + dump(topArchiveName, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", "-Xlog:cds+dynamic=info", + use_whitebox_jar, + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + output.shouldContain("Archiving hidden ParallelLambdaLoad$$Lambda$") + .shouldHaveExitValue(0); + }); + + run(topArchiveName, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + use_whitebox_jar, + "-cp", appJar, mainClass, "run") + .assertNormalExit(output -> { + output.shouldHaveExitValue(0); + }); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/PredicateTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/PredicateTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/PredicateTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019, 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 + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @build PredicateApp sun.hotspot.WhiteBox LambdaVerification + * @run driver ClassFileInstaller -jar predicate_app.jar PredicateApp LambdaVerification + * @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. PredicateTest + */ + +public class PredicateTest extends DynamicArchiveTestBase { + public static void main(String[] args) throws Exception { + runTest(PredicateTest::test); + } + + static void test() throws Exception { + String topArchiveName = getNewArchiveName(); + String appJar = ClassFileInstaller.getJarPath("predicate_app.jar"); + String mainClass = "PredicateApp"; + String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + dump(topArchiveName, "-XX:+UnlockDiagnosticVMOptions", "-XX:+ShowHiddenFrames", + "-XX:+WhiteBoxAPI", + "-Xlog:class+load=debug,cds+dynamic=info", + use_whitebox_jar, + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + output.shouldContain("Archiving hidden PredicateApp$$Lambda$") + .shouldHaveExitValue(0); + }); + + run(topArchiveName, "-XX:+UnlockDiagnosticVMOptions", "-XX:+ShowHiddenFrames", + "-XX:+WhiteBoxAPI", + use_whitebox_jar, + "-Xlog:class+load=debug,class+resolve=debug", + "-cp", appJar, mainClass, "run") + .assertNormalExit(output -> { + output.shouldHaveExitValue(0); + }); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/RegularHiddenClass.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/RegularHiddenClass.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/RegularHiddenClass.java @@ -0,0 +1,70 @@ +/* + * 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 + * @summary Hidden class not of type lambda proxy class will not be archived. + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @compile ../../../HiddenClasses/InstantiateHiddenClass.java + * ../../../../../../lib/jdk/test/lib/compiler/InMemoryJavaCompiler.java + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller -jar hidden.jar InstantiateHiddenClass + * jdk/test/lib/compiler/InMemoryJavaCompiler + * jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper$1 + * jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper + * jdk/test/lib/compiler/InMemoryJavaCompiler$MemoryJavaFileObject + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. RegularHiddenClass + */ + +public class RegularHiddenClass extends DynamicArchiveTestBase { + public static void main(String[] args) throws Exception { + runTest(RegularHiddenClass::test); + } + + static void test() throws Exception { + String topArchiveName = getNewArchiveName(); + String appJar = ClassFileInstaller.getJarPath("hidden.jar"); + String mainClass = "InstantiateHiddenClass"; + + dump(topArchiveName, + "-Xlog:class+load=debug,cds+dynamic,cds", + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + output.shouldMatch("cds.*Skipping.TestClass.0x.*Hidden.class") + .shouldNotMatch("cds.dynamic.*Archiving.hidden.TestClass.*") + .shouldHaveExitValue(0); + }); + + run(topArchiveName, + "-Xlog:class+load=debug,cds+dynamic,cds", + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + output.shouldMatch("class.load.*TestClass.*source.*InstantiateHiddenClass") + .shouldHaveExitValue(0); + }); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/StaticInnerTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/StaticInnerTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/StaticInnerTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2019, 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 Outer class is directly referenced during dump time but not during + * runtime. This test makes sure the nest host of a lambda proxy class + * could be loaded from the archive during runtime though it isn't being + * referenced directly. + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @build StaticInnerApp sun.hotspot.WhiteBox LambdaVerification + * @run driver ClassFileInstaller -jar static_inner_app.jar StaticInnerApp HelloStaticInner HelloStaticInner$InnerHello + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. StaticInnerTest + */ + +public class StaticInnerTest extends DynamicArchiveTestBase { + public static void main(String[] args) throws Exception { + runTest(StaticInnerTest::test); + } + + static void test() throws Exception { + String topArchiveName = getNewArchiveName(); + String appJar = ClassFileInstaller.getJarPath("static_inner_app.jar"); + String mainClass = "StaticInnerApp"; + + dump(topArchiveName, + "-Xlog:class+load=info,class+nestmates=trace,cds+dynamic=info", + "-cp", appJar, mainClass, "dump") + .assertNormalExit(output -> { + output.shouldContain("Archiving hidden HelloStaticInner$InnerHello$$Lambda$") + .shouldHaveExitValue(0); + }); + + run(topArchiveName, + "-Xlog:class+load=info", + "-cp", appJar, mainClass, "run") + .assertNormalExit(output -> { + output.shouldHaveExitValue(0) + .shouldContain("HelloStaticInner source: shared objects file (top)") + .shouldMatch(".class.load. HelloStaticInner[$]InnerHello[$][$]Lambda[$]1/0x.*source:.*shared.*objects.*file.*(top)"); + }); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/UnsafeAnonymous.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/UnsafeAnonymous.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/UnsafeAnonymous.java @@ -0,0 +1,72 @@ +/* + * 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 + * @summary VM unsafe anonymous classes will not be archived. + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @modules java.base/jdk.internal.misc + * @compile test-classes/UnsafeAnonymousApp.java + * ../../../../../../lib/jdk/test/lib/compiler/InMemoryJavaCompiler.java + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller -jar unsafe.jar UnsafeAnonymousApp + * jdk/test/lib/compiler/InMemoryJavaCompiler + * jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper$1 + * jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper + * jdk/test/lib/compiler/InMemoryJavaCompiler$MemoryJavaFileObject + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. UnsafeAnonymous + */ + +public class UnsafeAnonymous extends DynamicArchiveTestBase { + public static void main(String[] args) throws Exception { + runTest(UnsafeAnonymous::test); + } + + static void test() throws Exception { + String topArchiveName = getNewArchiveName(); + String appJar = ClassFileInstaller.getJarPath("unsafe.jar"); + String mainClass = "UnsafeAnonymousApp"; + + dump(topArchiveName, + "-Xlog:class+load=debug,cds+dynamic,cds", + "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + output.shouldContain("Skipping TestClass: Unsafe anonymous class") + .shouldHaveExitValue(0); + }); + + run(topArchiveName, + "-Xlog:class+load=debug,cds+dynamic,cds", + "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED", + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + output.shouldMatch("class.load.*TestClass/0x.*source.*UnsafeAnonymousApp") + .shouldHaveExitValue(0); + }); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/UsedAllArchivedLambdas.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/UsedAllArchivedLambdas.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/UsedAllArchivedLambdas.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2019, 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 Test exhaustive use of archived lambda proxy classes. + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @build UsedAllArchivedLambdasApp sun.hotspot.WhiteBox LambdaVerification + * @run driver ClassFileInstaller -jar used_all_lambda.jar UsedAllArchivedLambdasApp LambdaVerification + * @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. UsedAllArchivedLambdas + */ + +public class UsedAllArchivedLambdas extends DynamicArchiveTestBase { + public static void main(String[] args) throws Exception { + runTest(UsedAllArchivedLambdas::test); + } + + static void test() throws Exception { + String topArchiveName = getNewArchiveName(); + String appJar = ClassFileInstaller.getJarPath("used_all_lambda.jar"); + String mainClass = "UsedAllArchivedLambdasApp"; + String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + dump(topArchiveName, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", "-Xlog:cds+dynamic=info", + use_whitebox_jar, + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + output.shouldContain("Archiving hidden UsedAllArchivedLambdasApp$$Lambda$") + .shouldHaveExitValue(0); + }); + + run(topArchiveName, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + use_whitebox_jar, + "-Xlog:cds=debug", + "-cp", appJar, mainClass, "run") + .assertNormalExit(output -> { + output.shouldContain("Used all archived lambda proxy classes for: UsedAllArchivedLambdasApp run()Ljava/lang/Runnable;") + .shouldHaveExitValue(0); + }); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/CDSMHTest_generate.sh b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/CDSMHTest_generate.sh new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/CDSMHTest_generate.sh @@ -0,0 +1,100 @@ +#!/bin/bash +# 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. +# + +echo "// --- start auto-generated" + +testnames=( MethodHandlesGeneralTest MethodHandlesAsCollectorTest MethodHandlesCastFailureTest MethodHandlesInvokersTest MethodHandlesPermuteArgumentsTest MethodHandlesSpreadArgumentsTest ) +name_suffix='.java' + +for i in "${testnames[@]}" +do + fname="$i$name_suffix" + cat << EOF > $fname + +// this file is auto-generated by $0. Do not edit manually. + +/* + * @test + * @summary Run the $fname test in dynamic CDS archive mode. + * @requires vm.cds & vm.compMode != "Xcomp" + * @comment Some of the tests run excessively slowly with -Xcomp. The original + * tests aren't executed with -Xcomp in the CI pipeline, so let's exclude + * the generated tests from -Xcomp execution as well. + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @compile ../../../../../../../jdk/java/lang/invoke/MethodHandlesTest.java + * ../../../../../../../lib/jdk/test/lib/Utils.java + * ../../../../../../../jdk/java/lang/invoke/$fname + * ../../../../../../../jdk/java/lang/invoke/remote/RemoteExample.java + * ../../../../../../../jdk/java/lang/invoke/common/test/java/lang/invoke/lib/CodeCacheOverflowProcessor.java + * ../test-classes/TestMHApp.java + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox\$WhiteBoxPermission + * @run junit/othervm/timeout=240 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. $i + */ + +import org.junit.Test; + +import java.io.File; + +import jtreg.SkippedException; + +import sun.hotspot.gc.GC; + +public class $i extends DynamicArchiveTestBase { + @Test + public void test() throws Exception { + runTest($i::testImpl); + } + + private static final String classDir = System.getProperty("test.classes"); + private static final String mainClass = "TestMHApp"; + private static final String javaClassPath = System.getProperty("java.class.path"); + private static final String ps = System.getProperty("path.separator"); + private static final String testPackageName = "test.java.lang.invoke"; + private static final String testClassName = "$i"; + private static final String skippedException = "jtreg.SkippedException: Unable to map shared archive: test did not complete"; + + static void testImpl() throws Exception { + String topArchiveName = getNewArchiveName(); + JarBuilder.build("MH", new File(classDir), null); + String appJar = classDir + File.separator + "MH.jar"; + + String[] classPaths = javaClassPath.split(File.pathSeparator); + String junitJar = null; + for (String path : classPaths) { + if (path.endsWith("junit.jar")) { + junitJar = path; + break; + } + } + + dumpAndRun(topArchiveName, "-Xlog:cds,cds+dynamic=debug,class+load=trace", + "-cp", appJar + ps + junitJar, + mainClass, testPackageName + "." + testClassName); + } +} +EOF +done +echo "// --- end auto-generated" diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesAsCollectorTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesAsCollectorTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesAsCollectorTest.java @@ -0,0 +1,87 @@ +/* + * 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. + * + */ +// this file is auto-generated by ./CDSMHTest_generate.sh. Do not edit manually. + +/* + * @test + * @summary Run the MethodHandlesAsCollectorTest.java test in dynamic CDS archive mode. + * @requires vm.cds & vm.compMode != "Xcomp" + * @comment Some of the tests run excessively slowly with -Xcomp. The original + * tests aren't executed with -Xcomp in the CI pipeline, so let's exclude + * the generated tests from -Xcomp execution as well. + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @compile ../../../../../../../jdk/java/lang/invoke/MethodHandlesTest.java + * ../../../../../../../lib/jdk/test/lib/Utils.java + * ../../../../../../../jdk/java/lang/invoke/MethodHandlesAsCollectorTest.java + * ../../../../../../../jdk/java/lang/invoke/remote/RemoteExample.java + * ../../../../../../../jdk/java/lang/invoke/common/test/java/lang/invoke/lib/CodeCacheOverflowProcessor.java + * ../test-classes/TestMHApp.java + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run junit/othervm/timeout=240 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. MethodHandlesAsCollectorTest + */ + +import org.junit.Test; + +import java.io.File; + +import jtreg.SkippedException; + +import sun.hotspot.gc.GC; + +public class MethodHandlesAsCollectorTest extends DynamicArchiveTestBase { + @Test + public void test() throws Exception { + runTest(MethodHandlesAsCollectorTest::testImpl); + } + + private static final String classDir = System.getProperty("test.classes"); + private static final String mainClass = "TestMHApp"; + private static final String javaClassPath = System.getProperty("java.class.path"); + private static final String ps = System.getProperty("path.separator"); + private static final String testPackageName = "test.java.lang.invoke"; + private static final String testClassName = "MethodHandlesAsCollectorTest"; + private static final String skippedException = "jtreg.SkippedException: Unable to map shared archive: test did not complete"; + + static void testImpl() throws Exception { + String topArchiveName = getNewArchiveName(); + JarBuilder.build("MH", new File(classDir), null); + String appJar = classDir + File.separator + "MH.jar"; + + String[] classPaths = javaClassPath.split(File.pathSeparator); + String junitJar = null; + for (String path : classPaths) { + if (path.endsWith("junit.jar")) { + junitJar = path; + break; + } + } + + dumpAndRun(topArchiveName, "-Xlog:cds,cds+dynamic=debug,class+load=trace", + "-cp", appJar + ps + junitJar, + mainClass, testPackageName + "." + testClassName); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesCastFailureTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesCastFailureTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesCastFailureTest.java @@ -0,0 +1,87 @@ +/* + * 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. + * + */ +// this file is auto-generated by ./CDSMHTest_generate.sh. Do not edit manually. + +/* + * @test + * @summary Run the MethodHandlesCastFailureTest.java test in dynamic CDS archive mode. + * @requires vm.cds & vm.compMode != "Xcomp" + * @comment Some of the tests run excessively slowly with -Xcomp. The original + * tests aren't executed with -Xcomp in the CI pipeline, so let's exclude + * the generated tests from -Xcomp execution as well. + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @compile ../../../../../../../jdk/java/lang/invoke/MethodHandlesTest.java + * ../../../../../../../lib/jdk/test/lib/Utils.java + * ../../../../../../../jdk/java/lang/invoke/MethodHandlesCastFailureTest.java + * ../../../../../../../jdk/java/lang/invoke/remote/RemoteExample.java + * ../../../../../../../jdk/java/lang/invoke/common/test/java/lang/invoke/lib/CodeCacheOverflowProcessor.java + * ../test-classes/TestMHApp.java + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run junit/othervm/timeout=240 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. MethodHandlesCastFailureTest + */ + +import org.junit.Test; + +import java.io.File; + +import jtreg.SkippedException; + +import sun.hotspot.gc.GC; + +public class MethodHandlesCastFailureTest extends DynamicArchiveTestBase { + @Test + public void test() throws Exception { + runTest(MethodHandlesCastFailureTest::testImpl); + } + + private static final String classDir = System.getProperty("test.classes"); + private static final String mainClass = "TestMHApp"; + private static final String javaClassPath = System.getProperty("java.class.path"); + private static final String ps = System.getProperty("path.separator"); + private static final String testPackageName = "test.java.lang.invoke"; + private static final String testClassName = "MethodHandlesCastFailureTest"; + private static final String skippedException = "jtreg.SkippedException: Unable to map shared archive: test did not complete"; + + static void testImpl() throws Exception { + String topArchiveName = getNewArchiveName(); + JarBuilder.build("MH", new File(classDir), null); + String appJar = classDir + File.separator + "MH.jar"; + + String[] classPaths = javaClassPath.split(File.pathSeparator); + String junitJar = null; + for (String path : classPaths) { + if (path.endsWith("junit.jar")) { + junitJar = path; + break; + } + } + + dumpAndRun(topArchiveName, "-Xlog:cds,cds+dynamic=debug,class+load=trace", + "-cp", appJar + ps + junitJar, + mainClass, testPackageName + "." + testClassName); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesGeneralTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesGeneralTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesGeneralTest.java @@ -0,0 +1,87 @@ +/* + * 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. + * + */ +// this file is auto-generated by ./CDSMHTest_generate.sh. Do not edit manually. + +/* + * @test + * @summary Run the MethodHandlesGeneralTest.java test in dynamic CDS archive mode. + * @requires vm.cds & vm.compMode != "Xcomp" + * @comment Some of the tests run excessively slowly with -Xcomp. The original + * tests aren't executed with -Xcomp in the CI pipeline, so let's exclude + * the generated tests from -Xcomp execution as well. + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @compile ../../../../../../../jdk/java/lang/invoke/MethodHandlesTest.java + * ../../../../../../../lib/jdk/test/lib/Utils.java + * ../../../../../../../jdk/java/lang/invoke/MethodHandlesGeneralTest.java + * ../../../../../../../jdk/java/lang/invoke/remote/RemoteExample.java + * ../../../../../../../jdk/java/lang/invoke/common/test/java/lang/invoke/lib/CodeCacheOverflowProcessor.java + * ../test-classes/TestMHApp.java + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run junit/othervm/timeout=240 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. MethodHandlesGeneralTest + */ + +import org.junit.Test; + +import java.io.File; + +import jtreg.SkippedException; + +import sun.hotspot.gc.GC; + +public class MethodHandlesGeneralTest extends DynamicArchiveTestBase { + @Test + public void test() throws Exception { + runTest(MethodHandlesGeneralTest::testImpl); + } + + private static final String classDir = System.getProperty("test.classes"); + private static final String mainClass = "TestMHApp"; + private static final String javaClassPath = System.getProperty("java.class.path"); + private static final String ps = System.getProperty("path.separator"); + private static final String testPackageName = "test.java.lang.invoke"; + private static final String testClassName = "MethodHandlesGeneralTest"; + private static final String skippedException = "jtreg.SkippedException: Unable to map shared archive: test did not complete"; + + static void testImpl() throws Exception { + String topArchiveName = getNewArchiveName(); + JarBuilder.build("MH", new File(classDir), null); + String appJar = classDir + File.separator + "MH.jar"; + + String[] classPaths = javaClassPath.split(File.pathSeparator); + String junitJar = null; + for (String path : classPaths) { + if (path.endsWith("junit.jar")) { + junitJar = path; + break; + } + } + + dumpAndRun(topArchiveName, "-Xlog:cds,cds+dynamic=debug,class+load=trace", + "-cp", appJar + ps + junitJar, + mainClass, testPackageName + "." + testClassName); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesInvokersTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesInvokersTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesInvokersTest.java @@ -0,0 +1,87 @@ +/* + * 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. + * + */ +// this file is auto-generated by ./CDSMHTest_generate.sh. Do not edit manually. + +/* + * @test + * @summary Run the MethodHandlesInvokersTest.java test in dynamic CDS archive mode. + * @requires vm.cds & vm.compMode != "Xcomp" + * @comment Some of the tests run excessively slowly with -Xcomp. The original + * tests aren't executed with -Xcomp in the CI pipeline, so let's exclude + * the generated tests from -Xcomp execution as well. + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @compile ../../../../../../../jdk/java/lang/invoke/MethodHandlesTest.java + * ../../../../../../../lib/jdk/test/lib/Utils.java + * ../../../../../../../jdk/java/lang/invoke/MethodHandlesInvokersTest.java + * ../../../../../../../jdk/java/lang/invoke/remote/RemoteExample.java + * ../../../../../../../jdk/java/lang/invoke/common/test/java/lang/invoke/lib/CodeCacheOverflowProcessor.java + * ../test-classes/TestMHApp.java + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run junit/othervm/timeout=240 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. MethodHandlesInvokersTest + */ + +import org.junit.Test; + +import java.io.File; + +import jtreg.SkippedException; + +import sun.hotspot.gc.GC; + +public class MethodHandlesInvokersTest extends DynamicArchiveTestBase { + @Test + public void test() throws Exception { + runTest(MethodHandlesInvokersTest::testImpl); + } + + private static final String classDir = System.getProperty("test.classes"); + private static final String mainClass = "TestMHApp"; + private static final String javaClassPath = System.getProperty("java.class.path"); + private static final String ps = System.getProperty("path.separator"); + private static final String testPackageName = "test.java.lang.invoke"; + private static final String testClassName = "MethodHandlesInvokersTest"; + private static final String skippedException = "jtreg.SkippedException: Unable to map shared archive: test did not complete"; + + static void testImpl() throws Exception { + String topArchiveName = getNewArchiveName(); + JarBuilder.build("MH", new File(classDir), null); + String appJar = classDir + File.separator + "MH.jar"; + + String[] classPaths = javaClassPath.split(File.pathSeparator); + String junitJar = null; + for (String path : classPaths) { + if (path.endsWith("junit.jar")) { + junitJar = path; + break; + } + } + + dumpAndRun(topArchiveName, "-Xlog:cds,cds+dynamic=debug,class+load=trace", + "-cp", appJar + ps + junitJar, + mainClass, testPackageName + "." + testClassName); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesPermuteArgumentsTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesPermuteArgumentsTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesPermuteArgumentsTest.java @@ -0,0 +1,87 @@ +/* + * 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. + * + */ +// this file is auto-generated by ./CDSMHTest_generate.sh. Do not edit manually. + +/* + * @test + * @summary Run the MethodHandlesPermuteArgumentsTest.java test in dynamic CDS archive mode. + * @requires vm.cds & vm.compMode != "Xcomp" + * @comment Some of the tests run excessively slowly with -Xcomp. The original + * tests aren't executed with -Xcomp in the CI pipeline, so let's exclude + * the generated tests from -Xcomp execution as well. + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @compile ../../../../../../../jdk/java/lang/invoke/MethodHandlesTest.java + * ../../../../../../../lib/jdk/test/lib/Utils.java + * ../../../../../../../jdk/java/lang/invoke/MethodHandlesPermuteArgumentsTest.java + * ../../../../../../../jdk/java/lang/invoke/remote/RemoteExample.java + * ../../../../../../../jdk/java/lang/invoke/common/test/java/lang/invoke/lib/CodeCacheOverflowProcessor.java + * ../test-classes/TestMHApp.java + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run junit/othervm/timeout=240 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. MethodHandlesPermuteArgumentsTest + */ + +import org.junit.Test; + +import java.io.File; + +import jtreg.SkippedException; + +import sun.hotspot.gc.GC; + +public class MethodHandlesPermuteArgumentsTest extends DynamicArchiveTestBase { + @Test + public void test() throws Exception { + runTest(MethodHandlesPermuteArgumentsTest::testImpl); + } + + private static final String classDir = System.getProperty("test.classes"); + private static final String mainClass = "TestMHApp"; + private static final String javaClassPath = System.getProperty("java.class.path"); + private static final String ps = System.getProperty("path.separator"); + private static final String testPackageName = "test.java.lang.invoke"; + private static final String testClassName = "MethodHandlesPermuteArgumentsTest"; + private static final String skippedException = "jtreg.SkippedException: Unable to map shared archive: test did not complete"; + + static void testImpl() throws Exception { + String topArchiveName = getNewArchiveName(); + JarBuilder.build("MH", new File(classDir), null); + String appJar = classDir + File.separator + "MH.jar"; + + String[] classPaths = javaClassPath.split(File.pathSeparator); + String junitJar = null; + for (String path : classPaths) { + if (path.endsWith("junit.jar")) { + junitJar = path; + break; + } + } + + dumpAndRun(topArchiveName, "-Xlog:cds,cds+dynamic=debug,class+load=trace", + "-cp", appJar + ps + junitJar, + mainClass, testPackageName + "." + testClassName); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesSpreadArgumentsTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesSpreadArgumentsTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/methodHandles/MethodHandlesSpreadArgumentsTest.java @@ -0,0 +1,87 @@ +/* + * 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. + * + */ +// this file is auto-generated by ./CDSMHTest_generate.sh. Do not edit manually. + +/* + * @test + * @summary Run the MethodHandlesSpreadArgumentsTest.java test in dynamic CDS archive mode. + * @requires vm.cds & vm.compMode != "Xcomp" + * @comment Some of the tests run excessively slowly with -Xcomp. The original + * tests aren't executed with -Xcomp in the CI pipeline, so let's exclude + * the generated tests from -Xcomp execution as well. + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @compile ../../../../../../../jdk/java/lang/invoke/MethodHandlesTest.java + * ../../../../../../../lib/jdk/test/lib/Utils.java + * ../../../../../../../jdk/java/lang/invoke/MethodHandlesSpreadArgumentsTest.java + * ../../../../../../../jdk/java/lang/invoke/remote/RemoteExample.java + * ../../../../../../../jdk/java/lang/invoke/common/test/java/lang/invoke/lib/CodeCacheOverflowProcessor.java + * ../test-classes/TestMHApp.java + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run junit/othervm/timeout=240 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. MethodHandlesSpreadArgumentsTest + */ + +import org.junit.Test; + +import java.io.File; + +import jtreg.SkippedException; + +import sun.hotspot.gc.GC; + +public class MethodHandlesSpreadArgumentsTest extends DynamicArchiveTestBase { + @Test + public void test() throws Exception { + runTest(MethodHandlesSpreadArgumentsTest::testImpl); + } + + private static final String classDir = System.getProperty("test.classes"); + private static final String mainClass = "TestMHApp"; + private static final String javaClassPath = System.getProperty("java.class.path"); + private static final String ps = System.getProperty("path.separator"); + private static final String testPackageName = "test.java.lang.invoke"; + private static final String testClassName = "MethodHandlesSpreadArgumentsTest"; + private static final String skippedException = "jtreg.SkippedException: Unable to map shared archive: test did not complete"; + + static void testImpl() throws Exception { + String topArchiveName = getNewArchiveName(); + JarBuilder.build("MH", new File(classDir), null); + String appJar = classDir + File.separator + "MH.jar"; + + String[] classPaths = javaClassPath.split(File.pathSeparator); + String junitJar = null; + for (String path : classPaths) { + if (path.endsWith("junit.jar")) { + junitJar = path; + break; + } + } + + dumpAndRun(topArchiveName, "-Xlog:cds,cds+dynamic=debug,class+load=trace", + "-cp", appJar + ps + junitJar, + mainClass, testPackageName + "." + testClassName); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/BasicLambdaApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/BasicLambdaApp.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/BasicLambdaApp.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, 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. + * + */ + +public class BasicLambdaApp { + static boolean isRuntime; + public static void main(String[] args) { + isRuntime = (args.length == 1 && args[0].equals("run")) ? true : false; + testNonCapturingLambda(); + testCapturingLambda(); + } + + static void testNonCapturingLambda() { + doit(() -> { + System.out.println("Hello from Lambda"); + LambdaVerification.verifyCallerIsArchivedLambda(isRuntime); + }); + } + + static int lastX; + static void testCapturingLambda() { + lastX = -1; + for (int i=0; i<10; i++) { + final int x = i; + doit(() -> { + if (x != lastX + 1) { + throw new RuntimeException(x + " != " + (lastX + 1)); + } + lastX = x; + LambdaVerification.verifyCallerIsArchivedLambda(isRuntime); + }); + } + } + + static void doit(Runnable t) { + t.run(); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/CustomLoaderApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/CustomLoaderApp.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/CustomLoaderApp.java @@ -0,0 +1,54 @@ +/* + * 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.File; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; + +public class CustomLoaderApp { + private static String className = "LambHello"; + + public static void main(String args[]) throws Exception { + String path = args[0]; + URL url = new File(path).toURI().toURL(); + URL[] urls = new URL[] {url}; + System.out.println(path); + System.out.println(url); + + URLClassLoader urlClassLoader = + new URLClassLoader("HelloClassLoader", urls, null); + Class c = Class.forName(className, true, urlClassLoader); + System.out.println(c); + System.out.println(c.getClassLoader()); + Object o = c.newInstance(); + + if (c.getClassLoader() != urlClassLoader) { + throw new RuntimeException("c.getClassLoader() == " + c.getClassLoader() + + ", expected == " + urlClassLoader); + } + + Method method = c.getDeclaredMethod("doTest"); + method.invoke(o); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/LambHello.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/LambHello.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/LambHello.java @@ -0,0 +1,36 @@ +/* + * 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. + * + */ +public class LambHello { + public static void main(String[] args) { + } + + public void doTest() { + doit(() -> { + System.out.println("Hello from doTest"); + }); + } + static void doit(Runnable t) { + t.run(); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/LambdaVerification.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/LambdaVerification.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/LambdaVerification.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +import java.lang.StackWalker.StackFrame; +import java.util.List; +import java.util.stream.Collectors; +import java.util.Set; +import sun.hotspot.WhiteBox; + +public class LambdaVerification { + static void verifyCallerIsArchivedLambda(boolean isRuntime) { + // getCallerClass(0) = verifyCallerIsArchivedLambda + // getCallerClass(1) = testNonCapturingLambda or testCapturingLambda + // getCallerClass(2) = the lambda proxy class that should be archived by CDS. + Class c = getCallerClass(2); + System.out.println("Lambda proxy class = " + c); + String cn = c.getName(); + System.out.println(" cn = " + cn); + String hiddenClassName = cn.replace('/', '+'); + String lambdaClassName = cn.substring(0, cn.lastIndexOf('/')); + System.out.println(" lambda name = " + lambdaClassName); + WhiteBox wb = WhiteBox.getWhiteBox(); + if (isRuntime) { + // check that c is a shared class + if (wb.isSharedClass(c)) { + System.out.println("As expected, " + c + " is in shared space."); + } else { + throw new java.lang.RuntimeException(c + " must be in shared space."); + } + // check that lambda class cannot be found manually + try { + Class.forName(hiddenClassName); + } catch (ClassNotFoundException cnfe) { + cnfe.printStackTrace(System.out); + System.out.println("As expected, loading of " + hiddenClassName + " should result in ClassNotFoundException."); + } catch (Exception ex) { + throw ex; + } + // check that lambda class is alive + if (wb.isClassAlive(hiddenClassName)) { + System.out.println("As expected, " + cn + " is alive."); + } else { + throw new java.lang.RuntimeException(cn + " should be alive."); + } + } else { + if (wb.isSharedClass(c)) { + throw new java.lang.RuntimeException(c + " must not be in shared space."); + } else { + System.out.println("As expected, " + c + " is not in shared space."); + } + } + //System.out.println("=== Here's the call stack"); + //(new Throwable()).printStackTrace(System.out); + //System.out.println("==="); + System.out.println("Succeeded"); + } + + // depth is 0-based -- i.e., depth==0 returns the class of the immediate caller of getCallerClass + static Class getCallerClass(int depth) { + // Need to add the frame of the getCallerClass -- so the immediate caller (depth==0) of this method + // is at stack.get(1) == stack.get(depth+1); + StackWalker walker = StackWalker.getInstance( + Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE, + StackWalker.Option.SHOW_HIDDEN_FRAMES)); + List stack = walker.walk(s -> s.limit(depth+2).collect(Collectors.toList())); + return stack.get(depth+1).getDeclaringClass(); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/NestApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/NestApp.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/NestApp.java @@ -0,0 +1,52 @@ +/* + * 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. + * + */ + +public class NestApp { + + public static void main(String[] args) throws Exception { + Class clazzInnerInnerA = NestApp.InnerA.InnerInnerA.class; + NestApp.InnerA.InnerInnerA iia = (NestApp.InnerA.InnerInnerA)clazzInnerInnerA.newInstance(); + } + + public NestApp(String type, int weight) { + } + + static class InnerA { + + static class InnerInnerA { + static { + doit(() -> { + System.out.println("Hello from InnerInnerA"); + }); + } + } + } + + public class InnerB { + } + + static void doit(Runnable t) { + t.run(); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ParallelLambdaLoad.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ParallelLambdaLoad.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/ParallelLambdaLoad.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2019, 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.util.concurrent.atomic.AtomicInteger; +import java.util.List; +import java.util.HashMap; +import java.util.ArrayList; + +public class ParallelLambdaLoad { + public static int NUM_THREADS = 10; + public static int MAX_CLASSES = 10; + public static AtomicInteger num_ready[]; + public static boolean isRuntime; + + public static void main(String args[]) throws Throwable { + isRuntime = (args.length == 1 && args[0].equals("run")) ? true : false; + num_ready = new AtomicInteger[MAX_CLASSES]; + for (int i = 0; i < MAX_CLASSES; i++) { + num_ready[i] = new AtomicInteger(); + } + + ArrayList list = new ArrayList<>(); + + list.add(new Thread(() -> { + DoSomething ds = new DoSomething(0); + {ThreadUtil.WaitForLock(0); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(1); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(2); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(3); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(4); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(5); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(6); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(7); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(8); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(9); Runnable runner = ds::run; runner.run();} + })); + list.add(new Thread(() -> { + DoSomething ds = new DoSomething(1); + {ThreadUtil.WaitForLock(0); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(1); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(2); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(3); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(4); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(5); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(6); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(7); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(8); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(9); Runnable runner = ds::run; runner.run();} + })); + list.add(new Thread(() -> { + DoSomething ds = new DoSomething(2); + {ThreadUtil.WaitForLock(0); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(1); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(2); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(3); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(4); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(5); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(6); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(7); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(8); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(9); Runnable runner = ds::run; runner.run();} + })); + list.add(new Thread(() -> { + DoSomething ds = new DoSomething(3); + {ThreadUtil.WaitForLock(0); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(1); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(2); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(3); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(4); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(5); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(6); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(7); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(8); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(9); Runnable runner = ds::run; runner.run();} + })); + list.add(new Thread(() -> { + DoSomething ds = new DoSomething(4); + {ThreadUtil.WaitForLock(0); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(1); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(2); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(3); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(4); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(5); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(6); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(7); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(8); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(9); Runnable runner = ds::run; runner.run();} + })); + list.add(new Thread(() -> { + DoSomething ds = new DoSomething(5); + {ThreadUtil.WaitForLock(0); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(1); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(2); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(3); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(4); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(5); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(6); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(7); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(8); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(9); Runnable runner = ds::run; runner.run();} + })); + list.add(new Thread(() -> { + DoSomething ds = new DoSomething(6); + {ThreadUtil.WaitForLock(0); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(1); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(2); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(3); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(4); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(5); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(6); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(7); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(8); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(9); Runnable runner = ds::run; runner.run();} + })); + list.add(new Thread(() -> { + DoSomething ds = new DoSomething(7); + {ThreadUtil.WaitForLock(0); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(1); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(2); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(3); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(4); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(5); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(6); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(7); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(8); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(9); Runnable runner = ds::run; runner.run();} + })); + list.add(new Thread(() -> { + DoSomething ds = new DoSomething(8); + {ThreadUtil.WaitForLock(0); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(1); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(2); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(3); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(4); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(5); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(6); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(7); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(8); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(9); Runnable runner = ds::run; runner.run();} + })); + list.add(new Thread(() -> { + DoSomething ds = new DoSomething(9); + {ThreadUtil.WaitForLock(0); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(1); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(2); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(3); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(4); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(5); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(6); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(7); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(8); Runnable runner = ds::run; runner.run();} + {ThreadUtil.WaitForLock(9); Runnable runner = ds::run; runner.run();} + })); + + + for (Thread t : list) { + t.start(); + } + + for (Thread t : list) { + t.join(); + } + + synchronized(map) { + System.out.println("map size " + map.size()); + int expectedSize = NUM_THREADS * MAX_CLASSES; + if (map.size() != expectedSize) { + throw new RuntimeException("Expected number of lamdba classes is " + + expectedSize + " but got only " + map.size()); + } + } + } + + static HashMap, Class> map = new HashMap<>(); +} + +class DoSomething { + DoSomething(int dummy) {} + + public void run() { + Class c = LambdaVerification.getCallerClass(1); + synchronized(ParallelLambdaLoad.map) { + ParallelLambdaLoad.map.put(c, c); + } + LambdaVerification.verifyCallerIsArchivedLambda(ParallelLambdaLoad.isRuntime); + } +} + +class ThreadUtil { + static void WaitForLock(int i) { + // Spin until every thread is ready to proceed + ParallelLambdaLoad.num_ready[i].incrementAndGet(); + while (ParallelLambdaLoad.num_ready[i].intValue() < ParallelLambdaLoad.NUM_THREADS) { + ; + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/PredicateApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/PredicateApp.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/PredicateApp.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019, 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.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +public class PredicateApp { + public static void main(String[] args){ + boolean isRuntime = (args.length == 1 && args[0].equals("run")) ? true : false; + List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp"); + + doit(() -> { + System.out.println("Languages which starts with J :"); + filter(languages, (str)->((String)str).startsWith("J")); + LambdaVerification.verifyCallerIsArchivedLambda(isRuntime); + }); + + doit(() -> { + System.out.println("Languages which ends with a "); + filter(languages, (str)->((String)str).endsWith("a")); + LambdaVerification.verifyCallerIsArchivedLambda(isRuntime); + }); + + doit(() -> { + System.out.println("Print all languages :"); + filter(languages, (str)->true); + LambdaVerification.verifyCallerIsArchivedLambda(isRuntime); + }); + + doit(() -> { + System.out.println("Print no language : "); + filter(languages, (str)->false); + LambdaVerification.verifyCallerIsArchivedLambda(isRuntime); + }); + + doit(() -> { + System.out.println("Print language whose length greater than 4:"); + filter(languages, (str)->((String)str).length() > 4); + LambdaVerification.verifyCallerIsArchivedLambda(isRuntime); + }); + } + + public static void filter(List names, Predicate condition) { + names.stream().filter((name) -> (condition.test(name))).forEach((name) -> { + System.out.println(name + " "); + }); + } + + static void doit(Runnable t) { + t.run(); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/SimpleApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/SimpleApp.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/SimpleApp.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019, 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. + * + */ + +@FunctionalInterface +interface MyInterface { + public String sayHello(); +} + +class MyClass implements MyInterface { + public String sayHello() { + return "Hello"; + } +} + +public class SimpleApp { + public static boolean useLambda; + + public static void main(String args[]) { + useLambda = (args.length == 1 && args[0].equals("lambda")) ? true : false; + if (!useLambda) { + MyClass mc = new MyClass(); + System.out.println(mc.sayHello()); + } else { + MyInterface msg = () -> { + return "Hello"; + }; + System.out.println(msg.sayHello()); + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/StaticInnerApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/StaticInnerApp.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/StaticInnerApp.java @@ -0,0 +1,46 @@ +/* + * 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. + * + */ + +class HelloStaticInner { + + static class InnerHello { + public static void m1() { + doit(() -> { + System.out.println("Hello from Lambda"); + }); + } + static void doit(Runnable t) { + t.run(); + } + } +} + +public class StaticInnerApp { + public static void main(String[] args) { + if (args.length > 0 && args[0].equals("dump")) { + Object o = new HelloStaticInner(); + } + HelloStaticInner.InnerHello.m1(); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/TestMHApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/TestMHApp.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/TestMHApp.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. + * + */ + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; + +public class TestMHApp { + public static void main(String args[]) throws Exception { + try { + Class testClass = Class.forName(args[0]); + System.out.println(testClass); + Object obj = testClass.newInstance(); + final List allMethods = new ArrayList(Arrays.asList(testClass.getDeclaredMethods())); + for (final Method method : allMethods) { + //System.out.println(method.toString()); + method.setAccessible(true); + Annotation[] annotations = null; + try { + annotations = method.getDeclaredAnnotations(); + } catch (Throwable th) { + System.out.println("skipping method"); + continue; + } + boolean isTest = false; + for (Annotation annotation : annotations) { + String annotationString = annotation.toString(); + System.out.println(" annotation: " + annotationString); + if (annotationString.startsWith("@org.junit.Test")) { + isTest = true; + } + } + if (isTest) { + System.out.println(" invoking method: " + method.getName()); + try { + method.invoke(obj); + } catch (IllegalAccessException iae) { + System.out.println("Got IllegalAccessException!!!"); + System.out.println(iae.getCause()); + } catch (InvocationTargetException ite) { + System.out.println("Got InvocationTargetException!!!"); + //System.out.println(ite.getCause()); + throw ite; + } + } + } + } catch (ClassNotFoundException cnfe) { + System.out.println("Class not found: " + args[0]); + } catch (java.lang.IllegalAccessError iae) { + System.out.println("Skipping test: " + args[0]); + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/TestStreamApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/TestStreamApp.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/TestStreamApp.java @@ -0,0 +1,94 @@ +/* + * 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.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; + +public class TestStreamApp { + public static final String testAnnotation = "@org.testng.annotations.Test"; + public static void main(String args[]) throws Exception { + try { + Class testClass = Class.forName(args[0]); + System.out.println(testClass); + Annotation[] classAnnotations = null; + boolean classHasTestAnnotation = false; + try { + classAnnotations= testClass.getDeclaredAnnotations(); + for (Annotation classAnnotation : classAnnotations) { + String annoString = classAnnotation.toString(); + System.out.println(" class annotation: " + annoString); + if (annoString.startsWith(testAnnotation)) { + classHasTestAnnotation = true; + } + } + } catch (Throwable th) { + System.out.println("Skip class annotation"); + } + Object obj = testClass.newInstance(); + final List allMethods = new ArrayList(Arrays.asList(testClass.getDeclaredMethods())); + for (final Method method : allMethods) { + //System.out.println(method.toString()); + Annotation[] annotations = method.getDeclaredAnnotations(); + boolean isTest = false; + /* + if (classHasTestAnnotation) { + if (method.toString().indexOf(args[0] + ".test") != -1) { + isTest = true; + } + } else { + for (Annotation annotation : annotations) { + String annotationString = annotation.toString(); + System.out.println(" annotation: " + annotationString); + if (annotationString.startsWith(testAnnotation)) { + isTest = true; + } + } + } + */ + if (method.getName().startsWith("test")) { + isTest = true; + } + if (isTest) { + System.out.println(" invoking method: " + method.getName()); + try { + method.invoke(obj); + } catch (IllegalAccessException iae) { + System.out.println("Got IllegalAccessException!!!"); + System.out.println(iae.getCause()); + } catch (InvocationTargetException ite) { + System.out.println("Got InvocationTargetException!!!"); + //System.out.println(ite.getCause()); + throw ite; + } + } + } + } catch (ClassNotFoundException cnfe) { + System.out.println("Class not found!"); + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/UnsafeAnonymousApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/UnsafeAnonymousApp.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/UnsafeAnonymousApp.java @@ -0,0 +1,39 @@ +/* + * 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 jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.internal.misc.Unsafe; + +public class UnsafeAnonymousApp { + + static byte klassbuf[] = InMemoryJavaCompiler.compile("TestClass", + "public class TestClass { " + + "} "); + + public static void main(String args[]) throws Exception { + Unsafe unsafe = Unsafe.getUnsafe(); + + Class klass = unsafe.defineAnonymousClass(UnsafeAnonymousApp.class, klassbuf, new Object[0]); + Object obj = klass.newInstance(); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/UsedAllArchivedLambdasApp.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/UsedAllArchivedLambdasApp.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes/UsedAllArchivedLambdasApp.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019, 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.util.HashMap; +import sun.hotspot.WhiteBox; + +public class UsedAllArchivedLambdasApp { + public static boolean isRuntime; + public static int NUM_CLASSES = 3; + public static WhiteBox wb = WhiteBox.getWhiteBox(); + public static HashMap, Class> inArchiveMap = new HashMap<>(); + public static HashMap, Class> notInArchiveMap = new HashMap<>(); + + public static void main(String args[]) { + isRuntime = (args.length == 1 && args[0].equals("run")) ? true : false; + {Runnable run1 = UsedAllArchivedLambdasApp::myrun; run1.run();} + {Runnable run1 = UsedAllArchivedLambdasApp::myrun; run1.run();} + {Runnable run1 = UsedAllArchivedLambdasApp::myrun; run1.run();} + if (isRuntime) { + {Runnable run1 = UsedAllArchivedLambdasApp::myrun; run1.run();} + {Runnable run1 = UsedAllArchivedLambdasApp::myrun; run1.run();} + {Runnable run1 = UsedAllArchivedLambdasApp::myrun; run1.run();} + } + + int mapSize = 0; + + if (isRuntime) { + mapSize = inArchiveMap.size(); + System.out.println("Number of lambda classes in archive: " + mapSize); + if (mapSize != NUM_CLASSES) { + throw new RuntimeException("Expected number of lambda classes in archive is " + + NUM_CLASSES + " but got " + mapSize); + } + mapSize = notInArchiveMap.size(); + System.out.println("Number of lambda classes in archive: " + mapSize); + if (mapSize != NUM_CLASSES) { + throw new RuntimeException("Expected number of lambda classes NOT in archive is " + + NUM_CLASSES + " but got " + mapSize); + } + } + } + + static void myrun() { + Class c = LambdaVerification.getCallerClass(1); + if (isRuntime) { + if (wb.isSharedClass(c)) { + System.out.println(c.getName() + " is a shared class"); + inArchiveMap.put(c,c); + } else { + System.out.println(c.getName() + " is NOT a shared class"); + notInArchiveMap.put(c,c); + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/javaldr/AnonVmClassesDuringDump.java b/test/hotspot/jtreg/runtime/cds/appcds/javaldr/AnonVmClassesDuringDump.java --- a/test/hotspot/jtreg/runtime/cds/appcds/javaldr/AnonVmClassesDuringDump.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/javaldr/AnonVmClassesDuringDump.java @@ -45,6 +45,9 @@ public static String cdsDiagnosticOption = "-XX:+AllowArchivingWithJavaAgent"; + public static final boolean dynamicMode = + Boolean.getBoolean(System.getProperty("test.dynamic.cds.archive", "false")); + public static void main(String[] args) throws Throwable { String agentJar = ClassFileInstaller.writeJar("AnonVmClassesDuringDumpTransformer.jar", @@ -71,13 +74,17 @@ // during run time, anonymous classes shouldn't be loaded from the archive TestCommon.run("-cp", appJar, "-XX:+UnlockDiagnosticVMOptions", cdsDiagnosticOption, Hello.class.getName()) - .assertNormalExit(output -> output.shouldNotMatch(pattern)); + .assertNormalExit(dynamicMode ? + output -> output.shouldMatch(pattern) : + output -> output.shouldNotMatch(pattern)); // inspect the archive and make sure no anonymous class is in there TestCommon.run("-cp", appJar, "-XX:+UnlockDiagnosticVMOptions", cdsDiagnosticOption, "-XX:+PrintSharedArchiveAndExit", "-XX:+PrintSharedDictionary", Hello.class.getName()) - .assertNormalExit(output -> output.shouldNotMatch(class_pattern)); + .assertNormalExit(dynamicMode ? + output -> output.shouldMatch(pattern) : + output -> output.shouldNotMatch(pattern)); } } diff --git a/test/lib/jdk/test/lib/Utils.java b/test/lib/jdk/test/lib/Utils.java --- a/test/lib/jdk/test/lib/Utils.java +++ b/test/lib/jdk/test/lib/Utils.java @@ -26,6 +26,7 @@ import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; +import java.lang.reflect.Method; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -834,7 +835,7 @@ public static String getTestName() { String result = null; // If we are using testng, then we should be able to load the "Test" annotation. - Class testClassAnnotation; + Class testClassAnnotation, junitTestClassAnnotation; try { testClassAnnotation = (Class)Class.forName("org.testng.annotations.Test"); @@ -842,13 +843,21 @@ testClassAnnotation = null; } + // If we are using junit, then we should be able to load the "Test" annotation. + try { + junitTestClassAnnotation = (Class)Class.forName("org.junit.Test"); + } catch (ClassNotFoundException e) { + junitTestClassAnnotation = null; + } + StackTraceElement[] elms = (new Throwable()).getStackTrace(); for (StackTraceElement n: elms) { String className = n.getClassName(); // If this is a "main" method, then use its class name, but only - // if we are not using testng. - if (testClassAnnotation == null && "main".equals(n.getMethodName())) { + // if we are not using testng or junit. + if (testClassAnnotation == null && junitTestClassAnnotation == null && + "main".equals(n.getMethodName())) { result = className; break; } @@ -867,6 +876,26 @@ throw new RuntimeException("Unexpected exception: " + e, e); } } + + // If this is a junit test, the test will have no "main" method. We can + // detect a junit test class by going through all the methods and + // check if the method has the org.junit.Test annotation. If present, + // then use the name of this class. + if (junitTestClassAnnotation != null) { + try { + Class c = Class.forName(className); + Method[] methods = c.getMethods(); + for (Method method : methods) { + if (method.getName().equals(n.getMethodName()) && + method.isAnnotationPresent(junitTestClassAnnotation)) { + result = className; + break; + } + } + } catch (ClassNotFoundException e) { + throw new RuntimeException("Unexpected exception: " + e, e); + } + } } if (result == null) {