8149574: Fix java/lang/invoke/MethodHandleImpl's use of Unsafe.defineAnonymousClass()
authorsrastogi
Thu, 19 May 2016 11:13:52 +0200
changeset 38422 638589997770
parent 38421 f75f729fdc5c
child 38423 5d6ff7d76bf4
8149574: Fix java/lang/invoke/MethodHandleImpl's use of Unsafe.defineAnonymousClass() Reviewed-by: vlivanov, psandoz, sundar, mhaupt
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed May 18 11:31:23 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu May 19 11:13:52 2016 +0200
@@ -41,9 +41,15 @@
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.VerifyType;
 import sun.invoke.util.Wrapper;
+
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+
 import static java.lang.invoke.LambdaForm.*;
 import static java.lang.invoke.MethodHandleStatics.*;
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
 
 /**
  * Trusted implementation code for MethodHandle.
@@ -1155,6 +1161,8 @@
     // Put the whole mess into its own nested class.
     // That way we can lazily load the code and set up the constants.
     private static class BindCaller {
+        private static MethodType INVOKER_MT = MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
+
         static
         MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
             // Do not use this function to inject calls into system classes.
@@ -1173,38 +1181,15 @@
         }
 
         private static MethodHandle makeInjectedInvoker(Class<?> hostClass) {
-            Class<?> bcc = UNSAFE.defineAnonymousClass(hostClass, T_BYTES, null);
-            if (hostClass.getClassLoader() != bcc.getClassLoader())
-                throw new InternalError(hostClass.getName()+" (CL)");
             try {
-                if (hostClass.getProtectionDomain() != bcc.getProtectionDomain())
-                    throw new InternalError(hostClass.getName()+" (PD)");
-            } catch (SecurityException ex) {
-                // Self-check was blocked by security manager.  This is OK.
-                // In fact the whole try body could be turned into an assertion.
-            }
-            try {
-                MethodHandle init = IMPL_LOOKUP.findStatic(bcc, "init", MethodType.methodType(void.class));
-                init.invokeExact();  // force initialization of the class
-            } catch (Throwable ex) {
-                throw uncaughtException(ex);
-            }
-            MethodHandle bccInvoker;
-            try {
-                MethodType invokerMT = MethodType.methodType(Object.class, MethodHandle.class, Object[].class);
-                bccInvoker = IMPL_LOOKUP.findStatic(bcc, "invoke_V", invokerMT);
+                Class<?> invokerClass = UNSAFE.defineAnonymousClass(hostClass, INJECTED_INVOKER_TEMPLATE, null);
+                assert checkInjectedInvoker(hostClass, invokerClass);
+                return IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
             } catch (ReflectiveOperationException ex) {
                 throw uncaughtException(ex);
             }
-            // Test the invoker, to ensure that it really injects into the right place.
-            try {
-                MethodHandle vamh = prepareForInvoker(MH_checkCallerClass);
-                Object ok = bccInvoker.invokeExact(vamh, new Object[]{hostClass, bcc});
-            } catch (Throwable ex) {
-                throw new InternalError(ex);
-            }
-            return bccInvoker;
         }
+
         private static ClassValue<MethodHandle> CV_makeInjectedInvoker = new ClassValue<MethodHandle>() {
             @Override protected MethodHandle computeValue(Class<?> hostClass) {
                 return makeInjectedInvoker(hostClass);
@@ -1235,62 +1220,82 @@
             return mh;
         }
 
+        private static boolean checkInjectedInvoker(Class<?> hostClass, Class<?> invokerClass) {
+            assert (hostClass.getClassLoader() == invokerClass.getClassLoader()) : hostClass.getName()+" (CL)";
+            try {
+                assert (hostClass.getProtectionDomain() == invokerClass.getProtectionDomain()) : hostClass.getName()+" (PD)";
+            } catch (SecurityException ex) {
+                // Self-check was blocked by security manager. This is OK.
+            }
+            try {
+                // Test the invoker to ensure that it really injects into the right place.
+                MethodHandle invoker = IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
+                MethodHandle vamh = prepareForInvoker(MH_checkCallerClass);
+                return (boolean)invoker.invoke(vamh, new Object[]{ invokerClass });
+            } catch (Throwable ex) {
+                throw new InternalError(ex);
+            }
+        }
+
         private static final MethodHandle MH_checkCallerClass;
         static {
             final Class<?> THIS_CLASS = BindCaller.class;
-            assert(checkCallerClass(THIS_CLASS, THIS_CLASS));
+            assert(checkCallerClass(THIS_CLASS));
             try {
                 MH_checkCallerClass = IMPL_LOOKUP
                     .findStatic(THIS_CLASS, "checkCallerClass",
-                                MethodType.methodType(boolean.class, Class.class, Class.class));
-                assert((boolean) MH_checkCallerClass.invokeExact(THIS_CLASS, THIS_CLASS));
+                                MethodType.methodType(boolean.class, Class.class));
+                assert((boolean) MH_checkCallerClass.invokeExact(THIS_CLASS));
             } catch (Throwable ex) {
                 throw new InternalError(ex);
             }
         }
 
         @CallerSensitive
-        private static boolean checkCallerClass(Class<?> expected, Class<?> expected2) {
-            // This method is called via MH_checkCallerClass and so it's
-            // correct to ask for the immediate caller here.
+        private static boolean checkCallerClass(Class<?> expected) {
+            // This method is called via MH_checkCallerClass and so it's correct to ask for the immediate caller here.
             Class<?> actual = Reflection.getCallerClass();
-            if (actual != expected && actual != expected2)
-                throw new InternalError("found "+actual.getName()+", expected "+expected.getName()
-                                        +(expected == expected2 ? "" : ", or else "+expected2.getName()));
+            if (actual != expected)
+                throw new InternalError("found " + actual.getName() + ", expected " + expected.getName());
             return true;
         }
 
-        private static final byte[] T_BYTES;
-        static {
-            final Object[] values = {null};
-            AccessController.doPrivileged(new PrivilegedAction<>() {
-                    public Void run() {
-                        try {
-                            Class<T> tClass = T.class;
-                            String tName = tClass.getName();
-                            String tResource = tName.substring(tName.lastIndexOf('.')+1)+".class";
-                            try (java.io.InputStream in = tClass.getResourceAsStream(tResource)) {
-                                values[0] = in.readAllBytes();
-                            }
-                        } catch (java.io.IOException ex) {
-                            throw new InternalError(ex);
-                        }
-                        return null;
-                    }
-                });
-            T_BYTES = (byte[]) values[0];
-        }
+        private static final byte[] INJECTED_INVOKER_TEMPLATE = generateInvokerTemplate();
+
+        /** Produces byte code for a class that is used as an injected invoker. */
+        private static byte[] generateInvokerTemplate() {
+            ClassWriter cw = new ClassWriter(0);
+
+            // private static class InjectedInvoker {
+            //     @Hidden
+            //     static Object invoke_V(MethodHandle vamh, Object[] args) throws Throwable {
+            //        return vamh.invokeExact(args);
+            //     }
+            // }
+            cw.visit(52, ACC_PRIVATE | ACC_SUPER, "InjectedInvoker", null, "java/lang/Object", null);
 
-        // The following class is used as a template for Unsafe.defineAnonymousClass:
-        private static class T {
-            static void init() { }  // side effect: initializes this class
-            static Object invoke_V(MethodHandle vamh, Object[] args) throws Throwable {
-                return vamh.invokeExact(args);
-            }
+            MethodVisitor mv = cw.visitMethod(ACC_STATIC, "invoke_V",
+                          "(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;",
+                          null, null);
+
+            // Suppress invoker method in stack traces.
+            AnnotationVisitor av0 = mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
+            av0.visitEnd();
+
+            mv.visitCode();
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitVarInsn(ALOAD, 1);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle", "invokeExact",
+                               "([Ljava/lang/Object;)Ljava/lang/Object;", false);
+            mv.visitInsn(ARETURN);
+            mv.visitMaxs(2, 2);
+            mv.visitEnd();
+
+            cw.visitEnd();
+            return cw.toByteArray();
         }
     }
 
-
     /** This subclass allows a wrapped method handle to be re-associated with an arbitrary member name. */
     private static final class WrappedMember extends DelegatingMethodHandle {
         private final MethodHandle target;