Java Forum / General / February 2007
Strange runtime error: AbstractMethodError
Oliver Wong - 07 Feb 2007 20:20 GMT Here's my code:
<SSCCE> package com.castortech.tests;
import net.sf.saxon.om.SiblingCountingNode;
import com.castortech.common.parser.tokenstreaming.Token;
public class BugTest { public static void main(String[] args) { Token t = new Token(); t.getParent(); SiblingCountingNode scn = t; scn.getParent(); /*<-- line 12*/ } } </SSCCE>
It compiles fine, and Token does indeed implement SiblingCountingNode (although indirectly), which is why no cast is required. However, when I try to run this program, I get the following runtime error:
<error> Exception in thread "main" java.lang.AbstractMethodError: com.castortech.common.parser.tokenstreaming.Token.getParent()Lnet/sf/saxon/om/NodeInfo; at com.castortech.tests.BugTest.main(BugTest.java:12) </error>
In case this is useful, the hierarchy is:
class Token extends class BaseNode class BaseNode extends class ElementNode class ElementNode extends class AbstractNode and implements interface IElementNode class AbstractNode implements interface INode interface IElementNode extends interface INode interface INode extends interfaces IAdaptable, NodeInfo and SiblingCountingNode
Token, BaseNode, ElementNode, AbstractNode, IElementNode and INode are from our internal codebase. IAdaptable is from Eclipse's API NodeInfo and SiblingCountingNode are from Saxon (http://saxon.sourceforge.net/)
And in case this is a bug in the Eclipse compiler, I've tried it with 3.3M4 and I20070206-0010 and get the same error on both.
The baffling part, for me, is that the call to getParent() (which is NOT a static method) works if the reference is Token, but not if the reference is SiblingCountingNode.
Any hints on how to fix this error?
- Oliver
Mike Schilling - 07 Feb 2007 20:48 GMT > Here's my code: > [quoted text clipped - 50 lines] > > Any hints on how to fix this error? It is axiomatic that AbstractMethodError means that something changed between compilation time and runtime. If you could recompile the whole thing from source, then either your problem would go away or you'd see a compilation error that explains the problem.
My guess is that some of the definitions of getParent() are incompatible (i.e. return different types), but that's just a guess. It's a place to start though: what type does Token.getParent return? The overload that can't be found returns net.sf.saxon.om.NodeInfo
Oliver Wong - 07 Feb 2007 21:51 GMT > It is axiomatic that AbstractMethodError means that something changed > between compilation time and runtime. If you could recompile the whole > thing from source, then either your problem would go away or you'd see a > compilation error that explains the problem. Or that there's a bug in the compiler/JVM? ;) Elsewhere in this thread, I've posted an SSCCE that doesn't require the Saxon JAR, compiled it completely from scratch, and got the same error message.
> My guess is that some of the definitions of getParent() are incompatible > (i.e. return different types), but that's just a guess. It's a place to > start though: what type does Token.getParent return? The overload that > can't be found returns net.sf.saxon.om.NodeInfo Right. If you take a look at the new SSCCE, getParent() (now renamed "someMethod") is defined twice, each one with a different return type. But I thought this was legal in Java... "Return Type Covariance", it's called, or something like that, right?
- Oliver
opalpa opalpa@gmail.com http://opalpa.info - 07 Feb 2007 20:51 GMT > Here's my code: > [quoted text clipped - 52 lines] > > - Oliver That's quite confounding. What does the end of the this line mean? com.castortech.common.parser.tokenstreaming.Token.getParent()Lnet/sf/ saxon/om/NodeInfo
Why is the end NodeInfo instead of SiblingCountingNode?
Do multiple interfaces have the same method signature in them?
Does the same method name have different signatures? That is: is there a getParent with parameters along with the parameterless getParent?
These are just a couple of thoughts. It appears that you've found a fault in Java.
Will write if any other thoughts come to mind. Good luck Oliver.
opalpa opalpa@gmail.com http://opalpa.info/
Oliver Wong - 07 Feb 2007 21:58 GMT <opalpa@gmail.com> wrote in message news:1170881500.032689.119040@s48g2000cws.googlegroups.com... [details of my original problem snipped]
> That's quite confounding. What does the end of the this line mean? > com.castortech.common.parser.tokenstreaming.Token.getParent()Lnet/sf/ [quoted text clipped - 12 lines] > > Will write if any other thoughts come to mind. Good luck Oliver. Thanks. It looks like you were on the right track: I posted a second SSCCE that doesn't require any external libraries, and it does seem to involve multiple interfaces with the same method, but with different signatures. However, I still don't know whether this is a problem with the JVM, compiler, or my understanding of Java, so I'm not yet able to fix the "real" code where this problem is occurring.
- Oliver
Oliver Wong - 07 Feb 2007 21:47 GMT After a bit for trimming, I've formed an SSCCE that doesn't require any external libraries:
<SSCCE> interface Root { public Root someMethod(); }
interface Intermediary extends Root { public Leaf someMethod(); }
class Leaf implements Intermediary { @Override public Leaf someMethod() { return null; } }
public class BugTest { public static void main(String[] args) { Leaf leafReference = new Leaf(); leafReference.someMethod(); Root rootReference = leafReference; rootReference.someMethod(); /* throws error */ } } </SSCCE>
<output> Exception in thread "main" java.lang.AbstractMethodError: Leaf.someMethod()LRoot; at BugTest.main(BugTest.java:21) </output>
But I'm still unsure if this is a bug in the JVM, the compiler, or if this is the correct behaviour of the Java language, using some rule I'm not familiar with.
- Oliver
Lew - 07 Feb 2007 22:52 GMT > After a bit for trimming, I've formed an SSCCE that doesn't require any > external libraries: [quoted text clipped - 34 lines] > is the correct behaviour of the Java language, using some rule I'm not > familiar with. I think we're dealing with <http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8.4>
When I try your example I immediately but not consistently get an error from Netbeans on the @Override,
"method does not override a method from its superclass"
but javac doesn't show me this, not even with -Xlint:overrides.
Now, try adding these two classes to the source:
interface Othif extends Root { public String someMethod(); // String is not a subtype of Root }
class OthImpl implements Othif { @Override public String someMethod() { return null; } }
These give the expected compilation errors:
src/testit/BugTest.java:30: someMethod() in testit.Othif clashes with someMethod() in testit.Root; attempting to use incompatible return type found : java.lang.String required: testit.Root public String someMethod();
src/testit/BugTest.java:33: testit.OthImpl is not abstract and does not override abstract method someMethod() in testit.Root class OthImpl implements Othif
src/testit/BugTest.java:36: someMethod() in testit.OthImpl cannot implement someMethod() in testit.Root; attempting to use incompatible return type found : java.lang.String required: testit.Root public String someMethod()
- Lew
Mike Schilling - 07 Feb 2007 22:58 GMT > After a bit for trimming, I've formed an SSCCE that doesn't require any > external libraries: [quoted text clipped - 30 lines] > at BugTest.main(BugTest.java:21) > </output> Using JDK 1.4, this won't compile. Which makes sense, since covariant return types were introduced in 1.5. In case anyone's interested, the errors are:
Intermediary.java:2: someMethod() in Intermediary clashes with someMethod() in Root; attempting to use incompatible return type found : Leaf required: Root public Leaf someMethod(); ^ Leaf.java:4: someMethod() in Leaf cannot implement someMethod() in Root; attempting to use incompatible return type found : Leaf required: Root public Leaf someMethod() { ^
Using jdk1.5.0_05's javac, I get the error:
Leaf.java:3: method does not override a method from its superclass @Override
Which is true, I suppose. Commenting out the annotation, it all compiles correctly and runs successfully as well (still using jdk1.5.0_05) . Since javap shows that the overload being called is Root.someMethod:()LRoot;, there must be some logic in the JVM to realize that someMethod:()LLeaf; is "close enough".
So it appears that the problem (in your SSCCE, at least) is a JVM bug.
Mike Schilling - 08 Feb 2007 16:52 GMT > Which is true, I suppose. Commenting out the annotation, it all compiles > correctly and runs successfully as well (still using jdk1.5.0_05) . Since > javap shows that the overload being called is Root.someMethod:()LRoot;, > there must be some logic in the JVM to realize that someMethod:()LLeaf; is > "close enough". Actually, I'm wrong here. javap on Leaf shows that it contains both someMethod:()LRoot and someMethod:()LLeaf, where someMethod:()LRoot is automatically generated as if you'd coded
public Root someMethod() { return someMethod(); // someMethod:()LLeaf }
This is javac's usual trick for implementing covariant return types.
So, more likely than a JVM bug is that Eclipse's compiler isn't generating this extra method.
Chris Uppal - 08 Feb 2007 15:22 GMT > After a bit for trimming, I've formed an SSCCE that doesn't require any > external libraries: I can't repeat this using Sun's javac (JDK1.5 or JDK1.6), nor with Eclipse's own compiler (3.2.1 targeting "5.0 compliance level").
I changed the example slightly, so that:
class Leaf implements Intermediary { public Leaf someMethod() { System.out.println("Yup, we're here as expected"); return null; } }
and it prints out the message twice, as expected.
I suspect that the (original) problem you are seeing is directly connected with the godawful covariate returns hack. I presume that somewhere in the complex inheritance of Token from SiblingCountInfo, the Eclipse compiler has "forgotten" to generate the necessary bridging method (or methods) to adapt the return-type(s) of whatever overrides of getParent() there may be.
Possibly the (perfectly legitimate) duplicate inheritance of NodeInfo in INode (both directly and via SiblingCountingNode), has confused it.
If you can be bothered it would be interesting to see what actual implementations of getParent() from each of the classes (not interfaces) there are in the chain. Not their contents, just which ones define them and how they are declared. Also (there's no point without this too) use javap to see what /actual/ implementations have been defined in each of those classes (again, not the bytecode, just the declarations).
-- chris
Oliver Wong - 08 Feb 2007 16:16 GMT > After a bit for trimming, I've formed an SSCCE that doesn't require any > external libraries: [quoted text clipped - 24 lines] > } > </SSCCE> Thank you, everyone, for your help.
I made the change Chris suggested, and added a System.out.println("Got here") to Leaf for debugging purposes.
If I take out the @Override annotation, I get the exact same result with Eclipse's compiler: No compile errors, but the AbstractMethodError at runtime. However if I compile the original source code (with the @Override annotation, and with the system.out.println) using Sun's javac compiler, it compiles fine, and runs fine (the message is printed out twice as expected).
So it sounds like this is a bug in Eclipse's compiler. I did a disassembly of both the javac classes and the eclipsec classes, and all the files are identical except for Leaf.class.
Here's the javac version:
<disassembly> Compiled from "BugTest.java" class Leaf extends java.lang.Object implements Intermediary{ Leaf(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return
public Leaf someMethod(); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String Yup, we're here. 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: aconst_null 9: areturn
public Root someMethod(); Code: 0: aload_0 1: invokevirtual #5; //Method someMethod:()LLeaf; 4: areturn
} </disassembly>
And here's the Eclipse version:
<disassembly> Compiled from "BugTest.java" class Leaf extends java.lang.Object implements Intermediary{ Leaf(); Code: 0: aload_0 1: invokespecial #10; //Method java/lang/Object."<init>":()V 4: return
public Leaf someMethod(); Code: 0: getstatic #18; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #24; //String Yup, we're here. 5: invokevirtual #26; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: aconst_null 9: areturn
} </disassembly>
The main difference being the lack of "public Root someMethod();" within Eclipse's version.
I filed this as a bug with Eclipse: https://bugs.eclipse.org/bugs/show_bug.cgi?id=173477
- Oliver
Lew - 09 Feb 2007 00:17 GMT > So it sounds like this is a bug in Eclipse's compiler. I did a > disassembly of both the javac classes and the eclipsec classes, and all the > files are identical except for Leaf.class. ...
> The main difference being the lack of "public Root someMethod();" within > Eclipse's version. > > I filed this as a bug with Eclipse: > https://bugs.eclipse.org/bugs/show_bug.cgi?id=173477 That explains why I only saw behavior in keeping with the JLS. I used Netbeans and command-line javac. (JDK 6)
- Lew
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|