changeset 4830:57f7c7db785f

8006439: Improve MethodHandles coverage Summary: Fill out caller-sensitive list. Recognize aliases of non-static methods. Remove use of MethodUtil Trampoline. Reviewed-by: mchung, twisti, jdn, skoivu
author andrew
date Thu, 14 Feb 2013 23:23:10 +0000
parents eb7b1c391c31
children 804fa94e3ac9
files src/share/classes/java/lang/invoke/MethodHandleImpl.java src/share/classes/java/lang/invoke/MethodHandleNatives.java src/share/classes/java/lang/invoke/MethodHandles.java
diffstat 3 files changed, 64 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Mon Jan 21 13:34:48 2013 -0800
+++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu Feb 14 23:23:10 2013 +0000
@@ -1278,12 +1278,11 @@
         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() ||
+            if (hostClass == null
+                ||    (hostClass.isArray() ||
                        hostClass.isPrimitive() ||
                        hostClass.getName().startsWith("java.") ||
-                       hostClass.getName().startsWith("sun.")) {
+                       hostClass.getName().startsWith("sun."))) {
                 throw new InternalError();  // does not happen, and should not anyway
             }
             // For simplicity, convert mh to a varargs-like method.
@@ -1293,23 +1292,6 @@
             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.toString());
-            }
-            C_Trampoline = tramp;
-        }
-
         private static final Unsafe UNSAFE = Unsafe.getUnsafe();
 
         private static MethodHandle makeInjectedInvoker(Class<?> hostClass) {
--- a/src/share/classes/java/lang/invoke/MethodHandleNatives.java	Mon Jan 21 13:34:48 2013 -0800
+++ b/src/share/classes/java/lang/invoke/MethodHandleNatives.java	Thu Feb 14 23:23:10 2013 +0000
@@ -412,11 +412,14 @@
      */
     // FIXME: Replace this pattern match by an annotation @sun.reflect.CallerSensitive.
     static boolean isCallerSensitive(MemberName mem) {
-        assert(mem.isInvocable());
+        if (!mem.isInvocable())  return false;  // fields are not caller sensitive
         Class<?> defc = mem.getDeclaringClass();
         switch (mem.getName()) {
         case "doPrivileged":
+        case "doPrivilegedWithCombiner":
             return defc == java.security.AccessController.class;
+        case "checkMemberAccess":
+            return canBeCalledVirtual(mem, java.lang.SecurityManager.class);
         case "getUnsafe":
             return defc == sun.misc.Unsafe.class;
         case "lookup":
@@ -489,7 +492,7 @@
             if (defc == java.util.concurrent.atomic.AtomicReferenceFieldUpdater.class)  return true;
             break;
         case "getContextClassLoader":
-            return defc == java.lang.Thread.class;
+            return canBeCalledVirtual(mem, java.lang.Thread.class);
         case "getPackage":
         case "getPackages":
             return defc == java.lang.Package.class;
@@ -507,6 +510,8 @@
             break;
         case "getCallerClassLoader":
             return defc == java.lang.ClassLoader.class;
+        case "registerAsParallelCapable":
+            return canBeCalledVirtual(mem, java.lang.ClassLoader.class);
         case "getProxyClass":
         case "newProxyInstance":
             return defc == java.lang.reflect.Proxy.class;
@@ -518,4 +523,11 @@
         }
         return false;
     }
+    static boolean canBeCalledVirtual(MemberName symbolicRef, Class<?> definingClass) {
+        Class<?> symbolicRefClass = symbolicRef.getDeclaringClass();
+        if (symbolicRefClass == definingClass)  return true;
+        if (symbolicRef.isStatic() || symbolicRef.isPrivate())  return false;
+        return (definingClass.isAssignableFrom(symbolicRefClass) ||  // Msym overrides Mdef
+                symbolicRefClass.isInterface());                     // Mdef implements Msym
+    }
 }
--- a/src/share/classes/java/lang/invoke/MethodHandles.java	Mon Jan 21 13:34:48 2013 -0800
+++ b/src/share/classes/java/lang/invoke/MethodHandles.java	Thu Feb 14 23:23:10 2013 +0000
@@ -584,19 +584,20 @@
         MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             MemberName method = resolveOrFail(refc, name, type, true);
             checkSecurityManager(refc, method);  // stack walk magic: do not refactor
-            return accessStatic(refc, method);
+            Class<?> callerClass = findBoundCallerClass(method);  // stack walk magic: do not refactor
+            return accessStatic(refc, method, callerClass);
         }
         private
