1 /*
2 * Copyright (c) 2016, 2025, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.lang.invoke;
27
28 import sun.invoke.util.Wrapper;
29 import sun.util.logging.PlatformLogger;
30
31 import java.lang.classfile.ClassFile;
32 import java.lang.classfile.attribute.SourceFileAttribute;
33 import java.lang.constant.ClassDesc;
34 import java.util.ArrayList;
35 import java.util.HashSet;
36 import java.util.Map;
37 import java.util.Objects;
38 import java.util.Set;
39 import java.util.TreeMap;
40 import java.util.TreeSet;
41 import java.util.stream.Stream;
42
43 import static java.lang.classfile.ClassFile.*;
44 import static java.lang.invoke.LambdaForm.BasicType.*;
45 import static java.lang.invoke.LambdaForm.Kind.*;
46 import static java.lang.invoke.MethodTypeForm.*;
47
48 /**
49 * Helper class to assist the GenerateJLIClassesPlugin to get access to
50 * generate classes ahead of time.
51 */
52 class GenerateJLIClassesHelper {
53 // Map from DirectMethodHandle method type name to index to LambdaForms
54 static final Map<String, Integer> DMH_METHOD_TYPE_MAP =
55 Map.of(
56 DIRECT_INVOKE_VIRTUAL.methodName, LF_INVVIRTUAL,
57 DIRECT_INVOKE_STATIC.methodName, LF_INVSTATIC,
58 DIRECT_INVOKE_SPECIAL.methodName, LF_INVSPECIAL,
59 DIRECT_NEW_INVOKE_SPECIAL.methodName, LF_NEWINVSPECIAL,
60 DIRECT_INVOKE_INTERFACE.methodName, LF_INVINTERFACE,
61 DIRECT_INVOKE_STATIC_INIT.methodName, LF_INVSTATIC_INIT,
62 DIRECT_INVOKE_SPECIAL_IFC.methodName, LF_INVSPECIAL_IFC
63 );
64
65 static final String DIRECT_HOLDER = "java/lang/invoke/DirectMethodHandle$Holder";
66 static final String DELEGATING_HOLDER = "java/lang/invoke/DelegatingMethodHandle$Holder";
67 static final String BASIC_FORMS_HOLDER = "java/lang/invoke/LambdaForm$Holder";
68 static final String INVOKERS_HOLDER = "java/lang/invoke/Invokers$Holder";
69 static final String INVOKERS_HOLDER_CLASS_NAME = INVOKERS_HOLDER.replace('/', '.');
70 static final String BMH_SPECIES_PREFIX = "java.lang.invoke.BoundMethodHandle$Species_";
71
72 static class HolderClassBuilder {
73
74
75 private final TreeSet<String> speciesTypes = new TreeSet<>();
76 private final TreeSet<String> invokerTypes = new TreeSet<>();
77 private final TreeSet<String> callSiteTypes = new TreeSet<>();
78 private final Map<String, Set<String>> dmhMethods = new TreeMap<>();
79
80 HolderClassBuilder addSpeciesType(String type) {
81 speciesTypes.add(expandSignature(type));
82 return this;
83 }
84
85 HolderClassBuilder addInvokerType(String methodType) {
86 validateMethodType(methodType);
87 invokerTypes.add(methodType);
88 return this;
89 }
90
91 HolderClassBuilder addCallSiteType(String csType) {
92 validateMethodType(csType);
93 callSiteTypes.add(csType);
94 return this;
95 }
96
97 Map<String, byte[]> build() {
98 int count = 0;
99 for (Set<String> entry : dmhMethods.values()) {
100 count += entry.size();
101 }
102 MethodType[] directMethodTypes = new MethodType[count];
103 int[] dmhTypes = new int[count];
104 int index = 0;
105 for (Map.Entry<String, Set<String>> entry : dmhMethods.entrySet()) {
106 String dmhType = entry.getKey();
107 for (String type : entry.getValue()) {
108 // The DMH type to actually ask for is retrieved by removing
109 // the first argument, which needs to be of Object.class
110 MethodType mt = asMethodType(type);
111 if (mt.parameterCount() < 1 ||
112 mt.parameterType(0) != Object.class) {
113 throw new RuntimeException(
114 "DMH type parameter must start with L: " + dmhType + " " + type);
115 }
116 // Adapt the method type of the LF to retrieve
117 directMethodTypes[index] = mt.dropParameterTypes(0, 1);
118 // invokeVirtual and invokeInterface must have a leading Object
119 // parameter, i.e., the receiver
120 dmhTypes[index] = DMH_METHOD_TYPE_MAP.get(dmhType);
121 if (dmhTypes[index] == LF_INVINTERFACE || dmhTypes[index] == LF_INVVIRTUAL) {
122 if (mt.parameterCount() < 2 ||
123 mt.parameterType(1) != Object.class) {
124 throw new RuntimeException(
125 "DMH type parameter must start with LL: " + dmhType + " " + type);
126 }
127 }
128 index++;
129 }
130 }
131
132 // The invoker type to ask for is retrieved by removing the first
133 // and the last argument, which needs to be of Object.class
134 MethodType[] invokerMethodTypes = new MethodType[invokerTypes.size()];
135 index = 0;
136 for (String invokerType : invokerTypes) {
137 MethodType mt = asMethodType(invokerType);
138 final int lastParam = mt.parameterCount() - 1;
139 if (!checkInvokerTypeParams(mt)) {
140 throw new RuntimeException(
141 "Invoker type parameter must start and end with Object: " + invokerType);
142 }
143 mt = mt.dropParameterTypes(lastParam, lastParam + 1);
144 invokerMethodTypes[index] = mt.dropParameterTypes(0, 1);
145 index++;
146 }
147
148 // The callSite type to ask for is retrieved by removing the last
149 // argument, which needs to be of Object.class
150 MethodType[] callSiteMethodTypes = new MethodType[callSiteTypes.size()];
151 index = 0;
152 for (String callSiteType : callSiteTypes) {
153 MethodType mt = asMethodType(callSiteType);
154 final int lastParam = mt.parameterCount() - 1;
155 if (mt.parameterCount() < 1 ||
156 mt.parameterType(lastParam) != Object.class) {
157 throw new RuntimeException(
158 "CallSite type parameter must end with Object: " + callSiteType);
159 }
160 callSiteMethodTypes[index] = mt.dropParameterTypes(lastParam, lastParam + 1);
161 index++;
162 }
163 Map<String, byte[]> result = new TreeMap<>();
164 result.put(DIRECT_HOLDER,
165 generateDirectMethodHandleHolderClassBytes(
166 DIRECT_HOLDER, directMethodTypes, dmhTypes));
167 result.put(DELEGATING_HOLDER,
168 generateDelegatingMethodHandleHolderClassBytes(
169 DELEGATING_HOLDER, directMethodTypes));
170 result.put(INVOKERS_HOLDER,
171 generateInvokersHolderClassBytes(INVOKERS_HOLDER,
172 invokerMethodTypes, callSiteMethodTypes));
173 result.put(BASIC_FORMS_HOLDER,
174 generateBasicFormsClassBytes(BASIC_FORMS_HOLDER));
175
176 speciesTypes.forEach(types -> {
177 Map.Entry<String, byte[]> entry = generateConcreteBMHClassBytes(types);
178 result.put(entry.getKey(), entry.getValue());
179 });
180 // clear builder
181 speciesTypes.clear();
182 invokerTypes.clear();
183 callSiteTypes.clear();
184 dmhMethods.clear();
185 return result;
186 }
187
188 public static MethodType asMethodType(String basicSignatureString) {
189 String[] parts = basicSignatureString.split("_");
190 assert (parts.length == 2);
191 assert (parts[1].length() == 1);
192 String parameters = expandSignature(parts[0]);
193 Class<?> rtype = simpleType(parts[1].charAt(0));
194 if (parameters.isEmpty()) {
195 return MethodType.methodType(rtype);
196 } else {
197 Class<?>[] ptypes = new Class<?>[parameters.length()];
198 for (int i = 0; i < ptypes.length; i++) {
199 ptypes[i] = simpleType(parameters.charAt(i));
200 }
201 return MethodType.methodType(rtype, ptypes);
202 }
203 }
204
205 public static boolean checkInvokerTypeParams(MethodType mt) {
206 final int lastParam = mt.parameterCount() - 1;
207 return (mt.parameterCount() >= 2 &&
208 mt.parameterType(0) == Object.class &&
209 mt.parameterType(lastParam) == Object.class);
210 }
211
212 private void addDMHMethodType(String dmh, String methodType) {
213 validateMethodType(methodType);
214 Set<String> methodTypes = dmhMethods.get(dmh);
215 if (methodTypes == null) {
216 methodTypes = new TreeSet<>();
217 dmhMethods.put(dmh, methodTypes);
218 }
219 methodTypes.add(methodType);
220 }
221
222 private static void validateMethodType(String type) {
223 String[] typeParts = type.split("_");
224 // check return type (second part)
225 if (typeParts.length != 2 || typeParts[1].length() != 1
226 || !isBasicTypeChar(typeParts[1].charAt(0))) {
227 throw new RuntimeException(
228 "Method type signature must be of form [LJIFD]*_[LJIFDV]");
229 }
230 // expand and check arguments (first part)
231 expandSignature(typeParts[0]);
232 }
233
234 // Convert LL -> LL, L3 -> LLL
235 private static String expandSignature(String signature) {
236 StringBuilder sb = new StringBuilder();
237 char last = 'X';
238 int count = 0;
239 for (int i = 0; i < signature.length(); i++) {
240 char c = signature.charAt(i);
241 if (c >= '0' && c <= '9') {
242 count *= 10;
243 count += (c - '0');
244 } else {
245 requireBasicType(c);
246 for (int j = 1; j < count; j++) {
247 sb.append(last);
248 }
249 sb.append(c);
250 last = c;
251 count = 0;
252 }
253 }
254
255 // ended with a number, e.g., "L2": append last char count - 1 times
256 if (count > 1) {
257 requireBasicType(last);
258 for (int j = 1; j < count; j++) {
259 sb.append(last);
260 }
261 }
262 return sb.toString();
263 }
264
265 private static void requireBasicType(char c) {
266 if (!isArgBasicTypeChar(c)) {
267 throw new RuntimeException(
268 "Character " + c + " must correspond to a basic field type: LIJFD");
269 }
270 }
271
272 private static Class<?> simpleType(char c) {
273 if (isBasicTypeChar(c)) {
274 return LambdaForm.BasicType.basicType(c).basicTypeClass();
275 }
276 switch (c) {
277 case 'Z':
278 case 'B':
279 case 'S':
280 case 'C':
281 throw new IllegalArgumentException("Not a valid primitive: " + c +
282 " (use I instead)");
283 default:
284 throw new IllegalArgumentException("Not a primitive: " + c);
285 }
286 }
287 }
288
289 /*
290 * Returns a map of class name in internal form to the corresponding class bytes
291 * per the given stream of SPECIES_RESOLVE and LF_RESOLVE trace logs.
292 *
293 * Used by GenerateJLIClassesPlugin to pre-generate holder classes during
294 * jlink phase.
295 */
296 static Map<String, byte[]> generateHolderClasses(Stream<String> traces) {
297 Objects.requireNonNull(traces);
298 HolderClassBuilder builder = new HolderClassBuilder();
299 traces.map(line -> line.split(" "))
300 .forEach(parts -> {
301 switch (parts[0]) {
302 case "[SPECIES_RESOLVE]":
303 // Allow for new types of species data classes being resolved here
304 assert parts.length >= 2;
305 if (parts[1].startsWith(BMH_SPECIES_PREFIX)) {
306 String species = parts[1].substring(BMH_SPECIES_PREFIX.length());
307 if (!"L".equals(species)) {
308 builder.addSpeciesType(species);
309 }
310 }
311 break;
312 case "[LF_RESOLVE]":
313 assert parts.length > 3;
314 String methodType = parts[3];
315 if (parts[1].equals(INVOKERS_HOLDER_CLASS_NAME)) {
316 if ("linkToTargetMethod".equals(parts[2]) ||
317 "linkToCallSite".equals(parts[2])) {
318 builder.addCallSiteType(methodType);
319 } else {
320 MethodType mt = HolderClassBuilder.asMethodType(methodType);
321 // Work around JDK-8327499
322 if (HolderClassBuilder.checkInvokerTypeParams(mt)) {
323 builder.addInvokerType(methodType);
324 } else {
325 PlatformLogger.getLogger("java.lang.invoke")
326 .warning("Invalid LF_RESOLVE " + parts[1] + " " + parts[2] + " " + parts[3]);
327 }
328 }
329 } else if (parts[1].contains("DirectMethodHandle")) {
330 String dmh = parts[2];
331 // ignore getObject etc for now (generated by default)
332 if (DMH_METHOD_TYPE_MAP.containsKey(dmh)) {
333 builder.addDMHMethodType(dmh, methodType);
334 }
335 }
336 break;
337 default:
338 break; // ignore
339 }
340 });
341
342 return builder.build();
343 }
344
345 /**
346 * Returns a {@code byte[]} representation of a class implementing
347 * the zero and identity forms of all {@code LambdaForm.BasicType}s.
348 */
349 static byte[] generateBasicFormsClassBytes(String className) {
350 ArrayList<LambdaForm> forms = new ArrayList<>();
351 ArrayList<String> names = new ArrayList<>();
352 HashSet<String> dedupSet = new HashSet<>();
353 for (LambdaForm.BasicType type : LambdaForm.BasicType.values()) {
354 String name;
355
356 LambdaForm identity = LambdaForm.identityForm(type);
357 name = identity.kind.defaultLambdaName
358 + "_" + identity.returnType().basicTypeChar();
359 if (dedupSet.add(name)) {
360 names.add(name);
361 forms.add(identity);
362 }
363
364 if (type != V_TYPE) {
365 LambdaForm constant = LambdaForm.constantForm(type);
366 name = constant.kind.defaultLambdaName
367 + "_" + constant.returnType().basicTypeChar();
368 if (dedupSet.add(name)) {
369 names.add(name);
370 forms.add(constant);
371 }
372 }
373 }
374 return generateCodeBytesForLFs(className,
375 names.toArray(new String[0]),
376 forms.toArray(new LambdaForm[0]));
377 }
378
379 /**
380 * Returns a {@code byte[]} representation of a class implementing
381 * DirectMethodHandle of each pairwise combination of {@code MethodType} and
382 * an {@code int} representing method type.
383 */
384 static byte[] generateDirectMethodHandleHolderClassBytes(String className,
385 MethodType[] methodTypes, int[] types) {
386 ArrayList<LambdaForm> forms = new ArrayList<>();
387 ArrayList<String> names = new ArrayList<>();
388 for (int i = 0; i < methodTypes.length; i++) {
389 // invokeVirtual and invokeInterface must have a leading Object
390 // parameter, i.e., the receiver
391 if (types[i] == LF_INVVIRTUAL || types[i] == LF_INVINTERFACE) {
392 if (methodTypes[i].parameterCount() < 1 ||
393 methodTypes[i].parameterType(0) != Object.class) {
394 throw new InternalError("Invalid method type for " +
395 (types[i] == LF_INVVIRTUAL ? "invokeVirtual" : "invokeInterface") +
396 " DMH, needs at least two leading reference arguments: " +
397 methodTypes[i]);
398 }
399 }
400
401 LambdaForm form = DirectMethodHandle.makePreparedLambdaForm(methodTypes[i], types[i]);
402 forms.add(form);
403 names.add(form.kind.defaultLambdaName);
404 }
405 for (Wrapper wrapper : Wrapper.values()) {
406 int ftype = wrapper == Wrapper.VOID ? DirectMethodHandle.FT_CHECKED_REF : DirectMethodHandle.ftypeKind(wrapper.primitiveType());
407 for (byte b = DirectMethodHandle.AF_GETFIELD; b < DirectMethodHandle.AF_LIMIT; b++) {
408 LambdaForm form = DirectMethodHandle
409 .makePreparedFieldLambdaForm(b, /*isVolatile*/false, ftype);
410 if (form.kind == GENERIC)
411 throw new InternalError(b + " non-volatile " + ftype);
412 forms.add(form);
413 names.add(form.kind.defaultLambdaName);
414 // volatile
415 form = DirectMethodHandle
416 .makePreparedFieldLambdaForm(b, /*isVolatile*/true, ftype);
417 if (form.kind == GENERIC)
418 throw new InternalError(b + " volatile " + ftype);
419 forms.add(form);
420 names.add(form.kind.defaultLambdaName);
421 }
422 }
423 return generateCodeBytesForLFs(className,
424 names.toArray(new String[0]),
425 forms.toArray(new LambdaForm[0]));
426 }
427
428 /**
429 * Returns a {@code byte[]} representation of a class implementing
430 * DelegatingMethodHandles of each {@code MethodType} kind in the
431 * {@code methodTypes} argument.
432 */
433 static byte[] generateDelegatingMethodHandleHolderClassBytes(String className,
434 MethodType[] methodTypes) {
435
436 HashSet<MethodType> dedupSet = new HashSet<>();
437 ArrayList<LambdaForm> forms = new ArrayList<>();
438 ArrayList<String> names = new ArrayList<>();
439 for (int i = 0; i < methodTypes.length; i++) {
440 // generate methods representing the DelegatingMethodHandle
441 if (dedupSet.add(methodTypes[i])) {
442 // reinvokers are variant with the associated SpeciesData
443 // and shape of the target LF, but we can easily pregenerate
444 // the basic reinvokers associated with Species_L. Ultimately we
445 // may want to consider pregenerating more of these, which will
446 // require an even more complex naming scheme
447 LambdaForm reinvoker = makeReinvokerFor(methodTypes[i]);
448 forms.add(reinvoker);
449 String speciesSig = BoundMethodHandle.speciesDataFor(reinvoker).key();
450 assert(speciesSig.equals("L"));
451 names.add(reinvoker.kind.defaultLambdaName + "_" + speciesSig);
452
453 LambdaForm delegate = makeDelegateFor(methodTypes[i]);
454 forms.add(delegate);
455 names.add(delegate.kind.defaultLambdaName);
456 }
457 }
458 return generateCodeBytesForLFs(className,
459 names.toArray(new String[0]),
460 forms.toArray(new LambdaForm[0]));
461 }
462
463 /**
464 * Returns a {@code byte[]} representation of a class implementing
465 * the invoker forms for the set of supplied {@code invokerMethodTypes}
466 * and {@code callSiteMethodTypes}.
467 */
468 static byte[] generateInvokersHolderClassBytes(String className,
469 MethodType[] invokerMethodTypes, MethodType[] callSiteMethodTypes) {
470
471 HashSet<MethodType> dedupSet = new HashSet<>();
472 ArrayList<LambdaForm> forms = new ArrayList<>();
473 ArrayList<String> names = new ArrayList<>();
474 int[] types = {
475 MethodTypeForm.LF_EX_LINKER,
476 MethodTypeForm.LF_EX_INVOKER,
477 MethodTypeForm.LF_GEN_LINKER,
478 MethodTypeForm.LF_GEN_INVOKER
479 };
480
481 for (int i = 0; i < invokerMethodTypes.length; i++) {
482 // generate methods representing invokers of the specified type
483 if (dedupSet.add(invokerMethodTypes[i])) {
484 for (int type : types) {
485 LambdaForm invokerForm = Invokers.invokeHandleForm(invokerMethodTypes[i],
486 /*customized*/false, type);
487 forms.add(invokerForm);
488 names.add(invokerForm.kind.defaultLambdaName);
489 }
490 }
491 }
492
493 dedupSet = new HashSet<>();
494 for (int i = 0; i < callSiteMethodTypes.length; i++) {
495 // generate methods representing invokers of the specified type
496 if (dedupSet.add(callSiteMethodTypes[i])) {
497 LambdaForm callSiteForm = Invokers.callSiteForm(callSiteMethodTypes[i], true);
498 forms.add(callSiteForm);
499 names.add(callSiteForm.kind.defaultLambdaName);
500
501 LambdaForm methodHandleForm = Invokers.callSiteForm(callSiteMethodTypes[i], false);
502 forms.add(methodHandleForm);
503 names.add(methodHandleForm.kind.defaultLambdaName);
504 }
505 }
506
507 return generateCodeBytesForLFs(className,
508 names.toArray(new String[0]),
509 forms.toArray(new LambdaForm[0]));
510 }
511
512 /*
513 * Generate customized code for a set of LambdaForms of specified types into
514 * a class with a specified name.
515 */
516 private static byte[] generateCodeBytesForLFs(String className, String[] names, LambdaForm[] forms) {
517 return ClassFile.of().build(ClassDesc.ofInternalName(className), clb -> {
518 clb.withFlags(ACC_PRIVATE | ACC_FINAL | ACC_SUPER)
519 .withSuperclass(InvokerBytecodeGenerator.INVOKER_SUPER_DESC)
520 .with(SourceFileAttribute.of(className.substring(className.lastIndexOf('/') + 1)));
521 for (int i = 0; i < forms.length; i++) {
522 new InvokerBytecodeGenerator(className, names[i], forms[i], forms[i].methodType()).addMethod(clb, false);
523 }
524 });
525 }
526
527 private static LambdaForm makeReinvokerFor(MethodType type) {
528 MethodHandle emptyHandle = MethodHandles.empty(type);
529 return DelegatingMethodHandle.makeReinvokerForm(emptyHandle,
530 MethodTypeForm.LF_REBIND,
531 BoundMethodHandle.speciesData_L(),
532 BoundMethodHandle.speciesData_L().getterFunction(0));
533 }
534
535 private static LambdaForm makeDelegateFor(MethodType type) {
536 MethodHandle handle = MethodHandles.empty(type);
537 return DelegatingMethodHandle.makeReinvokerForm(
538 handle,
539 MethodTypeForm.LF_DELEGATE,
540 DelegatingMethodHandle.class,
541 DelegatingMethodHandle.NF_getTarget);
542 }
543
544 /**
545 * Returns a {@code byte[]} representation of {@code BoundMethodHandle}
546 * species class implementing the signature defined by {@code types}.
547 */
548 @SuppressWarnings({"rawtypes", "unchecked"})
549 static Map.Entry<String, byte[]> generateConcreteBMHClassBytes(final String types) {
550 for (char c : types.toCharArray()) {
551 if (!isArgBasicTypeChar(c)) {
552 throw new IllegalArgumentException("All characters must "
553 + "correspond to a basic field type: LIJFD");
554 }
555 }
556 final BoundMethodHandle.SpeciesData species = BoundMethodHandle.SPECIALIZER.findSpecies(types);
557 final String className = species.speciesCode().getName();
558 final ClassSpecializer.Factory factory = BoundMethodHandle.SPECIALIZER.factory();
559 final byte[] code = factory.generateConcreteSpeciesCodeFile(className, species);
560 return Map.entry(className.replace('.', '/'), code);
561 }
562
563 }