Breaking binary compatibility due to adding private method to an interface compiled inconsistently

Alex Buckley alex.buckley at oracle.com
Wed Oct 19 17:54:00 UTC 2016


I am uneasy about ch.13's treatment of private methods in general -- see 
JDK-8043703 -- but the particular case outlined below would never be a 
binary-compatible change because it's not a single change to a single 
compilation unit. The intent of ch.13 is to cover inconsistent separate 
compilation of one codebase where a recompilation is actually possible, 
not to cover consistent separate compilation of multiple codebases that 
are then combined.

Alex

On 10/19/2016 5:16 AM, David M. Lloyd wrote:
> Forgive me, but that's quite an incredible response.  How many
> recompilations equals a hex edit?  You can just as well say that Ch.13
> doesn't apply to any changes ever because any recompilation is
> tantamount to a hex edit, which is clearly counter to the intent of the
> chapter.
>
> It is completely reasonable to have two separate libraries in a
> configuration that matches the description, and to have one of those
> libraries be updated to a new version, which would cause the effect
> quite trivially from the user's perspective.  This is not an obscure
> case at all.  All he did was compact this into a simple
> easy-to-understand test case, which yes might be more obscure but also
> serves to illustrate the problem more clearly.
>
> On 10/18/2016 05:52 PM, Alex Buckley wrote:
>> I understand that you're going from executing A.class + B.class +
>> Test.class to executing A.class + B.class[MODIFIED] + Test.class.
>> However, because B.class[MODIFIED] was produced by a non-trivial
>> sequence of recompilations, it's tantamount to a hex-edited version of
>> the initial B.class. Ch.13 doesn't describe the binary compatibility of
>> hex edits, only the binary compatibility of a single compilable change
>> to a single compilation unit. I agree with Dan that the binary
>> compatibility of adding a private method to an interface inherently
>> assumes you immediately compile the interface.
>>
>> Alex
>>
>> On 10/18/2016 7:55 AM, Georgiy Rakov wrote:
>>> I'd like to emphasize that in this case preexisting binaries supplied to
>>> JVM during Test.main execution doesn't change and if they don't then,
>>> according to my understanding, it doesn't matter how the modified B
>>> class-file was produced.
>>>
>>> Thank you,
>>> Georgiy.
>>>
>>> On 17.10.2016 21:15, Alex Buckley wrote:
>>>> The key phrase is "with preexisting binaries". Given the initial
>>>> A.class + B.class + Test.class, you get to make ONE change to ONE
>>>> compilation unit that you recompile independently but inconsistently.
>>>> The change you would like to make is adding a private method to B, but
>>>> you can't make that change because of B's superinterface, so no
>>>> independent recompilation is possible, and the question of whether the
>>>> change is binary-incompatible is moot.
>>>>
>>>> Alex
>>>>
>>>> On 10/17/2016 10:26 AM, Georgiy Rakov wrote:
>>>>> Hello Dan,
>>>>>
>>>>> comment [1] specifies following assertion and a note:
>>>>>
>>>>>     Adding an 'abstract', ***'private', or 'static'*** method to an
>>>>>     interface does not break compatibility with preexisting binaries.
>>>>>
>>>>>     [Note: I believe we can make this assertion because "adding a
>>>>>     method" means "adding a method that will compile", presumably?
>>>>>     Private and static methods can introduce errors if a reference
>>>>> like
>>>>>     I.m()V previously resolved to an abstract/default method in a
>>>>>     superinterface, and now resolves to the private/static method
>>>>> in I.
>>>>>     But that new method won't compile if there's a method with a
>>>>>     matching signature in a superinterface of I.]
>>>>>
>>>>> Namely, the note reads:
>>>>>
>>>>>     ... "adding a method" means "adding a method that will compile"
>>>>> ...
>>>>>
>>>>> It's questionable if this is a valid point since spec should take into
>>>>> account that a method added might be resulted from inconsistent
>>>>> compilation. For instance:
>>>>>
>>>>> 1. First we compile sources A.java, B.java, Test.java presented below.
>>>>>
>>>>>     A.java:
>>>>>     interface A {
>>>>>          default void same() {}
>>>>>     }
>>>>>
>>>>>     B.java:
>>>>>     interface B extends A {
>>>>>          //private void same() {}
>>>>>     }
>>>>>
>>>>>     Test.java:
>>>>>     class Test {
>>>>>          public static void main(String[] argv) {
>>>>>               B i = new B(){};
>>>>>               i.same();
>>>>>          }
>>>>>     }
>>>>>
>>>>> 2. Then we compile sources A.java, B.java presented below (toggle
>>>>> commenting 'same' method in A and B):
>>>>>
>>>>>     A.java:
>>>>>     interface A {
>>>>>          //default void same() {}
>>>>>     }
>>>>>
>>>>>     B.java:
>>>>>     interface B extends A {
>>>>>          private void same() {}
>>>>>     }
>>>>>
>>>>>
>>>>> 3. Then we compile source A.java presented below (returning back A
>>>>> class
>>>>> file as it resulted from step 1):
>>>>>
>>>>>     A.java:
>>>>>     interface A {
>>>>>          default void same() {}
>>>>>     }
>>>>>
>>>>> 4. Run Test.main(), this results in a run-time error:
>>>>>
>>>>>     Exception in thread "main" java.lang.IllegalAccessError: tried to
>>>>>     access method B.same()V from class Test
>>>>>
>>>>> So adding a private method seems to cause breaking binary
>>>>> compatibility.
>>>>> The change between binaries resulted from step 1 and step 3 is B class
>>>>> file with a private method added, the rest of the class files are the
>>>>> same.
>>>>>
>>>>> Should it be considered as a spec issue?
>>>>>
>>>>> [1]
>>>>> https://bugs.openjdk.java.net/browse/JDK-8072872?focusedCommentId=13610992
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Thank you,
>>>>> Georgiy.
>>>
>


More information about the compiler-dev mailing list