JDK-1240831 : fp.bugs 3936 code should give `AbstractMethodError' or equal error
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 1.0
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: solaris_2.5
  • CPU: sparc
  • Submitted: 1996-03-04
  • Updated: 2014-11-04
  • Resolved: 1999-01-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
1.2.0 1.2beta3Fixed
Related Reports
Relates :  
Relates :  
Description
From: Pavel Curtis <###@###.###>
This does not look like form output to me.


I believe that what follows is a bug in the JDK 1.0 `javac' compiler, the JDK
1.0 `java' interpreter, or both.  Create the following four short files:

*** a/Main.java ------------------------------------------------------------

package a;

import b.Bar;
import java.applet.Applet;

public class Main extends Applet {
    public void start() {
        Foo f = new Bar();

        f.mumble();
    }
}

*** a/Foo.java -------------------------------------------------------------

package a;

public abstract class Foo {
    abstract void mumble();
}

*** b/Bar.java -------------------------------------------------------------

package b;

import a.Foo;

public class Bar extends Foo {
    public void mumble() {
        System.err.println("Got here.");
    }
}

*** test.html --------------------------------------------------------------

<applet code="a.Main" height=100 width=100></applet>

*** ------------------------------------------------------------------------

Your directory tree should now look like this:

        ./test.html
        ./a/Main.java
        ./a/Foo.java
        ./b/Bar.java

Run the appletviewer on test.html (I used a file: URL, but I don't think it
matters).  Also run Netscape-2.0-FCS on it.

Under appletviewer you see `Got here.' on stderr.  Under Netscape you see (in
the Java console) an `AbstractMethodError' when a.Main.start() tries to call
f.mumble().

I think that javac should be reporting an error in the compilation of
b/Bar.java, to the effect that Bar must be declared abstract because it isn't
providing an implementation for the Foo.mumble method, even though it *looks*
like Bar *is* providing such an implementation.  The problem is that Foo's
mumble() method is not public, so it's not visible to Bar (which is in a
different package), so Bar *can't* override/implement it.

Regardless of this, though, the interpreter should be raising an error like
Netscape does when the call happens, since the visible `mumble()' method, the
one that Bar *did not* override/implement, is abstract.

This is a potentially serious security hole, since Bar, from a different
package, is being allowed to override a method it can't see, possibly upsetting
invariants in Foo's code.

Comments?

        Pavel Curtis
        Xerox PARC

[Sheri Good 03/13/97]  Another user has reported what looks like the same issue:

Category: java
Subcategory: compiler
Bug/rfe/eou: bug
Synopsis: javac allows default access method to not be implemented in
derived class
Severity Impact: (internal)
Severity Functionality: (internal)
Priority: (internal)
Description: The following code should be rejected at compile-time:

    package p1;

    public abstract class Broken {
        abstract void f (); // default access
    }

    package p2;

    class TimeBomb extends p1.Broken {
        void f () {
        }
    }

because calling f() on a TimeBomb object via a Broken reference results
in an AbstractMethodError.
Work around: Don't use default access methods��(which is good advice
anyway :-)
Comments: This is biggie, needs a posting on your 'Known Bugs' page!
customer_rec: new
Company: GMID sa
Employee: Roly Perera
Release: 1.1_Final
Hardware version: PC
O/S version: Win95
User Role: (internal)
User Type: (internal)
Sun Contact: (internal)
end_customer_rec: (internal)
BUG_END:




Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: generic FIXED IN: 1.2beta3 INTEGRATED IN: 1.2beta3
14-06-2004

