jdk/src/share/classes/java/lang/invoke/MethodHandles.java
changeset 16906 44dfee24cb71
parent 16898 b321dfd1163f
child 18156 edb590d448c5
child 18264 286a7973575a
--- a/jdk/src/share/classes/java/lang/invoke/MethodHandles.java	Wed Apr 17 10:15:33 2013 +0800
+++ b/jdk/src/share/classes/java/lang/invoke/MethodHandles.java	Tue Apr 16 21:39:52 2013 -0700
@@ -26,13 +26,17 @@
 package java.lang.invoke;
 
 import java.lang.reflect.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.VerifyAccess;
 import sun.invoke.util.Wrapper;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Arrays;
+import sun.reflect.CallerSensitive;
 import sun.reflect.Reflection;
+import sun.security.util.SecurityConstants;
 import static java.lang.invoke.MethodHandleStatics.*;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 
@@ -65,8 +69,9 @@
      * This lookup object is a <em>capability</em> which may be delegated to trusted agents.
      * Do not store it in place where untrusted code can access it.
      */
+    @CallerSensitive
     public static Lookup lookup() {
-        return new Lookup();
+        return new Lookup(Reflection.getCallerClass());
     }
 
     /**
@@ -416,20 +421,13 @@
          * for method handle creation.
          * Must be called by from a method in this package,
          * which in turn is called by a method not in this package.
-         * <p>
-         * Also, don't make it private, lest javac interpose
-         * an access$N method.
          */
-        Lookup() {
-            this(getCallerClassAtEntryPoint(false), ALL_MODES);
+        Lookup(Class<?> lookupClass) {
+            this(lookupClass, ALL_MODES);
             // make sure we haven't accidentally picked up a privileged class:
             checkUnprivilegedlookupClass(lookupClass);
         }
 
-        Lookup(Class<?> lookupClass) {
-            this(lookupClass, ALL_MODES);
-        }
-
         private Lookup(Class<?> lookupClass, int allowedModes) {
             this.lookupClass = lookupClass;
             this.allowedModes = allowedModes;
@@ -554,20 +552,6 @@
             }
         }
 
-        /* Obtain the external caller class, when called from Lookup.<init> or a first-level subroutine. */
-        private static Class<?> getCallerClassAtEntryPoint(boolean inSubroutine) {
-            final int CALLER_DEPTH = 4;
-            //  Stack for the constructor entry point (inSubroutine=false):
-            // 0: Reflection.getCC, 1: getCallerClassAtEntryPoint,
-            // 2: Lookup.<init>, 3: MethodHandles.*, 4: caller
-            //  The stack is slightly different for a subroutine of a Lookup.find* method:
-            // 2: Lookup.*, 3: Lookup.find*.*, 4: caller
-            // Note:  This should be the only use of getCallerClass in this file.
-            assert(Reflection.getCallerClass(CALLER_DEPTH-2) == Lookup.class);
-            assert(Reflection.getCallerClass(CALLER_DEPTH-1) == (inSubroutine ? Lookup.class : MethodHandles.class));
-            return Reflection.getCallerClass(CALLER_DEPTH);
-        }
-
         /**
          * Produces a method handle for a static method.
          * The type of the method handle will be that of the method.
@@ -594,12 +578,14 @@
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
          */
+        @CallerSensitive
         public
         MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             MemberName method = resolveOrFail(REF_invokeStatic, refc, name, type);
-            checkSecurityManager(refc, method);  // stack walk magic: do not refactor
-            Class<?> callerClass = findBoundCallerClass(method);  // stack walk magic: do not refactor
-            return getDirectMethod(REF_invokeStatic, refc, method, callerClass);
+            Class<?> callerClass = Reflection.getCallerClass();
+            checkSecurityManager(refc, method, callerClass);
+            return getDirectMethod(REF_invokeStatic, refc, method,
+                                   findBoundCallerClass(method, callerClass));
         }
 
         /**
@@ -645,6 +631,7 @@
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
          */
+        @CallerSensitive
         public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             if (refc == MethodHandle.class) {
                 MethodHandle mh = findVirtualForMH(name, type);
@@ -652,9 +639,10 @@
             }
             byte refKind = (refc.isInterface() ? REF_invokeInterface : REF_invokeVirtual);
             MemberName method = resolveOrFail(refKind, refc, name, type);
