RFR: JDK-8216558: Lookup.unreflectSetter(Field) fails to throw IllegalAccessException for final fields

David Holmes david.holmes at oracle.com
Tue Jan 15 02:01:43 UTC 2019


Hi John,

On 15/01/2019 11:47 am, John Rose wrote:
> On Jan 11, 2019, at 4:53 PM, David Holmes <david.holmes at oracle.com 
> <mailto:david.holmes at oracle.com>> wrote:
>>
>> I think there is a problem knowing when "access check" means just 
>> access check and when it means "access check plus the special hack for 
>> setting final fields". I'm not reading any final field semantics into 
>> the existing text, because bytecode does not know about setAccessible 
>> and final fields.
> 
> The "bytecode behavior" of an unreflectSetter MH is clearly documented
> by appealing to a call to Field::set.  That is a pre-existing specification
> that proves that the observed behavior for statics is a bug, and for
> non-statics is a feature.  No CSR is needed, and, David, you are simply
> looking in the wrong places.  I admit that the right place is tricky to 
> find.

Thanks John - yes I missed that and thought it appealed to direct 
bytecode (putField/putStatic etc).

Cheers,
David
-----

> 
>> I can see an argument to be made for the case where 
>> setAccessible(true) has been called to disable language level access 
>> checks, and you want the MH to follow the same access rules. But that 
>> does not to me imply that the ability to write to a final field should 
>> be part of that. If that is intended then it needs to be very, very 
>> clearly spelt out.
> 
> Yes, the documentation should be improved, so that email threads like
> this won't be necessary to tease out the meaning of the spec.  I placed
> a proposal for *clarifying* (not *changing*) it in the bug comments.
> 
>> And as per my other email there is already a problem with the bytecode 
>> equivalence of MH semantics and final fields, because you can set 
>> final fields in <init> or <clinit> (though only once). I don't think 
>> MH captures that - nor can it.
> 
> That's true.  The most it can do is capture the effect, if any, of
> setAcc(true) on a reflected Field.
> 
>> Which means we can't defer to bytecode semantics here but rather have 
>> to very clearly define how MH interact with final fields.
> 
> And that's already done, except for the "clearly" part.
> 
> Thanks for raising this issue!
> 
> — John
> 
> diff --git 
> a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 
> b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
> --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
> +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
> @@ -421,6 +421,10 @@
>        * because the desired class member is missing, or because the
>        * desired class member is not accessible to the lookup class, or
>        * because the lookup object is not trusted enough to access the 
> member.
> +     * In the case of a field setter function on a {@code final} field,
> +     * finality enforcement is treated as a kind of access control,
> +     * and the lookup will fail, except in special cases of
> +     * {@link Lookup#unreflectSetter Lookup.unreflectSetter}.
>        * In any of these cases, a {@code ReflectiveOperationException} 
> will be
>        * thrown from the attempted lookup.  The exact class will be one of
>        * the following:
> @@ -1436,6 +1440,7 @@
>            * @return a method handle which can store values into the field
>            * @throws NoSuchFieldException if the field does not exist
>            * @throws IllegalAccessException if access checking fails, or 
> if the field is {@code static}
> +         *                                or {@code final}
>            * @exception SecurityException if a security manager is 
> present and it
>            *                              <a 
> href="MethodHandles.Lookup.html#secmgr">refuses access</a>
>            * @throws NullPointerException if any argument is null
> @@ -1559,6 +1564,7 @@
>            * @return a method handle which can store values into the field
>            * @throws NoSuchFieldException if the field does not exist
>            * @throws IllegalAccessException if access checking fails, or 
> if the field is not {@code static}
> +         *                                or is {@code final}
>            * @exception SecurityException if a security manager is 
> present and it
>            *                              <a 
> href="MethodHandles.Lookup.html#secmgr">refuses access</a>
>            * @throws NullPointerException if any argument is null
> @@ -1868,19 +1874,29 @@
>           /**
>            * Produces a method handle giving write access to a reflected 
> field.
>            * The type of the method handle will have a void return type.
> -         * If the field is static, the method handle will take a single
> +         * If the field is {@code static}, the method handle will take 
> a single
>            * argument, of the field's value type, the value to be stored.
>            * Otherwise, the two arguments will be the instance containing
>            * the field, and the value to be stored.
> -         * If the field's {@code accessible} flag is not set,
> +         * If the {@code Field} object's {@code accessible} flag is not 
> set,
>            * access checking is performed immediately on behalf of the 
> lookup class.
>            * <p>
> -         * If the field is static, and
> +         * If the field is {@code final}, write access will not be
> +         * allowed and access checking will fail, except under certain
> +         * narrow circumstances documented for {@link Field#set Field.set}.
> +         * A method handle is returned only if a corresponding call to
> +         * the {@code Field} object's {@code set} method could return
> +         * normally.  In particular, fields which are both {@code static}
> +         * and {@code final} may never be set.
> +         * <p>
> +         * If the field is {@code static}, and
>            * if the returned method handle is invoked, the field's class 
> will
>            * be initialized, if it has not already been initialized.
>            * @param f the reflected field
>            * @return a method handle which can store values into the 
> reflected field
> -         * @throws IllegalAccessException if access checking fails
> +         * @throws IllegalAccessException if access checking fails,
> +         *         or if the field is {@code final} and write access
> +         *         is not enabled on the {@code Field} object
>            * @throws NullPointerException if the argument is null
>            */
>           public MethodHandle unreflectSetter(Field f) throws 
> IllegalAccessException {
> 


More information about the core-libs-dev mailing list