PUBLIC COMMENTS The compiler should generate an error if a class is abstract. There are certain situations which involve package-private protection which make it unclear whether a class should be considered abstract or not. The following code illustrates this: <pre><code> :::::::::::::: p1/Broken.java :::::::::::::: package p1; public abstract class Broken { abstract void f (); // default access } :::::::::::::: p1/test.java :::::::::::::: package p1; public class test { public static void main(String[] args) { Broken b = new p2.TimeBomb(); b.f(); } } :::::::::::::: p2/TimeBomb.java :::::::::::::: package p2; public class TimeBomb extends p1.Broken { void f () { } } :::::::::::::: <\code><\pre>
10-06-2004

EVALUATION In the 1.1fcs candidate the compiler complains: javac a/Main.java ./b/Bar.java:7: Note: Method void mumble() in class b.Bar does not override the corresponding method in class a.Foo, which is private to a different package. public void mumble() { ^ 1 warning I'm not sure whether that is right or not. However, with the thing compiled, the appletviewer does throw an AbstractMethodError as Pavel expects. So this bug is fixed modulo the appropriateness of the compiler's response. timothy.lindholm@Eng 1997-01-29 ======================================== Well, this is partly a compiler bug and, in my opinion, partly a spec bug. Here is my code for this problem: :::::::::::::: p1/Broken.java :::::::::::::: package p1; public abstract class Broken { abstract void f (); // default access } :::::::::::::: p1/test.java :::::::::::::: package p1; public class test { public static void main(String[] args) { Broken b = new p2.TimeBomb(); b.f(); } } :::::::::::::: p2/TimeBomb.java :::::::::::::: package p2; public class TimeBomb extends p1.Broken { void f () { } } :::::::::::::: The problem is a compiler problem *and* a spec problem. In my view, the question is "should the class TimeBomb be considered abstract?" Because with the current understanding, it seems that outside of package p1 it is not abstract, and inside the package p1 it is abstract. This is a bad situation. I have a lot more to say about this, and I have a draconian spec-level "solution". I will write more about these later. todd.turnidge@Eng 1997-07-18 ======================================= Here is a note written by Gilad Bracha which describes this and similar problems: From ###@###.### Sun Jul 20 19:15:01 1997 Date: Fri, 18 Jul 1997 16:44:31 -0700 From: Gilad Bracha <###@###.###> To: todd.turnidge@Eng Subject: Package problems I wrote a note to myself regarding this package visibility problem(s). I thought you might find it useful. : This is an attempt to summarize the specification problems that arise with inheritance of package private methods across package boundaries. According to the JLS, classes inherit exactly the members of their direct ancestors (superclass and superinterfaces) that are visible to them - namely public and protected members and and package visible members if the classes are in the same package. Consider: package Alpha; public class A { void foo() {...}; }; package Beta; import Alpha.A; public class B extends A { void foo(){...}; // legal; B did not inherit A.foo() - it is invisible }; package Alpha; import Beta.B; .... A a = new A(); B b = new B(); a.foo(); b.foo(); // illegal - B.foo() is invisible here a = b; a.foo(); // legal; demonstrates the absence of subsumption in Java ! // Question: what method gets called in the line above? // if you believe JLS 15.11.4, B.foo()! // if you believe JLS 6.6.5, A.foo() Determining the behavior of the code above is problematic. According to JLS 15.11.4, B.foo() will be called. This is of some concern, since this allows methods declared to be package private to be invoked outside their package. However, JLS 6.6.5 contradicts JLS 15.11.4. JLS 6.6.5 indicates that A.foo() will be called. The contradiction can be made most clear by applying the procedure of JLS 15.11.4 to the example of JLS 6.6.5, which yields different results than those stated in 6.6.5. In practice, this is compounded by the behavior of the implementation in these cases, which does not match the specification. The behavior of the implementation has changed over time, but has never conformed to the JLS. Note that the HotSpot VM implementation will naturally conform to the method lookup specification given in JLS 15.11.4. Consider also: package Alpha; import Beta.B; class C extends B { // note: C does not inherit a foo(); B.foo() is invisible here }; class D extends C { Object foo() {...}; // legal; No inherited foo() from C to conflict with }; .... A a = new A(); C c = new C(); D d = new D(); c.foo(); // illegal - C.foo() does not exist a = c; // legal - C is assignment compatible with A a.foo(); // legal; what gets called depends on what section of the JLS you believe a = d; d.foo(); // legal - looks for Object foo() and finds D.foo() a.foo(); // legal - looks for void foo() and finds B.foo() or A.foo(), depending on interpretation The main question is how can the specification be fixed. One option is to modify the look up process so that it is cognizant of visibility. If the caller does not have access to the method found, the lookup ignores it and continues to search (or fails, as the case may be). This is a change to the specification, that influences all implementations. In addition, it is an undesirable complication of the VM. The VM would not be disturbed if we mangled the names of package visible members. However, this influences the interoperability of parts of a package compiled by different compilers (unless this is standardized across all vendors; it's a bit late for that, given all the binaries that already exist). Another option is to ban the last set of cases can be banned by a blanket rule, as suggested by Guy Steele : A class in a package A may not inherit from a class or interface from another package if that class or interface inherits from a class or interface of package A. Is this too restrictive? Does it impact a meaningful body of existing code? In any case, it does not solve the original problem. A stronger version might be: A class in a package A may not import a class or interface from another package if that class or interface inherits from a class or interface of package A (that has package visible members?). The problem with such band-aid rules is that it is hard to prove that they cover all cases. Additional problems can occur: package Alpha; public abstract class A { abstract void foo() {...}; }; package Beta; import Alpha.A; public class B extends A { // How do we know if B is abstract or not? void foo(){...}; // legal; B did not inherit A.foo() - it is invisible }; package Alpha; import Beta.B; .... A a = new B(); a.foo(); // We can get an abstract method error; it should be caught during "preparation". Here we encounter a problem similar to the static typing of delegation. We do not have an exact type for A, since its package private members are invisible to us. We therefore cannot be sure if we have completed all of its abstract methods. We must rely on a dynamic (in this case, link time) check to catch the problem. Todd suggests (if I understood him correctly) that a specific rule be formulated to ban this case: A public class must not have abstract members with default visibility. This seems over-restrictive, as it bans legitimate cases (where the class is used as a type outside the package). In addition, this problem is not so severe. Finally, we must not have an explosion of special rules patching up pieces of the general problem. --------- In a recent meeting, two JLS authors agreed that, if a class c1 in package p1 extends a class c2 in package p2, and c2 has abstract, default-access methods, then c2 is abstract regardless of whether it can see the default-access method. If you want to extend a class, leave no abstract default methods in it. Otherwise it is permanently abstract out of package. todd.turnidge@Eng 1997-11-21 I have modified the compiler to produce the error message. todd.turnidge@Eng 1997-12-18
21-11-1997