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