Strange runtime error: AbstractMethodError

  • Follow


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 


0
Reply owong (5281) 2/7/2007 8:20:37 PM

"Oliver Wong" <owong@castortech.com> wrote in message 
news:kwqyh.94077$vT5.1680881@wagner.videotron.net...
> 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 


0
Reply mscottschilling (1976) 2/7/2007 8:48:21 PM


On Feb 7, 4:20 pm, "Oliver Wong" <o...@castortech.com> wrote:
> 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
opalpa@gmail.com
http://opalpa.info/

0
Reply opalpa (277) 2/7/2007 8:51:40 PM

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 


0
Reply owong (5281) 2/7/2007 9:47:53 PM

"Mike Schilling" <mscottschilling@hotmail.com> wrote in message 
news:pWqyh.25398$yC5.9050@newssvr27.news.prodigy.net...
>
> 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 


0
Reply owong (5281) 2/7/2007 9:51:26 PM

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


0
Reply owong (5281) 2/7/2007 9:58:22 PM

Oliver Wong wrote:
> 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: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
0
Reply lew7583 (253) 2/7/2007 10:52:31 PM

"Oliver Wong" <owong@castortech.com> wrote in message 
news:7Oryh.94111$vT5.1685226@wagner.videotron.net...
> 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. 


0
Reply mscottschilling (1976) 2/7/2007 10:58:06 PM

Oliver Wong wrote:

> 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



0
Reply chris.uppal (3970) 2/8/2007 3:22:35 PM

"Oliver Wong" <owong@castortech.com> wrote in message 
news:7Oryh.94111$vT5.1685226@wagner.videotron.net...
> 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 


0
Reply owong (5281) 2/8/2007 4:16:31 PM

"Mike Schilling" <mscottschilling@hotmail.com> wrote in message 
news:2Qsyh.74355$qO4.40916@newssvr13.news.prodigy.net...

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


0
Reply mscottschilling (1976) 2/8/2007 4:52:00 PM

Oliver Wong wrote:
>     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
0
Reply lew7583 (253) 2/9/2007 12:17:34 AM

11 Replies
44 Views

(page loaded in 1.257 seconds)


Reply: