| /* |
| * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| #ifndef SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP |
| #define SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP |
| |
| #include "cds/archiveBuilder.hpp" |
| #include "cds/metaspaceShared.hpp" |
| #include "classfile/javaClasses.hpp" |
| #include "memory/metaspaceClosure.hpp" |
| #include "utilities/growableArray.hpp" |
| #include "utilities/resourceHash.hpp" |
| |
| // This file contains *legacy* optimization for lambdas before JEP 483. May be removed in the future. |
| // |
| // The functionalties in this file are used only when CDSConfig::is_dumping_lambdas_in_legacy_mode() |
| // returns true during the creation of a CDS archive. |
| // |
| // With the legacy optimization, generated lambda proxy classes (with names such as |
| // java.util.ResourceBundle$Control$$Lambda/0x80000001d) are stored inside the CDS archive, accessible |
| // by LambdaProxyClassDictionary::find_proxy_class(). This saves part of the time for resolving a |
| // lambda call site (proxy class generation). However, a significant portion of the cost of |
| // the lambda call site resolution still remains in the production run. |
| // |
| // In contrast, with JEP 483, the entire lambda call site (starting from the constant pool entry), is |
| // resolved in the AOT cache assembly phase. No extra resolution is needed in the production run. |
| |
| class InstanceKlass; |
| class Method; |
| class MetaspaceClosure; |
| class Symbol; |
| class outputStream; |
| |
| class LambdaProxyClassKey { |
| 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 metaspace_pointers_do(MetaspaceClosure* it) { |
| it->push(&_caller_ik); |
| it->push(&_invoked_name); |
| it->push(&_invoked_type); |
| it->push(&_method_type); |
| it->push(&_member_method); |
| it->push(&_instantiated_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; |
| |
| static unsigned int dumptime_hash(Symbol* sym) { |
| if (sym == nullptr) { |
| // _invoked_name maybe null |
| return 0; |
| } |
| return java_lang_String::hash_code((const jbyte*)sym->bytes(), sym->utf8_length()); |
| } |
| |
| unsigned int dumptime_hash() const { |
| return dumptime_hash(_caller_ik->name()) + |
| dumptime_hash(_invoked_name) + |
| dumptime_hash(_invoked_type) + |
| dumptime_hash(_method_type) + |
| dumptime_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)); |
| } |
| |
| InstanceKlass* caller_ik() const { return _caller_ik; } |
| Symbol* invoked_name() const { return _invoked_name; } |
| Symbol* invoked_type() const { return _invoked_type; } |
| Symbol* method_type() const { return _method_type; } |
| Method* member_method() const { return _member_method; } |
| Symbol* instantiated_method_type() const { return _instantiated_method_type; } |
| |
| #ifndef PRODUCT |
| void print_on(outputStream* st) const; |
| #endif |
| }; |
| |
| class RunTimeLambdaProxyClassKey { |
| u4 _caller_ik; |
| u4 _invoked_name; |
| u4 _invoked_type; |
| u4 _method_type; |
| u4 _member_method; |
| u4 _instantiated_method_type; |
| |
| RunTimeLambdaProxyClassKey(u4 caller_ik, |
| u4 invoked_name, |
| u4 invoked_type, |
| u4 method_type, |
| u4 member_method, |
| u4 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) {} |
| |
| public: |
| static RunTimeLambdaProxyClassKey init_for_dumptime(LambdaProxyClassKey& key) { |
| assert(ArchiveBuilder::is_active(), "sanity"); |
| ArchiveBuilder* b = ArchiveBuilder::current(); |
| |
| u4 caller_ik = b->any_to_offset_u4(key.caller_ik()); |
| u4 invoked_name = b->any_to_offset_u4(key.invoked_name()); |
| u4 invoked_type = b->any_to_offset_u4(key.invoked_type()); |
| u4 method_type = b->any_to_offset_u4(key.method_type()); |
| u4 member_method = b->any_or_null_to_offset_u4(key.member_method()); // could be null |
| u4 instantiated_method_type = b->any_to_offset_u4(key.instantiated_method_type()); |
| |
| return RunTimeLambdaProxyClassKey(caller_ik, invoked_name, invoked_type, method_type, |
| member_method, instantiated_method_type); |
| } |
| |
| static RunTimeLambdaProxyClassKey init_for_runtime(InstanceKlass* caller_ik, |
| Symbol* invoked_name, |
| Symbol* invoked_type, |
| Symbol* method_type, |
| Method* member_method, |
| Symbol* instantiated_method_type) { |
| // All parameters must be in shared space, or else you'd get an assert in |
| // ArchiveUtils::to_offset(). |
| return RunTimeLambdaProxyClassKey(ArchiveUtils::archived_address_to_offset(caller_ik), |
| ArchiveUtils::archived_address_to_offset(invoked_name), |
| ArchiveUtils::archived_address_to_offset(invoked_type), |
| ArchiveUtils::archived_address_to_offset(method_type), |
| ArchiveUtils::archived_address_or_null_to_offset(member_method), // could be null |
| ArchiveUtils::archived_address_to_offset(instantiated_method_type)); |
| } |
| |
| unsigned int hash() const; |
| bool equals(RunTimeLambdaProxyClassKey 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; |
| } |
| |
| #ifndef PRODUCT |
| void print_on(outputStream* st) const; |
| #endif |
| }; |
| |
| class DumpTimeLambdaProxyClassInfo { |
| public: |
| GrowableArray<InstanceKlass*>* _proxy_klasses; |
| DumpTimeLambdaProxyClassInfo() : _proxy_klasses(nullptr) {} |
| DumpTimeLambdaProxyClassInfo& operator=(const DumpTimeLambdaProxyClassInfo&) = delete; |
| ~DumpTimeLambdaProxyClassInfo(); |
| |
| void add_proxy_klass(InstanceKlass* proxy_klass) { |
| if (_proxy_klasses == nullptr) { |
| _proxy_klasses = new (mtClassShared) GrowableArray<InstanceKlass*>(5, mtClassShared); |
| } |
| assert(_proxy_klasses != nullptr, "sanity"); |
| _proxy_klasses->append(proxy_klass); |
| } |
| |
| void metaspace_pointers_do(MetaspaceClosure* it) { |
| for (int i=0; i<_proxy_klasses->length(); i++) { |
| it->push(_proxy_klasses->adr_at(i)); |
| } |
| } |
| }; |
| |
| class RunTimeLambdaProxyClassInfo { |
| RunTimeLambdaProxyClassKey _key; |
| InstanceKlass* _proxy_klass_head; |
| public: |
| RunTimeLambdaProxyClassInfo(RunTimeLambdaProxyClassKey key, InstanceKlass* proxy_klass_head) : |
| _key(key), _proxy_klass_head(proxy_klass_head) {} |
| |
| InstanceKlass* proxy_klass_head() const { return _proxy_klass_head; } |
| |
| // Used by LambdaProxyClassDictionary to implement OffsetCompactHashtable::EQUALS |
| static inline bool EQUALS( |
| const RunTimeLambdaProxyClassInfo* value, RunTimeLambdaProxyClassKey* key, int len_unused) { |
| return (value->_key.equals(*key)); |
| } |
| void init(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info); |
| |
| unsigned int hash() const { |
| return _key.hash(); |
| } |
| RunTimeLambdaProxyClassKey key() const { |
| return _key; |
| } |
| #ifndef PRODUCT |
| void print_on(outputStream* st) const; |
| #endif |
| }; |
| |
| class DumpTimeLambdaProxyClassDictionary |
| : public ResourceHashtable<LambdaProxyClassKey, |
| DumpTimeLambdaProxyClassInfo, |
| 137, // prime number |
| AnyObj::C_HEAP, |
| mtClassShared, |
| LambdaProxyClassKey::DUMPTIME_HASH, |
| LambdaProxyClassKey::DUMPTIME_EQUALS> { |
| public: |
| DumpTimeLambdaProxyClassDictionary() : _count(0) {} |
| int _count; |
| }; |
| |
| // *Legacy* optimization for lambdas before JEP 483. May be removed in the future. |
| class LambdaProxyClassDictionary : public OffsetCompactHashtable< |
| RunTimeLambdaProxyClassKey*, |
| const RunTimeLambdaProxyClassInfo*, |
| RunTimeLambdaProxyClassInfo::EQUALS> |
| { |
| private: |
| class CleanupDumpTimeLambdaProxyClassTable; |
| static DumpTimeLambdaProxyClassDictionary* _dumptime_table; |
| static LambdaProxyClassDictionary _runtime_static_table; // for static CDS archive |
| static LambdaProxyClassDictionary _runtime_dynamic_table; // for dynamic CDS archive |
| |
| static void add_to_dumptime_table(LambdaProxyClassKey& key, |
| InstanceKlass* proxy_klass); |
| static InstanceKlass* find_lambda_proxy_class(const RunTimeLambdaProxyClassInfo* info); |
| static InstanceKlass* find_lambda_proxy_class(InstanceKlass* caller_ik, |
| Symbol* invoked_name, |
| Symbol* invoked_type, |
| Symbol* method_type, |
| Method* member_method, |
| Symbol* instantiated_method_type); |
| static InstanceKlass* load_and_init_lambda_proxy_class(InstanceKlass* lambda_ik, |
| InstanceKlass* caller_ik, TRAPS); |
| static void reset_registered_lambda_proxy_class(InstanceKlass* ik); |
| static InstanceKlass* get_shared_nest_host(InstanceKlass* lambda_ik); |
| |
| public: |
| static void dumptime_init(); |
| static void dumptime_classes_do(MetaspaceClosure* it); |
| 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, |
| TRAPS); |
| static bool is_supported_invokedynamic(BootstrapInfo* bsi); |
| static bool is_registered_lambda_proxy_class(InstanceKlass* ik); |
| static InstanceKlass* load_shared_lambda_proxy_class(InstanceKlass* caller_ik, |
| Symbol* invoked_name, |
| Symbol* invoked_type, |
| Symbol* method_type, |
| Method* member_method, |
| Symbol* instantiated_method_type, |
| TRAPS); |
| static void write_dictionary(bool is_static_archive); |
| static void adjust_dumptime_table(); |
| static void cleanup_dumptime_table(); |
| |
| static void reset_dictionary(bool is_static_archive) { |
| if (is_static_archive) { |
| _runtime_static_table.reset(); |
| } else { |
| _runtime_dynamic_table.reset(); |
| } |
| } |
| |
| static void serialize(SerializeClosure* soc, bool is_static_archive) { |
| if (is_static_archive) { |
| _runtime_static_table.serialize_header(soc); |
| } else { |
| _runtime_dynamic_table.serialize_header(soc); |
| } |
| } |
| |
| static void print_on(const char* prefix, outputStream* st, |
| int start_index, bool is_static_archive); |
| static void print_statistics(outputStream* st, bool is_static_archive); |
| }; |
| |
| #endif // SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP |