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