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 // Try to preserve the order. get_shared_lambda_proxy_class returns shared classes in reverse order.
214 _proxy_klasses->insert_before(0, proxy_klass);
215 }
216
217 void metaspace_pointers_do(MetaspaceClosure* it) {
218 for (int i=0; i<_proxy_klasses->length(); i++) {
219 it->push(_proxy_klasses->adr_at(i));
220 }
221 }
222 };
223
224 class RunTimeLambdaProxyClassInfo {
225 RunTimeLambdaProxyClassKey _key;
226 InstanceKlass* _proxy_klass_head;
227 public:
228 RunTimeLambdaProxyClassInfo(RunTimeLambdaProxyClassKey key, InstanceKlass* proxy_klass_head) :
229 _key(key), _proxy_klass_head(proxy_klass_head) {}
230
231 InstanceKlass* proxy_klass_head() const { return _proxy_klass_head; }
232
233 // Used by LambdaProxyClassDictionary to implement OffsetCompactHashtable::EQUALS
234 static inline bool EQUALS(
235 const RunTimeLambdaProxyClassInfo* value, RunTimeLambdaProxyClassKey* key, int len_unused) {
236 return (value->_key.equals(*key));
237 }
238 void init(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info);
239
240 unsigned int hash() const {
241 return _key.hash();
242 }
243 RunTimeLambdaProxyClassKey key() const {
244 return _key;
245 }
246 #ifndef PRODUCT
247 void print_on(outputStream* st) const;
248 #endif
249 };
250
251 class DumpTimeLambdaProxyClassDictionary
252 : public HashTable<LambdaProxyClassKey,
253 DumpTimeLambdaProxyClassInfo,
254 137, // prime number
255 AnyObj::C_HEAP,
256 mtClassShared,
257 LambdaProxyClassKey::DUMPTIME_HASH,
258 LambdaProxyClassKey::DUMPTIME_EQUALS> {
259 public:
260 DumpTimeLambdaProxyClassDictionary() : _count(0) {}
261 int _count;
262 };
263
264 // *Legacy* optimization for lambdas before JEP 483. May be removed in the future.
265 class LambdaProxyClassDictionary : public OffsetCompactHashtable<
266 RunTimeLambdaProxyClassKey*,
267 const RunTimeLambdaProxyClassInfo*,
268 RunTimeLambdaProxyClassInfo::EQUALS>
269 {
270 private:
271 class CleanupDumpTimeLambdaProxyClassTable;
272 static DumpTimeLambdaProxyClassDictionary* _dumptime_table;
273 static LambdaProxyClassDictionary _runtime_static_table; // for static CDS archive
274 static LambdaProxyClassDictionary _runtime_dynamic_table; // for dynamic CDS archive
275
276 static void add_to_dumptime_table(LambdaProxyClassKey& key,
277 InstanceKlass* proxy_klass);
278 static InstanceKlass* find_lambda_proxy_class(const RunTimeLambdaProxyClassInfo* info);
279 static InstanceKlass* find_lambda_proxy_class(InstanceKlass* caller_ik,
280 Symbol* invoked_name,
281 Symbol* invoked_type,
282 Symbol* method_type,
283 Method* member_method,
284 Symbol* instantiated_method_type);
285 static InstanceKlass* load_and_init_lambda_proxy_class(InstanceKlass* lambda_ik,
286 InstanceKlass* caller_ik, TRAPS);
287 static void reset_registered_lambda_proxy_class(InstanceKlass* ik);
288 static InstanceKlass* get_shared_nest_host(InstanceKlass* lambda_ik);
289
290 public:
291 static void dumptime_init();
292 static void dumptime_classes_do(MetaspaceClosure* it);
293 static void add_lambda_proxy_class(InstanceKlass* caller_ik,
294 InstanceKlass* lambda_ik,
295 Symbol* invoked_name,
296 Symbol* invoked_type,
297 Symbol* method_type,
298 Method* member_method,
299 Symbol* instantiated_method_type,
300 TRAPS);
301 static bool is_supported_invokedynamic(BootstrapInfo* bsi);
302 static bool is_registered_lambda_proxy_class(InstanceKlass* ik);
303 static InstanceKlass* load_shared_lambda_proxy_class(InstanceKlass* caller_ik,
304 Symbol* invoked_name,
305 Symbol* invoked_type,
306 Symbol* method_type,
307 Method* member_method,
308 Symbol* instantiated_method_type,
309 TRAPS);
310 static void write_dictionary(bool is_static_archive);
311 static void adjust_dumptime_table();
312 static void cleanup_dumptime_table();
313
314 static void reset_dictionary(bool is_static_archive) {
315 if (is_static_archive) {
316 _runtime_static_table.reset();
317 } else {
318 _runtime_dynamic_table.reset();
319 }
320 }
321
322 static void serialize(SerializeClosure* soc, bool is_static_archive) {
323 if (is_static_archive) {
324 _runtime_static_table.serialize_header(soc);
325 } else {
326 _runtime_dynamic_table.serialize_header(soc);
327 }
328 }
329
330 static void print_on(const char* prefix, outputStream* st,
331 int start_index, bool is_static_archive);
332 static void print_statistics(outputStream* st, bool is_static_archive);
333 };
334
335 #endif // SHARE_CDS_LAMBDAPROXYCLASSINFO_HPP