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