-            checkSecurityManager(refc, method);  // stack walk magic: do not refactor
-            Class<?> callerClass = findBoundCallerClass(method);
-            return getDirectMethod(refKind, refc, method, callerClass);
+            Class<?> callerClass = Reflection.getCallerClass();
+            checkSecurityManager(refc, method, callerClass);
+            return getDirectMethod(refKind, refc, method,
+                                   findBoundCallerClass(method, callerClass));
         }
         private MethodHandle findVirtualForMH(String name, MethodType type) {
             // these names require special lookups because of the implicit MethodType argument
@@ -691,10 +679,11 @@
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
          */
+        @CallerSensitive
         public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             String name = "<init>";
             MemberName ctor = resolveOrFail(REF_newInvokeSpecial, refc, name, type);
-            checkSecurityManager(refc, ctor);  // stack walk magic: do not refactor
+            checkSecurityManager(refc, ctor, Reflection.getCallerClass());
             return getDirectConstructor(refc, ctor);
         }
 
@@ -732,14 +721,16 @@
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
          */
+        @CallerSensitive
         public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
                                         Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
             checkSpecialCaller(specialCaller);
             Lookup specialLookup = this.in(specialCaller);
             MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type);
-            checkSecurityManager(refc, method);  // stack walk magic: do not refactor
-            Class<?> callerClass = findBoundCallerClass(method);
-            return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method, callerClass);
+            Class<?> callerClass = Reflection.getCallerClass();
+            checkSecurityManager(refc, method, callerClass);
+            return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method,
+                                                 findBoundCallerClass(method, callerClass));
         }
 
         /**
@@ -759,9 +750,10 @@
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
          */
+        @CallerSensitive
         public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
             MemberName field = resolveOrFail(REF_getField, refc, name, type);
-            checkSecurityManager(refc, field);  // stack walk magic: do not refactor
+            checkSecurityManager(refc, field, Reflection.getCallerClass());
             return getDirectField(REF_getField, refc, field);
         }
 
@@ -782,9 +774,10 @@
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
          */
+        @CallerSensitive
         public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
             MemberName field = resolveOrFail(REF_putField, refc, name, type);
-            checkSecurityManager(refc, field);  // stack walk magic: do not refactor
+            checkSecurityManager(refc, field, Reflection.getCallerClass());
             return getDirectField(REF_putField, refc, field);
         }
 
@@ -804,9 +797,10 @@
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
          */
+        @CallerSensitive
         public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
             MemberName field = resolveOrFail(REF_getStatic, refc, name, type);
-            checkSecurityManager(refc, field);  // stack walk magic: do not refactor
+            checkSecurityManager(refc, field, Reflection.getCallerClass());
             return getDirectField(REF_getStatic, refc, field);
         }
 
@@ -826,9 +820,10 @@
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
          */
+        @CallerSensitive
         public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
             MemberName field = resolveOrFail(REF_putStatic, refc, name, type);
-            checkSecurityManager(refc, field);  // stack walk magic: do not refactor
+            checkSecurityManager(refc, field, Reflection.getCallerClass());
             return getDirectField(REF_putStatic, refc, field);
         }
 
@@ -878,12 +873,14 @@
          *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws NullPointerException if any argument is null
          */
+        @CallerSensitive
         public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
             Class<? extends Object> refc = receiver.getClass(); // may get NPE
             MemberName method = resolveOrFail(REF_invokeSpecial, refc, name, type);
-            checkSecurityManager(refc, method);  // stack walk magic: do not refactor
-            Class<?> callerClass = findBoundCallerClass(method);  // stack walk magic: do not refactor
-            MethodHandle mh = getDirectMethodNoRestrict(REF_invokeSpecial, refc, method, callerClass);
+            Class<?> callerClass = Reflection.getCallerClass();
+            checkSecurityManager(refc, method, callerClass);
+            MethodHandle mh = getDirectMethodNoRestrict(REF_invokeSpecial, refc, method,
+                                                        findBoundCallerClass(method, callerClass));
             return mh.bindReceiver(receiver).setVarargs(method);
         }
 
@@ -908,13 +905,14 @@
          *                                is set and {@code asVarargsCollector} fails
          * @throws NullPointerException if the argument is null
          */
+        @CallerSensitive
         public MethodHandle unreflect(Method m) throws IllegalAccessException {
             MemberName method = new MemberName(m);
             byte refKind = method.getReferenceKind();
             if (refKind == REF_invokeSpecial)
                 refKind = REF_invokeVirtual;
             assert(method.isMethod());
-            Class<?> callerClass = findBoundCallerClass(method);  // stack walk magic: do not refactor
+            Class<?> callerClass = findBoundCallerClass(method, Reflection.getCallerClass());
             Lookup lookup = m.isAccessible() ? IMPL_LOOKUP : this;
             return lookup.getDirectMethod(refKind, method.getDeclaringClass(), method, callerClass);
         }
@@ -940,12 +938,13 @@
          *                                is set and {@code asVarargsCollector} fails
          * @throws NullPointerException if any argument is null
          */
