--- a/jdk/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java Thu Feb 09 17:21:47 2017 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java Fri Feb 10 09:04:39 2017 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -28,38 +28,44 @@
import java.lang.annotation.Annotation;
import java.security.AccessController;
-import jdk.internal.misc.VM;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.reflect.ReflectionFactory;
-import sun.security.action.GetPropertyAction;
/**
- * The AccessibleObject class is the base class for Field, Method and
- * Constructor objects. It provides the ability to flag a reflected
- * object as suppressing default Java language access control checks
- * when it is used. The access checks -- <em>module boundaries</em>,
- * public, default (package) access, protected, and private members --
- * are performed when Fields, Methods or Constructors are used to set
- * or get fields, to invoke methods or to create and initialize new
- * instances of classes, respectively. Unlike access control specified
- * in the <cite>The Java™ Language Specification</cite> and
- * <cite>The Java Virtual Machine Specification</cite>, access checks
- * with reflected objects assume {@link Module#canRead readability}.
+ * The {@code AccessibleObject} class is the base class for {@code Field},
+ * {@code Method}, and {@code Constructor} objects (known as <em>reflected
+ * objects</em>). It provides the ability to flag a reflected object as
+ * suppressing checks for Java language access control when it is used. This
+ * permits sophisticated applications with sufficient privilege, such as Java
+ * Object Serialization or other persistence mechanisms, to manipulate objects
+ * in a manner that would normally be prohibited.
*
- * <p>Setting the {@code accessible} flag in a reflected object
- * permits sophisticated applications with sufficient privilege, such
- * as Java Object Serialization or other persistence mechanisms, to
- * manipulate objects in a manner that would normally be prohibited.
- *
- * <p>By default, a reflected object is <em>not</em> accessible.
+ * <p> Java language access control prevents use of private members outside
+ * their class; package access members outside their package; protected members
+ * outside their package or subclasses; and public members outside their
+ * module unless they are declared in an {@link Module#isExported(String,Module)
+ * exported} package and the user {@link Module#canRead reads} their module. By
+ * default, Java language access control is enforced (with one variation) when
+ * {@code Field}s, {@code Method}s, or {@code Constructor}s are used to get or
+ * set fields, to invoke methods, or to create and initialize new instances of
+ * classes, respectively. Every reflected object checks that the code using it
+ * is in an appropriate class, package, or module. </p>
*
- * @see Field
- * @see Method
- * @see Constructor
- * @see ReflectPermission
+ * <p> The one variation from Java language access control is that the checks
+ * by reflected objects assume readability. That is, the module containing
+ * the use of a reflected object is assumed to read the module in which
+ * the underlying field, method, or constructor is declared. </p>
*
+ * <p> Whether the checks for Java language access control can be suppressed
+ * (and thus, whether access can be enabled) depends on whether the reflected
+ * object corresponds to a member in an exported or open package
+ * (see {@link #setAccessible(boolean)}). </p>
+ *
+ * @jls 6.6 Access Control
* @since 1.2
+ * @revised 9
+ * @spec JPMS
*/
public class AccessibleObject implements AnnotatedElement {
@@ -78,15 +84,11 @@
/**
* Convenience method to set the {@code accessible} flag for an
- * array of objects with a single security check (for efficiency).
+ * array of reflected objects with a single security check (for efficiency).
*
- * <p>This method cannot be used to enable access to an object that is a
- * {@link Member member} of a class in a different module to the caller and
- * where the class is in a package that is not exported to the caller's
- * module. Additionally, if the member is non-public or its declaring
- * class is non-public, then this method can only be used to enable access
- * if the package is {@link Module#isOpen(String,Module) open} to at least
- * the caller's module.
+ * <p> This method may be used to enable access to all reflected objects in
+ * the array when access to each reflected object can be enabled as
+ * specified by {@link #setAccessible(boolean) setAccessible(boolean)}. </p>
*
* <p>If there is a security manager, its
* {@code checkPermission} method is first called with a
@@ -99,10 +101,15 @@
* @param array the array of AccessibleObjects
* @param flag the new value for the {@code accessible} flag
* in each object
- * @throws InaccessibleObjectException if access cannot be enabled
- * @throws SecurityException if the request is denied.
+ * @throws InaccessibleObjectException if access cannot be enabled for all
+ * objects in the array
+ * @throws SecurityException if the request is denied by the security manager
+ * or an element in the array is a constructor for {@code
+ * java.lang.Class}
* @see SecurityManager#checkPermission
* @see ReflectPermission
+ * @revised 9
+ * @spec JPMS
*/
@CallerSensitive
public static void setAccessible(AccessibleObject[] array, boolean flag) {
@@ -120,41 +127,143 @@
}
/**
- * Set the {@code accessible} flag for this object to
+ * Set the {@code accessible} flag for this reflected object to
* the indicated boolean value. A value of {@code true} indicates that
- * the reflected object should suppress Java language access
- * checking when it is used. A value of {@code false} indicates
- * that the reflected object should enforce Java language access checks
- * while assuming readability (as noted in the class description).
+ * the reflected object should suppress checks for Java language access
+ * control when it is used. A value of {@code false} indicates that
+ * the reflected object should enforce checks for Java language access
+ * control when it is used, with the variation noted in the class description.
+ *
+ * <p> This method may be used by a caller in class {@code C} to enable
+ * access to a {@link Member member} of {@link Member#getDeclaringClass()
+ * declaring class} {@code D} if any of the following hold: </p>
+ *
+ * <ul>
+ * <li> {@code C} and {@code D} are in the same module. </li>
+ *
+ * <li> The member is {@code public} and {@code D} is {@code public} in
+ * a package that the module containing {@code D} {@link
+ * Module#isExported(String,Module) exports} to at least the module
+ * containing {@code C}. </li>
*
- * <p>This method cannot be used to enable access to an object that is a
- * {@link Member member} of a class in a different module to the caller and
- * where the class is in a package that is not exported to the caller's
- * module. Additionally, if the member is non-public or its declaring
- * class is non-public, then this method can only be used to enable access
- * if the package is {@link Module#isOpen(String,Module) open} to at least
- * the caller's module.
+ * <li> The member is {@code protected} {@code static}, {@code D} is
+ * {@code public} in a package that the module containing {@code D}
+ * exports to at least the module containing {@code C}, and {@code C}
+ * is a subclass of {@code D}. </li>
*
- * <p>If there is a security manager, its
+ * <li> {@code D} is in a package that the module containing {@code D}
+ * {@link Module#isOpen(String,Module) opens} to at least the module
+ * containing {@code C}.
+ * All packages in unnamed and open modules are open to all modules and
+ * so this method always succeeds when {@code D} is in an unnamed or
+ * open module. </li>
+ * </ul>
+ *
+ * <p> This method cannot be used to enable access to private members,
+ * members with default (package) access, protected instance members, or
+ * protected constructors when the declaring class is in a different module
+ * to the caller and the package containing the declaring class is not open
+ * to the caller's module. </p>
+ *
+ * <p> If there is a security manager, its
* {@code checkPermission} method is first called with a
* {@code ReflectPermission("suppressAccessChecks")} permission.
*
* @param flag the new value for the {@code accessible} flag
* @throws InaccessibleObjectException if access cannot be enabled
- * @throws SecurityException if the request is denied
- * @see SecurityManager#checkPermission
- * @see ReflectPermission
+ * @throws SecurityException if the request is denied by the security manager
+ * @see #trySetAccessible
* @see java.lang.invoke.MethodHandles#privateLookupIn
+ * @revised 9
+ * @spec JPMS
*/
public void setAccessible(boolean flag) {
AccessibleObject.checkPermission();
setAccessible0(flag);
}
- void setAccessible0(boolean flag) {
+ /**
+ * Sets the accessible flag and returns the new value
+ */
+ boolean setAccessible0(boolean flag) {
this.override = flag;
+ return flag;
}
+ /**
+ * Set the {@code accessible} flag for this reflected object to {@code true}
+ * if possible. This method sets the {@code accessible} flag, as if by
+ * invoking {@link #setAccessible(boolean) setAccessible(true)}, and returns
+ * the possibly-updated value for the {@code accessible} flag. If access
+ * cannot be enabled, i.e. the checks or Java language access control cannot
+ * be suppressed, this method returns {@code false} (as opposed to {@code
+ * setAccessible(true)} throwing {@code InaccessibleObjectException} when
+ * it fails).
+ *
+ * <p> This method is a no-op if the {@code accessible} flag for
+ * this reflected object is {@code true}.
+ *
+ * <p> For example, a caller can invoke {@code trySetAccessible}
+ * on a {@code Method} object for a private instance method
+ * {@code p.T::privateMethod} to suppress the checks for Java language access
+ * control when the {@code Method} is invoked.
+ * If {@code p.T} class is in a different module to the caller and
+ * package {@code p} is open to at least the caller's module,
+ * the code below successfully sets the {@code accessible} flag
+ * to {@code true}.
+ *
+ * <pre>
+ * {@code
+ * p.T obj = ....; // instance of p.T
+ * :
+ * Method m = p.T.class.getDeclaredMethod("privateMethod");
+ * if (m.trySetAccessible()) {
+ * m.invoke(obj);
+ * } else {
+ * // package p is not opened to the caller to access private member of T
+ * ...
+ * }
+ * }</pre>
+ *
+ * <p> If there is a security manager, its {@code checkPermission} method
+ * is first called with a {@code ReflectPermission("suppressAccessChecks")}
+ * permission. </p>
+ *
+ * @return {@code true} if the {@code accessible} flag is set to {@code true};
+ * {@code false} if access cannot be enabled.
+ * @throws SecurityException if the request is denied by the security manager
+ *
+ * @since 9
+ * @spec JPMS
+ * @see java.lang.invoke.MethodHandles#privateLookupIn
+ */
+ @CallerSensitive
+ public final boolean trySetAccessible() {
+ AccessibleObject.checkPermission();
+
+ if (override == true) return true;
+
+ // if it's not a Constructor, Method, Field then no access check
+ if (!Member.class.isInstance(this)) {
+ return setAccessible0(true);
+ }
+
+ // does not allow to suppress access check for Class's constructor
+ Class<?> declaringClass = ((Member) this).getDeclaringClass();
+ if (declaringClass == Class.class && this instanceof Constructor) {
+ return false;
+ }
+
+ if (checkCanSetAccessible(Reflection.getCallerClass(),
+ declaringClass,
+ false)) {
+ return setAccessible0(true);
+ } else {
+ return false;
+ }
+ }
+
+
/**
* If the given AccessibleObject is a {@code Constructor}, {@code Method}
* or {@code Field} then checks that its declaring class is in a package
@@ -164,22 +273,29 @@
// do nothing, needs to be overridden by Constructor, Method, Field
}
+
void checkCanSetAccessible(Class<?> caller, Class<?> declaringClass) {
+ checkCanSetAccessible(caller, declaringClass, true);
+ }
+
+ private boolean checkCanSetAccessible(Class<?> caller,
+ Class<?> declaringClass,
+ boolean throwExceptionIfDenied) {
Module callerModule = caller.getModule();
Module declaringModule = declaringClass.getModule();
- if (callerModule == declaringModule) return;
- if (callerModule == Object.class.getModule()) return;
- if (!declaringModule.isNamed()) return;
+ if (callerModule == declaringModule) return true;
+ if (callerModule == Object.class.getModule()) return true;
+ if (!declaringModule.isNamed()) return true;
// package is open to caller
String pn = packageName(declaringClass);
if (declaringModule.isOpen(pn, callerModule)) {
- printStackTraceIfOpenedReflectively(declaringModule, pn, callerModule);
- return;
+ dumpStackIfOpenedReflectively(declaringModule, pn, callerModule);
+ return true;
}
- // package is exported to caller and class/member is public
+ // package is exported to caller
boolean isExported = declaringModule.isExported(pn, callerModule);
boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers());
int modifiers;
@@ -188,48 +304,72 @@
} else {
modifiers = ((Field) this).getModifiers();
}
- boolean isMemberPublic = Modifier.isPublic(modifiers);
- if (isExported && isClassPublic && isMemberPublic) {
- printStackTraceIfExportedReflectively(declaringModule, pn, callerModule);
- return;
+ if (isExported && isClassPublic) {
+
+ // member is public
+ if (Modifier.isPublic(modifiers)) {
+ dumpStackIfExportedReflectively(declaringModule, pn, callerModule);
+ return true;
+ }
+
+ // member is protected-static
+ if (Modifier.isProtected(modifiers)
+ && Modifier.isStatic(modifiers)
+ && isSubclassOf(caller, declaringClass)) {
+ dumpStackIfExportedReflectively(declaringModule, pn, callerModule);
+ return true;
+ }
}
- // not accessible
- String msg = "Unable to make ";
- if (this instanceof Field)
- msg += "field ";
- msg += this + " accessible: " + declaringModule + " does not \"";
- if (isClassPublic && isMemberPublic)
- msg += "exports";
- else
- msg += "opens";
- msg += " " + pn + "\" to " + callerModule;
- InaccessibleObjectException e = new InaccessibleObjectException(msg);
- if (Reflection.printStackTraceWhenAccessFails()) {
- e.printStackTrace(System.err);
+ if (throwExceptionIfDenied) {
+ // not accessible
+ String msg = "Unable to make ";
+ if (this instanceof Field)
+ msg += "field ";
+ msg += this + " accessible: " + declaringModule + " does not \"";
+ if (isClassPublic && Modifier.isPublic(modifiers))
+ msg += "exports";
+ else
+ msg += "opens";
+ msg += " " + pn + "\" to " + callerModule;
+ InaccessibleObjectException e = new InaccessibleObjectException(msg);
+ if (Reflection.printStackTraceWhenAccessFails()) {
+ e.printStackTrace(System.err);
+ }
+ throw e;
}
- throw e;
+ return false;
}
- private void printStackTraceIfOpenedReflectively(Module module,
- String pn,
- Module other) {
- printStackTraceIfExposedReflectively(module, pn, other, true);
+ private boolean isSubclassOf(Class<?> queryClass, Class<?> ofClass) {
+ while (queryClass != null) {
+ if (queryClass == ofClass) {
+ return true;
+ }
+ queryClass = queryClass.getSuperclass();
+ }
+ return false;
}
- private void printStackTraceIfExportedReflectively(Module module,
- String pn,
- Module other) {
- printStackTraceIfExposedReflectively(module, pn, other, false);
+ private void dumpStackIfOpenedReflectively(Module module,
+ String pn,
+ Module other) {
+ dumpStackIfExposedReflectively(module, pn, other, true);
}
- private void printStackTraceIfExposedReflectively(Module module,
- String pn,
- Module other,
- boolean open)
+ private void dumpStackIfExportedReflectively(Module module,
+ String pn,
+ Module other) {
+ dumpStackIfExposedReflectively(module, pn, other, false);
+ }
+
+ private void dumpStackIfExposedReflectively(Module module,
+ String pn,
+ Module other,
+ boolean open)
{
if (Reflection.printStackTraceWhenAccessSucceeds()
- && !module.isStaticallyExportedOrOpen(pn, other, open))
+ && !module.isStaticallyExportedOrOpen(pn, other, open))
{
String msg = other + " allowed to invoke setAccessible on ";
if (this instanceof Field)
@@ -256,15 +396,100 @@
}
/**
- * Get the value of the {@code accessible} flag for this object.
+ * Get the value of the {@code accessible} flag for this reflected object.
*
* @return the value of the object's {@code accessible} flag
+ *
+ * @deprecated
+ * This method is deprecated because its name hints that it checks
+ * if the reflected object is accessible when it actually indicates
+ * if the checks for Java language access control are suppressed.
+ * This method may return {@code false} on a reflected object that is
+ * accessible to the caller. To test if this reflected object is accessible,
+ * it should use {@link #canAccess(Object)}.
+ *
+ * @revised 9
*/
+ @Deprecated(since="9")
public boolean isAccessible() {
return override;
}
/**
+ * Test if the caller can access this reflected object. If this reflected
+ * object corresponds to an instance method or field then this method tests
+ * if the caller can access the given {@code obj} with the reflected object.
+ * For instance methods or fields then the {@code obj} argument must be an
+ * instance of the {@link Member#getDeclaringClass() declaring class}. For
+ * static members and constructors then {@code obj} must be {@code null}.
+ *
+ * <p> This method returns {@code true} if the {@code accessible} flag
+ * is set to {@code true}, i.e. the checks for Java language access control
+ * are suppressed, or if the caller can access the member as
+ * specified in <cite>The Java™ Language Specification</cite>,
+ * with the variation noted in the class description. </p>
+ *
+ * @param obj an instance object of the declaring class of this reflected
+ * object if it is an instance method or field
+ *
+ * @return {@code true} if the caller can access this reflected object.
+ *
+ * @throws IllegalArgumentException
+ * <ul>
+ * <li> if this reflected object is a static member or constructor and
+ * the given {@code obj} is non-{@code null}, or </li>
+ * <li> if this reflected object is an instance method or field
+ * and the given {@code obj} is {@code null} or of type
+ * that is not a subclass of the {@link Member#getDeclaringClass()
+ * declaring class} of the member.</li>
+ * </ul>
+ *
+ * @since 9
+ * @spec JPMS
+ * @jls 6.6 Access Control
+ * @see #trySetAccessible
+ * @see #setAccessible(boolean)
+ */
+ @CallerSensitive
+ public final boolean canAccess(Object obj) {
+ if (!Member.class.isInstance(this)) {
+ return override;
+ }
+
+ Class<?> declaringClass = ((Member) this).getDeclaringClass();
+ int modifiers = ((Member) this).getModifiers();
+ if (!Modifier.isStatic(modifiers) &&
+ (this instanceof Method || this instanceof Field)) {
+ if (obj == null) {
+ throw new IllegalArgumentException("null object for " + this);
+ }
+ // if this object is an instance member, the given object
+ // must be a subclass of the declaring class of this reflected object
+ if (!declaringClass.isAssignableFrom(obj.getClass())) {
+ throw new IllegalArgumentException("object is not an instance of "
+ + declaringClass.getName());
+ }
+ } else if (obj != null) {
+ throw new IllegalArgumentException("non-null object for " + this);
+ }
+
+ // access check is suppressed
+ if (override) return true;
+
+ Class<?> caller = Reflection.getCallerClass();
+ Class<?> targetClass;
+ if (this instanceof Constructor) {
+ targetClass = declaringClass;
+ } else {
+ targetClass = Modifier.isStatic(modifiers) ? null : obj.getClass();
+ }
+ return Reflection.verifyMemberAccess(caller,
+ declaringClass,
+ targetClass,
+ modifiers);
+ }
+
+ /**
* Constructor: only used by the Java Virtual Machine.
*/
protected AccessibleObject() {}