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 jdk.internal.misc.PreviewFeatures;
  29 import jdk.internal.org.objectweb.asm.Attribute;
  30 import jdk.internal.org.objectweb.asm.ByteVector;
  31 import jdk.internal.org.objectweb.asm.ClassWriter;
  32 import jdk.internal.org.objectweb.asm.Label;
  33 import jdk.internal.org.objectweb.asm.MethodVisitor;
  34 import jdk.internal.org.objectweb.asm.Opcodes;
  35 import jdk.internal.org.objectweb.asm.Type;
  36 import sun.invoke.util.Wrapper;
  37 import sun.security.action.GetBooleanAction;
  38 
  39 import java.io.IOException;
  40 import java.lang.invoke.MethodType;
  41 import java.nio.file.Files;
  42 import java.nio.file.Path;
  43 import java.util.ArrayList;
  44 import java.util.Arrays;
  45 import java.util.HashSet;
  46 import java.util.LinkedHashMap;
  47 import java.util.List;
  48 import java.util.ListIterator;
  49 import java.util.Map;
  50 import java.util.Set;
  51 
  52 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  53 
  54 /**
  55  * ProxyGenerator contains the code to generate a dynamic proxy class
  56  * for the java.lang.reflect.Proxy API.
  57  * <p>
  58  * The external interface to ProxyGenerator is the static
  59  * "generateProxyClass" method.
  60  */
  61 final class ProxyGenerator extends ClassWriter {
  62     private static final int CLASSFILE_VERSION = ClassFileFormatVersion.latest().major();
  63     private static final String JL_CLASS = "java/lang/Class";
  64     private static final String JL_OBJECT = "java/lang/Object";
  65     private static final String JL_THROWABLE = "java/lang/Throwable";
  66     private static final String JL_CLASS_NOT_FOUND_EX = "java/lang/ClassNotFoundException";
  67     private static final String JL_ILLEGAL_ACCESS_EX = "java/lang/IllegalAccessException";
  68 
  69     private static final String JL_NO_CLASS_DEF_FOUND_ERROR = "java/lang/NoClassDefFoundError";
  70     private static final String JL_NO_SUCH_METHOD_EX = "java/lang/NoSuchMethodException";
  71     private static final String JL_NO_SUCH_METHOD_ERROR = "java/lang/NoSuchMethodError";
  72     private static final String JLI_LOOKUP = "java/lang/invoke/MethodHandles$Lookup";
  73     private static final String JLI_METHODHANDLES = "java/lang/invoke/MethodHandles";
  74 
  75     private static final String JLR_INVOCATION_HANDLER = "java/lang/reflect/InvocationHandler";
  76     private static final String JLR_PROXY = "java/lang/reflect/Proxy";
  77     private static final String JLR_UNDECLARED_THROWABLE_EX = "java/lang/reflect/UndeclaredThrowableException";
  78 
  79     private static final String LJL_CLASS = "Ljava/lang/Class;";
  80     private static final String LJL_CLASSLOADER = "Ljava/lang/ClassLoader;";
  81     private static final String LJLR_METHOD = "Ljava/lang/reflect/Method;";
  82     private static final String LJLR_INVOCATION_HANDLER = "Ljava/lang/reflect/InvocationHandler;";
  83 
  84     private static final String MJLR_INVOCATIONHANDLER = "(Ljava/lang/reflect/InvocationHandler;)V";
  85 
  86     private static final String NAME_CTOR = "<init>";
  87     private static final String NAME_CLINIT = "<clinit>";
  88     private static final String NAME_LOOKUP_ACCESSOR = "proxyClassLookup";
  89 
  90     private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
  91 
  92     /**
  93      * name of field for storing a proxy instance's invocation handler
  94      */
  95     private static final String handlerFieldName = "h";
  96 
  97     /**
  98      * debugging flag for saving generated class files
  99      */
 100     @SuppressWarnings("removal")
 101     private static final boolean saveGeneratedFiles =
 102             java.security.AccessController.doPrivileged(
 103                     new GetBooleanAction(
 104                             "jdk.proxy.ProxyGenerator.saveGeneratedFiles"));
 105 
 106     /* Preloaded ProxyMethod objects for methods in java.lang.Object */
 107     private static final ProxyMethod hashCodeMethod;
 108     private static final ProxyMethod equalsMethod;
 109     private static final ProxyMethod toStringMethod;
 110 
 111     static {
 112         try {
 113             hashCodeMethod = new ProxyMethod(Object.class.getMethod("hashCode"), "m0");
 114             equalsMethod = new ProxyMethod(Object.class.getMethod("equals", Object.class), "m1");
 115             toStringMethod = new ProxyMethod(Object.class.getMethod("toString"), "m2");
 116         } catch (NoSuchMethodException e) {
 117             throw new NoSuchMethodError(e.getMessage());
 118         }
 119     }
 120 
 121     /**
 122      * Class loader
 123      */
 124     private final ClassLoader loader;
 125 
 126     /**
 127      * Name of proxy class
 128      */
 129     private final String className;
 130 
 131     /**
 132      * Proxy interfaces
 133      */
 134     private final List<Class<?>> interfaces;
 135 
 136     /**
 137      * Proxy class access flags
 138      */
 139     private final int accessFlags;
 140 
 141     /**
 142      * Maps method signature string to list of ProxyMethod objects for
 143      * proxy methods with that signature.
 144      * Kept in insertion order to make it easier to compare old and new.
 145      */
 146     private final Map<String, List<ProxyMethod>> proxyMethods = new LinkedHashMap<>();
 147 
 148     /**
 149      * Ordinal of next ProxyMethod object added to proxyMethods.
 150      * Indexes are reserved for hashcode(0), equals(1), toString(2).
 151      */
 152     private int proxyMethodCount = 3;
 153 
 154     /**
 155      * Construct a ProxyGenerator to generate a proxy class with the
 156      * specified name and for the given interfaces.
 157      * <p>
 158      * A ProxyGenerator object contains the state for the ongoing
 159      * generation of a particular proxy class.
 160      */
 161     private ProxyGenerator(ClassLoader loader, String className, List<Class<?>> interfaces,
 162                            int accessFlags) {
 163         super(ClassWriter.COMPUTE_FRAMES);
 164         this.loader = loader;
 165         this.className = className;
 166         this.interfaces = interfaces;
 167         this.accessFlags = accessFlags;
 168     }
 169 
 170     /**
 171      * Generate a proxy class given a name and a list of proxy interfaces.
 172      *
 173      * @param name        the class name of the proxy class
 174      * @param interfaces  proxy interfaces
 175      * @param accessFlags access flags of the proxy class
 176      */
 177     @SuppressWarnings("removal")
 178     static byte[] generateProxyClass(ClassLoader loader,
 179                                      final String name,
 180                                      List<Class<?>> interfaces,
 181                                      int accessFlags) {
 182         ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags);
 183         final byte[] classFile = gen.generateClassFile();
 184 
 185         if (saveGeneratedFiles) {
 186             java.security.AccessController.doPrivileged(
 187                     new java.security.PrivilegedAction<Void>() {
 188                         public Void run() {
 189                             try {
 190                                 int i = name.lastIndexOf('.');
 191                                 Path path;
 192                                 if (i > 0) {
 193                                     Path dir = Path.of(dotToSlash(name.substring(0, i)));
 194                                     Files.createDirectories(dir);
 195                                     path = dir.resolve(name.substring(i + 1) + ".class");
 196                                 } else {
 197                                     path = Path.of(name + ".class");
 198                                 }
 199                                 Files.write(path, classFile);
 200                                 return null;
 201                             } catch (IOException e) {
 202                                 throw new InternalError(
 203                                         "I/O exception saving generated file: " + e);
 204                             }
 205                         }
 206                     });
 207         }
 208 
 209         return classFile;
 210     }
 211 
 212     /**
 213      * Return an array of the class and interface names from an array of Classes.
 214      *
 215      * @param classes an array of classes or interfaces
 216      * @return the array of class and interface names; or null if classes is
 217      * null or empty
 218      */
 219     private static String[] typeNames(List<Class<?>> classes) {
 220         if (classes == null || classes.size() == 0)
 221             return null;
 222         int size = classes.size();
 223         String[] ifaces = new String[size];
 224         for (int i = 0; i < size; i++)
 225             ifaces[i] = dotToSlash(classes.get(i).getName());
 226         return ifaces;
 227     }
 228 
 229     /**
 230      * For a given set of proxy methods with the same signature, check
 231      * that their return types are compatible according to the Proxy
 232      * specification.
 233      *
 234      * Specifically, if there is more than one such method, then all
 235      * of the return types must be reference types, and there must be
 236      * one return type that is assignable to each of the rest of them.
 237      */
 238     private static void checkReturnTypes(List<ProxyMethod> methods) {
 239         /*
 240          * If there is only one method with a given signature, there
 241          * cannot be a conflict.  This is the only case in which a
 242          * primitive (or void) return type is allowed.
 243          */
 244         if (methods.size() < 2) {
 245             return;
 246         }
 247 
 248         /*
 249          * List of return types that are not yet known to be
 250          * assignable from ("covered" by) any of the others.
 251          */
 252         List<Class<?>> uncoveredReturnTypes = new ArrayList<>(1);
 253 
 254         nextNewReturnType:
 255         for (ProxyMethod pm : methods) {
 256             Class<?> newReturnType = pm.returnType;
 257             if (newReturnType.isPrimitive()) {
 258                 throw new IllegalArgumentException(
 259                         "methods with same signature " +
 260                                 pm.shortSignature +
 261                                 " but incompatible return types: " +
 262                                 newReturnType.getName() + " and others");
 263             }
 264             boolean added = false;
 265 
 266             /*
 267              * Compare the new return type to the existing uncovered
 268              * return types.
 269              */
 270             ListIterator<Class<?>> liter = uncoveredReturnTypes.listIterator();
 271             while (liter.hasNext()) {
 272                 Class<?> uncoveredReturnType = liter.next();
 273 
 274                 /*
 275                  * If an existing uncovered return type is assignable
 276                  * to this new one, then we can forget the new one.
 277                  */
 278                 if (newReturnType.isAssignableFrom(uncoveredReturnType)) {
 279                     assert !added;
 280                     continue nextNewReturnType;
 281                 }
 282 
 283                 /*
 284                  * If the new return type is assignable to an existing
 285                  * uncovered one, then should replace the existing one
 286                  * with the new one (or just forget the existing one,
 287                  * if the new one has already be put in the list).
 288                  */
 289                 if (uncoveredReturnType.isAssignableFrom(newReturnType)) {
 290                     // (we can assume that each return type is unique)
 291                     if (!added) {
 292                         liter.set(newReturnType);
 293                         added = true;
 294                     } else {
 295                         liter.remove();
 296                     }
 297                 }
 298             }
 299 
 300             /*
 301              * If we got through the list of existing uncovered return
 302              * types without an assignability relationship, then add
 303              * the new return type to the list of uncovered ones.
 304              */
 305             if (!added) {
 306                 uncoveredReturnTypes.add(newReturnType);
 307             }
 308         }
 309 
 310         /*
 311          * We shouldn't end up with more than one return type that is
 312          * not assignable from any of the others.
 313          */
 314         if (uncoveredReturnTypes.size() > 1) {
 315             ProxyMethod pm = methods.get(0);
 316             throw new IllegalArgumentException(
 317                     "methods with same signature " +
 318                             pm.shortSignature +
 319                             " but incompatible return types: " + uncoveredReturnTypes);
 320         }
 321     }
 322 
 323     /**
 324      * Given the exceptions declared in the throws clause of a proxy method,
 325      * compute the exceptions that need to be caught from the invocation
 326      * handler's invoke method and rethrown intact in the method's
 327      * implementation before catching other Throwables and wrapping them
 328      * in UndeclaredThrowableExceptions.
 329      *
 330      * The exceptions to be caught are returned in a List object.  Each
 331      * exception in the returned list is guaranteed to not be a subclass of
 332      * any of the other exceptions in the list, so the catch blocks for
 333      * these exceptions may be generated in any order relative to each other.
 334      *
 335      * Error and RuntimeException are each always contained by the returned
 336      * list (if none of their superclasses are contained), since those
 337      * unchecked exceptions should always be rethrown intact, and thus their
 338      * subclasses will never appear in the returned list.
 339      *
 340      * The returned List will be empty if java.lang.Throwable is in the
 341      * given list of declared exceptions, indicating that no exceptions
 342      * need to be caught.
 343      */
 344     private static List<Class<?>> computeUniqueCatchList(Class<?>[] exceptions) {
 345         List<Class<?>> uniqueList = new ArrayList<>();
 346         // unique exceptions to catch
 347 
 348         uniqueList.add(Error.class);            // always catch/rethrow these
 349         uniqueList.add(RuntimeException.class);
 350 
 351         nextException:
 352         for (Class<?> ex : exceptions) {
 353             if (ex.isAssignableFrom(Throwable.class)) {
 354                 /*
 355                  * If Throwable is declared to be thrown by the proxy method,
 356                  * then no catch blocks are necessary, because the invoke
 357                  * can, at most, throw Throwable anyway.
 358                  */
 359                 uniqueList.clear();
 360                 break;
 361             } else if (!Throwable.class.isAssignableFrom(ex)) {
 362                 /*
 363                  * Ignore types that cannot be thrown by the invoke method.
 364                  */
 365                 continue;
 366             }
 367             /*
 368              * Compare this exception against the current list of
 369              * exceptions that need to be caught:
 370              */
 371             for (int j = 0; j < uniqueList.size(); ) {
 372                 Class<?> ex2 = uniqueList.get(j);
 373                 if (ex2.isAssignableFrom(ex)) {
 374                     /*
 375                      * if a superclass of this exception is already on
 376                      * the list to catch, then ignore this one and continue;
 377                      */
 378                     continue nextException;
 379                 } else if (ex.isAssignableFrom(ex2)) {
 380                     /*
 381                      * if a subclass of this exception is on the list
 382                      * to catch, then remove it;
 383                      */
 384                     uniqueList.remove(j);
 385                 } else {
 386                     j++;        // else continue comparing.
 387                 }
 388             }
 389             // This exception is unique (so far): add it to the list to catch.
 390             uniqueList.add(ex);
 391         }
 392         return uniqueList;
 393     }
 394 
 395     /**
 396      * Convert a fully qualified class name that uses '.' as the package
 397      * separator, the external representation used by the Java language
 398      * and APIs, to a fully qualified class name that uses '/' as the
 399      * package separator, the representation used in the class file
 400      * format (see JVMS section {@jvms 4.2}).
 401      */
 402     private static String dotToSlash(String name) {
 403         return name.replace('.', '/');
 404     }
 405 
 406     /**
 407      * Return the number of abstract "words", or consecutive local variable
 408      * indexes, required to contain a value of the given type.  See JVMS
 409      * section {@jvms 3.6.1}.
 410      * <p>
 411      * Note that the original version of the JVMS contained a definition of
 412      * this abstract notion of a "word" in section 3.4, but that definition
 413      * was removed for the second edition.
 414      */
 415     private static int getWordsPerType(Class<?> type) {
 416         if (type == long.class || type == double.class) {
 417             return 2;
 418         } else {
 419             return 1;
 420         }
 421     }
 422 
 423     /**
 424      * Add to the given list all of the types in the "from" array that
 425      * are not already contained in the list and are assignable to at
 426      * least one of the types in the "with" array.
 427      * <p>
 428      * This method is useful for computing the greatest common set of
 429      * declared exceptions from duplicate methods inherited from
 430      * different interfaces.
 431      */
 432     private static void collectCompatibleTypes(Class<?>[] from,
 433                                                Class<?>[] with,
 434                                                List<Class<?>> list) {
 435         for (Class<?> fc : from) {
 436             if (!list.contains(fc)) {
 437                 for (Class<?> wc : with) {
 438                     if (wc.isAssignableFrom(fc)) {
 439                         list.add(fc);
 440                         break;
 441                     }
 442                 }
 443             }
 444         }
 445     }
 446 
 447     /**
 448      * Returns the {@link ClassLoader} to be used by the default implementation of {@link
 449      * #getCommonSuperClass(String, String)}, that of this {@link ClassWriter}'s runtime type by
 450      * default.
 451      *
 452      * @return ClassLoader
 453      */
 454     protected ClassLoader getClassLoader() {
 455         return loader;
 456     }
 457 
 458     /**
 459      * Generate a class file for the proxy class.  This method drives the
 460      * class file generation process.
 461      */
 462     private byte[] generateClassFile() {
 463         int version = CLASSFILE_VERSION | (PreviewFeatures.isEnabled() ? Opcodes.V_PREVIEW : 0);
 464         visit(version, accessFlags, dotToSlash(className), null,
 465                 JLR_PROXY, typeNames(interfaces));
 466 
 467         /*
 468          * Add proxy methods for the hashCode, equals,
 469          * and toString methods of java.lang.Object.  This is done before
 470          * the methods from the proxy interfaces so that the methods from
 471          * java.lang.Object take precedence over duplicate methods in the
 472          * proxy interfaces.
 473          */
 474         addProxyMethod(hashCodeMethod);
 475         addProxyMethod(equalsMethod);
 476         addProxyMethod(toStringMethod);
 477 
 478         /*
 479          * Accumulate all of the methods from the proxy interfaces.
 480          */
 481         for (Class<?> intf : interfaces) {
 482             for (Method m : intf.getMethods()) {
 483                 if (!Modifier.isStatic(m.getModifiers())) {
 484                     addProxyMethod(m, intf);
 485                 }
 486             }
 487         }
 488 
 489         /*
 490          * For each set of proxy methods with the same signature,
 491          * verify that the methods' return types are compatible.
 492          *
 493          * Determine if any value classes to be preloaded.
 494          */
 495         Set<Class<?>> preloadClasses = new HashSet<>();
 496         for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
 497             checkReturnTypes(sigmethods);
 498             for (ProxyMethod pm : sigmethods) {
 499                 preloadClasses.addAll(pm.preloadClasses());
 500             }
 501         }
 502 
 503         generateConstructor();
 504 
 505         for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
 506             for (ProxyMethod pm : sigmethods) {
 507                 // add static field for the Method object
 508                 visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, pm.methodFieldName,
 509                         LJLR_METHOD, null, null);
 510 
 511                 // Generate code for proxy method
 512                 pm.generateMethod(this, className);
 513             }
 514         }
 515         if (preloadClasses.size() > 0) {
 516             generatePreloadAttribute(preloadClasses);
 517         }
 518 
 519         generateStaticInitializer();
 520         generateLookupAccessor();
 521         return toByteArray();
 522     }
 523 
 524     /**
 525      * Add another method to be proxied, either by creating a new
 526      * ProxyMethod object or augmenting an old one for a duplicate
 527      * method.
 528      *
 529      * "fromClass" indicates the proxy interface that the method was
 530      * found through, which may be different from (a subinterface of)
 531      * the method's "declaring class".  Note that the first Method
 532      * object passed for a given name and descriptor identifies the
 533      * Method object (and thus the declaring class) that will be
 534      * passed to the invocation handler's "invoke" method for a given
 535      * set of duplicate methods.
 536      */
 537     private void addProxyMethod(Method m, Class<?> fromClass) {
 538         Class<?> returnType = m.getReturnType();
 539         Class<?>[] exceptionTypes = m.getSharedExceptionTypes();
 540 
 541         String sig = m.toShortSignature();
 542         List<ProxyMethod> sigmethods = proxyMethods.computeIfAbsent(sig,
 543                 (f) -> new ArrayList<>(3));
 544         for (ProxyMethod pm : sigmethods) {
 545             if (returnType == pm.returnType) {
 546                 /*
 547                  * Found a match: reduce exception types to the
 548                  * greatest set of exceptions that can be thrown
 549                  * compatibly with the throws clauses of both
 550                  * overridden methods.
 551                  */
 552                 List<Class<?>> legalExceptions = new ArrayList<>();
 553                 collectCompatibleTypes(
 554                         exceptionTypes, pm.exceptionTypes, legalExceptions);
 555                 collectCompatibleTypes(
 556                         pm.exceptionTypes, exceptionTypes, legalExceptions);
 557                 pm.exceptionTypes = legalExceptions.toArray(EMPTY_CLASS_ARRAY);
 558                 return;
 559             }
 560         }
 561         sigmethods.add(new ProxyMethod(m, sig, m.getSharedParameterTypes(), returnType,
 562                 exceptionTypes, fromClass,
 563                 "m" + proxyMethodCount++));
 564     }
 565 
 566     /**
 567      * Add an existing ProxyMethod (hashcode, equals, toString).
 568      *
 569      * @param pm an existing ProxyMethod
 570      */
 571     private void addProxyMethod(ProxyMethod pm) {
 572         String sig = pm.shortSignature;
 573         List<ProxyMethod> sigmethods = proxyMethods.computeIfAbsent(sig,
 574                 (f) -> new ArrayList<>(3));
 575         sigmethods.add(pm);
 576     }
 577 
 578     /**
 579      * Generate the constructor method for the proxy class.
 580      */
 581     private void generateConstructor() {
 582         MethodVisitor ctor = visitMethod(Modifier.PUBLIC, NAME_CTOR,
 583                 MJLR_INVOCATIONHANDLER, null, null);
 584         ctor.visitParameter(null, 0);
 585         ctor.visitCode();
 586         ctor.visitVarInsn(ALOAD, 0);
 587         ctor.visitVarInsn(ALOAD, 1);
 588         ctor.visitMethodInsn(INVOKESPECIAL, JLR_PROXY, NAME_CTOR,
 589                 MJLR_INVOCATIONHANDLER, false);
 590         ctor.visitInsn(RETURN);
 591 
 592         // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
 593         ctor.visitMaxs(-1, -1);
 594         ctor.visitEnd();
 595     }
 596 
 597     /**
 598      * Generate the static initializer method for the proxy class.
 599      */
 600     private void generateStaticInitializer() {
 601 
 602         MethodVisitor mv = visitMethod(Modifier.STATIC, NAME_CLINIT,
 603                 "()V", null, null);
 604         mv.visitCode();
 605         Label L_startBlock = new Label();
 606         Label L_endBlock = new Label();
 607         Label L_NoMethodHandler = new Label();
 608         Label L_NoClassHandler = new Label();
 609 
 610         mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_NoMethodHandler,
 611                 JL_NO_SUCH_METHOD_EX);
 612         mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_NoClassHandler,
 613                 JL_CLASS_NOT_FOUND_EX);
 614 
 615         // Put ClassLoader at local variable index 0, used by
 616         // Class.forName(String, boolean, ClassLoader) calls
 617         mv.visitLdcInsn(Type.getObjectType(dotToSlash(className)));
 618         mv.visitMethodInsn(INVOKEVIRTUAL, JL_CLASS,
 619                 "getClassLoader", "()" + LJL_CLASSLOADER, false);
 620         mv.visitVarInsn(ASTORE, 0);
 621 
 622         mv.visitLabel(L_startBlock);
 623         for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
 624             for (ProxyMethod pm : sigmethods) {
 625                 pm.codeFieldInitialization(mv, className);
 626             }
 627         }
 628         mv.visitInsn(RETURN);
 629         mv.visitLabel(L_endBlock);
 630         // Generate exception handler
 631 
 632         mv.visitLabel(L_NoMethodHandler);
 633         mv.visitVarInsn(ASTORE, 1);
 634         mv.visitTypeInsn(Opcodes.NEW, JL_NO_SUCH_METHOD_ERROR);
 635         mv.visitInsn(DUP);
 636         mv.visitVarInsn(ALOAD, 1);
 637         mv.visitMethodInsn(INVOKEVIRTUAL, JL_THROWABLE,
 638                 "getMessage", "()Ljava/lang/String;", false);
 639         mv.visitMethodInsn(INVOKESPECIAL, JL_NO_SUCH_METHOD_ERROR,
 640                 "<init>", "(Ljava/lang/String;)V", false);
 641         mv.visitInsn(ATHROW);
 642 
 643         mv.visitLabel(L_NoClassHandler);
 644         mv.visitVarInsn(ASTORE, 1);
 645         mv.visitTypeInsn(Opcodes.NEW, JL_NO_CLASS_DEF_FOUND_ERROR);
 646         mv.visitInsn(DUP);
 647         mv.visitVarInsn(ALOAD, 1);
 648         mv.visitMethodInsn(INVOKEVIRTUAL, JL_THROWABLE,
 649                 "getMessage", "()Ljava/lang/String;", false);
 650         mv.visitMethodInsn(INVOKESPECIAL, JL_NO_CLASS_DEF_FOUND_ERROR,
 651                 "<init>", "(Ljava/lang/String;)V", false);
 652         mv.visitInsn(ATHROW);
 653 
 654         // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
 655         mv.visitMaxs(-1, -1);
 656         mv.visitEnd();
 657     }
 658 
 659     /**
 660      * Generate the static lookup accessor method that returns the Lookup
 661      * on this proxy class if the caller's lookup class is java.lang.reflect.Proxy;
 662      * otherwise, IllegalAccessException is thrown
 663      */
 664     private void generateLookupAccessor() {
 665         MethodVisitor mv = visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_LOOKUP_ACCESSOR,
 666                 "(Ljava/lang/invoke/MethodHandles$Lookup;)Ljava/lang/invoke/MethodHandles$Lookup;", null,
 667                 new String[] { JL_ILLEGAL_ACCESS_EX });
 668         mv.visitCode();
 669         Label L_illegalAccess = new Label();
 670 
 671         mv.visitVarInsn(ALOAD, 0);
 672         mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "lookupClass",
 673                 "()Ljava/lang/Class;", false);
 674         mv.visitLdcInsn(Type.getType(Proxy.class));
 675         mv.visitJumpInsn(IF_ACMPNE, L_illegalAccess);
 676         mv.visitVarInsn(ALOAD, 0);
 677         mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "hasFullPrivilegeAccess",
 678                 "()Z", false);
 679         mv.visitJumpInsn(IFEQ, L_illegalAccess);
 680         mv.visitMethodInsn(INVOKESTATIC, JLI_METHODHANDLES, "lookup",
 681                 "()Ljava/lang/invoke/MethodHandles$Lookup;", false);
 682         mv.visitInsn(ARETURN);
 683 
 684         mv.visitLabel(L_illegalAccess);
 685         mv.visitTypeInsn(Opcodes.NEW, JL_ILLEGAL_ACCESS_EX);
 686         mv.visitInsn(DUP);
 687         mv.visitVarInsn(ALOAD, 0);
 688         mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "toString",
 689                 "()Ljava/lang/String;", false);
 690         mv.visitMethodInsn(INVOKESPECIAL, JL_ILLEGAL_ACCESS_EX,
 691                 "<init>", "(Ljava/lang/String;)V", false);
 692         mv.visitInsn(ATHROW);
 693 
 694         // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
 695         mv.visitMaxs(-1, -1);
 696         mv.visitEnd();
 697     }
 698 
 699     private void generatePreloadAttribute(Set<Class<?>> preloadClasses) {
 700         Attribute attr = new Attribute("Preload") {
 701             @Override
 702             protected ByteVector write(ClassWriter cw,
 703                                        byte[] code,
 704                                        int len,
 705                                        int maxStack,
 706                                        int maxLocals) {
 707                 ByteVector attr = new ByteVector();
 708                 attr.putShort(preloadClasses.size());
 709                 for (Class<?> c : preloadClasses) {
 710                     attr.putShort(cw.newClass(Type.getInternalName(c)));
 711                 }
 712                 return attr;
 713             }
 714         };
 715         visitAttribute(attr);
 716     }
 717     /**
 718      * A ProxyMethod object represents a proxy method in the proxy class
 719      * being generated: a method whose implementation will encode and
 720      * dispatch invocations to the proxy instance's invocation handler.
 721      */
 722     private static class ProxyMethod {
 723 
 724         private final Method method;
 725         private final String shortSignature;
 726         private final Class<?> fromClass;
 727         private final Class<?>[] parameterTypes;
 728         private final Class<?> returnType;
 729         private final String methodFieldName;
 730         private Class<?>[] exceptionTypes;
 731 
 732         private ProxyMethod(Method method, String sig, Class<?>[] parameterTypes,
 733                             Class<?> returnType, Class<?>[] exceptionTypes,
 734                             Class<?> fromClass, String methodFieldName) {
 735             this.method = method;
 736             this.shortSignature = sig;
 737             this.parameterTypes = parameterTypes;
 738             this.returnType = returnType;
 739             this.exceptionTypes = exceptionTypes;
 740             this.fromClass = fromClass;
 741             this.methodFieldName = methodFieldName;
 742         }
 743 
 744         /**
 745          * Create a new specific ProxyMethod with a specific field name
 746          *
 747          * @param method          The method for which to create a proxy
 748          * @param methodFieldName the fieldName to generate
 749          */
 750         private ProxyMethod(Method method, String methodFieldName) {
 751             this(method, method.toShortSignature(),
 752                     method.getSharedParameterTypes(), method.getReturnType(),
 753                     method.getSharedExceptionTypes(), method.getDeclaringClass(), methodFieldName);
 754         }
 755 
 756         /**
 757          * Generate this method, including the code and exception table entry.
 758          */
 759         private void generateMethod(ClassWriter cw, String className) {
 760             MethodType mt = MethodType.methodType(returnType, parameterTypes);
 761             String desc = mt.toMethodDescriptorString();
 762             int accessFlags = ACC_PUBLIC | ACC_FINAL;
 763             if (method.isVarArgs()) accessFlags |= ACC_VARARGS;
 764 
 765             MethodVisitor mv = cw.visitMethod(accessFlags,
 766                     method.getName(), desc, null,
 767                     typeNames(Arrays.asList(exceptionTypes)));
 768 
 769             int[] parameterSlot = new int[parameterTypes.length];
 770             int nextSlot = 1;
 771             for (int i = 0; i < parameterSlot.length; i++) {
 772                 parameterSlot[i] = nextSlot;
 773                 nextSlot += getWordsPerType(parameterTypes[i]);
 774             }
 775 
 776             mv.visitCode();
 777             Label L_startBlock = new Label();
 778             Label L_endBlock = new Label();
 779             Label L_RuntimeHandler = new Label();
 780             Label L_ThrowableHandler = new Label();
 781 
 782             List<Class<?>> catchList = computeUniqueCatchList(exceptionTypes);
 783             if (catchList.size() > 0) {
 784                 for (Class<?> ex : catchList) {
 785                     mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_RuntimeHandler,
 786                             dotToSlash(ex.getName()));
 787                 }
 788 
 789                 mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_ThrowableHandler,
 790                         JL_THROWABLE);
 791             }
 792             mv.visitLabel(L_startBlock);
 793 
 794             mv.visitVarInsn(ALOAD, 0);
 795             mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName,
 796                     LJLR_INVOCATION_HANDLER);
 797             mv.visitVarInsn(ALOAD, 0);
 798             mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName,
 799                     LJLR_METHOD);
 800 
 801             if (parameterTypes.length > 0) {
 802                 // Create an array and fill with the parameters converting primitives to wrappers
 803                 emitIconstInsn(mv, parameterTypes.length);
 804                 mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_OBJECT);
 805                 for (int i = 0; i < parameterTypes.length; i++) {
 806                     mv.visitInsn(DUP);
 807                     emitIconstInsn(mv, i);
 808                     codeWrapArgument(mv, parameterTypes[i], parameterSlot[i]);
 809                     mv.visitInsn(Opcodes.AASTORE);
 810                 }
 811             } else {
 812                 mv.visitInsn(Opcodes.ACONST_NULL);
 813             }
 814 
 815             mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER,
 816                     "invoke",
 817                     "(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
 818                             "[Ljava/lang/Object;)Ljava/lang/Object;", true);
 819 
 820             if (returnType == void.class) {
 821                 mv.visitInsn(POP);
 822                 mv.visitInsn(RETURN);
 823             } else {
 824                 codeUnwrapReturnValue(mv, returnType);
 825             }
 826 
 827             mv.visitLabel(L_endBlock);
 828 
 829             // Generate exception handler
 830             mv.visitLabel(L_RuntimeHandler);
 831             mv.visitInsn(ATHROW);   // just rethrow the exception
 832 
 833             mv.visitLabel(L_ThrowableHandler);
 834             mv.visitVarInsn(ASTORE, 1);
 835             mv.visitTypeInsn(Opcodes.NEW, JLR_UNDECLARED_THROWABLE_EX);
 836             mv.visitInsn(DUP);
 837             mv.visitVarInsn(ALOAD, 1);
 838             mv.visitMethodInsn(INVOKESPECIAL, JLR_UNDECLARED_THROWABLE_EX,
 839                     "<init>", "(Ljava/lang/Throwable;)V", false);
 840             mv.visitInsn(ATHROW);
 841             // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
 842             mv.visitMaxs(-1, -1);
 843             mv.visitEnd();
 844         }
 845 
 846         Set<Class<?>> preloadClasses() {
 847             Set<Class<?>> preloadClasses = new HashSet<>();
 848             for (Class<?> type : parameterTypes) {
 849                 if (requiresPreload(type)) {
 850                     preloadClasses.add(type);
 851                 }
 852             }
 853             if (requiresPreload(returnType)) {
 854                 preloadClasses.add(returnType);
 855             }
 856             return preloadClasses;
 857         }
 858 
 859         boolean requiresPreload(Class<?> cls) {
 860             Class<?> c = cls;
 861             while (c.isArray()) {
 862                 c = c.getComponentType();
 863             }
 864             return c.isValue();
 865         }
 866 
 867         /**
 868          * Generate code for wrapping an argument of the given type
 869          * whose value can be found at the specified local variable
 870          * index, in order for it to be passed (as an Object) to the
 871          * invocation handler's "invoke" method.
 872          */
 873         private void codeWrapArgument(MethodVisitor mv, Class<?> type, int slot) {
 874             if (type.isPrimitive()) {
 875                 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
 876 
 877                 mv.visitVarInsn(prim.loadOpcode, slot);
 878                 mv.visitMethodInsn(INVOKESTATIC, prim.wrapperClassName, "valueOf",
 879                         prim.wrapperValueOfDesc, false);
 880             } else {
 881                 mv.visitVarInsn(ALOAD, slot);
 882             }
 883         }
 884 
 885         /**
 886          * Generate code for unwrapping a return value of the given
 887          * type from the invocation handler's "invoke" method (as type
 888          * Object) to its correct type.
 889          */
 890         private void codeUnwrapReturnValue(MethodVisitor mv, Class<?> type) {
 891             if (type.isPrimitive()) {
 892                 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
 893 
 894                 mv.visitTypeInsn(CHECKCAST, prim.wrapperClassName);
 895                 mv.visitMethodInsn(INVOKEVIRTUAL,
 896                         prim.wrapperClassName,
 897                         prim.unwrapMethodName, prim.unwrapMethodDesc, false);
 898 
 899                 mv.visitInsn(prim.returnOpcode);
 900             } else {
 901                 mv.visitTypeInsn(CHECKCAST, dotToSlash(type.getName()));
 902                 mv.visitInsn(ARETURN);
 903             }
 904         }
 905 
 906         /**
 907          * Generate code for initializing the static field that stores
 908          * the Method object for this proxy method. A class loader is
 909          * anticipated at local variable index 0.
 910          */
 911         private void codeFieldInitialization(MethodVisitor mv, String className) {
 912             codeClassForName(mv, fromClass);
 913 
 914             mv.visitLdcInsn(method.getName());
 915 
 916             emitIconstInsn(mv, parameterTypes.length);
 917 
 918             mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_CLASS);
 919 
 920             // Construct an array with the parameter types mapping primitives to Wrapper types
 921             for (int i = 0; i < parameterTypes.length; i++) {
 922                 mv.visitInsn(DUP);
 923                 emitIconstInsn(mv, i);
 924 
 925                 if (parameterTypes[i].isPrimitive()) {
 926                     PrimitiveTypeInfo prim =
 927                             PrimitiveTypeInfo.get(parameterTypes[i]);
 928                     mv.visitFieldInsn(GETSTATIC,
 929                             prim.wrapperClassName, "TYPE", LJL_CLASS);
 930                 } else {
 931                     codeClassForName(mv, parameterTypes[i]);
 932                 }
 933                 mv.visitInsn(Opcodes.AASTORE);
 934             }
 935             // lookup the method
 936             mv.visitMethodInsn(INVOKEVIRTUAL,
 937                     JL_CLASS,
 938                     "getMethod",
 939                     "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
 940                     false);
 941 
 942             mv.visitFieldInsn(PUTSTATIC,
 943                     dotToSlash(className),
 944                     methodFieldName, LJLR_METHOD);
 945         }
 946 
 947         /*
 948          * =============== Code Generation Utility Methods ===============
 949          */
 950 
 951         /**
 952          * Generate code to invoke the Class.forName with the name of the given
 953          * class to get its Class object at runtime.  And also generate code
 954          * to invoke Class::asValueType if the class is a primitive value type.
 955          *
 956          * The code is written to the supplied stream.  Note that the code generated
 957          * by this method may caused the checked ClassNotFoundException to be thrown.
 958          * A class loader is anticipated at local variable index 0.
 959          */
 960         private void codeClassForName(MethodVisitor mv, Class<?> cl) {
 961             mv.visitLdcInsn(cl.getName());
 962             mv.visitInsn(ICONST_0); // false
 963             mv.visitVarInsn(ALOAD, 0); // classLoader
 964             mv.visitMethodInsn(INVOKESTATIC,
 965                     JL_CLASS,
 966                     "forName",
 967                     "(Ljava/lang/String;Z" + LJL_CLASSLOADER + ")Ljava/lang/Class;",
 968                     false);
 969         }
 970 
 971         /**
 972          * Visit a bytecode for a constant.
 973          *
 974          * @param mv  The MethodVisitor
 975          * @param cst The constant value
 976          */
 977         private void emitIconstInsn(MethodVisitor mv, final int cst) {
 978             if (cst >= -1 && cst <= 5) {
 979                 mv.visitInsn(Opcodes.ICONST_0 + cst);
 980             } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
 981                 mv.visitIntInsn(Opcodes.BIPUSH, cst);
 982             } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
 983                 mv.visitIntInsn(Opcodes.SIPUSH, cst);
 984             } else {
 985                 mv.visitLdcInsn(cst);
 986             }
 987         }
 988 
 989         @Override
 990         public String toString() {
 991             return method.toShortString();
 992         }
 993     }
 994 
 995     /**
 996      * A PrimitiveTypeInfo object contains bytecode-related information about
 997      * a primitive type in its instance fields. The struct for a particular
 998      * primitive type can be obtained using the static "get" method.
 999      */
