1 /*
   2  * Copyright 2008-2009 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package impl.java.dyn.util;
  27 
  28 import impl.java.dyn.MemberName;
  29 import java.lang.reflect.Method;
  30 import java.lang.reflect.Modifier;
  31 import java.util.Arrays;
  32 
  33 /**
  34  * This class centralizes information about the JVM's linkage access control.
  35  * @author jrose
  36  */
  37 public class VerifyAccess {
  38 
  39     private VerifyAccess() { }  // cannot instantiate
  40 
  41     /**
  42      * Evaluate the JVM linkage rules for access to the given method on behalf of caller.
  43      * Return non-null if and only if the given accessing class has at least partial
  44      * privileges to invoke the given method.  The return value {@code Object.class}
  45      * denotes unlimited privileges.
  46      * <p>
  47      * Some circumstances require an additional check on the
  48      * leading parameter (the receiver) of the method, if it is non-static.
  49      * In the case of {@code invokespecial} ({@code doDispatch} is false),
  50      * the leading parameter must be the accessing class or a subclass.
  51      * In the case of a call to a {@code protected} method outside the same
  52      * package, the same constraint applies.
  53      * @param m the proposed callee
  54      * @param doDispatch if false, a non-static m will be invoked as if by {@code invokespecial}
  55      * @param accessingClass the class for which the access check is being made
  56      * @return null if the method is not accessible, else a receiver type constraint, else {@code Object.class}
  57      */
  58     public static Class<?> isAccessible(Class<?> defc, int mods,
  59             boolean doDispatch, Class<?> accessingClass) {
  60         if (!isAccessible(defc, accessingClass))
  61             return null;
  62         Class<?> constraint = Object.class;
  63         if (!doDispatch && !Modifier.isStatic(mods)) {
  64             constraint = accessingClass;
  65         }
  66         if (Modifier.isPublic(mods))
  67             return constraint;
  68         if (Modifier.isPrivate(mods))
  69             return (defc == accessingClass) ? constraint : null;
  70         if (isSamePackage(defc, accessingClass))
  71             return constraint;
  72         if (Modifier.isProtected(mods) && defc.isAssignableFrom(accessingClass))
  73             return constraint;
  74         // else it is private or package scoped, and not close enough
  75         return null;
  76     }
  77 
  78     /**
  79      * Evaluate the JVM linkage rules for access to the given class on behalf of caller.
  80      */
  81     public static boolean isAccessible(Class<?> refc, Class<?> accessingClass) {
  82         int mods = refc.getModifiers();
  83         if (Modifier.isPublic(mods))
  84             return true;
  85         if (isSamePackage(accessingClass, refc))
  86             return true;
  87         return false;
  88     }
  89 
  90     /**
  91      * Test if two classes have the same class loader and package qualifier.
  92      * @param class1
  93      * @param class2
  94      * @return whether they are in the same package
  95      */
  96     public static boolean isSamePackage(Class<?> class1, Class<?> class2) {
  97         if (class1 == class2)
  98             return true;
  99         if (class1.getClassLoader() != class2.getClassLoader())
 100             return false;
 101         String name1 = class1.getName(), name2 = class2.getName();
 102         int dot = name1.lastIndexOf('.');
 103         if (dot != name2.lastIndexOf('.'))
 104             return false;
 105         for (int i = 0; i < dot; i++) {
 106             if (name1.charAt(i) != name2.charAt(i))
 107                 return false;
 108         }
 109         return true;
 110     }
 111 
 112     /**
 113      * Test if two classes are defined as part of the same package member (top-level class).
 114      * If this is true, they can share private access with each other.
 115      * @param class1
 116      * @param class2
 117      * @return whether they are identical or nested together
 118      */
 119     public static boolean isSamePackageMember(Class<?> class1, Class<?> class2) {
 120         if (class1 == class2)
 121             return true;
 122         if (!isSamePackage(class1, class2))
 123             return false;
 124         if (getOutermostEnclosingClass(class1) != getOutermostEnclosingClass(class2))
 125             return false;
 126         return true;
 127     }
 128 
 129     private static Class<?> getOutermostEnclosingClass(Class<?> c) {
 130         Class<?> pkgmem = c;
 131         for (Class<?> enc = c; (enc = enc.getEnclosingClass()) != null; )
 132             pkgmem = enc;
 133         return pkgmem;
 134     }
 135 }