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.ClassWriter;
  30 import jdk.internal.org.objectweb.asm.Label;
  31 import jdk.internal.org.objectweb.asm.MethodVisitor;
  32 import jdk.internal.org.objectweb.asm.Opcodes;
  33 import jdk.internal.org.objectweb.asm.Type;
  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.HashMap;
  43 import java.util.LinkedHashMap;
  44 import java.util.List;
  45 import java.util.ListIterator;
  46 import java.util.Map;
  47 
  48 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  49 
  50 /**
  51  * ProxyGenerator contains the code to generate a dynamic proxy class
  52  * for the java.lang.reflect.Proxy API.
  53  * <p>
  54  * The external interface to ProxyGenerator is the static
  55  * "generateProxyClass" method.
  56  */
  57 final class ProxyGenerator extends ClassWriter {
  58     private static final int CLASSFILE_VERSION = VM.classFileVersion();
  59     private static final String JL_CLASS = "java/lang/Class";
  60     private static final String JL_OBJECT = "java/lang/Object";
  61     private static final String JL_THROWABLE = "java/lang/Throwable";
  62     private static final String JL_CLASS_NOT_FOUND_EX = "java/lang/ClassNotFoundException";
  63     private static final String JL_ILLEGAL_ACCESS_EX = "java/lang/IllegalAccessException";
  64 
  65     private static final String JL_NO_CLASS_DEF_FOUND_ERROR = "java/lang/NoClassDefFoundError";
  66     private static final String JL_NO_SUCH_METHOD_EX = "java/lang/NoSuchMethodException";
  67     private static final String JL_NO_SUCH_METHOD_ERROR = "java/lang/NoSuchMethodError";
  68     private static final String JLI_LOOKUP = "java/lang/invoke/MethodHandles$Lookup";
  69     private static final String JLI_METHODHANDLES = "java/lang/invoke/MethodHandles";
  70 
  71     private static final String JLR_INVOCATION_HANDLER = "java/lang/reflect/InvocationHandler";
  72     private static final String JLR_PROXY = "java/lang/reflect/Proxy";
  73     private static final String JLR_UNDECLARED_THROWABLE_EX = "java/lang/reflect/UndeclaredThrowableException";
  74 
  75     private static final String LJL_CLASS = "Ljava/lang/Class;";
  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.getExceptionTypes();
 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.getParameterTypes(), 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         mv.visitLabel(L_startBlock);
 601         for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
 602             for (ProxyMethod pm : sigmethods) {
 603                 pm.codeFieldInitialization(mv, className);
 604             }
 605         }
 606         mv.visitInsn(RETURN);
 607         mv.visitLabel(L_endBlock);
 608         // Generate exception handler
 609 
 610         mv.visitLabel(L_NoMethodHandler);
 611         mv.visitVarInsn(ASTORE, 1);
 612         mv.visitTypeInsn(Opcodes.NEW, JL_NO_SUCH_METHOD_ERROR);
 613         mv.visitInsn(DUP);
 614         mv.visitVarInsn(ALOAD, 1);
 615         mv.visitMethodInsn(INVOKEVIRTUAL, JL_THROWABLE,
 616                 "getMessage", "()Ljava/lang/String;", false);
 617         mv.visitMethodInsn(INVOKESPECIAL, JL_NO_SUCH_METHOD_ERROR,
 618                 "<init>", "(Ljava/lang/String;)V", false);
 619         mv.visitInsn(ATHROW);
 620 
 621         mv.visitLabel(L_NoClassHandler);
 622         mv.visitVarInsn(ASTORE, 1);
 623         mv.visitTypeInsn(Opcodes.NEW, JL_NO_CLASS_DEF_FOUND_ERROR);
 624         mv.visitInsn(DUP);
 625         mv.visitVarInsn(ALOAD, 1);
 626         mv.visitMethodInsn(INVOKEVIRTUAL, JL_THROWABLE,
 627                 "getMessage", "()Ljava/lang/String;", false);
 628         mv.visitMethodInsn(INVOKESPECIAL, JL_NO_CLASS_DEF_FOUND_ERROR,
 629                 "<init>", "(Ljava/lang/String;)V", false);
 630         mv.visitInsn(ATHROW);
 631 
 632         // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
 633         mv.visitMaxs(-1, -1);
 634         mv.visitEnd();
 635     }
 636 
 637     /**
 638      * Generate the static lookup accessor method that returns the Lookup
 639      * on this proxy class if the caller's lookup class is java.lang.reflect.Proxy;
 640      * otherwise, IllegalAccessException is thrown
 641      */
 642     private void generateLookupAccessor() {
 643         MethodVisitor mv = visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_LOOKUP_ACCESSOR,
 644                 "(Ljava/lang/invoke/MethodHandles$Lookup;)Ljava/lang/invoke/MethodHandles$Lookup;", null,
 645                 new String[] { JL_ILLEGAL_ACCESS_EX });
 646         mv.visitCode();
 647         Label L_illegalAccess = new Label();
 648 
 649         mv.visitVarInsn(ALOAD, 0);
 650         mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "lookupClass",
 651                 "()Ljava/lang/Class;", false);
 652         mv.visitLdcInsn(Type.getType(Proxy.class));
 653         mv.visitJumpInsn(IF_ACMPNE, L_illegalAccess);
 654         mv.visitVarInsn(ALOAD, 0);
 655         mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "hasFullPrivilegeAccess",
 656                 "()Z", false);
 657         mv.visitJumpInsn(IFEQ, L_illegalAccess);
 658         mv.visitMethodInsn(INVOKESTATIC, JLI_METHODHANDLES, "lookup",
 659                 "()Ljava/lang/invoke/MethodHandles$Lookup;", false);
 660         mv.visitInsn(ARETURN);
 661 
 662         mv.visitLabel(L_illegalAccess);
 663         mv.visitTypeInsn(Opcodes.NEW, JL_ILLEGAL_ACCESS_EX);
 664         mv.visitInsn(DUP);
 665         mv.visitVarInsn(ALOAD, 0);
 666         mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "toString",
 667                 "()Ljava/lang/String;", false);
 668         mv.visitMethodInsn(INVOKESPECIAL, JL_ILLEGAL_ACCESS_EX,
 669                 "<init>", "(Ljava/lang/String;)V", false);
 670         mv.visitInsn(ATHROW);
 671 
 672         // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
 673         mv.visitMaxs(-1, -1);
 674         mv.visitEnd();
 675     }
 676 
 677     /**
 678      * A ProxyMethod object represents a proxy method in the proxy class
 679      * being generated: a method whose implementation will encode and
 680      * dispatch invocations to the proxy instance's invocation handler.
 681      */
 682     private static class ProxyMethod {
 683 
 684         private final Method method;
 685         private final String shortSignature;
 686         private final Class<?> fromClass;
 687         private final Class<?>[] parameterTypes;
 688         private final Class<?> returnType;
 689         private final String methodFieldName;
 690         private Class<?>[] exceptionTypes;
 691 
 692         private ProxyMethod(Method method, String sig, Class<?>[] parameterTypes,
 693                             Class<?> returnType, Class<?>[] exceptionTypes,
 694                             Class<?> fromClass, String methodFieldName) {
 695             this.method = method;
 696             this.shortSignature = sig;
 697             this.parameterTypes = parameterTypes;
 698             this.returnType = returnType;
 699             this.exceptionTypes = exceptionTypes;
 700             this.fromClass = fromClass;
 701             this.methodFieldName = methodFieldName;
 702         }
 703 
 704         /**
 705          * Create a new specific ProxyMethod with a specific field name
 706          *
 707          * @param method          The method for which to create a proxy
 708          * @param methodFieldName the fieldName to generate
 709          */
 710         private ProxyMethod(Method method, String methodFieldName) {
 711             this(method, method.toShortSignature(),
 712                     method.getParameterTypes(), method.getReturnType(),
 713                     method.getExceptionTypes(), method.getDeclaringClass(), methodFieldName);
 714         }
 715 
 716         /**
 717          * Generate this method, including the code and exception table entry.
 718          */
 719         private void generateMethod(ClassWriter cw, String className) {
 720             MethodType mt = MethodType.methodType(returnType, parameterTypes);
 721             String desc = mt.toMethodDescriptorString();
 722             int accessFlags = ACC_PUBLIC | ACC_FINAL;
 723             if (method.isVarArgs()) accessFlags |= ACC_VARARGS;
 724 
 725             MethodVisitor mv = cw.visitMethod(accessFlags,
 726                     method.getName(), desc, null,
 727                     typeNames(Arrays.asList(exceptionTypes)));
 728 
 729             int[] parameterSlot = new int[parameterTypes.length];
 730             int nextSlot = 1;
 731             for (int i = 0; i < parameterSlot.length; i++) {
 732                 parameterSlot[i] = nextSlot;
 733                 nextSlot += getWordsPerType(parameterTypes[i]);
 734             }
 735 
 736             mv.visitCode();
 737             Label L_startBlock = new Label();
 738             Label L_endBlock = new Label();
 739             Label L_RuntimeHandler = new Label();
 740             Label L_ThrowableHandler = new Label();
 741 
 742             List<Class<?>> catchList = computeUniqueCatchList(exceptionTypes);
 743             if (catchList.size() > 0) {
 744                 for (Class<?> ex : catchList) {
 745                     mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_RuntimeHandler,
 746                             dotToSlash(ex.getName()));
 747                 }
 748 
 749                 mv.visitTryCatchBlock(L_startBlock, L_endBlock, L_ThrowableHandler,
 750                         JL_THROWABLE);
 751             }
 752             mv.visitLabel(L_startBlock);
 753 
 754             mv.visitVarInsn(ALOAD, 0);
 755             mv.visitFieldInsn(GETFIELD, JLR_PROXY, handlerFieldName,
 756                     LJLR_INVOCATION_HANDLER);
 757             mv.visitVarInsn(ALOAD, 0);
 758             mv.visitFieldInsn(GETSTATIC, dotToSlash(className), methodFieldName,
 759                     LJLR_METHOD);
 760 
 761             if (parameterTypes.length > 0) {
 762                 // Create an array and fill with the parameters converting primitives to wrappers
 763                 emitIconstInsn(mv, parameterTypes.length);
 764                 mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_OBJECT);
 765                 for (int i = 0; i < parameterTypes.length; i++) {
 766                     mv.visitInsn(DUP);
 767                     emitIconstInsn(mv, i);
 768                     codeWrapArgument(mv, parameterTypes[i], parameterSlot[i]);
 769                     mv.visitInsn(Opcodes.AASTORE);
 770                 }
 771             } else {
 772                 mv.visitInsn(Opcodes.ACONST_NULL);
 773             }
 774 
 775             mv.visitMethodInsn(INVOKEINTERFACE, JLR_INVOCATION_HANDLER,
 776                     "invoke",
 777                     "(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
 778                             "[Ljava/lang/Object;)Ljava/lang/Object;", true);
 779 
 780             if (returnType == void.class) {
 781                 mv.visitInsn(POP);
 782                 mv.visitInsn(RETURN);
 783             } else {
 784                 codeUnwrapReturnValue(mv, returnType);
 785             }
 786 
 787             mv.visitLabel(L_endBlock);
 788 
 789             // Generate exception handler
 790             mv.visitLabel(L_RuntimeHandler);
 791             mv.visitInsn(ATHROW);   // just rethrow the exception
 792 
 793             mv.visitLabel(L_ThrowableHandler);
 794             mv.visitVarInsn(ASTORE, 1);
 795             mv.visitTypeInsn(Opcodes.NEW, JLR_UNDECLARED_THROWABLE_EX);
 796             mv.visitInsn(DUP);
 797             mv.visitVarInsn(ALOAD, 1);
 798             mv.visitMethodInsn(INVOKESPECIAL, JLR_UNDECLARED_THROWABLE_EX,
 799                     "<init>", "(Ljava/lang/Throwable;)V", false);
 800             mv.visitInsn(ATHROW);
 801             // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
 802             mv.visitMaxs(-1, -1);
 803             mv.visitEnd();
 804         }
 805 
 806         /**
 807          * Generate code for wrapping an argument of the given type
 808          * whose value can be found at the specified local variable
 809          * index, in order for it to be passed (as an Object) to the
 810          * invocation handler's "invoke" method.
 811          */
 812         private void codeWrapArgument(MethodVisitor mv, Class<?> type, int slot) {
 813             if (type.isPrimitive()) {
 814                 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
 815 
 816                 if (type == int.class ||
 817                         type == boolean.class ||
 818                         type == byte.class ||
 819                         type == char.class ||
 820                         type == short.class) {
 821                     mv.visitVarInsn(ILOAD, slot);
 822                 } else if (type == long.class) {
 823                     mv.visitVarInsn(LLOAD, slot);
 824                 } else if (type == float.class) {
 825                     mv.visitVarInsn(FLOAD, slot);
 826                 } else if (type == double.class) {
 827                     mv.visitVarInsn(DLOAD, slot);
 828                 } else {
 829                     throw new AssertionError();
 830                 }
 831                 mv.visitMethodInsn(INVOKESTATIC, prim.wrapperClassName, "valueOf",
 832                         prim.wrapperValueOfDesc, false);
 833             } else {
 834                 mv.visitVarInsn(ALOAD, slot);
 835             }
 836         }
 837 
 838         /**
 839          * Generate code for unwrapping a return value of the given
 840          * type from the invocation handler's "invoke" method (as type
 841          * Object) to its correct type.
 842          */
 843         private void codeUnwrapReturnValue(MethodVisitor mv, Class<?> type) {
 844             if (type.isPrimitive()) {
 845                 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
 846 
 847                 mv.visitTypeInsn(CHECKCAST, prim.wrapperClassName);
 848                 mv.visitMethodInsn(INVOKEVIRTUAL,
 849                         prim.wrapperClassName,
 850                         prim.unwrapMethodName, prim.unwrapMethodDesc, false);
 851 
 852                 if (type == int.class ||
 853                         type == boolean.class ||
 854                         type == byte.class ||
 855                         type == char.class ||
 856                         type == short.class) {
 857                     mv.visitInsn(IRETURN);
 858                 } else if (type == long.class) {
 859                     mv.visitInsn(LRETURN);
 860                 } else if (type == float.class) {
 861                     mv.visitInsn(FRETURN);
 862                 } else if (type == double.class) {
 863                     mv.visitInsn(DRETURN);
 864                 } else {
 865                     throw new AssertionError();
 866                 }
 867             } else {
 868                 String internalName = dotToSlash(type.getName());
 869                 if (type.isValueType()) {
 870                     internalName = 'Q' + internalName + ";";
 871                 }
 872                 mv.visitTypeInsn(CHECKCAST, internalName);
 873                 mv.visitInsn(ARETURN);
 874             }
 875         }
 876 
 877         /**
 878          * Generate code for initializing the static field that stores
 879          * the Method object for this proxy method.
 880          */
 881         private void codeFieldInitialization(MethodVisitor mv, String className) {
 882             codeClassForName(mv, fromClass);
 883 
 884             mv.visitLdcInsn(method.getName());
 885 
 886             emitIconstInsn(mv, parameterTypes.length);
 887 
 888             mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_CLASS);
 889 
 890             // Construct an array with the parameter types mapping primitives to Wrapper types
 891             for (int i = 0; i < parameterTypes.length; i++) {
 892                 mv.visitInsn(DUP);
 893                 emitIconstInsn(mv, i);
 894 
 895                 if (parameterTypes[i].isPrimitive()) {
 896                     PrimitiveTypeInfo prim =
 897                             PrimitiveTypeInfo.get(parameterTypes[i]);
 898                     mv.visitFieldInsn(GETSTATIC,
 899                             prim.wrapperClassName, "TYPE", LJL_CLASS);
 900                 } else {
 901                     codeClassForName(mv, parameterTypes[i]);
 902                 }
 903                 mv.visitInsn(Opcodes.AASTORE);
 904             }
 905             // lookup the method
 906             mv.visitMethodInsn(INVOKEVIRTUAL,
 907                     JL_CLASS,
 908                     "getMethod",
 909                     "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
 910                     false);
 911 
 912             mv.visitFieldInsn(PUTSTATIC,
 913                     dotToSlash(className),
 914                     methodFieldName, LJLR_METHOD);
 915         }
 916 
 917         /*
 918          * =============== Code Generation Utility Methods ===============
 919          */
 920 
 921         /**
 922          * Generate code to invoke the Class.forName with the name of the given
 923          * class to get its Class object at runtime.  And also generate code
 924          * to invoke Class::asValueType if the class is a primitive value type.
 925          *
 926          * The code is written to the supplied stream.  Note that the code generated
 927          * by this method may caused the checked ClassNotFoundException to be thrown.
 928          */
 929         private void codeClassForName(MethodVisitor mv, Class<?> cl) {
 930             mv.visitLdcInsn(cl.getName());
 931             mv.visitMethodInsn(INVOKESTATIC,
 932                     JL_CLASS,
 933                     "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false);
 934             if (cl.isValueType()) {
 935               mv.visitMethodInsn(INVOKEVIRTUAL,
 936                                  JL_CLASS,
 937                                  "asValueType", "()Ljava/lang/Class;", false);
 938             }
 939         }
 940 
 941         /**
 942          * Visit a bytecode for a constant.
 943          *
 944          * @param mv  The MethodVisitor
 945          * @param cst The constant value
 946          */
 947         private void emitIconstInsn(MethodVisitor mv, final int cst) {
 948             if (cst >= -1 && cst <= 5) {
 949                 mv.visitInsn(Opcodes.ICONST_0 + cst);
 950             } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
 951                 mv.visitIntInsn(Opcodes.BIPUSH, cst);
 952             } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
 953                 mv.visitIntInsn(Opcodes.SIPUSH, cst);
 954             } else {
 955                 mv.visitLdcInsn(cst);
 956             }
 957         }
 958 
 959         @Override
 960         public String toString() {
 961             return method.toShortString();
 962         }
 963     }
 964 
 965     /**
 966      * A PrimitiveTypeInfo object contains assorted information about
 967      * a primitive type in its public fields.  The struct for a particular
 968      * primitive type can be obtained using the static "get" method.
 969      */
 970     private static class PrimitiveTypeInfo {
 971 
 972         private static Map<Class<?>, PrimitiveTypeInfo> table = new HashMap<>();
 973 
 974         static {
 975             add(byte.class, Byte.class);
 976             add(char.class, Character.class);
 977             add(double.class, Double.class);
 978             add(float.class, Float.class);
 979             add(int.class, Integer.class);
 980             add(long.class, Long.class);
 981             add(short.class, Short.class);
 982             add(boolean.class, Boolean.class);
 983         }
 984 
 985         /**
 986          * name of corresponding wrapper class
 987          */
 988         private String wrapperClassName;
 989         /**
 990          * method descriptor for wrapper class "valueOf" factory method
 991          */
 992         private String wrapperValueOfDesc;
 993         /**
 994          * name of wrapper class method for retrieving primitive value
 995          */
 996         private String unwrapMethodName;
 997         /**
 998          * descriptor of same method
 999          */
1000         private String unwrapMethodDesc;
1001 
1002         private PrimitiveTypeInfo(Class<?> primitiveClass, Class<?> wrapperClass) {
1003             assert primitiveClass.isPrimitive();
1004 
1005             /**
1006              * "base type" used in various descriptors (see JVMS section 4.3.2)
1007              */
1008             String baseTypeString =
1009                     Array.newInstance(primitiveClass, 0)
1010                             .getClass().getName().substring(1);
1011             wrapperClassName = dotToSlash(wrapperClass.getName());
1012             wrapperValueOfDesc =
1013                     "(" + baseTypeString + ")L" + wrapperClassName + ";";
1014             unwrapMethodName = primitiveClass.getName() + "Value";
1015             unwrapMethodDesc = "()" + baseTypeString;
1016         }
1017 
1018         private static void add(Class<?> primitiveClass, Class<?> wrapperClass) {
1019             table.put(primitiveClass,
1020                     new PrimitiveTypeInfo(primitiveClass, wrapperClass));
1021         }
1022 
1023         public static PrimitiveTypeInfo get(Class<?> cl) {
1024             return table.get(cl);
1025         }
1026     }
1027 }