+        @CallerSensitive
         public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
             checkSpecialCaller(specialCaller);
             Lookup specialLookup = this.in(specialCaller);
             MemberName method = new MemberName(m, true);
             assert(method.isMethod());
-            Class<?> callerClass = findBoundCallerClass(method);  // stack walk magic: do not refactor
+            Class<?> callerClass = findBoundCallerClass(method, Reflection.getCallerClass());
             // ignore m.isAccessible:  this is a new kind of access
             return specialLookup.getDirectMethod(REF_invokeSpecial, method.getDeclaringClass(), method, callerClass);
         }
@@ -1050,20 +1049,35 @@
          * 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<?> findBoundCallerClass(MemberName m, Class<?> callerAtEntryPoint) {
             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));
+                               : callerAtEntryPoint);
             }
             return callerClass;
         }
+
+        /**
+         * Determine whether a security manager has an overridden
+         * SecurityManager.checkMemberAccess method.
+         */
+        private boolean isCheckMemberAccessOverridden(SecurityManager sm) {
+            final Class<? extends SecurityManager> cls = sm.getClass();
+            if (cls == SecurityManager.class) return false;
+
+            try {
+                return cls.getMethod("checkMemberAccess", Class.class, int.class).
+                    getDeclaringClass() != SecurityManager.class;
+            } catch (NoSuchMethodException e) {
+                throw new InternalError("should not reach here");
+            }
+        }
+
         /**
          * Perform necessary <a href="MethodHandles.Lookup.html#secmgr">access checks</a>.
          * Determines a trustable caller class to compare with refc, the symbolic reference class.
@@ -1071,46 +1085,55 @@
          * 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) {
+        void checkSecurityManager(Class<?> refc, MemberName m, Class<?> caller) {
             SecurityManager smgr = System.getSecurityManager();
             if (smgr == null)  return;
             if (allowedModes == TRUSTED)  return;
+
+            final boolean overridden = isCheckMemberAccessOverridden(smgr);
             // Step 1:
-            smgr.checkMemberAccess(refc, Member.PUBLIC);
+            {
+                // Default policy is to allow Member.PUBLIC; no need to check
+                // permission if SecurityManager is the default implementation
+                final int which = Member.PUBLIC;
+                final Class<?> clazz = refc;
+                if (overridden) {
+                    // Don't refactor; otherwise break the stack depth for
+                    // checkMemberAccess of subclasses of SecurityManager as specified.
+                    smgr.checkMemberAccess(clazz, which);
+                }
+            }
+
             // Step 2:
             Class<?> callerClass = ((allowedModes & PRIVATE) != 0
                                     ? lookupClass  // for strong access modes, no extra check
-                                    // next line does stack walk magic; do not refactor:
-                                    : getCallerClassAtEntryPoint(true));
+                                    : caller);
             if (!VerifyAccess.classLoaderIsAncestor(lookupClass, refc) ||
                 (callerClass != lookupClass &&
                  !VerifyAccess.classLoaderIsAncestor(callerClass, refc)))
                 smgr.checkPackageAccess(VerifyAccess.getPackageName(refc));
+
             // Step 3:
             if (m.isPublic()) return;
             Class<?> defc = m.getDeclaringClass();
-            smgr.checkMemberAccess(defc, Member.DECLARED);  // STACK WALK HERE
+            {
+                // Inline SecurityManager.checkMemberAccess
+                final int which = Member.DECLARED;
+                final Class<?> clazz = defc;
+                if (!overridden) {
+                    if (caller.getClassLoader() != clazz.getClassLoader()) {
+                        smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
+                    }
+                } else {
+                    // Don't refactor; otherwise break the stack depth for
+                    // checkMemberAccess of subclasses of SecurityManager as specified.
+                    smgr.checkMemberAccess(clazz, which);
+                }
+            }
+
             // Step 4:
             if (defc != refc)
                 smgr.checkPackageAccess(VerifyAccess.getPackageName(defc));
-
-            // Comment from SM.checkMemberAccess, where which=DECLARED:
-            /*
-             * stack depth of 4 should be the caller of one of the
-             * methods in java.lang.Class that invoke checkMember
-             * access. The stack should look like:
-             *
-             * someCaller                        [3]
-             * java.lang.Class.someReflectionAPI [2]
-             * java.lang.Class.checkMemberAccess [1]
-             * SecurityManager.checkMemberAccess [0]
-             *
-             */
-            // For us it is this stack:
-            // someCaller                        [3]
-            // Lookup.findSomeMember             [2]
-            // Lookup.checkSecurityManager       [1]
-            // SecurityManager.checkMemberAccess [0]
         }
 
         void checkMethod(byte refKind, Class<?> refc, MemberName m) throws IllegalAccessException {