1 /*
2 * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25 #ifndef SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP
26 #define SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP
27
28 #include "cds/aotCompressedPointers.hpp"
29 #include "cds/aotMetaspace.hpp"
30 #include "classfile/compactHashtable.hpp"
31 #include "classfile/javaClasses.hpp"
32 #include "memory/metaspaceClosure.hpp"
33 #include "utilities/growableArray.hpp"
34 #include "utilities/hashTable.hpp"
35
36 // This file contains *legacy* optimization for lambdas before JEP 483. May be removed in the future.
37 //
38 // The functionalties in this file are used only when CDSConfig::is_dumping_lambdas_in_legacy_mode()
39 // returns true during the creation of a CDS archive.
40 //
41 // With the legacy optimization, generated lambda proxy classes (with names such as
42 // java.util.ResourceBundle$Control$$Lambda/0x80000001d) are stored inside the CDS archive, accessible
43 // by LambdaProxyClassDictionary::find_proxy_class(). This saves part of the time for resolving a
44 // lambda call site (proxy class generation). However, a significant portion of the cost of
45 // the lambda call site resolution still remains in the production run.
46 //
47 // In contrast, with JEP 483, the entire lambda call site (starting from the constant pool entry), is
48 // resolved in the AOT cache assembly phase. No extra resolution is needed in the production run.
49
50 class InstanceKlass;
51 class Method;
52 class MetaspaceClosure;
53 class Symbol;
54 class outputStream;
55
56 class LambdaProxyClassKey {
57 InstanceKlass* _caller_ik;
58 Symbol* _invoked_name;
59 Symbol* _invoked_type;
60 Symbol* _method_type;
61 Method* _member_method;
62 Symbol* _instantiated_method_type;
63
64 public:
65 LambdaProxyClassKey(InstanceKlass* caller_ik,
66 Symbol* invoked_name,
67 Symbol* invoked_type,
68 Symbol* method_type,
69 Method* member_method,
70 Symbol* instantiated_method_type) :
71 _caller_ik(caller_ik),
72 _invoked_name(invoked_name),
73 _invoked_type(invoked_type),
74 _method_type(method_type),
75 _member_method(member_method),
76 _instantiated_method_type(instantiated_method_type) {}
77
78 void metaspace_pointers_do(MetaspaceClosure* it) {
79 it->push(&_caller_ik);
80 it->push(&_invoked_name);
81 it->push(&_invoked_type);
82 it->push(&_method_type);
83 it->push(&_member_method);
84 it->push(&_instantiated_method_type);
85 }
86
87 bool equals(LambdaProxyClassKey const& other) const {
88 return _caller_ik == other._caller_ik &&
89 _invoked_name == other._invoked_name &&
90 _invoked_type == other._invoked_type &&
91 _method_type == other._method_type &&
92 _member_method == other._member_method &&
93 _instantiated_method_type == other._instantiated_method_type;
94 }
95
96 unsigned int hash() const;
97
98 static unsigned int dumptime_hash(Symbol* sym) {
99 if (sym == nullptr) {
100 // _invoked_name maybe null
101 return 0;
102 }
103 return java_lang_String::hash_code((const jbyte*)sym->bytes(), sym->utf8_length());
104 }
105
106 unsigned int dumptime_hash() const {
107 return dumptime_hash(_caller_ik->name()) +
108 dumptime_hash(_invoked_name) +
109 dumptime_hash(_invoked_type) +
110 dumptime_hash(_method_type) +
111 dumptime_hash(_instantiated_method_type);
112 }
113
114 static inline unsigned int DUMPTIME_HASH(LambdaProxyClassKey const& key) {
115 return (key.dumptime_hash());
116 }
117
118 static inline bool DUMPTIME_EQUALS(
119 LambdaProxyClassKey const& k1, LambdaProxyClassKey const& k2) {
120 return (k1.equals(k2));
121 }
122
123 InstanceKlass* caller_ik() const { return _caller_ik; }
124 Symbol* invoked_name() const { return _invoked_name; }
125 Symbol* invoked_type() const { return _invoked_type; }
126 Symbol* method_type() const { return _method_type; }
127 Method* member_method() const { return _member_method; }
128 Symbol* instantiated_method_type() const { return _instantiated_method_type; }
129
130 #ifndef PRODUCT
131 void print_on(outputStream* st) const;
132 #endif
133 };
134
135 class RunTimeLambdaProxyClassKey {
136 using narrowPtr = AOTCompressedPointers::narrowPtr;
137 narrowPtr _caller_ik;
138 narrowPtr _invoked_name;
139 narrowPtr _invoked_type;
140 narrowPtr _method_type;
141 narrowPtr _member_method;
142 narrowPtr _instantiated_method_type;
143
144 RunTimeLambdaProxyClassKey(narrowPtr caller_ik,
145 narrowPtr invoked_name,
146 narrowPtr invoked_type,
147 narrowPtr method_type,
148 narrowPtr member_method,
149 narrowPtr instantiated_method_type) :
150 _caller_ik(caller_ik),
151 _invoked_name(invoked_name),
152 _invoked_type(invoked_type),
153 _method_type(method_type),
154 _member_method(member_method),
155 _instantiated_method_type(instantiated_method_type) {}
156
157 public:
158 static RunTimeLambdaProxyClassKey init_for_dumptime(LambdaProxyClassKey& key) {
159 narrowPtr caller_ik = AOTCompressedPointers::encode_not_null(key.caller_ik());
160 narrowPtr invoked_name = AOTCompressedPointers::encode_not_null(key.invoked_name());
161 narrowPtr invoked_type = AOTCompressedPointers::encode_not_null(key.invoked_type());
162 narrowPtr method_type = AOTCompressedPointers::encode_not_null(key.method_type());
163 narrowPtr member_method = AOTCompressedPointers::encode(key.member_method()); // could be null
164 narrowPtr instantiated_method_type = AOTCompressedPointers::encode_not_null(key.instantiated_method_type());
165
166 return RunTimeLambdaProxyClassKey(caller_ik, invoked_name, invoked_type, method_type,
167 member_method, instantiated_method_type);
168 }
169
170 static RunTimeLambdaProxyClassKey init_for_runtime(InstanceKlass* caller_ik,
171 Symbol* invoked_name,
172 Symbol* invoked_type,
173 Symbol* method_type,
174 Method* member_method,
175 Symbol* instantiated_method_type) {
176 // All parameters must be in shared space, or else you'd get an assert in
177 // ArchiveUtils::to_offset().
178 return RunTimeLambdaProxyClassKey(AOTCompressedPointers::encode_address_in_cache(caller_ik),
179 AOTCompressedPointers::encode_address_in_cache(invoked_name),
180 AOTCompressedPointers::encode_address_in_cache(invoked_type),
181 AOTCompressedPointers::encode_address_in_cache(method_type),
182 AOTCompressedPointers::encode_address_in_cache_or_null(member_method), // could be null
183 AOTCompressedPointers::encode_address_in_cache(instantiated_method_type));
184 }
185
186 unsigned int hash() const;
187 bool equals(RunTimeLambdaProxyClassKey const& other) const {
188 return _caller_ik == other._caller_ik &&
189 _invoked_name == other._invoked_name &&
190 _invoked_type == other._invoked_type &&
191 _method_type == other._method_type &&
192 _member_method == other._member_method &&
193 _instantiated_method_type == other._instantiated_method_type;
194 }
195
196 #ifndef PRODUCT
197 void print_on(outputStream* st) const;
198 #endif
199 };
200
201 class DumpTimeLambdaProxyClassInfo {
202 public:
203 GrowableArray<InstanceKlass*>* _proxy_klasses;
204 DumpTimeLambdaProxyClassInfo() : _proxy_klasses(nullptr) {}
205 DumpTimeLambdaProxyClassInfo& operator=(const DumpTimeLambdaProxyClassInfo&) = delete;
206 ~DumpTimeLambdaProxyClassInfo();
207
208 void add_proxy_klass(InstanceKlass* proxy_klass) {
209 if (_proxy_klasses == nullptr) {
210 _proxy_klasses = new (mtClassShared) GrowableArray<InstanceKlass*>(5, mtClassShared);
211 }
212 assert(_proxy_klasses != nullptr, "sanity");
213 _proxy_klasses->append(proxy_klass);
214 }
215
216 void metaspace_pointers_do(MetaspaceClosure* it) {
217 for (int i=0; i<_proxy_klasses->length(); i++) {
218 it->push(_proxy_klasses->adr_at(i));
219 }
220 }
221 };
222
223 class RunTimeLambdaProxyClassInfo {
224 RunTimeLambdaProxyClassKey _key;
225 InstanceKlass* _proxy_klass_head;
226 public:
227 RunTimeLambdaProxyClassInfo(RunTimeLambdaProxyClassKey key, InstanceKlass* proxy_klass_head) :
228 _key(key), _proxy_klass_head(proxy_klass_head) {}
229
230 InstanceKlass* proxy_klass_head() const { return _proxy_klass_head; }
231
232 // Used by LambdaProxyClassDictionary to implement OffsetCompactHashtable::EQUALS
233 static inline bool EQUALS(
234 const RunTimeLambdaProxyClassInfo* value, RunTimeLambdaProxyClassKey* key, int len_unused) {
235 return (value->_key.equals(*key));
236 }
237 void init(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info);
238
239 unsigned int hash() const {
240 return _key.hash();
241 }
242 RunTimeLambdaProxyClassKey key() const {
243 return _key;
244 }
245 #ifndef PRODUCT
246 void print_on(outputStream* st) const;
247 #endif
248 };
249
250 class DumpTimeLambdaProxyClassDictionary
251 : public HashTable<LambdaProxyClassKey,
252 DumpTimeLambdaProxyClassInfo,
253 137, // prime number
254 AnyObj::C_HEAP,
255 mtClassShared,
256 LambdaProxyClassKey::DUMPTIME_HASH,
257 LambdaProxyClassKey::DUMPTIME_EQUALS> {
258 public:
259 DumpTimeLambdaProxyClassDictionary() : _count(0) {}
260 int _count;
261 };
262
263 // *Legacy* optimization for lambdas before JEP 483. May be removed in the future.
264 class LambdaProxyClassDictionary : public OffsetCompactHashtable<
265 RunTimeLambdaProxyClassKey*,
266 const RunTimeLambdaProxyClassInfo*,
267 RunTimeLambdaProxyClassInfo::EQUALS>
268 {
269 private:
270 class CleanupDumpTimeLambdaProxyClassTable;
271 static DumpTimeLambdaProxyClassDictionary* _dumptime_table;
272 static LambdaProxyClassDictionary _runtime_static_table; // for static CDS archive
273 static LambdaProxyClassDictionary _runtime_dynamic_table; // for dynamic CDS archive
274
275 static void add_to_dumptime_table(LambdaProxyClassKey& key,
276 InstanceKlass* proxy_klass);
277 static InstanceKlass* find_lambda_proxy_class(const RunTimeLambdaProxyClassInfo* info);
278 static InstanceKlass* find_lambda_proxy_class(InstanceKlass* caller_ik,
279 Symbol* invoked_name,
280 Symbol* invoked_type,
281 Symbol* method_type,
282 Method* member_method,
283 Symbol* instantiated_method_type);
284 static InstanceKlass* load_and_init_lambda_proxy_class(InstanceKlass* lambda_ik,
285 InstanceKlass* caller_ik, TRAPS);
286 static void reset_registered_lambda_proxy_class(InstanceKlass* ik);
287 static InstanceKlass* get_shared_nest_host(InstanceKlass* lambda_ik);
288
289 public:
290 static void dumptime_init();
291 static void dumptime_classes_do(MetaspaceClosure* it);
292 static void add_lambda_proxy_class(InstanceKlass* caller_ik,
293 InstanceKlass* lambda_ik,
294 Symbol* invoked_name,
295 Symbol* invoked_type,
296 Symbol* method_type,
297 Method* member_method,
298 Symbol* instantiated_method_type,
299 TRAPS);
300 static bool is_supported_invokedynamic(BootstrapInfo* bsi);
301 static bool is_registered_lambda_proxy_class(InstanceKlass* ik);
302 static InstanceKlass* load_shared_lambda_proxy_class(InstanceKlass* caller_ik,
303 Symbol* invoked_name,
304 Symbol* invoked_type,
305 Symbol* method_type,
306 Method* member_method,
307 Symbol* instantiated_method_type,
308 TRAPS);
309 static void write_dictionary(bool is_static_archive);
310 static void adjust_dumptime_table();
311 static void cleanup_dumptime_table();
312
313 static void reset_dictionary(bool is_static_archive) {
314 if (is_static_archive) {
315 _runtime_static_table.reset();
316 } else {
317 _runtime_dynamic_table.reset();
318 }
319 }
320
321 static void serialize(SerializeClosure* soc, bool is_static_archive) {
322 if (is_static_archive) {
323 _runtime_static_table.serialize_header(soc);
324 } else {
325 _runtime_dynamic_table.serialize_header(soc);
326 }
327 }
328
329 static void print_on(const char* prefix, outputStream* st,
330 int start_index, bool is_static_archive);
331 static void print_statistics(outputStream* st, bool is_static_archive);
332 };
333
334 #endif // SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP