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.org.objectweb.asm.ClassWriter;
29 import jdk.internal.org.objectweb.asm.Label;
30 import jdk.internal.org.objectweb.asm.MethodVisitor;
31 import jdk.internal.org.objectweb.asm.Opcodes;
32 import jdk.internal.org.objectweb.asm.Type;
33 import sun.security.action.GetBooleanAction;
34 
35 import java.io.IOException;
36 import java.lang.invoke.MethodType;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.HashMap;
42 import java.util.LinkedHashMap;
43 import java.util.LinkedList;
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 
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      */
ProxyGenerator(ClassLoader loader, String className, List<Class<?>> interfaces, int accessFlags)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")
generateProxyClass(ClassLoader loader, final String name, List<Class<?>> interfaces, int accessFlags)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      */
typeNames(List<Class<?>> classes)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      */
checkReturnTypes(List<ProxyMethod> methods)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         LinkedList<Class<?>> uncoveredReturnTypes = new LinkedList<>();
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      */
computeUniqueCatchList(Class<?>[] exceptions)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      */
dotToSlash(String name)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      */
getWordsPerType(Class<?> type)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      */
collectCompatibleTypes(Class<?>[] from, Class<?>[] with, List<Class<?>> list)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      */
getClassLoader()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      */
generateClassFile()457     private byte[] generateClassFile() {
458         visit(V14, 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      */
addProxyMethod(Method m, Class<?> fromClass)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      */
addProxyMethod(ProxyMethod pm)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      */
generateConstructor()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      */
generateStaticInitializer()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      */
generateLookupAccessor()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 
ProxyMethod(Method method, String sig, Class<?>[] parameterTypes, Class<?> returnType, Class<?>[] exceptionTypes, Class<?> fromClass, String methodFieldName)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          */
ProxyMethod(Method method, String methodFieldName)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          */
generateMethod(ClassWriter cw, String className)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          */
codeWrapArgument(MethodVisitor mv, Class<?> type, int slot)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          */
codeUnwrapReturnValue(MethodVisitor mv, Class<?> type)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                 mv.visitTypeInsn(CHECKCAST, dotToSlash(type.getName()));
869                 mv.visitInsn(ARETURN);
870             }
871         }
872 
873         /**
874          * Generate code for initializing the static field that stores
875          * the Method object for this proxy method.
876          */
codeFieldInitialization(MethodVisitor mv, String className)877         private void codeFieldInitialization(MethodVisitor mv, String className) {
878             codeClassForName(mv, fromClass);
879 
880             mv.visitLdcInsn(method.getName());
881 
882             emitIconstInsn(mv, parameterTypes.length);
883 
884             mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_CLASS);
885 
886             // Construct an array with the parameter types mapping primitives to Wrapper types
887             for (int i = 0; i < parameterTypes.length; i++) {
888                 mv.visitInsn(DUP);
889                 emitIconstInsn(mv, i);
890 
891                 if (parameterTypes[i].isPrimitive()) {
892                     PrimitiveTypeInfo prim =
893                             PrimitiveTypeInfo.get(parameterTypes[i]);
894                     mv.visitFieldInsn(GETSTATIC,
895                             prim.wrapperClassName, "TYPE", LJL_CLASS);
896                 } else {
897                     codeClassForName(mv, parameterTypes[i]);
898                 }
899                 mv.visitInsn(Opcodes.AASTORE);
900             }
901             // lookup the method
902             mv.visitMethodInsn(INVOKEVIRTUAL,
903                     JL_CLASS,
904                     "getMethod",
905                     "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
906                     false);
907 
908             mv.visitFieldInsn(PUTSTATIC,
909                     dotToSlash(className),
910                     methodFieldName, LJLR_METHOD);
911         }
912 
913         /*
914          * =============== Code Generation Utility Methods ===============
915          */
916 
917         /**
918          * Generate code to invoke the Class.forName with the name of the given
919          * class to get its Class object at runtime.  The code is written to
920          * the supplied stream.  Note that the code generated by this method
921          * may cause the checked ClassNotFoundException to be thrown.
922          */
codeClassForName(MethodVisitor mv, Class<?> cl)923         private void codeClassForName(MethodVisitor mv, Class<?> cl) {
924             mv.visitLdcInsn(cl.getName());
925             mv.visitMethodInsn(INVOKESTATIC,
926                     JL_CLASS,
927                     "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false);
928         }
929 
930         /**
931          * Visit a bytecode for a constant.
932          *
933          * @param mv  The MethodVisitor
934          * @param cst The constant value
935          */
emitIconstInsn(MethodVisitor mv, final int cst)936         private void emitIconstInsn(MethodVisitor mv, final int cst) {
937             if (cst >= -1 && cst <= 5) {
938                 mv.visitInsn(Opcodes.ICONST_0 + cst);
939             } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
940                 mv.visitIntInsn(Opcodes.BIPUSH, cst);
941             } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
942                 mv.visitIntInsn(Opcodes.SIPUSH, cst);
943             } else {
944                 mv.visitLdcInsn(cst);
945             }
946         }
947 
948         @Override
toString()949         public String toString() {
950             return method.toShortString();
951         }
952     }
953 
954     /**
955      * A PrimitiveTypeInfo object contains assorted information about
956      * a primitive type in its public fields.  The struct for a particular
957      * primitive type can be obtained using the static "get" method.
958      */
959     private static class PrimitiveTypeInfo {
960 
961         private static Map<Class<?>, PrimitiveTypeInfo> table = new HashMap<>();
962 
963         static {
add(byte.class, Byte.class)964             add(byte.class, Byte.class);
add(char.class, Character.class)965             add(char.class, Character.class);
add(double.class, Double.class)966             add(double.class, Double.class);
add(float.class, Float.class)967             add(float.class, Float.class);
add(int.class, Integer.class)968             add(int.class, Integer.class);
add(long.class, Long.class)969             add(long.class, Long.class);
add(short.class, Short.class)970             add(short.class, Short.class);
add(boolean.class, Boolean.class)971             add(boolean.class, Boolean.class);
972         }
973 
974         /**
975          * name of corresponding wrapper class
976          */
977         private String wrapperClassName;
978         /**
979          * method descriptor for wrapper class "valueOf" factory method
980          */
981         private String wrapperValueOfDesc;
982         /**
983          * name of wrapper class method for retrieving primitive value
984          */
985         private String unwrapMethodName;
986         /**
987          * descriptor of same method
988          */
989         private String unwrapMethodDesc;
990 
PrimitiveTypeInfo(Class<?> primitiveClass, Class<?> wrapperClass)991         private PrimitiveTypeInfo(Class<?> primitiveClass, Class<?> wrapperClass) {
992             assert primitiveClass.isPrimitive();
993 
994             /**
995              * "base type" used in various descriptors (see JVMS section 4.3.2)
996              */
997             String baseTypeString =
998                     Array.newInstance(primitiveClass, 0)
999                             .getClass().getName().substring(1);
1000             wrapperClassName = dotToSlash(wrapperClass.getName());
1001             wrapperValueOfDesc =
1002                     "(" + baseTypeString + ")L" + wrapperClassName + ";";
1003             unwrapMethodName = primitiveClass.getName() + "Value";
1004             unwrapMethodDesc = "()" + baseTypeString;
1005         }
1006 
add(Class<?> primitiveClass, Class<?> wrapperClass)1007         private static void add(Class<?> primitiveClass, Class<?> wrapperClass) {
1008             table.put(primitiveClass,
1009                     new PrimitiveTypeInfo(primitiveClass, wrapperClass));
1010         }
1011 
get(Class<?> cl)1012         public static PrimitiveTypeInfo get(Class<?> cl) {
1013             return table.get(cl);
1014         }
1015     }
1016 }
1017