-        MethodHandle accessStatic(Class<?> refc, MemberName method) throws IllegalAccessException {
+        MethodHandle accessStatic(Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
             checkMethod(refc, method, true);
             MethodHandle mh = MethodHandleImpl.findMethod(method, false, lookupClassOrNull());
-            mh = maybeBindCaller(method, mh);
+            mh = maybeBindCaller(method, mh, callerClass);
             return mh;
         }
         private
         MethodHandle resolveStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             MemberName method = resolveOrFail(refc, name, type, true);
-            return accessStatic(refc, method);
+            return accessStatic(refc, method, lookupClass);
         }
 
         /**
@@ -640,16 +641,17 @@
         public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             MemberName method = resolveOrFail(refc, name, type, false);
             checkSecurityManager(refc, method);  // stack walk magic: do not refactor
-            return accessVirtual(refc, method);
+            Class<?> callerClass = findBoundCallerClass(method);  // stack walk magic: do not refactor
+            return accessVirtual(refc, method, callerClass);
         }
         private MethodHandle resolveVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             MemberName method = resolveOrFail(refc, name, type, false);
-            return accessVirtual(refc, method);
+            return accessVirtual(refc, method, lookupClass);
         }
-        private MethodHandle accessVirtual(Class<?> refc, MemberName method) throws IllegalAccessException {
+        private MethodHandle accessVirtual(Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {
             checkMethod(refc, method, false);
             MethodHandle mh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull());
-            mh = maybeBindCaller(method, mh);
+            mh = maybeBindCaller(method, mh, callerClass);
             return restrictProtectedReceiver(method, mh);
         }
 
@@ -753,20 +755,22 @@
             checkSpecialCaller(specialCaller);
             MemberName method = resolveOrFail(refc, name, type, false, false, specialCaller);
             checkSecurityManager(refc, method);  // stack walk magic: do not refactor
-            return accessSpecial(refc, method, specialCaller);
+            Class<?> callerClass = findBoundCallerClass(method);  // stack walk magic: do not refactor
+            return accessSpecial(refc, method, callerClass, specialCaller);
         }
         private MethodHandle accessSpecial(Class<?> refc, MemberName method,
+                                           Class<?> callerClass,
                                            Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
             checkMethod(refc, method, false);
             MethodHandle mh = MethodHandleImpl.findMethod(method, false, specialCaller);
-            mh = maybeBindCaller(method, mh);
+            mh = maybeBindCaller(method, mh, callerClass);
             return restrictReceiver(method, mh, specialCaller);
         }
         private MethodHandle resolveSpecial(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             Class<?> specialCaller = lookupClass();
             checkSpecialCaller(specialCaller);
             MemberName method = resolveOrFail(refc, name, type, false, false, specialCaller);
-            return accessSpecial(refc, method, specialCaller);
+            return accessSpecial(refc, method, lookupClass, specialCaller);
         }
 
         /**
@@ -927,7 +931,8 @@
             checkSecurityManager(refc, method);  // stack walk magic: do not refactor
             checkMethod(refc, method, false);
             MethodHandle dmh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull());
-            MethodHandle bcmh = maybeBindCaller(method, dmh);
+            Class<?> callerClass = findBoundCallerClass(method);  // stack walk magic: do not refactor
+            MethodHandle bcmh = maybeBindCaller(method, dmh, callerClass);
             if (bcmh != dmh)  return fixVarargs(bcmh.bindTo(receiver), dmh);
             MethodHandle bmh = MethodHandleImpl.bindReceiver(dmh, receiver);
             if (bmh == null)
@@ -961,7 +966,8 @@
             assert(method.isMethod());
             if (!m.isAccessible())  checkMethod(method.getDeclaringClass(), method, method.isStatic());
             MethodHandle mh = MethodHandleImpl.findMethod(method, true, lookupClassOrNull());
-            mh = maybeBindCaller(method, mh);
+            Class<?> callerClass = findBoundCallerClass(method);  // stack walk magic: do not refactor
+            mh = maybeBindCaller(method, mh, callerClass);
             if (!m.isAccessible())  mh = restrictProtectedReceiver(method, mh);
             return mh;
         }
@@ -994,7 +1000,8 @@
             // ignore m.isAccessible:  this is a new kind of access
             checkMethod(m.getDeclaringClass(), method, false);
             MethodHandle mh = MethodHandleImpl.findMethod(method, false, lookupClassOrNull());
-            mh = maybeBindCaller(method, mh);
+            Class<?> callerClass = findBoundCallerClass(method);  // stack walk magic: do not refactor
+            mh = maybeBindCaller(method, mh, callerClass);
             return restrictReceiver(method, mh, specialCaller);
         }
 
@@ -1099,7 +1106,29 @@
         }
 
         /**
+         * Find my trustable caller class if m is a caller sensitive method.
+         * If this lookup object has private access, then the caller class is the lookupClass.
+         * Otherwise, it is the caller of the currently executing public API method (e.g., findVirtual).
+         * This is the same caller class as is used by checkSecurityManager.
+         * This function performs stack walk magic: do not refactor it.
+         */
+        Class<?> findBoundCallerClass(MemberName m) {
+            Class<?> callerClass = null;
+            if (MethodHandleNatives.isCallerSensitive(m)) {
+                // Do not refactor this to a more "logical" place, since it is stack walk magic.
+                // Note that this is the same expression as in Step 2 below in checkSecurityManager.
+                callerClass = ((allowedModes & PRIVATE) != 0
+                               ? lookupClass  // for strong access modes, no extra check
+                               // next line does stack walk magic; do not refactor:
+                               : getCallerClassAtEntryPoint(true));
+            }
+            return callerClass;
+        }
+        /**
          * Perform necessary <a href="MethodHandles.Lookup.html#secmgr">access checks</a>.
+         * Determines a trustable caller class to compare with refc, the symbolic reference class.
+         * If this lookup object has private access, then the caller class is the lookupClass.
+         * Otherwise, it is the caller of the currently executing public API method (e.g., findVirtual).
          * This function performs stack walk magic: do not refactor it.
          */
         void checkSecurityManager(Class<?> refc, MemberName m) {
@@ -1236,12 +1265,14 @@
             MethodHandle narrowMH = MethodHandleImpl.convertArguments(mh, narrowType, rawType, 0);
             return fixVarargs(narrowMH, mh);
         }
-        private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh) throws IllegalAccessException {
+        private MethodHandle maybeBindCaller(MemberName method, MethodHandle mh,
+                                             Class<?> callerClass)
+                                             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;
+                hostClass = callerClass;  // callerClass came from a security manager style stack walk
             MethodHandle cbmh = MethodHandleImpl.bindCaller(mh, hostClass);
             cbmh = fixVarargs(cbmh, mh);  // in JDK 7 version, varargs happens earlier and must be repaired
             return cbmh;