changeset 6524:762eee5e6e16

7196190: Improve method of handling MethodHandles Summary: Bind callers to caller-sensitive methods. Reviewed-by: twisti, jjh, vlivanov, ahgross
author jrose
date Thu, 20 Sep 2012 14:02:55 -0700
parents 3b49bd3c392b
children e113ffde452a
files src/share/classes/java/lang/invoke/MethodHandleImpl.java src/share/classes/java/lang/invoke/MethodHandleNatives.java src/share/classes/java/lang/invoke/MethodHandleStatics.java src/share/classes/java/lang/invoke/MethodHandles.java src/share/classes/sun/invoke/anon/AnonymousClassLoader.java src/share/classes/sun/invoke/util/ValueConversions.java test/java/lang/invoke/7196190/ClassForNameTest.java test/java/lang/invoke/7196190/GetUnsafeTest.java test/java/lang/invoke/7196190/MHProxyTest.java test/java/lang/invoke/7196190/jtreg.security.policy
diffstat 10 files changed, 699 insertions(+), 78 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Sep 19 21:42:21 2012 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu Sep 20 14:02:55 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,13 +25,14 @@
 
 package java.lang.invoke;
 
-import sun.invoke.util.VerifyType;
-
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import sun.invoke.empty.Empty;
 import sun.invoke.util.ValueConversions;
+import sun.invoke.util.VerifyType;
 import sun.invoke.util.Wrapper;
 import static java.lang.invoke.LambdaForm.*;
 import static java.lang.invoke.MethodHandleStatics.*;
@@ -781,4 +782,168 @@
         return mh;
     }
 
