9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.lang.reflect;
27
28 import jdk.internal.misc.VM;
29 import jdk.internal.org.objectweb.asm.ClassWriter;
30 import jdk.internal.org.objectweb.asm.Label;
31 import jdk.internal.org.objectweb.asm.MethodVisitor;
32 import jdk.internal.org.objectweb.asm.Opcodes;
33 import jdk.internal.org.objectweb.asm.Type;
34 import sun.security.action.GetBooleanAction;
35
36 import java.io.IOException;
37 import java.lang.invoke.MethodType;
38 import java.nio.file.Files;
39 import java.nio.file.Path;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.HashMap;
43 import java.util.LinkedHashMap;
44 import java.util.List;
45 import java.util.ListIterator;
46 import java.util.Map;
47
48 import static jdk.internal.org.objectweb.asm.Opcodes.*;
49
50 /**
51 * ProxyGenerator contains the code to generate a dynamic proxy class
52 * for the java.lang.reflect.Proxy API.
53 * <p>
54 * The external interface to ProxyGenerator is the static
55 * "generateProxyClass" method.
56 */
57 final class ProxyGenerator extends ClassWriter {
58 private static final int CLASSFILE_VERSION = VM.classFileVersion();
59 private static final String JL_CLASS = "java/lang/Class";
60 private static final String JL_OBJECT = "java/lang/Object";
61 private static final String JL_THROWABLE = "java/lang/Throwable";
62 private static final String JL_CLASS_NOT_FOUND_EX = "java/lang/ClassNotFoundException";
63 private static final String JL_ILLEGAL_ACCESS_EX = "java/lang/IllegalAccessException";
64
65 private static final String JL_NO_CLASS_DEF_FOUND_ERROR = "java/lang/NoClassDefFoundError";
66 private static final String JL_NO_SUCH_METHOD_EX = "java/lang/NoSuchMethodException";
473 * Accumulate all of the methods from the proxy interfaces.
474 */
475 for (Class<?> intf : interfaces) {
476 for (Method m : intf.getMethods()) {
477 if (!Modifier.isStatic(m.getModifiers())) {
478 addProxyMethod(m, intf);
479 }
480 }
481 }
482
483 /*
484 * For each set of proxy methods with the same signature,
485 * verify that the methods' return types are compatible.
486 */
487 for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
488 checkReturnTypes(sigmethods);
489 }
490
491 generateConstructor();
492
493 for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
494 for (ProxyMethod pm : sigmethods) {
495 // add static field for the Method object
496 visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, pm.methodFieldName,
497 LJLR_METHOD, null, null);
498
499 // Generate code for proxy method
500 pm.generateMethod(this, className);
501 }
502 }
503
504 generateStaticInitializer();
505 generateLookupAccessor();
506 return toByteArray();
507 }
508
509 /**
510 * Add another method to be proxied, either by creating a new
511 * ProxyMethod object or augmenting an old one for a duplicate
512 * method.
513 *
514 * "fromClass" indicates the proxy interface that the method was
515 * found through, which may be different from (a subinterface of)
516 * the method's "declaring class". Note that the first Method
517 * object passed for a given name and descriptor identifies the
518 * Method object (and thus the declaring class) that will be
519 * passed to the invocation handler's "invoke" method for a given
520 * set of duplicate methods.
521 */
522 private void addProxyMethod(Method m, Class<?> fromClass) {
657 mv.visitJumpInsn(IFEQ, L_illegalAccess);
658 mv.visitMethodInsn(INVOKESTATIC, JLI_METHODHANDLES, "lookup",
659 "()Ljava/lang/invoke/MethodHandles$Lookup;", false);
660 mv.visitInsn(ARETURN);
661
662 mv.visitLabel(L_illegalAccess);
663 mv.visitTypeInsn(Opcodes.NEW, JL_ILLEGAL_ACCESS_EX);
664 mv.visitInsn(DUP);
665 mv.visitVarInsn(ALOAD, 0);
666 mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "toString",
667 "()Ljava/lang/String;", false);
668 mv.visitMethodInsn(INVOKESPECIAL, JL_ILLEGAL_ACCESS_EX,
669 "<init>", "(Ljava/lang/String;)V", false);
670 mv.visitInsn(ATHROW);
671
672 // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
673 mv.visitMaxs(-1, -1);
674 mv.visitEnd();
675 }
676
677 /**
678 * A ProxyMethod object represents a proxy method in the proxy class
679 * being generated: a method whose implementation will encode and
680 * dispatch invocations to the proxy instance's invocation handler.
681 */
682 private static class ProxyMethod {
683
684 private final Method method;
685 private final String shortSignature;
686 private final Class<?> fromClass;
687 private final Class<?>[] parameterTypes;
688 private final Class<?> returnType;
689 private final String methodFieldName;
690 private Class<?>[] exceptionTypes;
691
692 private ProxyMethod(Method method, String sig, Class<?>[] parameterTypes,
693 Class<?> returnType, Class<?>[] exceptionTypes,
694 Class<?> fromClass, String methodFieldName) {
695 this.method = method;
696 this.shortSignature = sig;
786
787 mv.visitLabel(L_endBlock);
788
789 // Generate exception handler
790 mv.visitLabel(L_RuntimeHandler);
791 mv.visitInsn(ATHROW); // just rethrow the exception
792
793 mv.visitLabel(L_ThrowableHandler);
794 mv.visitVarInsn(ASTORE, 1);
795 mv.visitTypeInsn(Opcodes.NEW, JLR_UNDECLARED_THROWABLE_EX);
796 mv.visitInsn(DUP);
797 mv.visitVarInsn(ALOAD, 1);
798 mv.visitMethodInsn(INVOKESPECIAL, JLR_UNDECLARED_THROWABLE_EX,
799 "<init>", "(Ljava/lang/Throwable;)V", false);
800 mv.visitInsn(ATHROW);
801 // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
802 mv.visitMaxs(-1, -1);
803 mv.visitEnd();
804 }
805
806 /**
807 * Generate code for wrapping an argument of the given type
808 * whose value can be found at the specified local variable
809 * index, in order for it to be passed (as an Object) to the
810 * invocation handler's "invoke" method.
811 */
812 private void codeWrapArgument(MethodVisitor mv, Class<?> type, int slot) {
813 if (type.isPrimitive()) {
814 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
815
816 if (type == int.class ||
817 type == boolean.class ||
818 type == byte.class ||
819 type == char.class ||
820 type == short.class) {
821 mv.visitVarInsn(ILOAD, slot);
822 } else if (type == long.class) {
823 mv.visitVarInsn(LLOAD, slot);
824 } else if (type == float.class) {
825 mv.visitVarInsn(FLOAD, slot);
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 */
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);
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 */
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 */
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
|
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.lang.reflect;
27
28 import jdk.internal.misc.VM;
29 import jdk.internal.org.objectweb.asm.Attribute;
30 import jdk.internal.org.objectweb.asm.ByteVector;
31 import jdk.internal.org.objectweb.asm.ClassWriter;
32 import jdk.internal.org.objectweb.asm.Label;
33 import jdk.internal.org.objectweb.asm.MethodVisitor;
34 import jdk.internal.org.objectweb.asm.Opcodes;
35 import jdk.internal.org.objectweb.asm.Type;
36 import sun.security.action.GetBooleanAction;
37
38 import java.io.IOException;
39 import java.lang.invoke.MethodType;
40 import java.nio.file.Files;
41 import java.nio.file.Path;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.LinkedHashMap;
47 import java.util.List;
48 import java.util.ListIterator;
49 import java.util.Map;
50 import java.util.Set;
51
52 import static jdk.internal.org.objectweb.asm.Opcodes.*;
53
54 /**
55 * ProxyGenerator contains the code to generate a dynamic proxy class
56 * for the java.lang.reflect.Proxy API.
57 * <p>
58 * The external interface to ProxyGenerator is the static
59 * "generateProxyClass" method.
60 */
61 final class ProxyGenerator extends ClassWriter {
62 private static final int CLASSFILE_VERSION = VM.classFileVersion();
63 private static final String JL_CLASS = "java/lang/Class";
64 private static final String JL_OBJECT = "java/lang/Object";
65 private static final String JL_THROWABLE = "java/lang/Throwable";
66 private static final String JL_CLASS_NOT_FOUND_EX = "java/lang/ClassNotFoundException";
67 private static final String JL_ILLEGAL_ACCESS_EX = "java/lang/IllegalAccessException";
68
69 private static final String JL_NO_CLASS_DEF_FOUND_ERROR = "java/lang/NoClassDefFoundError";
70 private static final String JL_NO_SUCH_METHOD_EX = "java/lang/NoSuchMethodException";
477 * Accumulate all of the methods from the proxy interfaces.
478 */
479 for (Class<?> intf : interfaces) {
480 for (Method m : intf.getMethods()) {
481 if (!Modifier.isStatic(m.getModifiers())) {
482 addProxyMethod(m, intf);
483 }
484 }
485 }
486
487 /*
488 * For each set of proxy methods with the same signature,
489 * verify that the methods' return types are compatible.
490 */
491 for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
492 checkReturnTypes(sigmethods);
493 }
494
495 generateConstructor();
496
497 Set<Class<?>> preloadClasses = new HashSet<>();
498 for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
499 for (ProxyMethod pm : sigmethods) {
500 // add static field for the Method object
501 visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, pm.methodFieldName,
502 LJLR_METHOD, null, null);
503
504 // Generate code for proxy method
505 pm.generateMethod(this, className);
506 preloadClasses.addAll(pm.preloadClasses());
507 }
508 }
509 if (preloadClasses.size() > 0) {
510 generatePreloadAttribute(preloadClasses);
511 }
512
513 generateStaticInitializer();
514 generateLookupAccessor();
515 return toByteArray();
516 }
517
518 /**
519 * Add another method to be proxied, either by creating a new
520 * ProxyMethod object or augmenting an old one for a duplicate
521 * method.
522 *
523 * "fromClass" indicates the proxy interface that the method was
524 * found through, which may be different from (a subinterface of)
525 * the method's "declaring class". Note that the first Method
526 * object passed for a given name and descriptor identifies the
527 * Method object (and thus the declaring class) that will be
528 * passed to the invocation handler's "invoke" method for a given
529 * set of duplicate methods.
530 */
531 private void addProxyMethod(Method m, Class<?> fromClass) {
666 mv.visitJumpInsn(IFEQ, L_illegalAccess);
667 mv.visitMethodInsn(INVOKESTATIC, JLI_METHODHANDLES, "lookup",
668 "()Ljava/lang/invoke/MethodHandles$Lookup;", false);
669 mv.visitInsn(ARETURN);
670
671 mv.visitLabel(L_illegalAccess);
672 mv.visitTypeInsn(Opcodes.NEW, JL_ILLEGAL_ACCESS_EX);
673 mv.visitInsn(DUP);
674 mv.visitVarInsn(ALOAD, 0);
675 mv.visitMethodInsn(INVOKEVIRTUAL, JLI_LOOKUP, "toString",
676 "()Ljava/lang/String;", false);
677 mv.visitMethodInsn(INVOKESPECIAL, JL_ILLEGAL_ACCESS_EX,
678 "<init>", "(Ljava/lang/String;)V", false);
679 mv.visitInsn(ATHROW);
680
681 // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
682 mv.visitMaxs(-1, -1);
683 mv.visitEnd();
684 }
685
686 private void generatePreloadAttribute(Set<Class<?>> preloadClasses) {
687 Attribute attr = new Attribute("Preload") {
688 @Override
689 protected ByteVector write(ClassWriter cw,
690 byte[] code,
691 int len,
692 int maxStack,
693 int maxLocals) {
694 ByteVector attr = new ByteVector();
695 attr.putShort(preloadClasses.size());
696 for (Class<?> c : preloadClasses) {
697 attr.putShort(cw.newClass(Type.getInternalName(c)));
698 }
699 return attr;
700 }
701 };
702 visitAttribute(attr);
703 }
704 /**
705 * A ProxyMethod object represents a proxy method in the proxy class
706 * being generated: a method whose implementation will encode and
707 * dispatch invocations to the proxy instance's invocation handler.
708 */
709 private static class ProxyMethod {
710
711 private final Method method;
712 private final String shortSignature;
713 private final Class<?> fromClass;
714 private final Class<?>[] parameterTypes;
715 private final Class<?> returnType;
716 private final String methodFieldName;
717 private Class<?>[] exceptionTypes;
718
719 private ProxyMethod(Method method, String sig, Class<?>[] parameterTypes,
720 Class<?> returnType, Class<?>[] exceptionTypes,
721 Class<?> fromClass, String methodFieldName) {
722 this.method = method;
723 this.shortSignature = sig;
813
814 mv.visitLabel(L_endBlock);
815
816 // Generate exception handler
817 mv.visitLabel(L_RuntimeHandler);
818 mv.visitInsn(ATHROW); // just rethrow the exception
819
820 mv.visitLabel(L_ThrowableHandler);
821 mv.visitVarInsn(ASTORE, 1);
822 mv.visitTypeInsn(Opcodes.NEW, JLR_UNDECLARED_THROWABLE_EX);
823 mv.visitInsn(DUP);
824 mv.visitVarInsn(ALOAD, 1);
825 mv.visitMethodInsn(INVOKESPECIAL, JLR_UNDECLARED_THROWABLE_EX,
826 "<init>", "(Ljava/lang/Throwable;)V", false);
827 mv.visitInsn(ATHROW);
828 // Maxs computed by ClassWriter.COMPUTE_FRAMES, these arguments ignored
829 mv.visitMaxs(-1, -1);
830 mv.visitEnd();
831 }
832
833 Set<Class<?>> preloadClasses() {
834 Set<Class<?>> preloadClasses = new HashSet<>();
835 for (Class<?> type : parameterTypes) {
836 if (requiresPreload(type)) {
837 preloadClasses.add(type);
838 }
839 }
840 if (requiresPreload(returnType)) {
841 preloadClasses.add(returnType);
842 }
843 return preloadClasses;
844 }
845
846 boolean requiresPreload(Class<?> cls) {
847 Class<?> c = cls;
848 while (c.isArray()) {
849 c = c.getComponentType();
850 }
851 return (c.isValue() && !c.isPrimitiveClass()) || c.isPrimitiveValueType();
852 }
853
854 /**
855 * Generate code for wrapping an argument of the given type
856 * whose value can be found at the specified local variable
857 * index, in order for it to be passed (as an Object) to the
858 * invocation handler's "invoke" method.
859 */
860 private void codeWrapArgument(MethodVisitor mv, Class<?> type, int slot) {
861 if (type.isPrimitive()) {
862 PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
863
864 if (type == int.class ||
865 type == boolean.class ||
866 type == byte.class ||
867 type == char.class ||
868 type == short.class) {
869 mv.visitVarInsn(ILOAD, slot);
870 } else if (type == long.class) {
871 mv.visitVarInsn(LLOAD, slot);
872 } else if (type == float.class) {
873 mv.visitVarInsn(FLOAD, slot);
896 mv.visitMethodInsn(INVOKEVIRTUAL,
897 prim.wrapperClassName,
898 prim.unwrapMethodName, prim.unwrapMethodDesc, false);
899
900 if (type == int.class ||
901 type == boolean.class ||
902 type == byte.class ||
903 type == char.class ||
904 type == short.class) {
905 mv.visitInsn(IRETURN);
906 } else if (type == long.class) {
907 mv.visitInsn(LRETURN);
908 } else if (type == float.class) {
909 mv.visitInsn(FRETURN);
910 } else if (type == double.class) {
911 mv.visitInsn(DRETURN);
912 } else {
913 throw new AssertionError();
914 }
915 } else {
916 String internalName = dotToSlash(type.getName());
917 if (type.isPrimitiveValueType()) {
918 internalName = 'Q' + internalName + ";";
919 }
920 mv.visitTypeInsn(CHECKCAST, internalName);
921 mv.visitInsn(ARETURN);
922 }
923 }
924
925 /**
926 * Generate code for initializing the static field that stores
927 * the Method object for this proxy method.
928 */
929 private void codeFieldInitialization(MethodVisitor mv, String className) {
930 codeClassForName(mv, fromClass);
931
932 mv.visitLdcInsn(method.getName());
933
934 emitIconstInsn(mv, parameterTypes.length);
935
936 mv.visitTypeInsn(Opcodes.ANEWARRAY, JL_CLASS);
937
938 // Construct an array with the parameter types mapping primitives to Wrapper types
939 for (int i = 0; i < parameterTypes.length; i++) {
940 mv.visitInsn(DUP);
951 mv.visitInsn(Opcodes.AASTORE);
952 }
953 // lookup the method
954 mv.visitMethodInsn(INVOKEVIRTUAL,
955 JL_CLASS,
956 "getMethod",
957 "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
958 false);
959
960 mv.visitFieldInsn(PUTSTATIC,
961 dotToSlash(className),
962 methodFieldName, LJLR_METHOD);
963 }
964
965 /*
966 * =============== Code Generation Utility Methods ===============
967 */
968
969 /**
970 * Generate code to invoke the Class.forName with the name of the given
971 * class to get its Class object at runtime. And also generate code
972 * to invoke Class::asValueType if the class is a primitive value type.
973 *
974 * The code is written to the supplied stream. Note that the code generated
975 * by this method may caused the checked ClassNotFoundException to be thrown.
976 */
977 private void codeClassForName(MethodVisitor mv, Class<?> cl) {
978 mv.visitLdcInsn(cl.getName());
979 mv.visitMethodInsn(INVOKESTATIC,
980 JL_CLASS,
981 "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false);
982 if (cl.isPrimitiveValueType()) {
983 mv.visitMethodInsn(INVOKEVIRTUAL,
984 JL_CLASS,
985 "asValueType", "()Ljava/lang/Class;", false);
986 }
987 }
988
989 /**
990 * Visit a bytecode for a constant.
991 *
992 * @param mv The MethodVisitor
993 * @param cst The constant value
994 */
995 private void emitIconstInsn(MethodVisitor mv, final int cst) {
996 if (cst >= -1 && cst <= 5) {
997 mv.visitInsn(Opcodes.ICONST_0 + cst);
998 } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
999 mv.visitIntInsn(Opcodes.BIPUSH, cst);
1000 } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
1001 mv.visitIntInsn(Opcodes.SIPUSH, cst);
1002 } else {
1003 mv.visitLdcInsn(cst);
1004 }
1005 }
1006
|