1 /*
  2  * Copyright (c) 1999, 2023, 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.reflect;
 27 
 28 import java.lang.classfile.*;
 29 import java.lang.classfile.constantpool.*;
 30 import java.lang.classfile.attribute.ExceptionsAttribute;
 31 import sun.security.action.GetBooleanAction;
 32 
 33 import java.io.IOException;
 34 import static java.lang.classfile.ClassFile.*;
 35 import java.lang.classfile.attribute.StackMapFrameInfo;
 36 import java.lang.classfile.attribute.StackMapTableAttribute;
 37 import java.lang.constant.ClassDesc;
 38 import static java.lang.constant.ConstantDescs.*;
 39 import java.lang.constant.MethodTypeDesc;
 40 import java.nio.file.Files;
 41 import java.nio.file.Path;
 42 import java.util.ArrayList;
 43 import java.util.Arrays;
 44 import java.util.LinkedHashMap;
 45 import java.util.List;
 46 import java.util.ListIterator;
 47 import java.util.Map;
 48 import java.util.Objects;
 49 import java.util.function.IntFunction;
 50 
 51 /**
 52  * ProxyGenerator contains the code to generate a dynamic proxy class
 53  * for the java.lang.reflect.Proxy API.
 54  * <p>
 55  * The external interface to ProxyGenerator is the static
 56  * "generateProxyClass" method.
 57  */
 58 final class ProxyGenerator {
 59 
 60     private static final ClassDesc
 61             CD_ClassLoader = ClassDesc.ofInternalName("java/lang/ClassLoader"),
 62             CD_ClassNotFoundException = ClassDesc.ofInternalName("java/lang/ClassNotFoundException"),
 63             CD_IllegalAccessException = ClassDesc.ofInternalName("java/lang/IllegalAccessException"),
 64             CD_InvocationHandler = ClassDesc.ofInternalName("java/lang/reflect/InvocationHandler"),
 65             CD_Method = ClassDesc.ofInternalName("java/lang/reflect/Method"),
 66             CD_NoClassDefFoundError = ClassDesc.ofInternalName("java/lang/NoClassDefFoundError"),
 67             CD_NoSuchMethodError = ClassDesc.ofInternalName("java/lang/NoSuchMethodError"),
 68             CD_NoSuchMethodException = ClassDesc.ofInternalName("java/lang/NoSuchMethodException"),
 69             CD_Proxy = ClassDesc.ofInternalName("java/lang/reflect/Proxy"),
 70             CD_UndeclaredThrowableException = ClassDesc.ofInternalName("java/lang/reflect/UndeclaredThrowableException");
 71 
 72     private static final MethodTypeDesc
 73             MTD_boolean = MethodTypeDesc.of(CD_boolean),
 74             MTD_void_InvocationHandler = MethodTypeDesc.of(CD_void, CD_InvocationHandler),
 75             MTD_void_String = MethodTypeDesc.of(CD_void, CD_String),
 76             MTD_void_Throwable = MethodTypeDesc.of(CD_void, CD_Throwable),
 77             MTD_Class = MethodTypeDesc.of(CD_Class),
 78             MTD_Class_String_boolean_ClassLoader = MethodTypeDesc.of(CD_Class, CD_String, CD_boolean, CD_ClassLoader),
 79             MTD_ClassLoader = MethodTypeDesc.of(CD_ClassLoader),
 80             MTD_MethodHandles$Lookup = MethodTypeDesc.of(CD_MethodHandles_Lookup),
 81             MTD_MethodHandles$Lookup_MethodHandles$Lookup = MethodTypeDesc.of(CD_MethodHandles_Lookup, CD_MethodHandles_Lookup),
 82             MTD_Method_String_ClassArray = MethodTypeDesc.of(CD_Method, CD_String, CD_Class.arrayType()),
 83             MTD_Object_Object_Method_ObjectArray = MethodTypeDesc.of(CD_Object, CD_Object, CD_Method, CD_Object.arrayType()),
 84             MTD_String = MethodTypeDesc.of(CD_String);
 85 
 86     private static final String NAME_LOOKUP_ACCESSOR = "proxyClassLookup";
 87 
 88     private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
 89 
 90     /**
 91      * name of field for storing a proxy instance's invocation handler
 92      */
 93     private static final String handlerFieldName = "h";
 94 
 95     /**
 96      * debugging flag for saving generated class files
 97      */
 98     @SuppressWarnings("removal")
 99     private static final boolean saveGeneratedFiles =
100             java.security.AccessController.doPrivileged(
101                     new GetBooleanAction(
102                             "jdk.proxy.ProxyGenerator.saveGeneratedFiles"));
103 
104     /* Preloaded ProxyMethod objects for methods in java.lang.Object */
105     private static final ProxyMethod hashCodeMethod;
106     private static final ProxyMethod equalsMethod;
107     private static final ProxyMethod toStringMethod;
108 
109     private static final ClassModel TEMPLATE;
110 
111     private static final ClassEntry CE_Class;
112     private static final ClassEntry CE_ClassNotFoundException;
113     private static final ClassEntry CE_NoClassDefFoundError;
114     private static final ClassEntry CE_NoSuchMethodError;
115     private static final ClassEntry CE_NoSuchMethodException;
116     private static final ClassEntry CE_Object;
117     private static final ClassEntry CE_Throwable;
118     private static final ClassEntry CE_UndeclaredThrowableException;
119 
120     private static final FieldRefEntry FRE_Proxy_h;
121 
122     private static final InterfaceMethodRefEntry IMRE_InvocationHandler_invoke;
123 
124     private static final MethodRefEntry MRE_Class_forName;
125     private static final MethodRefEntry MRE_Class_getClassLoader;
126     private static final MethodRefEntry MRE_Class_getMethod;
127     private static final MethodRefEntry MRE_NoClassDefFoundError_init;
128     private static final MethodRefEntry MRE_NoSuchMethodError_init;
129     private static final MethodRefEntry MRE_Throwable_getMessage;
130     private static final MethodRefEntry MRE_UndeclaredThrowableException_init;
131 
132     private static final Utf8Entry UE_Method;
133 
134     private static final List<StackMapFrameInfo.VerificationTypeInfo> THROWABLE_STACK;
135 
136     @SuppressWarnings("unchecked")
137     private static <T extends PoolEntry> T entryByIndex(int index) {
138         return (T) TEMPLATE.constantPool().entryByIndex(index);
139     }
140 
141     static {
142         // static template ClassModel holds pre-defined constant pool entries
143         // proxy transformed from the template shares the template constant pool
144         // each direct use of the template pool entry is significantly faster
145         var cc = ClassFile.of();
146         var ei = new int[21];
147         TEMPLATE = cc.parse(cc.build(CD_Proxy, clb -> {
148             clb.withSuperclass(CD_Proxy);
149             generateConstructor(clb);
150             generateLookupAccessor(clb);
151             var cp = clb.constantPool();
152 
153             ei[0] = cp.classEntry(CD_Class).index();
154             ei[1] = cp.classEntry(CD_ClassNotFoundException).index();
155             ei[2] = cp.classEntry(CD_NoClassDefFoundError).index();
156             ei[3] = cp.classEntry(CD_NoSuchMethodError).index();
157             ei[4] = cp.classEntry(CD_NoSuchMethodException).index();
158             ei[5] = cp.classEntry(CD_Object).index();
159             ei[6] = cp.classEntry(CD_Throwable).index();
160             ei[7] = cp.classEntry(CD_UndeclaredThrowableException).index();
161 
162             ei[8] = cp.fieldRefEntry(CD_Proxy, handlerFieldName, CD_InvocationHandler).index();
163 
164             ei[9] = cp.interfaceMethodRefEntry(CD_InvocationHandler, "invoke", MTD_Object_Object_Method_ObjectArray).index();
165 
166             ei[10] = cp.methodRefEntry(CD_Class, "forName", MTD_Class_String_boolean_ClassLoader).index();
167             ei[11] = cp.methodRefEntry(CD_Class, "getClassLoader", MTD_ClassLoader).index();
168             ei[12] = cp.methodRefEntry(CD_Class, "getMethod", MTD_Method_String_ClassArray).index();
169             ei[13] = cp.methodRefEntry(CD_NoClassDefFoundError, INIT_NAME, MTD_void_String).index();
170             ei[14] = cp.methodRefEntry(CD_NoSuchMethodError, INIT_NAME, MTD_void_String).index();
171             ei[15] = cp.methodRefEntry(CD_Throwable, "getMessage", MTD_String).index();
172             ei[16] = cp.methodRefEntry(CD_UndeclaredThrowableException, INIT_NAME, MTD_void_Throwable).index();
173 
174             ei[17] = cp.utf8Entry(CD_Method).index();
175 
176             ei[18] = cp.utf8Entry("m0").index();
177             ei[19] = cp.utf8Entry("m1").index();
178             ei[20] = cp.utf8Entry("m2").index();
179         }));
180 
181         CE_Class = entryByIndex(ei[0]);
182         CE_ClassNotFoundException = entryByIndex(ei[1]);
183         CE_NoClassDefFoundError = entryByIndex(ei[2]);
184         CE_NoSuchMethodError = entryByIndex(ei[3]);
185         CE_NoSuchMethodException = entryByIndex(ei[4]);
186         CE_Object = entryByIndex(ei[5]);
187         CE_Throwable = entryByIndex(ei[6]);
188         CE_UndeclaredThrowableException = entryByIndex(ei[7]);
189 
190         FRE_Proxy_h = entryByIndex(ei[8]);
191 
192         IMRE_InvocationHandler_invoke = entryByIndex(ei[9]);
193 
194         MRE_Class_forName = entryByIndex(ei[10]);
195         MRE_Class_getClassLoader = entryByIndex(ei[11]);
196         MRE_Class_getMethod = entryByIndex(ei[12]);
197         MRE_NoClassDefFoundError_init = entryByIndex(ei[13]);
198         MRE_NoSuchMethodError_init = entryByIndex(ei[14]);
199         MRE_Throwable_getMessage = entryByIndex(ei[15]);
200         MRE_UndeclaredThrowableException_init = entryByIndex(ei[16]);
201 
202         UE_Method = entryByIndex(ei[17]);
203 
204         try {
205             hashCodeMethod = new ProxyMethod(Object.class.getMethod("hashCode"), entryByIndex(ei[18]));
206             equalsMethod = new ProxyMethod(Object.class.getMethod("equals", Object.class), entryByIndex(ei[19]));
207             toStringMethod = new ProxyMethod(Object.class.getMethod("toString"), entryByIndex(ei[20]));
208         } catch (NoSuchMethodException e) {
209             throw new NoSuchMethodError(e.getMessage());
210         }
211 
212         THROWABLE_STACK = List.of(StackMapFrameInfo.ObjectVerificationTypeInfo.of(CE_Throwable));
213     }
214 
215     /**
216      * Classfile context
217      */
218     private final ClassFile classfileContext;
219     private final ConstantPoolBuilder cp;
220 
221     /**
222      * Name of proxy class
223      */
224     private ClassEntry classEntry;
225 
226     /**
227      * Proxy interfaces
228      */
229     private final List<Class<?>> interfaces;
230 
231     /**
232      * Proxy class access flags
233      */
234     private final int accessFlags;
235 
236     /**
237      * Maps method signature string to list of ProxyMethod objects for
238      * proxy methods with that signature.
239      * Kept in insertion order to make it easier to compare old and new.
240      */
241     private final Map<String, List<ProxyMethod>> proxyMethods = new LinkedHashMap<>();
242 
243     /**
244      * Ordinal of next ProxyMethod object added to proxyMethods.
245      * Indexes are reserved for hashcode(0), equals(1), toString(2).
246      */
247     private int proxyMethodCount = 3;
248 
249     /**
250      * Construct a ProxyGenerator to generate a proxy class with the
251      * specified name and for the given interfaces.
252      * <p>
253      * A ProxyGenerator object contains the state for the ongoing
254      * generation of a particular proxy class.
255      */
256     private ProxyGenerator(ClassLoader loader, String className, List<Class<?>> interfaces,
257                            int accessFlags) {
258         this.classfileContext = ClassFile.of(
259                 ClassFile.StackMapsOption.DROP_STACK_MAPS,
260                 ClassFile.ClassHierarchyResolverOption.of(
261                         ClassHierarchyResolver.ofClassLoading(loader).cached()));
262         this.cp = ConstantPoolBuilder.of(TEMPLATE);
263         this.classEntry = cp.classEntry(ClassDesc.of(className));
264         this.interfaces = interfaces;
265         this.accessFlags = accessFlags;
266     }
267 
268     /**
269      * Generate a proxy class given a name and a list of proxy interfaces.
270      *
271      * @param name        the class name of the proxy class
272      * @param interfaces  proxy interfaces
273      * @param accessFlags access flags of the proxy class
274      */
275     @SuppressWarnings("removal")
276     static byte[] generateProxyClass(ClassLoader loader,
277                                      final String name,
278                                      List<Class<?>> interfaces,
279                                      int accessFlags) {
280         Objects.requireNonNull(interfaces);
281         ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags);
282         final byte[] classFile = gen.generateClassFile();
283 
284         if (saveGeneratedFiles) {
285             java.security.AccessController.doPrivileged(
286                     new java.security.PrivilegedAction<Void>() {
287                         public Void run() {
288                             try {
289                                 int i = name.lastIndexOf('.');
290                                 Path path;
291                                 if (i > 0) {
292                                     Path dir = Path.of(name.substring(0, i).replace('.', '/'));
293                                     Files.createDirectories(dir);
294                                     path = dir.resolve(name.substring(i + 1) + ".class");
295                                 } else {
296                                     path = Path.of(name + ".class");
297                                 }
298                                 Files.write(path, classFile);
299                                 return null;
300                             } catch (IOException e) {
301                                 throw new InternalError(
302                                         "I/O exception saving generated file: " + e);
303                             }
304                         }
305                     });
306         }
307 
308         return classFile;
309     }
310 
311     /**
312      * {@return the entries of the given type}
313      * @param types the {@code Class} objects, not primitive types nor array types
314      */
315     private static ClassEntry[] toClassEntries(ConstantPoolBuilder cp, List<Class<?>> types) {
316         var ces = new ClassEntry[types.size()];
317         for (int i = 0; i < ces.length; i++)
318             ces[i] = cp.classEntry(cp.utf8Entry(types.get(i).getName().replace('.', '/')));
319         return ces;
320     }
321 
322     /**
323      * {@return the {@code ClassDesc} of the given type}
324      * @param type the {@code Class} object
325      */
326     private static ClassDesc toClassDesc(Class<?> type) {
327         return ClassDesc.ofDescriptor(type.descriptorString());
328     }
329 
330     /**
331      * For a given set of proxy methods with the same signature, check
332      * that their return types are compatible according to the Proxy
333      * specification.
334      *
335      * Specifically, if there is more than one such method, then all
336      * of the return types must be reference types, and there must be
337      * one return type that is assignable to each of the rest of them.
338      */
339     private static void checkReturnTypes(List<ProxyMethod> methods) {
340         /*
341          * If there is only one method with a given signature, there
342          * cannot be a conflict.  This is the only case in which a
343          * primitive (or void) return type is allowed.
344          */
345         if (methods.size() < 2) {
346             return;
347         }
348 
349         /*
350          * List of return types that are not yet known to be
351          * assignable from ("covered" by) any of the others.
352          */
353         List<Class<?>> uncoveredReturnTypes = new ArrayList<>(1);
354 
355         nextNewReturnType:
356         for (ProxyMethod pm : methods) {
357             Class<?> newReturnType = pm.returnType;
358             if (newReturnType.isPrimitive()) {
359                 throw new IllegalArgumentException(
360                         "methods with same signature " +
361                                 pm.shortSignature +
362                                 " but incompatible return types: " +
363                                 newReturnType.getName() + " and others");
364             }
365             boolean added = false;
366 
367             /*
368              * Compare the new return type to the existing uncovered
369              * return types.
370              */
371             ListIterator<Class<?>> liter = uncoveredReturnTypes.listIterator();
372             while (liter.hasNext()) {
373                 Class<?> uncoveredReturnType = liter.next();
374 
375                 /*
376                  * If an existing uncovered return type is assignable
377                  * to this new one, then we can forget the new one.
378                  */
379                 if (newReturnType.isAssignableFrom(uncoveredReturnType)) {
380                     assert !added;
381                     continue nextNewReturnType;
382                 }
383 
384                 /*
385                  * If the new return type is assignable to an existing
386                  * uncovered one, then should replace the existing one
387                  * with the new one (or just forget the existing one,
388                  * if the new one has already be put in the list).
389                  */
390                 if (uncoveredReturnType.isAssignableFrom(newReturnType)) {
391                     // (we can assume that each return type is unique)
392                     if (!added) {
393                         liter.set(newReturnType);
394                         added = true;
395                     } else {
396                         liter.remove();
397                     }
398                 }
399             }
400 
401             /*
402              * If we got through the list of existing uncovered return
403              * types without an assignability relationship, then add
404              * the new return type to the list of uncovered ones.
405              */
406             if (!added) {
407                 uncoveredReturnTypes.add(newReturnType);
408             }
409         }
410 
411         /*
412          * We shouldn't end up with more than one return type that is
413          * not assignable from any of the others.
414          */
415         if (uncoveredReturnTypes.size() > 1) {
416             ProxyMethod pm = methods.get(0);
417             throw new IllegalArgumentException(
418                     "methods with same signature " +
419                             pm.shortSignature +
420                             " but incompatible return types: " + uncoveredReturnTypes);
421         }
422     }
423 
424     /**
425      * Given the exceptions declared in the throws clause of a proxy method,
426      * compute the exceptions that need to be caught from the invocation
427      * handler's invoke method and rethrown intact in the method's
428      * implementation before catching other Throwables and wrapping them
429      * in UndeclaredThrowableExceptions.
430      *
431      * The exceptions to be caught are returned in a List object.  Each
432      * exception in the returned list is guaranteed to not be a subclass of
433      * any of the other exceptions in the list, so the catch blocks for
434      * these exceptions may be generated in any order relative to each other.
435      *
436      * Error and RuntimeException are each always contained by the returned
437      * list (if none of their superclasses are contained), since those
438      * unchecked exceptions should always be rethrown intact, and thus their
439      * subclasses will never appear in the returned list.
440      *
441      * The returned List will be empty if java.lang.Throwable is in the
442      * given list of declared exceptions, indicating that no exceptions
443      * need to be caught.
444      */
445     private static List<Class<?>> computeUniqueCatchList(Class<?>[] exceptions) {
446         List<Class<?>> uniqueList = new ArrayList<>();
447         // unique exceptions to catch
448 
449         uniqueList.add(Error.class);            // always catch/rethrow these
450         uniqueList.add(RuntimeException.class);
451 
452         nextException:
453         for (Class<?> ex : exceptions) {
454             if (ex.isAssignableFrom(Throwable.class)) {
455                 /*
456                  * If Throwable is declared to be thrown by the proxy method,
457                  * then no catch blocks are necessary, because the invoke
458                  * can, at most, throw Throwable anyway.
459                  */
460                 uniqueList.clear();
461                 break;
462             } else if (!Throwable.class.isAssignableFrom(ex)) {
463                 /*
464                  * Ignore types that cannot be thrown by the invoke method.
465                  */
466                 continue;
467             }
468             /*
469              * Compare this exception against the current list of
470              * exceptions that need to be caught:
471              */
472             for (int j = 0; j < uniqueList.size(); ) {
473                 Class<?> ex2 = uniqueList.get(j);
474                 if (ex2.isAssignableFrom(ex)) {
475                     /*
476                      * if a superclass of this exception is already on
477                      * the list to catch, then ignore this one and continue;
478                      */
479                     continue nextException;
480                 } else if (ex.isAssignableFrom(ex2)) {
481                     /*
482                      * if a subclass of this exception is on the list
483                      * to catch, then remove it;
484                      */
485                     uniqueList.remove(j);
486                 } else {
487                     j++;        // else continue comparing.
488                 }
489             }
490             // This exception is unique (so far): add it to the list to catch.
491             uniqueList.add(ex);
492         }
493         return uniqueList;
494     }
495 
496     /**
497      * Add to the given list all of the types in the "from" array that
498      * are not already contained in the list and are assignable to at
499      * least one of the types in the "with" array.
500      * <p>
501      * This method is useful for computing the greatest common set of
502      * declared exceptions from duplicate methods inherited from
503      * different interfaces.
504      */
505     private static void collectCompatibleTypes(Class<?>[] from,
506                                                Class<?>[] with,
507                                                List<Class<?>> list) {
508         for (Class<?> fc : from) {
509             if (!list.contains(fc)) {
510                 for (Class<?> wc : with) {
511                     if (wc.isAssignableFrom(fc)) {
512                         list.add(fc);
513                         break;
514                     }
515                 }
516             }
517         }
518     }
519 
520     /**
521      * Generate a class file for the proxy class.  This method drives the
522      * class file generation process.
523      *
524      * If a proxy interface references any value classes, the value classes
525      * are listed in the loadable descriptors attribute of the interface class.  The
526      * classes that are referenced by the proxy interface have already
527      * been loaded before the proxy class.  Hence the proxy class is
528      * generated with no loadable descriptors attributes as it essentially has no effect.
529      */
530     private byte[] generateClassFile() {
531         /*
532          * Add proxy methods for the hashCode, equals,
533          * and toString methods of java.lang.Object.  This is done before
534          * the methods from the proxy interfaces so that the methods from
535          * java.lang.Object take precedence over duplicate methods in the
536          * proxy interfaces.
537          */
538         addProxyMethod(hashCodeMethod);
539         addProxyMethod(equalsMethod);
540         addProxyMethod(toStringMethod);
541 
542         /*
543          * Accumulate all of the methods from the proxy interfaces.
544          */
545         for (Class<?> intf : interfaces) {
546             for (Method m : intf.getMethods()) {
547                 if (!Modifier.isStatic(m.getModifiers())) {
548                     addProxyMethod(m, intf, cp);
549                 }
550             }
551         }
552 
553         /*
554          * For each set of proxy methods with the same signature,
555          * verify that the methods' return types are compatible.
556          */
557         for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
558             checkReturnTypes(sigmethods);
559         }
560 
561         return classfileContext.build(classEntry, cp, clb -> {
562             TEMPLATE.forEach(clb);
563             clb.withFlags(accessFlags);
564             clb.withInterfaces(toClassEntries(cp, interfaces));
565 
566             for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
567                 for (ProxyMethod pm : sigmethods) {
568                     // add static field for the Method object
569                     clb.withField(pm.methodFieldName, UE_Method, ACC_PRIVATE | ACC_STATIC | ACC_FINAL);
570 
571                     // Generate code for proxy method
572                     pm.generateMethod(clb, classEntry);
573                 }
574             }
575 
576             generateStaticInitializer(clb);
577         });
578     }
579 
580     /**
581      * Add another method to be proxied, either by creating a new
582      * ProxyMethod object or augmenting an old one for a duplicate
583      * method.
584      *
585      * "fromClass" indicates the proxy interface that the method was
586      * found through, which may be different from (a subinterface of)
587      * the method's "declaring class".  Note that the first Method
588      * object passed for a given name and descriptor identifies the
589      * Method object (and thus the declaring class) that will be
590      * passed to the invocation handler's "invoke" method for a given
591      * set of duplicate methods.
592      */
593     private void addProxyMethod(Method m, Class<?> fromClass, ConstantPoolBuilder cp) {
594         Class<?> returnType = m.getReturnType();
595         Class<?>[] exceptionTypes = m.getSharedExceptionTypes();
596 
597         String sig = m.toShortSignature();
598         List<ProxyMethod> sigmethods = proxyMethods.computeIfAbsent(sig,
599                 (f) -> new ArrayList<>(3));
600         for (ProxyMethod pm : sigmethods) {
601             if (returnType == pm.returnType) {
602                 /*
603                  * Found a match: reduce exception types to the
604                  * greatest set of exceptions that can be thrown
605                  * compatibly with the throws clauses of both
606                  * overridden methods.
607                  */
608                 List<Class<?>> legalExceptions = new ArrayList<>();
609                 collectCompatibleTypes(
610                         exceptionTypes, pm.exceptionTypes, legalExceptions);
611                 collectCompatibleTypes(
612                         pm.exceptionTypes, exceptionTypes, legalExceptions);
613                 pm.exceptionTypes = legalExceptions.toArray(EMPTY_CLASS_ARRAY);
614                 return;
615             }
616         }
617         sigmethods.add(new ProxyMethod(m, sig, m.getSharedParameterTypes(), returnType,
618                 exceptionTypes, fromClass,
619                 cp.utf8Entry("m" + proxyMethodCount++)));
620     }
621 
622     /**
623      * Add an existing ProxyMethod (hashcode, equals, toString).
624      *
625      * @param pm an existing ProxyMethod
626      */
627     private void addProxyMethod(ProxyMethod pm) {
628         String sig = pm.shortSignature;
629         List<ProxyMethod> sigmethods = proxyMethods.computeIfAbsent(sig,
630                 (f) -> new ArrayList<>(3));
631         sigmethods.add(pm);
632     }
633 
634     /**
635      * Generate the constructor method for the proxy class.
636      */
637     private static void generateConstructor(ClassBuilder clb) {
638         clb.withMethodBody(INIT_NAME, MTD_void_InvocationHandler, ACC_PUBLIC, cob -> cob
639                .aload(cob.receiverSlot())
640                .aload(cob.parameterSlot(0))
641                .invokespecial(CD_Proxy, INIT_NAME, MTD_void_InvocationHandler)
642                .return_());
643     }
644 
645     /**
646      * Generate the static initializer method for the proxy class.
647      */
648     private void generateStaticInitializer(ClassBuilder clb) {
649         clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, cob -> {
650             // Put ClassLoader at local variable index 0, used by
651             // Class.forName(String, boolean, ClassLoader) calls
652             cob.ldc(classEntry)
653                .invokevirtual(MRE_Class_getClassLoader)
654                .astore(0);
655             var ts = cob.newBoundLabel();
656             for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
657                 for (ProxyMethod pm : sigmethods) {
658                     pm.codeFieldInitialization(cob, classEntry);
659                 }
660             }
661             cob.return_();
662             var c1 = cob.newBoundLabel();
663             cob.exceptionCatch(ts, c1, c1, CE_NoSuchMethodException)
664                .new_(CE_NoSuchMethodError)
665                .dup_x1()
666                .swap()
667                .invokevirtual(MRE_Throwable_getMessage)
668                .invokespecial(MRE_NoSuchMethodError_init)
669                .athrow();
670             var c2 = cob.newBoundLabel();
671             cob.exceptionCatch(ts, c1, c2, CE_ClassNotFoundException)
672                .new_(CE_NoClassDefFoundError)
673                .dup_x1()
674                .swap()
675                .invokevirtual(MRE_Throwable_getMessage)
676                .invokespecial(MRE_NoClassDefFoundError_init)
677                .athrow()
678                .with(StackMapTableAttribute.of(List.of(
679                      StackMapFrameInfo.of(c1, List.of(), THROWABLE_STACK),
680                      StackMapFrameInfo.of(c2, List.of(), THROWABLE_STACK))));
681         });
682     }
683 
684     /**
685      * Generate the static lookup accessor method that returns the Lookup
686      * on this proxy class if the caller's lookup class is java.lang.reflect.Proxy;
687      * otherwise, IllegalAccessException is thrown
688      */
689     private static void generateLookupAccessor(ClassBuilder clb) {
690         clb.withMethod(NAME_LOOKUP_ACCESSOR,
691                 MTD_MethodHandles$Lookup_MethodHandles$Lookup,
692                 ACC_PRIVATE | ACC_STATIC,
693                 mb -> mb.with(ExceptionsAttribute.of(List.of(mb.constantPool().classEntry(CD_IllegalAccessException))))
694                         .withCode(cob -> cob
695                             .block(blockBuilder -> blockBuilder
696                                     .aload(cob.parameterSlot(0))
697                                     .invokevirtual(CD_MethodHandles_Lookup, "lookupClass", MTD_Class)
698                                     .constantInstruction(Opcode.LDC, CD_Proxy)
699                                     .if_acmpne(blockBuilder.breakLabel())
700                                     .aload(cob.parameterSlot(0))
701                                     .invokevirtual(CD_MethodHandles_Lookup, "hasFullPrivilegeAccess", MTD_boolean)
702                                     .ifeq(blockBuilder.breakLabel())
703                                     .invokestatic(CD_MethodHandles, "lookup", MTD_MethodHandles$Lookup)
704                                     .areturn())
705                             .new_(CD_IllegalAccessException)
706                             .dup()
707                             .aload(cob.parameterSlot(0))
708                             .invokevirtual(CD_MethodHandles_Lookup, "toString", MTD_String)
709                             .invokespecial(CD_IllegalAccessException, INIT_NAME, MTD_void_String)
710                             .athrow()));
711     }
712 
713     /**
714      * A ProxyMethod object represents a proxy method in the proxy class
715      * being generated: a method whose implementation will encode and
716      * dispatch invocations to the proxy instance's invocation handler.
717      */
718     private static class ProxyMethod {
719 
720         private final Method method;
721         private final String shortSignature;
722         private final Class<?> fromClass;
723         private final Class<?>[] parameterTypes;
724         private final Class<?> returnType;
725         private final Utf8Entry methodFieldName;
726         private Class<?>[] exceptionTypes;
727 
728         private ProxyMethod(Method method, String sig, Class<?>[] parameterTypes,
729                             Class<?> returnType, Class<?>[] exceptionTypes,
730                             Class<?> fromClass, Utf8Entry methodFieldName) {
731             this.method = method;
732             this.shortSignature = sig;
733             this.parameterTypes = parameterTypes;
734             this.returnType = returnType;
735             this.exceptionTypes = exceptionTypes;
736             this.fromClass = fromClass;
737             this.methodFieldName = methodFieldName;
738         }
739 
740         /**
741          * Create a new specific ProxyMethod with a specific field name
742          *
743          * @param method          The method for which to create a proxy
744          * @param methodFieldName the fieldName to generate
745          */
746         private ProxyMethod(Method method, Utf8Entry methodFieldName) {
747             this(method, method.toShortSignature(),
748                  method.getSharedParameterTypes(), method.getReturnType(),
749                  method.getSharedExceptionTypes(), method.getDeclaringClass(), methodFieldName);
750         }
751 
752         /**
753          * Generate this method, including the code and exception table entry.
754          */
755         private void generateMethod(ClassBuilder clb, ClassEntry className) {
756             var cp = clb.constantPool();
757             MethodTypeDesc desc = MethodTypeDesc.of(toClassDesc(returnType),
758                     Arrays.stream(parameterTypes).map(ProxyGenerator::toClassDesc).toArray(ClassDesc[]::new));
759             int accessFlags = (method.isVarArgs()) ? ACC_VARARGS | ACC_PUBLIC | ACC_FINAL
760                                                    : ACC_PUBLIC | ACC_FINAL;
761             var catchList = computeUniqueCatchList(exceptionTypes);
762             clb.withMethod(method.getName(), desc, accessFlags, mb ->
763                   mb.with(ExceptionsAttribute.of(toClassEntries(cp, List.of(exceptionTypes))))
764                     .withCode(cob -> {
765                         cob.aload(cob.receiverSlot())
766                            .getfield(FRE_Proxy_h)
767                            .aload(cob.receiverSlot())
768                            .getstatic(cp.fieldRefEntry(className, cp.nameAndTypeEntry(methodFieldName, UE_Method)));
769 
770                         if (parameterTypes.length > 0) {
771                             // Create an array and fill with the parameters converting primitives to wrappers
772                             cob.constantInstruction(parameterTypes.length)
773                                .anewarray(CE_Object);
774                             for (int i = 0; i < parameterTypes.length; i++) {
775                                 cob.dup()
776                                    .constantInstruction(i);
777                                 codeWrapArgument(cob, parameterTypes[i], cob.parameterSlot(i));
778                                 cob.aastore();
779                             }
780                         } else {
781                             cob.aconst_null();
782                         }
783 
784                         cob.invokeinterface(IMRE_InvocationHandler_invoke);
785 
786                         if (returnType == void.class) {
787                             cob.pop()
788                                .return_();
789                         } else {
790                             codeUnwrapReturnValue(cob, returnType);
791                         }
792                         if (!catchList.isEmpty()) {
793                             var c1 = cob.newBoundLabel();
794                             for (var exc : catchList) {
795                                 cob.exceptionCatch(cob.startLabel(), c1, c1, toClassDesc(exc));
796                             }
797                             cob.athrow();   // just rethrow the exception
798                             var c2 = cob.newBoundLabel();
799                             cob.exceptionCatchAll(cob.startLabel(), c1, c2)
800                                .new_(CE_UndeclaredThrowableException)
801                                .dup_x1()
802                                .swap()
803                                .invokespecial(MRE_UndeclaredThrowableException_init)
804                                .athrow()
805                                .with(StackMapTableAttribute.of(List.of(
806                                     StackMapFrameInfo.of(c1, List.of(), THROWABLE_STACK),
807                                     StackMapFrameInfo.of(c2, List.of(), THROWABLE_STACK))));
808                         }
809                     }));
810         }
811 
812         /**
813          * Generate code for wrapping an argument of the given type
814          * whose value can be found at the specified local variable
815          * index, in order for it to be passed (as an Object) to the
816          * invocation handler's "invoke" method.
817          */
818         private void codeWrapArgument(CodeBuilder cob, Class<?> type, int slot) {
819             if (type.isPrimitive()) {
820                 cob.loadInstruction(TypeKind.from(type).asLoadable(), slot);
821                 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
822                 cob.invokestatic(prim.wrapperMethodRef);
823             } else {
824                 cob.aload(slot);
825             }
826         }
827 
828         /**
829          * Generate code for unwrapping a return value of the given
830          * type from the invocation handler's "invoke" method (as type
831          * Object) to its correct type.
832          */
833         private void codeUnwrapReturnValue(CodeBuilder cob, Class<?> type) {
834             if (type.isPrimitive()) {
835                 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
836 
837                 cob.checkcast(prim.wrapperClass)
838                    .invokevirtual(prim.unwrapMethodRef)
839                    .returnInstruction(TypeKind.from(type).asLoadable());
840             } else {
841                 cob.checkcast(toClassDesc(type))
842                    .areturn();
843             }
844         }
845 
846         /**
847          * Generate code for initializing the static field that stores
848          * the Method object for this proxy method. A class loader is
849          * anticipated at local variable index 0.
850          */
851         private void codeFieldInitialization(CodeBuilder cob, ClassEntry className) {
852             var cp = cob.constantPool();
853             codeClassForName(cob, fromClass);
854 
855             cob.ldc(method.getName())
856                .constantInstruction(parameterTypes.length)
857                .anewarray(CE_Class);
858 
859             // Construct an array with the parameter types mapping primitives to Wrapper types
860             for (int i = 0; i < parameterTypes.length; i++) {
861                 cob.dup()
862                    .constantInstruction(i);
863                 if (parameterTypes[i].isPrimitive()) {
864                     PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(parameterTypes[i]);
865                     cob.getstatic(prim.typeFieldRef);
866                 } else {
867                     codeClassForName(cob, parameterTypes[i]);
868                 }
869                 cob.aastore();
870             }
871             // lookup the method
872             cob.invokevirtual(MRE_Class_getMethod)
873                .putstatic(cp.fieldRefEntry(className, cp.nameAndTypeEntry(methodFieldName, UE_Method)));
874         }
875 
876         /*
877          * =============== Code Generation Utility Methods ===============
878          */
879 
880         /**
881          * Generate code to invoke the Class.forName with the name of the given
882          * class to get its Class object at runtime.  The code is written to
883          * the supplied stream.  Note that the code generated by this method
884          * may cause the checked ClassNotFoundException to be thrown. A class
885          * loader is anticipated at local variable index 0.
886          */
887         private void codeClassForName(CodeBuilder cob, Class<?> cl) {
888             cob.ldc(cl.getName())
889                .iconst_0() // false
890                .aload(0)// classLoader
891                .invokestatic(MRE_Class_forName);
892         }
893 
894         @Override
895         public String toString() {
896             return method.toShortString();
897         }
898     }
899 
900     private static final ConstantPoolBuilder CP = ConstantPoolBuilder.of();
901     /**
902      * A PrimitiveTypeInfo object contains bytecode-related information about
903      * a primitive type in its instance fields. The struct for a particular
904      * primitive type can be obtained using the static "get" method.
905      */
906     private enum PrimitiveTypeInfo {
907         BYTE(byte.class, CD_byte, CD_Byte),
908         CHAR(char.class, CD_char, CD_Character),
909         DOUBLE(double.class, CD_double, CD_Double),
910         FLOAT(float.class, CD_float, CD_Float),
911         INT(int.class, CD_int, CD_Integer),
912         LONG(long.class, CD_long, CD_Long),
913         SHORT(short.class, CD_short, CD_Short),
914         BOOLEAN(boolean.class, CD_boolean, CD_Boolean);
915 
916         /**
917          * CP entry of corresponding wrapper class
918          */
919         private final ClassEntry wrapperClass;
920         /**
921          * CP entry for wrapper class "valueOf" factory method
922          */
923         private final MethodRefEntry wrapperMethodRef;
924         /**
925          * CP entry of wrapper class method for retrieving primitive value
926          */
927         private final MethodRefEntry unwrapMethodRef;
928         /**
929          * CP entry of wrapper class TYPE field
930          */
931         private final FieldRefEntry typeFieldRef;
932 
933         PrimitiveTypeInfo(Class<?> primitiveClass, ClassDesc baseType, ClassDesc wrapperClass) {
934             assert baseType.isPrimitive();
935             this.wrapperClass = CP.classEntry(wrapperClass);
936             this.wrapperMethodRef = CP.methodRefEntry(wrapperClass, "valueOf", MethodTypeDesc.of(wrapperClass, baseType));
937             this.unwrapMethodRef = CP.methodRefEntry(wrapperClass, primitiveClass.getName() + "Value", MethodTypeDesc.of(baseType));
938             this.typeFieldRef = CP.fieldRefEntry(wrapperClass, "TYPE", CD_Class);
939         }
940 
941         public static PrimitiveTypeInfo get(Class<?> cl) {
942             // Uses if chain for speed: 8284880
943             if (cl == int.class)     return INT;
944             if (cl == long.class)    return LONG;
945             if (cl == boolean.class) return BOOLEAN;
946             if (cl == short.class)   return SHORT;
947             if (cl == byte.class)    return BYTE;
948             if (cl == char.class)    return CHAR;
949             if (cl == float.class)   return FLOAT;
950             if (cl == double.class)  return DOUBLE;
951             throw new AssertionError(cl);
952         }
953     }
954 }