+    /**
+     * Create an alias for the method handle which, when called,
+     * appears to be called from the same class loader and protection domain
+     * as hostClass.
+     * This is an expensive no-op unless the method which is called
+     * is sensitive to its caller.  A small number of system methods
+     * are in this category, including Class.forName and Method.invoke.
+     */
+    static
+    MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
+        return BindCaller.bindCaller(mh, hostClass);
+    }
+
+    // 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 {
+        static
+        MethodHandle bindCaller(MethodHandle mh, Class<?> hostClass) {
+            // Do not use this function to inject calls into system classes.
+            if (hostClass == null) {
+                hostClass = C_Trampoline;
+            } else if (hostClass.isArray() ||
+                       hostClass.isPrimitive() ||
+                       hostClass.getName().startsWith("java.") ||
+                       hostClass.getName().startsWith("sun.")) {
+                throw new InternalError();  // does not happen, and should not anyway
+            }
+            // For simplicity, convert mh to a varargs-like method.
+            MethodHandle vamh = prepareForInvoker(mh);
+            // Cache the result of makeInjectedInvoker once per argument class.
+            MethodHandle bccInvoker = CV_makeInjectedInvoker.get(hostClass);
+            return restoreToType(bccInvoker.bindTo(vamh), mh.type());
+        }
+
+        // This class ("Trampoline") is known to be inside a dead-end class loader.
+        // Inject all doubtful calls into this class.
+        private static Class<?> C_Trampoline;
+        static {
+            Class<?> tramp = null;
+            try {
+                final int FRAME_COUNT_ARG = 1;  // [0] Reflection [1] Trampoline
+                java.lang.reflect.Method gcc = sun.reflect.Reflection.class.getMethod("getCallerClass", int.class);
+                tramp = (Class<?>) sun.reflect.misc.MethodUtil.invoke(gcc, null, new Object[]{ FRAME_COUNT_ARG });
+                if (tramp.getClassLoader() == BindCaller.class.getClassLoader())
+                    throw new RuntimeException(tramp.getName()+" class loader");
+            } catch (Throwable ex) {
+                throw new InternalError(ex);
+            }
+            C_Trampoline = tramp;
+        }
+
+        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);
+            } 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);
+            }
+        };
+
+        // Adapt mh so that it can be called directly from an injected invoker:
+        private static MethodHandle prepareForInvoker(MethodHandle mh) {
+            mh = mh.asFixedArity();
+            MethodType mt = mh.type();
+            int arity = mt.parameterCount();
+            MethodHandle vamh = mh.asType(mt.generic());
+            vamh.internalForm().compileToBytecode();  // eliminate LFI stack frames
+            vamh = vamh.asSpreader(Object[].class, arity);
+            vamh.internalForm().compileToBytecode();  // eliminate LFI stack frames
+            return vamh;
+        }
+
+        // Undo the adapter effect of prepareForInvoker:
+        private static MethodHandle restoreToType(MethodHandle vamh, MethodType type) {
+            return vamh.asCollector(Object[].class, type.parameterCount()).asType(type);
+        }
+
+        private static final MethodHandle MH_checkCallerClass;
+        static {
+            final Class<?> THIS_CLASS = BindCaller.class;
+            assert(checkCallerClass(THIS_CLASS, 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));
+            } catch (Throwable ex) {
+                throw new InternalError(ex);
+            }
+        }
+
+        private static boolean checkCallerClass(Class<?> expected, Class<?> expected2) {
+            final int FRAME_COUNT_ARG = 2;  // [0] Reflection [1] BindCaller [2] Expected
+            Class<?> actual = sun.reflect.Reflection.getCallerClass(FRAME_COUNT_ARG);
+            if (actual != expected && actual != expected2)
+                throw new InternalError("found "+actual.getName()+", expected "+expected.getName()
+                                        +(expected == expected2 ? "" : ", or else "+expected2.getName()));
+            return true;
+        }
+
+        private static final byte[] T_BYTES;
+        static {
+            final Object[] values = {null};
+            AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                    public Void run() {
+                        try {
+                            Class<T> tClass = T.class;
+                            String tName = tClass.getName();
+                            String tResource = tName.substring(tName.lastIndexOf('.')+1)+".class";
+                            java.net.URLConnection uconn = tClass.getResource(tResource).openConnection();
+                            int len = uconn.getContentLength();
+                            byte[] bytes = new byte[len];
+                            try (java.io.InputStream str = uconn.getInputStream()) {
+                                int nr = str.read(bytes);
+                                if (nr != len)  throw new java.io.IOException(tResource);
+                            }
+                            values[0] = bytes;
+                        } catch (java.io.IOException ex) {
+                            throw new InternalError(ex);
+                        }
+                        return null;
+                    }
+                });
+            T_BYTES = (byte[]) values[0];
+        }
+
+        // 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);
+            }
+        }
+    }
 }
--- a/src/share/classes/java/lang/invoke/MethodHandleNatives.java	Wed Sep 19 21:42:21 2012 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandleNatives.java	Thu Sep 20 14:02:55 2012 -0700
@@ -385,4 +385,101 @@
             throw err;
         }
     }
