Message-Id: <199707111745.KAA18870@homer.communities.com>
Subject: Re: Crashing bug in JVM-1.1.3
Date: Fri, 11 Jul 97 09:38:42 -0800
From: Dan Bornstein <danfuzz@communities.com>
To: "Li Gong" <gong@games.eng.sun.com>
I wrote:
> According to the JLS 13.5.3, adding a member to an interface does
> not break compatibility with pre-existing binaries
Li Gong writes:
>The JLS is unfortunately wrong on this point, and is being fixed.
>Adding a new method to an interface breaks backward compatibility, and
>JavaSoft people have all been working under this constraint in new
>feature development. (Whether crashing has any bad side effect is
>another question.)
I can accept that. However, there's still a question about whether an
abstract class needs to have method descriptors for every interface
method for interfaces it "implements." As I pointed out, javac adds
"abstract method" descriptors in such cases, but it's not obvious from
the spec that it's required, and in fact ecomp (EC's compiler) doesn't
currently do it, which is what led to me finding this anomoly in the
first place. Note that javac's behavior may be incorrect, in the face of
binary compatibility. If you *remove* a method from an interface,
abstract classes that "implement" it but leave the method abstract by not
defining it will end up with an extra method descriptor for an abstract
method. If you subsequently compile a concrete subclass, javac will
spuriously complain that the class "should be declared abstract" despite
the fact that by looking at the source one can see that the situation
should be just fine. (Full example below.)
Also, the exception that gets thrown by the VM in my example is also not
covered by the spec. According to JVMS 2.16.3, IllegalAccessErrors are
only thrown when a method has the wrong access scope (e.g., default
access when referenced outside the package), not when a class fails to
have a method descriptor at all. JVMS 5.1.1 and 5.2 also mention
IllegalAccessError, but again have nothing to say about this particular
situation.
Finally, while I'll accept that 13.5.3 is in some sense "wrong," I think
it may arguably be right in a more limited sense. In particular, should
the following statement be considered true? (rephrased from 13.5.3):
Adding a member to an interface does not break compatibility with
binaries for abstract classes declared to implement that interface.
Dan Bornstein
Electric Communities
########################################
[Note, this report has also been filed with the JavaSoft Bug Report form.]
Here's javac behaving badly:
Output:
javac -d . file1.java
javac -d . file2.java
javac -d . file3.java
file3.java:5: class milk.abstr.Cla must be declared abstract. It does not
define
void meth() from class milk.abstr.Abs.
class Cla
^
1 error
#!/bin/csh -f
#
# Demonstrate anomolous behavior in javac. Javac automatically spits out
# "abstract method" descriptors for interface methods not actually defined
# in an abstract class. This can lead to unexpected behavior in certain
# code upgrade situations. The last "javac" line in this script arguably
# should succeed but in fact fails.
rm -rf milk
echo "javac -d . file1.java"
javac -d . file1.java
echo "javac -d . file2.java"
javac -d . file2.java
echo "javac -d . file3.java"
javac -d . file3.java
// BEGIN file1.java
package milk.abstr;
interface Int
{
void meth();
}
abstract class Abs
implements Int
{
}
class Cla
extends Abs
{
public void meth() {
}
}
// END file1.java
// BEGIN file2.java
package milk.abstr;
interface Int
{
}
// END file2.java
// BEGIN file3.java
package milk.abstr;
class Cla
extends Abs
{
}
// END file3.java