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