+
+    /**
+     * Is this method a caller-sensitive method?
+     * I.e., does it call Reflection.getCallerClass or a similer method
+     * to ask about the identity of its caller?
+     */
+    // FIXME: Replace this pattern match by an annotation @sun.reflect.CallerSensitive.
+    static boolean isCallerSensitive(MemberName mem) {
+        assert(mem.isInvocable());
+        Class<?> defc = mem.getDeclaringClass();
+        switch (mem.getName()) {
+        case "doPrivileged":
+            return defc == java.security.AccessController.class;
+        case "getUnsafe":
+            return defc == sun.misc.Unsafe.class;
+        case "lookup":
+            return defc == java.lang.invoke.MethodHandles.class;
+        case "invoke":
+            return defc == java.lang.reflect.Method.class;
+        case "get":
+        case "getBoolean":
+        case "getByte":
+        case "getChar":
+        case "getShort":
+        case "getInt":
+        case "getLong":
+        case "getFloat":
+        case "getDouble":
+        case "set":
+        case "setBoolean":
+        case "setByte":
+        case "setChar":
+        case "setShort":
+        case "setInt":
+        case "setLong":
+        case "setFloat":
+        case "setDouble":
+            return defc == java.lang.reflect.Field.class;
+        case "newInstance":
+            if (defc == java.lang.reflect.Constructor.class)  return true;
+            if (defc == java.lang.Class.class)  return true;
+            break;
+        case "forName":
+        case "getClassLoader":
+        case "getClasses":
+        case "getFields":
+        case "getMethods":
+        case "getConstructors":
+        case "getDeclaredClasses":
+        case "getDeclaredFields":
+        case "getDeclaredMethods":
+        case "getDeclaredConstructors":
+        case "getField":
+        case "getMethod":
+        case "getConstructor":
+        case "getDeclaredField":
+        case "getDeclaredMethod":
+        case "getDeclaredConstructor":
+            return defc == java.lang.Class.class;
+        case "getConnection":
+        case "getDriver":
+        case "getDrivers":
+        case "deregisterDriver":
+            return defc == java.sql.DriverManager.class;
+        case "newUpdater":
+            if (defc == java.util.concurrent.atomic.AtomicIntegerFieldUpdater.class)  return true;
+            if (defc == java.util.concurrent.atomic.AtomicLongFieldUpdater.class)  return true;
+            if (defc == java.util.concurrent.atomic.AtomicReferenceFieldUpdater.class)  return true;
+            break;
+        case "getContextClassLoader":
+            return defc == java.lang.Thread.class;
+        case "getPackage":
+        case "getPackages":
+            return defc == java.lang.Package.class;
+        case "getParent":
+        case "getSystemClassLoader":
+            return defc == java.lang.ClassLoader.class;
+        case "load":
+        case "loadLibrary":
+            if (defc == java.lang.Runtime.class)  return true;
+            if (defc == java.lang.System.class)  return true;
+            break;
+        case "getCallerClass":
+            if (defc == sun.reflect.Reflection.class)  return true;
+            if (defc == java.lang.System.class)  return true;
+            break;
+        case "getCallerClassLoader":
+            return defc == java.lang.ClassLoader.class;
+        case "getProxyClass":
+        case "newProxyInstance":
+            return defc == java.lang.reflect.Proxy.class;
+        case "getBundle":
+        case "clearCache":
+            return defc == java.util.ResourceBundle.class;
+        }
+        return false;
+    }
 }
--- a/src/share/classes/java/lang/invoke/MethodHandleStatics.java	Wed Sep 19 21:42:21 2012 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandleStatics.java	Thu Sep 20 14:02:55 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -108,7 +108,7 @@
     /*non-public*/ static RuntimeException newIllegalArgumentException(String message, Object obj, Object obj2) {
         return new IllegalArgumentException(message(message, obj, obj2));
     }
