[Nestmates] Add a core reflection API to get nestmate information
Peter Levart
peter.levart at gmail.com
Thu Nov 16 11:37:44 UTC 2017
Hi,
I saw the proposal for a core reflection API to get nestmate information
and here are my thoughts about the decision to filter out class
resolution errors in some methods, discussed below...
On 11/08/2017 01:19 AM, David Holmes wrote:
> Hi Dan,
>
> Thanks for taking a look at this.
>
> On 8/11/2017 9:21 AM, Dan Smith wrote:
>> I would be careful to match the behavior of 'isNestmateOf' to JVMS
>> 5.4.4, rather than define it in terms of 'getNestHost'. In
>> particular, 'c.isNestmateOf(c)' shouldn't need to perform any class
>> loading.
>
> I could short-circuit that case, but why special case this instead of
> just retrieving the nest host (which may need to be loaded)? This is
> not an access-check (for which we bail out very early for the same
> class) but a simple query, so the access-check process per JVMS 5.4.4
> don't directly need to apply.
>
>> For other cases, if class loading errors occur, is the proposal to
>> swallow them and return 'false'? That seems okay, I guess, but is a
>> different conclusion than what we came up with in JVMS.
>
> This was recently discussed on this list under "nestmates spec open
> issues".
>
> http://mail.openjdk.java.net/pipermail/valhalla-spec-experts/2017-October/000386.html
>
>
> John prefers to minimize exceptions for the Java API. :)
Suppose that this is basic reflection API to get nestmate information
and that it will also be used for reflective access checks. For example
in jdk.internal.reflect.Reflection#verifyMemberAccess, which is used
from Method.invoke, Fileld.[get|set], Constructor.newInstance. The most
appropriate method for this check is Class.isNestmateOf(Class). For example:
/**
* Verify access to a member and return {@code true} if it is granted.
*
* @param currentClass the class performing the access
* @param memberClass the declaring class of the member being accessed
* @param targetClass the class of target object if accessing instance
* field or method;
* or the declaring class if accessing constructor;
* or null if accessing static field or method
* @param modifiers the member's access modifiers
* @return {@code true} if access to member is granted
*/
public static boolean verifyMemberAccess(Class<?> currentClass,
Class<?> memberClass,
Class<?> targetClass,
int modifiers)
{
if (currentClass.isNestmateOf(memberClass)) {
// Always succeeds
return true;
}
...
If there is a nest-host class resolution error and isNestmateOf()
returns false for two actual nest-mates that have lost their host,
reflective access is going to throw IllegalAccessException(s) and the
real cause of failure will be hidden. That doesn't help to diagnose the
problem.
If the reasoning behind the decision to hide resolution exceptions is
the desire to mimic OLD behavior, it is not successful, because the OLD
behavior allows private access between two nested classes when "top"
class is missing while then NEW behavior disallows it.
Enter symmetric scheme: instead of one nest-host with a list of
nets-matest + N-1 nest-mates that reference just the nest-host, there
would be N nest-mates with equal status where each lists all others.
With symmetric scheme there would be no need to hide resolution errors
that happen now when access checks are performed between two nest-mates
where neither of them is a nest-host.
If I remember correctly, the symmetric scheme was not considered,
because it would be complicated to resolve inconsistencies and spoofing.
Is it really that important to have a notion of nest-host? Is it really
that important to be able to (consistently) enumerate members of nest?
What problem are nest-mates solving? If the answer is just "access
checking", then the notion of nest-host and nest-mates enumeration are
only artificial implementation details. If those two concepts are really
not necessary ingredients, then a symmetric scheme is a simple
alternative for access checking. The only method needed is this:
Class.isNestmateOf(Class<?> c);
A method that returns true if (this == c || <this and c have each other
in their lists of nest-mates>).
Spoofing is not possible, because both parties must agree and resolution
errors don't need to be hidden, because only resolution of involved
parties is necessary.
You still have reflexive and symmetric, but you loose transitive
property: if A may access B.private and B may access C.private that
doesn't imply that A may access C.private, because the nest-lists of
classes may be:
A: [B]
B: [A, C]
C: [B]
But such cases may only arise as a consequence of faulty assembly.
Compilation (even partial/incremental) can always verify the consistency
of the whole nest and bail-out if needed.
If you really needed to enumerate the members of the nest, the result
would be dependent on the member that was given as an anchor point. A
good-enough algorithm is this:
x.getNestMembers() returns a list composed of x and all classes listed
in x's nest-list that also have x listed in their nest-list(s). For
above inconsistent example that would mean:
A.class.getNestMembers() = [A, B];
B.class.getNestMembers() = [A, B, C];
C.class.getNestMembers() = [B, C];
But then again, if you are asking a trusted class for the list of nest
members, no-one can spoof and add additional members to the list that
was established when that trusted class was compiled.
To wrap-up: symmetric scheme has pros and cons. The question is what is
more important - the consistent view (no matter who you ask, you always
get the same answer) but with potential resolution troubles when
nest-host is not resolvable, or a semi-consistent view (depends on who
you ask, but if you ask a trusted class, you get a trusted answer) but
never need for resolving any additional classes.
Regards, Peter
More information about the valhalla-spec-observers
mailing list