1000     private enum PrimitiveTypeInfo {
1001         BYTE(byte.class, ILOAD, IRETURN),
1002         CHAR(char.class, ILOAD, IRETURN),
1003         DOUBLE(double.class, DLOAD, DRETURN),
1004         FLOAT(float.class, FLOAD, FRETURN),
1005         INT(int.class, ILOAD, IRETURN),
1006         LONG(long.class, LLOAD, LRETURN),
1007         SHORT(short.class, ILOAD, IRETURN),
1008         BOOLEAN(boolean.class, ILOAD, IRETURN);
1009 
1010         /**
1011          * internal name of corresponding wrapper class
1012          */
1013         private final String wrapperClassName;
1014         /**
1015          * method descriptor for wrapper class "valueOf" factory method
1016          */
1017         private final String wrapperValueOfDesc;
1018         /**
1019          * name of wrapper class method for retrieving primitive value
1020          */
1021         private final String unwrapMethodName;
1022         /**
1023          * descriptor of same method
1024          */
1025         private final String unwrapMethodDesc;
1026         /**
1027          * Load opcode used by this primitive
1028          */
1029         private final int loadOpcode;
1030         /**
1031          * Return opcode used by this primitive
1032          */
1033         private final int returnOpcode;
1034 
1035         PrimitiveTypeInfo(Class<?> primitiveClass, int loadOpcode, int returnOpcode) {
1036             assert primitiveClass.isPrimitive();
1037             assert returnOpcode - IRETURN == loadOpcode - ILOAD;
1038 
1039             Wrapper wrapper = Wrapper.forPrimitiveType(primitiveClass);
1040             // single-char BaseType descriptor (see JVMS section 4.3.2)
1041             String baseTypeString = wrapper.basicTypeString();
1042             var wrapperType = wrapper.wrapperType();
1043             wrapperClassName = dotToSlash(wrapperType.getName());
1044             wrapperValueOfDesc =
1045                     "(" + baseTypeString + ")" + wrapperType.descriptorString();
1046             unwrapMethodName = primitiveClass.getName() + "Value";
1047             unwrapMethodDesc = "()" + baseTypeString;
1048             this.loadOpcode = loadOpcode;
1049             this.returnOpcode = returnOpcode;
1050         }
1051 
1052         public static PrimitiveTypeInfo get(Class<?> cl) {
1053             // Uses if chain for speed: 8284880
1054             if (cl == int.class)     return INT;
1055             if (cl == long.class)    return LONG;
1056             if (cl == boolean.class) return BOOLEAN;
1057             if (cl == short.class)   return SHORT;
1058             if (cl == byte.class)    return BYTE;
1059             if (cl == char.class)    return CHAR;
1060             if (cl == float.class)   return FLOAT;
1061             if (cl == double.class)  return DOUBLE;
1062             throw new AssertionError(cl);
1063         }
1064     }
1065 }