JDK-8189335: NPE in Lower due to class name clash

Vicente Romero vicente.romero at oracle.com
Mon Feb 26 17:35:51 UTC 2018



On 02/24/2018 03:39 PM, B. Blaser wrote:
> Hi Vicente,
>
> Thanks for your answer.
>
> On 23 February 2018 at 22:24, Vicente Romero <vicente.romero at oracle.com> wrote:
>> Hi Bernard,
>>
>> Usually when we touch this area of the compiler we do the following test:
>> build the whole JDK, another big corpus could make it too, with and without
>> the fix applied and then we compare the class files obtained, if no new tag
>> classes are generated for cases where they shouldn't and if there are no
>> unjustified changes to the rest of the classes, then we are more confident
>> about the fix.
> You're right, this would be a very good test but I probably won't have
> time to do that soon.
> I also know that all jdk tests have to pass successfully and I only
> ran javac tests, sorry...
> However, I hope my work will still be helpful.

sure, I understand, I will take care of it. And yes your work is very 
helpful,

Thanks,
Vicente

>
> Bernard
>
>> Thanks,
>> Vicente
>>
>>
>> On 02/22/2018 03:09 PM, B. Blaser wrote:
>>> On 21 February 2018 at 16:58, B. Blaser <bsrbnd at gmail.com> wrote:
>>>> On 19 February 2018 at 15:53, B. Blaser <bsrbnd at gmail.com> wrote:
>>>>> On 19 February 2018 at 00:42, B. Blaser <bsrbnd at gmail.com> wrote:
>>>>>> Hi,
>>>>>>
>>>>>> As explained in JDK-8189335, the following example crashes javac with
>>>>>> a NPE in Lower:
>>>>>>
>>>>>> class R {
>>>>>>       private class R$1 {}
>>>>>>       void f() {
>>>>>>           new R$1();
>>>>>>       }
>>>>>> }
>>>>>> class R$1 {}
>>>> Some precisions related to my laconic explanation about the problem and
>>>> the fix.
>>>>
>>>> All starts in 'Lower.accessConstructorTag()' when searching for the
>>>> anonymous class 'R$1':
>>>>
>>>>
>>>> http://hg.openjdk.java.net/jdk/jdk/file/02404e27d356/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java#l1238
>>>>
>>>> The top level class 'R$1' (not anonymous) is found and wrongly added
>>>> to 'accessConstrTags'.
>>>>
>>>> Then, when checking the access constructor tags in
>>>> 'Lower.checkAccessConstructorTags()', the 'outermostClass()' of the
>>>> top level class 'R$1' is requested:
>>>>
>>>>
>>>> http://hg.openjdk.java.net/jdk/jdk/file/02404e27d356/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java#l778
>>>>
>>>> 'R$1' being a top level class, its outermost class doesn't exist,
>>>> causing the NPE in 'Lower.makeEmptyClass()'.
>>> Errata: 'R$1' being a top level class, its outermost class definition
>>> ('R$1') doesn't exist within 'R', causing the NPE in
>>> 'Lower.makeEmptyClass()':
>>>
>>>
>>> http://hg.openjdk.java.net/jdk/jdk/file/02404e27d356/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java#l658
>>>
>>>> The fix I suggested two days ago (here under) is then quite
>>>> straightforward. If a class 'R$1' exists but isn't anonymous, it has
>>>> to be skipped and a fresh anonymous class 'R$2' has to be created as
>>>> access constructor tag:
>>>>
>>>>
>>>> http://hg.openjdk.java.net/jdk/jdk/file/02404e27d356/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java#l1239
>>>>
>>>> Nothing seems to be broken and the provided example compiles
>>>> successfully.
>>> Here is a small variant of the previous patch that makes sure to reuse
>>> the same tag when multiple private constructors are present and 'R$1'
>>> isn't anonymous, for example:
>>>
>>> class R {
>>>       private class A {}
>>>       private class B {}
>>>       void f() {
>>>           new A();
>>>           new B();
>>>       }
>>> }
>>> class R$1{}
>>>
>>> Any comment or review is welcome!
>>>
>>> Cheers,
>>> Bernard
>>>
>>>
>>> diff --git
>>> a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
>>> b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
>>> --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
>>> +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
>>> @@ -1232,15 +1232,19 @@
>>>        ClassSymbol accessConstructorTag() {
>>>            ClassSymbol topClass = currentClass.outermostClass();
>>>            ModuleSymbol topModle = topClass.packge().modle;
>>> -        Name flatname = names.fromString("" + topClass.getQualifiedName()
>>> +
>>> -                                         target.syntheticNameChar() +
>>> -                                         "1");
>>> -        ClassSymbol ctag = chk.getCompiled(topModle, flatname);
>>> -        if (ctag == null)
>>> -            ctag = makeEmptyClass(STATIC | SYNTHETIC, topClass).sym;
>>> -        // keep a record of all tags, to verify that all are
>>> generated as required
>>> -        accessConstrTags = accessConstrTags.prepend(ctag);
>>> -        return ctag;
>>> +        for (int i = 1; ; i++) {
>>> +            Name flatname = names.fromString("" +
>>> topClass.getQualifiedName() +
>>> +                                            target.syntheticNameChar() +
>>> +                                            i);
>>> +            ClassSymbol ctag = chk.getCompiled(topModle, flatname);
>>> +            if (ctag == null)
>>> +                ctag = makeEmptyClass(STATIC | SYNTHETIC, topClass).sym;
>>> +            else if (!ctag.isAnonymous())
>>> +                continue;
>>> +            // keep a record of all tags, to verify that all are
>>> generated as required
>>> +            accessConstrTags = accessConstrTags.prepend(ctag);
>>> +            return ctag;
>>> +        }
>>>        }
>>>
>>>        /** Add all required access methods for a private symbol to
>>> enclosing class.
>>>
>>>> What do you think (I hope this is clearer)?
>>>>
>>>> Thanks,
>>>> Bernard
>>>>
>>>>
>>>>> diff --git
>>>>> a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
>>>>> b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
>>>>> --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
>>>>> +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
>>>>> @@ -1236,7 +1236,7 @@
>>>>>                                             target.syntheticNameChar() +
>>>>>                                             "1");
>>>>>            ClassSymbol ctag = chk.getCompiled(topModle, flatname);
>>>>> -        if (ctag == null)
>>>>> +        if (ctag == null || !ctag.isAnonymous())
>>>>>                ctag = makeEmptyClass(STATIC | SYNTHETIC, topClass).sym;
>>>>>            // keep a record of all tags, to verify that all are
>>>>> generated as required
>>>>>            accessConstrTags = accessConstrTags.prepend(ctag);
>>



More information about the compiler-dev mailing list