/* * Copyright (c) 2021, 2026, 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/aotCompressedPointers.hpp" #include "cds/aotMetaspace.hpp" #include "classfile/compactHashtable.hpp" #include "classfile/javaClasses.hpp" #include "memory/metaspaceClosure.hpp" #include "utilities/growableArray.hpp" #include "utilities/hashTable.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 { using narrowPtr = AOTCompressedPointers::narrowPtr; narrowPtr _caller_ik; narrowPtr _invoked_name; narrowPtr _invoked_type; narrowPtr _method_type; narrowPtr _member_method; narrowPtr _instantiated_method_type; RunTimeLambdaProxyClassKey(narrowPtr caller_ik, narrowPtr invoked_name, narrowPtr invoked_type, narrowPtr method_type, narrowPtr member_method, narrowPtr 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) { narrowPtr caller_ik = AOTCompressedPointers::encode_not_null(key.caller_ik()); narrowPtr invoked_name = AOTCompressedPointers::encode_not_null(key.invoked_name()); narrowPtr invoked_type = AOTCompressedPointers::encode_not_null(key.invoked_type()); narrowPtr method_type = AOTCompressedPointers::encode_not_null(key.method_type()); narrowPtr member_method = AOTCompressedPointers::encode(key.member_method()); // could be null narrowPtr instantiated_method_type = AOTCompressedPointers::encode_not_null(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(AOTCompressedPointers::encode_address_in_cache(caller_ik), AOTCompressedPointers::encode_address_in_cache(invoked_name), AOTCompressedPointers::encode_address_in_cache(invoked_type), AOTCompressedPointers::encode_address_in_cache(method_type), AOTCompressedPointers::encode_address_in_cache_or_null(member_method), // could be null AOTCompressedPointers::encode_address_in_cache(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* _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(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 HashTable { 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