-    /*non-public*/ static Error uncaughtException(Exception ex) {
+    /*non-public*/ static Error uncaughtException(Throwable ex) {
         throw new InternalError("uncaught exception", ex);
     }
     static Error NYI() {
--- a/src/share/classes/java/lang/invoke/MethodHandles.java	Wed Sep 19 21:42:21 2012 +0400
+++ b/src/share/classes/java/lang/invoke/MethodHandles.java	Thu Sep 20 14:02:55 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -329,6 +329,7 @@
      *     where {@code defcPkg} is the package of {@code defc}.
      * </ul>
      */
+    // FIXME in MR1: clarify that the bytecode behavior of a caller-ID method (like Class.forName) is relative to the lookupClass used to create the method handle, not the dynamic caller of the method handle
     public static final
     class Lookup {
         /** The class on behalf of whom the lookup is being performed. */
@@ -1209,6 +1210,7 @@
             if (method.isMethodHandleInvoke())
                 return fakeMethodHandleInvoke(method);
             MethodHandle mh = DirectMethodHandle.make(refc, method);
+            mh = maybeBindCaller(method, mh);
             mh = mh.setVarargs(method);
             if (doRestrict)
                 mh = restrictReceiver(method, mh, lookupClass());
@@ -1217,6 +1219,16 @@
         private MethodHandle fakeMethodHandleInvoke(MemberName method) {
             return throwException(method.getReturnType(), UnsupportedOperationException.class);
         }
+        private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh) throws IllegalAccessException {
+            if (allowedModes == TRUSTED || !MethodHandleNatives.isCallerSensitive(method))
+                return mh;
+            Class<?> hostClass = lookupClass;
+            if ((allowedModes & PRIVATE) == 0)  // caller must use full-power lookup
+                hostClass = null;
+            MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, hostClass);
+            // Note: caller will apply varargs after this step happens.
+            return cbmh;
+        }
         private MethodHandle getDirectField(byte refKind, Class<?> refc, MemberName field) throws IllegalAccessException {
             checkField(refKind, refc, field);
             MethodHandle mh = DirectMethodHandle.make(refc, field);
@@ -1229,6 +1241,7 @@
         private MethodHandle getDirectConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException {
             assert(ctor.isConstructor());
             checkAccess(REF_newInvokeSpecial, refc, ctor);
+            assert(!MethodHandleNatives.isCallerSensitive(ctor));  // maybeBindCaller not relevant here
             return DirectMethodHandle.make(ctor).setVarargs(ctor);
         }
 
--- a/src/share/classes/sun/invoke/anon/AnonymousClassLoader.java	Wed Sep 19 21:42:21 2012 +0400
+++ b/src/share/classes/sun/invoke/anon/AnonymousClassLoader.java	Thu Sep 20 14:02:55 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -73,74 +73,14 @@
 public class AnonymousClassLoader {
     final Class<?> hostClass;
 
-    // Note: Do not refactor the calls to checkHostClass unless you
-    //       also adjust this constant:
-    private static int CHC_CALLERS = 3;
-
-    public AnonymousClassLoader() {
-        this.hostClass = checkHostClass(null);
-    }
-    public AnonymousClassLoader(Class<?> hostClass) {
-        this.hostClass = checkHostClass(hostClass);
+    // Privileged constructor.
+    private AnonymousClassLoader(Class<?> hostClass) {
+        this.hostClass = hostClass;
     }
 
-    private static Class<?> getTopLevelClass(Class<?> clazz) {
-      for(Class<?> outer = clazz.getDeclaringClass(); outer != null;
-          outer = outer.getDeclaringClass()) {
-        clazz = outer;
-      }
-      return clazz;
-    }
-
-    private static Class<?> checkHostClass(Class<?> hostClass) {
-        // called only from the constructor
-        // does a context-sensitive check on caller class
-        // CC[0..3] = {Reflection, this.checkHostClass, this.<init>, caller}
-        Class<?> caller = sun.reflect.Reflection.getCallerClass(CHC_CALLERS);
-
-        if (caller == null) {
-            // called from the JVM directly
-            if (hostClass == null)
-                return AnonymousClassLoader.class; // anything central will do
-            return hostClass;
-        }
-
-        if (hostClass == null)
-            hostClass = caller; // default value is caller itself
-
-        // anonymous class will access hostClass on behalf of caller
-        Class<?> callee = hostClass;
-
-        if (caller == callee)
-            // caller can always nominate itself to grant caller's own access rights
-            return hostClass;
-
-        // normalize caller and callee to their top-level classes:
-        caller = getTopLevelClass(caller);
-        callee = getTopLevelClass(callee);
-        if (caller == callee)
-            return caller;
-
-        ClassLoader callerCL = caller.getClassLoader();
-        if (callerCL == null) {
-            // caller is trusted code, so accept the proposed hostClass
-            return hostClass;
-        }
-
-        // %%% should do something with doPrivileged, because trusted
-        // code should have a way to execute on behalf of
-        // partially-trusted clients
-
-        // Does the caller have the right to access the private
-        // members of the callee?  If not, raise an error.
-        final int ACC_PRIVATE = 2;
-        try {
-            sun.reflect.Reflection.ensureMemberAccess(caller, callee, null, ACC_PRIVATE);
-        } catch (IllegalAccessException ee) {
-            throw new IllegalArgumentException(ee);
-        }
-
-        return hostClass;
+    public static AnonymousClassLoader make(sun.misc.Unsafe unsafe, Class<?> hostClass) {
+        if (unsafe == null)  throw new NullPointerException();
+        return new AnonymousClassLoader(hostClass);
     }
 
     public Class<?> loadClass(byte[] classFile) {
@@ -249,7 +189,7 @@
     private static int fakeNameCounter = 99999;
 
     // ignore two warnings on this line:
-    static sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
+    private static sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
     // preceding line requires that this class be on the boot class path
 
     static private final Method defineAnonymousClass;
--- a/src/share/classes/sun/invoke/util/ValueConversions.java	Wed Sep 19 21:42:21 2012 +0400
+++ b/src/share/classes/sun/invoke/util/ValueConversions.java	Thu Sep 20 14:02:55 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -522,13 +522,19 @@
     static {
         MethodHandle mh = null;
         try {
-            java.lang.reflect.Method m = MethodHandles.class
+            final java.lang.reflect.Method m = MethodHandles.class
                 .getDeclaredMethod("collectArguments",
                     MethodHandle.class, int.class, MethodHandle.class);
-            m.setAccessible(true);
+            AccessController.doPrivileged(new PrivilegedAction<Void>() {
+                    @Override
+                    public Void run() {
+                        m.setAccessible(true);
+                        return null;
+                    }
+                });
             mh = IMPL_LOOKUP.unreflect(m);
 
-        } catch (ReflectiveOperationException | SecurityException ex) {
+        } catch (ReflectiveOperationException ex) {
             throw new InternalError(ex);
         }
         COLLECT_ARGUMENTS = mh;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/7196190/ClassForNameTest.java	Thu Sep 20 14:02:55 2012 -0700
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/**
+ * @test
+ * @bug 7196190
+ * @summary Improve method of handling MethodHandles
+ *
+ * @run main/othervm ClassForNameTest
+ */
+
+import java.lang.invoke.*;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+public class ClassForNameTest {
+    final static String NAME = ClassForNameTest.class.getName();
+
+    public static void main(String[] args) throws Throwable {
+        {
+            final MethodType mt = MethodType.methodType(Class.class, String.class);
+            final MethodHandle mh = MethodHandles.lookup()
+                    .findStatic(Class.class, "forName", mt);
+
+            Class.forName(NAME);
+
+            mh.invoke(NAME);
+            mh.bindTo(NAME).invoke();
+            mh.invokeWithArguments(Arrays.asList(NAME));
+            mh.invokeWithArguments(NAME);
+            Class cls = (Class) mh.invokeExact(NAME);
+        }
+
+        {
+            final Method fnMethod = Class.class.getMethod("forName", String.class);
+            final MethodType mt = MethodType.methodType(Object.class, Object.class, Object[].class);
+            final MethodHandle mh = MethodHandles.lookup()
+                    .findVirtual(Method.class, "invoke", mt)
+                    .bindTo(fnMethod);
+
+            fnMethod.invoke(null, NAME);
+
+            mh.bindTo(null).bindTo(new Object[]{NAME}).invoke();
+            mh.invoke(null, new Object[]{NAME});
+            mh.invokeWithArguments(null, new Object[]{NAME});
+            mh.invokeWithArguments(Arrays.asList(null, new Object[]{NAME}));
+            Object obj = mh.invokeExact((Object) null, new Object[]{NAME});
+        }
+
+        {
+            final Method fnMethod = Class.class.getMethod("forName", String.class);
+            final MethodType mt = MethodType.methodType(Object.class, Object.class, Object[].class);
+
+            final MethodHandle mh = MethodHandles.lookup()
+                    .bind(fnMethod, "invoke", mt);
+
+            mh.bindTo(null).bindTo(new Object[]{NAME}).invoke();
+            mh.invoke(null, new Object[]{NAME});
+            mh.invokeWithArguments(null, NAME);
+            mh.invokeWithArguments(Arrays.asList(null, NAME));
+            Object obj = mh.invokeExact((Object) null, new Object[]{NAME});
+        }
+
+        {
+            final Method fnMethod = Class.class.getMethod("forName", String.class);
+            final MethodHandle mh = MethodHandles.lookup().unreflect(fnMethod);
+
+            mh.bindTo(NAME).invoke();
+            mh.invoke(NAME);
+            mh.invokeWithArguments(NAME);
+            mh.invokeWithArguments(Arrays.asList(NAME));
+            Class cls = (Class) mh.invokeExact(NAME);
+        }
+
+        System.out.println("TEST PASSED");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/7196190/GetUnsafeTest.java	Thu Sep 20 14:02:55 2012 -0700
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/**
+ * @test
+ * @bug 7196190
+ * @summary Improve method of handling MethodHandles
+ *
+ * @run main/othervm/policy=jtreg.security.policy/secure=java.lang.SecurityManager GetUnsafeTest
+ */
+
+import java.lang.invoke.*;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+public class GetUnsafeTest {
+    final static String NAME = "sun.misc.Unsafe";
+
+    private static boolean isTestFailed = false;
+
+    private static void fail() {
+        isTestFailed = true;
+        try { throw new Exception(); } catch (Throwable e) {
+            StackTraceElement frame = e.getStackTrace()[1];
+            System.out.printf("Failed at %s:%d\n", frame.getFileName(), frame.getLineNumber());
+        }
+    }
+
+    public static void main(String[] args) throws Throwable {
+        {
+            final MethodType mt = MethodType.methodType(Class.class, String.class);
+            final MethodHandle mh = MethodHandles.lookup()
+                    .findStatic(Class.class, "forName", mt);
+
+            try { Class.forName(NAME);                         fail(); } catch (Throwable e) {}
+
+            try { mh.invoke(NAME);                             fail(); } catch (Throwable e) {}
+            try { mh.bindTo(NAME).invoke();                    fail(); } catch (Throwable e) {}
+            try { mh.invokeWithArguments(Arrays.asList(NAME)); fail(); } catch (Throwable e) {}
+            try { mh.invokeWithArguments(NAME);                fail(); } catch (Throwable e) {}
+            try { Class cls = (Class) mh.invokeExact(NAME);    fail(); } catch (Throwable e) {}
+        }
+
+        {
+            final Method fnMethod = Class.class.getMethod("forName", String.class);
+            final MethodType mt = MethodType.methodType(Object.class, Object.class, Object[].class);
+            final MethodHandle mh = MethodHandles.lookup()
+                    .findVirtual(Method.class, "invoke", mt)
+                    .bindTo(fnMethod);
+
+            try { fnMethod.invoke(null, NAME); fail(); } catch (Throwable e) {}
+
+            try { mh.bindTo(null).bindTo(new Object[]{NAME}).invoke();             fail(); } catch (Throwable e) {}
+            try { mh.invoke(null, new Object[]{NAME});                             fail(); } catch (Throwable e) {}
+            try { mh.invokeWithArguments(null, new Object[]{NAME});                fail(); } catch (Throwable e) {}
+            try { mh.invokeWithArguments(Arrays.asList(null, new Object[]{NAME})); fail(); } catch (Throwable e) {}
+            try { Object obj = mh.invokeExact((Object) null, new Object[]{NAME});  fail(); } catch (Throwable e) {}
+        }
+
+        {
+            final Method fnMethod = Class.class.getMethod("forName", String.class);
+            final MethodType mt = MethodType.methodType(Object.class, Object.class, Object[].class);
+
+            final MethodHandle mh = MethodHandles.lookup().bind(fnMethod, "invoke", mt);
+
+            try { mh.bindTo(null).bindTo(new Object[]{NAME}).invoke();            fail(); } catch (Throwable e) {}
+            try { mh.invoke(null, new Object[]{NAME});                            fail(); } catch (Throwable e) {}
+            try { mh.invokeWithArguments(null, NAME);                             fail(); } catch (Throwable e) {}
+            try { mh.invokeWithArguments(Arrays.asList(null, NAME));              fail(); } catch (Throwable e) {}
+            try { Object obj = mh.invokeExact((Object) null, new Object[]{NAME}); fail(); } catch (Throwable e) {}
+        }
+
+        {
+            final Method fnMethod = Class.class.getMethod("forName", String.class);
+            final MethodHandle mh = MethodHandles.lookup().unreflect(fnMethod);
+
+            try { mh.bindTo(NAME).invoke();                    fail(); } catch (Throwable e) {}
+            try { mh.invoke(NAME);                             fail(); } catch (Throwable e) {}
+            try { mh.invokeWithArguments(NAME);                fail(); } catch (Throwable e) {}
+            try { mh.invokeWithArguments(Arrays.asList(NAME)); fail(); } catch (Throwable e) {}
+            try { Class cls = (Class) mh.invokeExact(NAME);    fail(); } catch (Throwable e) {}
+        }
+
+        if (!isTestFailed) {
+            System.out.println("TEST PASSED");
+        } else {
+            System.out.println("TEST FAILED");
+            System.exit(1);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/7196190/MHProxyTest.java	Thu Sep 20 14:02:55 2012 -0700
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/**
+ * @test
+ * @bug 7196190
+ * @summary Improve method of handling MethodHandles
+ *
+ * @run main/othervm MHProxyTest
+ */
+
+import java.lang.invoke.*;
+import java.security.*;
+import static java.lang.invoke.MethodHandles.*;
+import static java.lang.invoke.MethodType.*;
+
+public class MHProxyTest {
+    private static final Class<?> C_Unsafe;
+    private static final MethodHandle MH_getUnsafe;
+    static {
+        // Do these before there is a SM installed.
+        C_Unsafe = sun.misc.Unsafe.class;  // EXPECT A WARNING ON THIS LINE
+        Lookup lookup = lookup();
+        MethodHandle gumh = null;
+        try {
+            gumh = lookup.findStatic(C_Unsafe, "getUnsafe", methodType(C_Unsafe));
+        } catch (ReflectiveOperationException ex) {
+            throw new InternalError(ex.toString());
+        }
+        MH_getUnsafe = gumh;
+        // Try some different lookups:
+        try {
+            lookup.in(Object.class).findStatic(C_Unsafe, "getUnsafe", methodType(C_Unsafe));
+        } catch (ReflectiveOperationException ex) {
+            throw new InternalError(ex.toString());
+        }
+        lookup = lookup().in(C_Unsafe);
+        try {
+            lookup.in(C_Unsafe).findStatic(C_Unsafe, "getUnsafe", methodType(C_Unsafe));
+        } catch (ReflectiveOperationException ex) {
+            throw new InternalError(ex.toString());
+        }
+    }
+
+    public static void main(String[] args) throws Throwable {
+        System.setSecurityManager(new SecurityManager());
+        Lookup lookup = lookup();
+        testBasic(lookup);
+        testDoPriv(lookup);
+        testSetVar();
+        Lookup l2 = lookup.in(Object.class);
+        System.out.println("=== "+l2);
+        testBasic(l2);
+        testDoPriv(l2);
+        Lookup l3 = lookup.in(C_Unsafe);
+        System.out.println("=== "+l3);
+        testBasic(l3);
+        testDoPriv(l3);
+        if (failure != null)
+            throw failure;
+    }
+
+    private static Throwable failure;
+    private static void fail(Throwable ex) {
+        if (failure == null)
+            failure = ex;
+        StackTraceElement frame = new Exception().getStackTrace()[1];
+        System.out.printf("Failed at %s:%d: %s\n", frame.getFileName(), frame.getLineNumber(), ex);
+    }
+    private static void ok(Throwable ex) {
+        StackTraceElement frame = new Exception().getStackTrace()[1];
+        System.out.printf("OK at %s:%d: %s\n", frame.getFileName(), frame.getLineNumber(), ex);
+    }
+
+    private static void testBasic(Lookup lookup) throws Throwable {
+        // Verify that we can't get to this guy under the SM:
+        try {
+            MethodHandle badmh = lookup.findStatic(C_Unsafe, "getUnsafe", methodType(C_Unsafe));
+            assert(badmh.type() == methodType(C_Unsafe));
+            badmh = badmh.asType(badmh.type().generic());
+            Object u = C_Unsafe.cast(badmh.invokeExact());
+            assert(C_Unsafe.isInstance(u));
+            fail(new AssertionError("got mh to getUnsafe!"));
+        } catch (SecurityException ex) {
+            ok(ex);
+        }
+        try {
+            Object u = MH_getUnsafe.invokeWithArguments();
+            assert(C_Unsafe.isInstance(u));
+            fail(new AssertionError("got the Unsafe object! (MH invoke)"));
+        } catch (SecurityException ex) {
+            ok(ex);
+        }
+        try {
+            MethodHandle mh = MH_getUnsafe;
+            mh = mh.asType(mh.type().generic());
+            mh = foldArguments(identity(Object.class), mh);
+            mh = filterReturnValue(mh, identity(Object.class));
+            Object u = mh.invokeExact();
+            assert(C_Unsafe.isInstance(u));
+            fail(new AssertionError("got the Unsafe object! (MH invokeWithArguments)"));
+        } catch (SecurityException ex) {
+            ok(ex);
+        }
+    }
+
+    private static void testDoPriv(Lookup lookup) throws Throwable {
+        PrivilegedAction privAct = MethodHandleProxies.asInterfaceInstance(PrivilegedAction.class, MH_getUnsafe);
+        try {
+            Object u = AccessController.doPrivileged(privAct);
+            assert(C_Unsafe.isInstance(u));
+            fail(new AssertionError("got the Unsafe object! (static doPriv)"));
+        } catch (SecurityException ex) {
+            ok(ex);
+        }
+        MethodHandle MH_doPriv = lookup.findStatic(AccessController.class, "doPrivileged",
+                                                   methodType(Object.class, PrivilegedAction.class));
+        MH_doPriv = MH_doPriv.bindTo(privAct);
+        try {
+            Object u = MH_doPriv.invoke();
+            assert(C_Unsafe.isInstance(u));
+            fail(new AssertionError("got the Unsafe object! (MH + doPriv)"));
+        } catch (SecurityException ex) {
+            ok(ex);
+        }
+        // try one more layer of indirection:
+        Runnable rbl = MethodHandleProxies.asInterfaceInstance(Runnable.class, MH_doPriv);
+        try {
+            rbl.run();
+            fail(new AssertionError("got the Unsafe object! (Runnable + MH + doPriv)"));
+        } catch (SecurityException ex) {
+            ok(ex);
+        }
+    }
+
+    private static void testSetVar() throws Throwable {
+        {
+            // Test the box pattern:
+            Object[] box = new Object[1];
+            MethodHandle MH_getFoo = identity(Object.class).bindTo("foo");
+            MethodHandle MH_storeToBox = insertArguments(arrayElementSetter(Object[].class), 0, box, 0);
+            MethodHandle mh = filterReturnValue(MH_getFoo, MH_storeToBox);
+            mh.invokeExact();
+            assert(box[0] == "foo");
+        }
+        {
+            Object[] box = new Object[1];
+            MethodHandle MH_storeToBox = insertArguments(arrayElementSetter(Object[].class), 0, box, 0);
+            MethodHandle mh = filterReturnValue(MH_getUnsafe.asType(MH_getUnsafe.type().generic()), MH_storeToBox);
+            try {
+                mh.invokeExact();
+                Object u = box[0];
+                assert(C_Unsafe.isInstance(u));
+                fail(new AssertionError("got the Unsafe object! (MH + setElement)"));
+            } catch (SecurityException ex) {
+                ok(ex);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/invoke/7196190/jtreg.security.policy	Thu Sep 20 14:02:55 2012 -0700
@@ -0,0 +1,9 @@
+/*
+ * security policy used by the test process
+ * must allow file reads so that jtreg itself can run
+ */
+
+grant {
+  // standard test activation permissions
+  permission java.io.FilePermission "*", "read";
+};