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