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(V15, 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                 String internalName = dotToSlash(type.getName());
817                 if (type.isInlineClass() && !type.isIndirectType()) {
818                     internalName = 'Q' + internalName + ";";
819                 }
820                 mv.visitTypeInsn(CHECKCAST, internalName);
821                 mv.visitInsn(ARETURN);
822             }
823         }
824 
825         /**
826          * Generate code for initializing the static field that stores
827          * the Method object for this proxy method.
828          */
829         private void codeFieldInitialization(MethodVisitor mv, String className) {
830             codeClassForName(mv, fromClass);
831 
832             mv.visitLdcInsn(method.getName());
833 
834             emitIconstInsn(mv, parameterTypes.length);
835 
836             mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_CLASS);
837 
838             // Construct an array with the parameter types mapping primitives to Wrapper types
839             for (int i = 0; i < parameterTypes.length; i++) {
840                 mv.visitInsn(DUP);
841                 emitIconstInsn(mv, i);
842 
843                 if (parameterTypes[i].isPrimitive()) {
844                     PrimitiveTypeInfo prim =
845                             PrimitiveTypeInfo.get(parameterTypes[i]);
846                     mv.visitFieldInsn(GETSTATIC,
847                             prim.wrapperClassName, "TYPE", LJL_CLASS);
848                 } else {
849                     codeClassForName(mv, parameterTypes[i]);
850                 }
851                 mv.visitInsn(Opcodes.AASTORE);
852             }
853             // lookup the method
854             mv.visitMethodInsn(INVOKEVIRTUAL,
855                     JL_CLASS,
856                     "getMethod",
857                     "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
858                     false);
859 
860             mv.visitFieldInsn(PUTSTATIC,
861                     dotToSlash(className),
862                     methodFieldName, LJLR_METHOD);
863         }
864 
865         /*
866          * =============== Code Generation Utility Methods ===============
867          */
868 
869         /**
870          * Generate code to invoke the Class.forName with the name of the given
871          * class to get its Class object at runtime.  And also generate code
872          * to invoke Class.asPrimaryType if the class is regular value type.
873          *
874          * The code is written to the supplied stream.  Note that the code generated
875          * by this method may caused the checked ClassNotFoundException to be thrown.
876          */
877         private void codeClassForName(MethodVisitor mv, Class<?> cl) {
878             mv.visitLdcInsn(cl.getName());
879             mv.visitMethodInsn(INVOKESTATIC,
880                     JL_CLASS,
881                     "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false);
882             if (cl.isInlineClass() && cl == cl.asPrimaryType()) {
883                 mv.visitMethodInsn(INVOKEVIRTUAL,
884                     JL_CLASS,
885                     "asPrimaryType", "()Ljava/lang/Class;", false);
886             }
887         }
888 
889         /**
890          * Visit a bytecode for a constant.
891          *
892          * @param mv  The MethodVisitor
893          * @param cst The constant value
894          */
895         private void emitIconstInsn(MethodVisitor mv, final int cst) {
896             if (cst >= -1 && cst <= 5) {
897                 mv.visitInsn(Opcodes.ICONST_0 + cst);
898             } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
899                 mv.visitIntInsn(Opcodes.BIPUSH, cst);
900             } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
901                 mv.visitIntInsn(Opcodes.SIPUSH, cst);
902             } else {
903                 mv.visitLdcInsn(cst);
904             }
905         }
906 
907         @Override
908         public String toString() {
909             return method.toShortString();
910         }
911     }
912 
913     /**
914      * A PrimitiveTypeInfo object contains assorted information about
915      * a primitive type in its public fields.  The struct for a particular
916      * primitive type can be obtained using the static "get" method.
917      */
918     private static class PrimitiveTypeInfo {
919 
920         private static Map<Class<?>, PrimitiveTypeInfo> table = new HashMap<>();
921 
922         static {
923             add(byte.class, Byte.class);
924             add(char.class, Character.class);
925             add(double.class, Double.class);
926             add(float.class, Float.class);
927             add(int.class, Integer.class);
928             add(long.class, Long.class);
929             add(short.class, Short.class);
930             add(boolean.class, Boolean.class);
931         }
932 
933         /**
934          * name of corresponding wrapper class
935          */
936         private String wrapperClassName;
937         /**
938          * method descriptor for wrapper class "valueOf" factory method
939          */
940         private String wrapperValueOfDesc;
941         /**
942          * name of wrapper class method for retrieving primitive value
943          */
944         private String unwrapMethodName;
945         /**
946          * descriptor of same method
947          */
948         private String unwrapMethodDesc;
949 
950         private PrimitiveTypeInfo(Class<?> primitiveClass, Class<?> wrapperClass) {
951             assert primitiveClass.isPrimitive();
952 
953             /**
954              * "base type" used in various descriptors (see JVMS section 4.3.2)
955              */
956             String baseTypeString =
957                     Array.newInstance(primitiveClass, 0)
958                             .getClass().getName().substring(1);
959             wrapperClassName = dotToSlash(wrapperClass.getName());
960             wrapperValueOfDesc =
961                     "(" + baseTypeString + ")L" + wrapperClassName + ";";
962             unwrapMethodName = primitiveClass.getName() + "Value";
963             unwrapMethodDesc = "()" + baseTypeString;
964         }
965 
966         private static void add(Class<?> primitiveClass, Class<?> wrapperClass) {
967             table.put(primitiveClass,
968                     new PrimitiveTypeInfo(primitiveClass, wrapperClass));
969         }
970 
971         public static PrimitiveTypeInfo get(Class<?> cl) {
972             return table.get(cl);
973         }
974     }
975 }