Strange runtime error: AbstractMethodError

O

Oliver Wong

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
 
M

Mike Schilling

Oliver Wong said:
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?

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
 
O

opalpa opalpa

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


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
(e-mail address removed)
http://opalpa.info/
 
O

Oliver Wong

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
 
O

Oliver Wong

Mike Schilling said:
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
 
O

Oliver Wong

[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/
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.

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
 
L

Lew

Oliver said:
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.

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:eek:verrides.

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
 
M

Mike Schilling

Oliver Wong said:
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>

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.
 
C

Chris Uppal

Oliver said:
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
 
O

Oliver Wong

Oliver Wong said:
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>

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
 
M

Mike Schilling

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.
 
L

Lew

Oliver said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top