Anonymous classes are not final according to reflection API

Peter Levart peter.levart at gmail.com
Mon Jun 29 15:30:16 UTC 2015



On 06/29/2015 05:00 PM, Srikanth wrote:
>
>
> On Monday 29 June 2015 07:27 PM, Maurizio Cimadamore wrote:
>> Ouch. It seems like this problem existed for a very long time; the 
>> logic used to strip away the ACC_FINAL flag from anon classes is more 
>> or less like this:
>>
>> if (c.isInner() && c.name.isEmpty()) {
>>    flags &= ~FINAL;
>> }
>>
>> Now, isInner() will only return true _if the class has an enclosing 
>> instance_ which is clearly not the case if an anon class appears in a 
>> static context.
>>
>> In other words:
>>
>> class Test{
>>    void finalNo() {
>>       new Object() { }; //ACC_FINAL not set
>>    }
>>
>>     static void finalYesy() {
>>        new Object() { }; //ACC_FINAL set here
>>     }
>> }
>>
>> javap Test\$1.class
>> Compiled from "Main.java"
>> class Test$1 {
>>   final Test this$0;
>>   Test$1(Test);
>> }
>>
>> javap Test\$2.class
>> Compiled from "Main.java"
>> *final* class Test$2 {
>>   Test$2();
>> }
>>
>> I think this should be fixed so that anon classes (regardless of 
>> their context) always have their ACC_FINAL bit unset; this is 
>> probably not very hot from a compatibility perspective (given that 
>> it's been like that for a long time) - but, given the consistency 
>> issue and the fact that reflection cannot handle this anyway, I'd say 
>> we should just make this consistent.
>>
>> Thoughts?
>
> Alex's comment in https://bugs.openjdk.java.net/browse/JDK-4777101, 
> calls out that for anonymous classes declared in static context this 
> bit is set but *seems* to take
> a position of leaving that as it is - Don't know why.

Probably because the final bit is taken into account for default 
serialVersionUID computation. Serializable anonymous classes would 
incompatibly change their serial form if final bit changed. See 
ObjectStreamClass:

     private static long computeDefaultSUID(Class<?> cl) {
         if (!Serializable.class.isAssignableFrom(cl) || 
Proxy.isProxyClass(cl))
         {
             return 0L;
         }

         try {
             ByteArrayOutputStream bout = new ByteArrayOutputStream();
             DataOutputStream dout = new DataOutputStream(bout);

             dout.writeUTF(cl.getName());

             int classMods = cl.getModifiers() &
                 (Modifier.PUBLIC | Modifier.FINAL | // <-- HERE !!!
                  Modifier.INTERFACE | Modifier.ABSTRACT);
     ...


Perhaps the final bit could be changed in anonymous class files of 
version JDK9 if ObjectStreamClass was also changed to accommodate for 
backwards compatibility (by simulating what this bit represents now)? Or 
is this too much complication for too little gain?


Regards, Peter


>
> I also have https://bugs.openjdk.java.net/browse/JDK-8129576 against 
> my name which should closed as not an issue.
>
> Here is a query for ACC_FINAL + anonymous:
>
> https://bugs.openjdk.java.net/issues/?jql=text%20~%20%22ACC_FINAL%20anonymous%22
>
> Srikanth
>
>
>
>
>>
>> Maurizio
>>
>>
>> On 29/06/15 14:49, Maurizio Cimadamore wrote:
>>> More specifically, I was talking about this related issue:
>>>
>>> https://bugs.openjdk.java.net/browse/JDK-8130032
>>>
>>> Popping back one level, I think the issue you are seeing there, is 
>>> that ACC_FINAL is erroneosuly set by javac for diamond anon classes. 
>>> This seems to be against all the documentation that can be found on 
>>> JBS = example:
>>>
>>> https://bugs.openjdk.java.net/browse/JDK-8023945
>>>
>>> I seem to be able to get javac spit out ACC_FINAL reliably on JDK 
>>> 7/8/9 - so the question is - when did it come back? Was it 
>>> deliberate? If so, was reflection ever updated to handle this?
>>>
>>> Maurizio
>>>
>>> On 29/06/15 14:36, Remi Forax wrote:
>>>> yes, right,
>>>> it seems to be a backward compatibility land of mines.
>>>>
>>>> Rémi
>>>>
>>>> On 06/29/2015 03:29 PM, Maurizio Cimadamore wrote:
>>>>> I've closed it as 'not an issue' - this seems to be an outstanding 
>>>>> issue that was fixed for some time and then reverted because of 
>>>>> compatibility problems:
>>>>>
>>>>> https://bugs.openjdk.java.net/browse/JDK-6520152
>>>>>
>>>>> Maurizio
>>>>>
>>>>> On 23/06/15 14:42, Georgiy Rakov wrote:
>>>>>> I've filed the issue JDK-8129576. 
>>>>>> <https://bugs.openjdk.java.net/browse/JDK-8129576>
>>>>>>
>>>>>> There is following issue in JBS: JDK-4777101 
>>>>>> <https://bugs.openjdk.java.net/browse/JDK-4777101> which seems to 
>>>>>> relate to this one.
>>>>>>
>>>>>> Thank you,
>>>>>> Georgiy.
>>>>>>
>>>>>> On 22.06.2015 18:56, Victor Rudometov wrote:
>>>>>>> This seems to be a reflection bug, since ACC_FINAL is present in 
>>>>>>> Test12$1.class file:
>>>>>>>
>>>>>>> final class test.Test12$1 extends test.Test12$Foo<java.lang.Integer>
>>>>>>>   minor version: 0
>>>>>>>   major version: 52
>>>>>>>   flags: ACC_FINAL, ACC_SUPER
>>>>>>>
>>>>>>> Thanks.
>>>>>>> Victor.
>>>>>>>
>>>>>>> On 22-Jun-15 18:35, Remi Forax wrote:
>>>>>>>> I wonder if it's not a reflection bug,
>>>>>>>> did you check with 'javap -c -verbose' the modifiers of the 
>>>>>>>> generated class (something like Test12$1.class) ?
>>>>>>>>
>>>>>>>> cheers,
>>>>>>>> Rémi
>>>>>>>>
>>>>>>>> On 06/22/2015 05:17 PM, Georgiy Rakov wrote:
>>>>>>>>> Hello,
>>>>>>>>>
>>>>>>>>> if I understand correctly according to following assertion 
>>>>>>>>> from JLS 15.9.5 anonymous classes are always final:
>>>>>>>>>
>>>>>>>>>     An anonymous class is always implicitly final (§8.1.1.2).
>>>>>>>>>
>>>>>>>>> But reflection API reports that the class is not final. Namely 
>>>>>>>>> let's consider following code:
>>>>>>>>>
>>>>>>>>>     import java.lang.reflect.Modifier;
>>>>>>>>>
>>>>>>>>>     public class Test12 {
>>>>>>>>>         static class Foo<T> {}
>>>>>>>>>
>>>>>>>>>         public static void main(String argv[]) {
>>>>>>>>>             Foo<Integer> foo = new Foo<>() {};
>>>>>>>>>             if ( (foo.getClass().getModifiers() &
>>>>>>>>>     Modifier.FINAL) != 0 ) {
>>>>>>>>>                 System.out.println("final, modifiers: " +
>>>>>>>>>     foo.getClass().getModifiers());
>>>>>>>>>             } else {
>>>>>>>>>                 System.out.println("not final, modifiers: " +
>>>>>>>>>     foo.getClass().getModifiers());
>>>>>>>>>             }
>>>>>>>>>         }
>>>>>>>>>     }
>>>>>>>>>
>>>>>>>>> On JDK9b69 it reports:
>>>>>>>>>
>>>>>>>>>     not final, modifiers: 0
>>>>>>>>>
>>>>>>>>> Could you please tell if you consider this as a discrepancy 
>>>>>>>>> between spec and javac (VM?) which should be fixed.
>>>>>>>>>
>>>>>>>>> Thank you,
>>>>>>>>> Georgiy.
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20150629/36afacab/attachment-0001.html>


More information about the compiler-dev mailing list