Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling overridden vararg method from Java causes AbstractMethodError #1459

Closed
scabug opened this issue Oct 29, 2008 · 25 comments
Closed

Calling overridden vararg method from Java causes AbstractMethodError #1459

scabug opened this issue Oct 29, 2008 · 25 comments
Assignees
Milestone

Comments

@scabug
Copy link

scabug commented Oct 29, 2008

To see this error in action, just unzip the attachment and run compile.bat followed with run.bat.

I have a base class (Java) with one method:

public abstract class AbstractBase {
  abstract void doStuff(Object... params);
}

I create an implementation for it in scala:

class Concrete extends AbstractBase {
  override def doStuff(params:java.lang.Object*) = println("doStuff invoked")
}

Finally, I have a Caller class (Java) which just calls the abovementioned method, as follows:

public class Caller {
   public void callDoStuff(AbstractBase impl) {
	impl.doStuff(new Object());
   }
}

Now, a direct call succeeds:

  val impl = new Concrete
  impl.doStuff(null)

whereas the following fails

  val impl = new Concrete
  val caller = new Caller
  caller.callDoStuff(impl)

javac -version gives "1.6.0"
scalac -version gives "Scala compiler version 2.7.2.RC4"

I am running on Windows XP SP2 with scala 2.7.2.RC4.

java.lang.AbstractMethodError: base.AbstractBase.doStuff([Ljava/lang/Object;)V
        at base.Caller.callDoStuff(Caller.java:5)
        at foo.App$$.<init>(App.scala:17)
        at foo.App$$.<clinit>(App.scala)
        at foo.App.main(App.scala)
@scabug
Copy link
Author

scabug commented Oct 29, 2008

Imported From: https://issues.scala-lang.org/browse/SI-1459?orig=1
Reporter: Harri Kulmala (hkulmala)
Attachments:

@scabug
Copy link
Author

scabug commented Oct 31, 2008

Harri Kulmala (hkulmala) said:
Updated package to reproduce the problem.

@scabug
Copy link
Author

scabug commented Nov 4, 2008

Anders Bach Nielsen [X] (nielsen) said:
Error is verifies with the current compiler from trunk

@scabug
Copy link
Author

scabug commented Feb 18, 2009

Reuben (catdogboy) said (edited by @paulp on Feb 21, 2013 10:15:23 PM UTC):
I ran into this bug too. Here's another simple example implementing
a java interface with a varargs method, then unable to resolve
the virtual method:

runBug:
     [java] Exception in thread "main" java.lang.AbstractMethodError: bug.Bugger.whatever(Ljava/lang/String;[Ljava/lang/String;)V
     [java] 	at bug.BugRunner$.doWhatever(Bugger.scala:16)
     [java] 	at bug.BugRunner$.main(Bugger.scala:19)
     [java] 	at bug.BugRunner.main(Bugger.scala)
     [java] Java Result: 1


$ find bug build.xml -type f -print -exec cat '{}' ';'
bug/Bug.java
package bug;


public interface Bug {
    public void whatever( String s1, String ... vWhatever );
}

bug/Bugger.scala
package bug

import java.util.logging.{Level,Logger}


class Bugger extends Bug {
    private val olog = Logger.getLogger( classOf[Bugger].getName )

    override def whatever( s1:String, vWhatever:String* ) = {
        olog.log( Level.INFO, "Got args " + s1 + ", and " + vWhatever.length )
    }
}


object BugRunner {
  def doWhatever( bug:Bug ) = { bug.whatever( "Boo!" ) }

  def main(vArg:Array[String]) = {
      doWhatever( new Bugger() )
  }
}
build.xml
<?xml version="1.0" encoding="UTF-8" ?>

<project 
     name="bug" 
     basedir="." 
     default="compile"
     xmlns="antlib:org.apache.tools.ant"
  >
<property name="logging.properties" value="${user.home}/logging.properties" />
<property name="scala.home" value="/usr/local/scala" />
<property name="scala-compiler.jar" value="${scala.home}/lib/scala-compiler.jar"/>
<property name="scala-library.jar" value="${scala.home}/lib/scala-library.jar"/>

 <property name="build.dir" location="build" />
 <property name="dist.dir" location="dist" />
 <property name="project.jar" location="${dist.dir}/bug.jar" />

<path id="project.classpath">
  <pathelement location="${scala-compiler.jar}"/>
  <pathelement location="${scala-library.jar}"/>
   <!--
  <pathelement location="${share.home}/jar/scala-swing-2.7.3.jar"/>
    -->
</path>
 <path id="runjar.classpath">
   <path refid="project.classpath" />
   <pathelement location="${project.jar}"/>
 </path>

 <target name="prepare">
   <mkdir dir="${build.dir}" />
   <mkdir dir="${dist.dir}" />
 </target>

 <target name="clean"
         description="Clean the build area"
      >
  <delete dir="${build.dir}"/>
 </target>

<!-- 4. Define scala compiler command. -->
<target name="init">
    <property
      name="scala-library.jar"
      value="${scala.home}/lib/scala-library.jar"
    />
    <path id="build.classpath">
      <!--
      <pathelement location="${scala-library.jar}"/>
      <pathelement location="${your.path}"/>
       -->
      <pathelement location="${build.dir}"/>
    </path>
    <taskdef resource="scala/tools/ant/antlib.xml">
      <classpath>
        <pathelement location="${scala-compiler.jar}"/>
        <pathelement location="${scala-library.jar}"/>
      </classpath>
    </taskdef>
</target>


 <target name="project.classes" depends="prepare,init">
  <scalac 
     srcdir="." 
     destdir="${build.dir}" 
     force="changed"
     unchecked="yes"
     classpathref="project.classpath" 
    >
    <include name="**/*.scala"/>
    <include name="**/*.java"/>
  </scalac>
  <javac 
     srcdir="."
     destdir="${build.dir}" 
     debug="true"
      >
   <classpath refid="project.classpath" />
   <include name="**/*.java" />
  </javac>
 </target>

 <target name="compile" depends="project.classes"
         description="Compile the thing"
         >
 </target>

 <target name="jarup" depends="compile"
         description="jar up classes"
         >
    <jar id="jarup" 
         jarfile="${project.jar}"
         basedir="${build.dir}"
         index="true"
	 level="9"
         >
       <manifest>
    	 <attribute name="Main-Class" value="bug.BugRunner"/>
       </manifest>
    </jar>
 </target>

 <target name="runBug" 
         description="Run the bug demo"
	 depends="jarup"
         >
    <java classname="bug.BugRunner"
	  fork="true">
        <classpath refid="runjar.classpath" />
        <jvmarg value="-Djava.util.logging.config.file=${logging.properties}" />
    </java>
 </target>

</project>

@scabug
Copy link
Author

scabug commented May 28, 2009

@dubochet said:
doStuff in AbstractBase has type (Array[Object])Unit
doStuff in AbstractBase has type (Sequence[_])Unit

doStuff is called in Caller as (Array[Object])Unit, which has not been overriden.

Should there be a Java bridge method for every star-parameter method that is inherited from Java?

@scabug
Copy link
Author

scabug commented Aug 19, 2009

Matt Hellige (hellige) said:
The call doesn't need to be from Java... This happens whenever the target of the call is statically typed as the interface:

Java:

public interface Test {
    public void foo(String... args);
}

Scala:

class Foo extends Test {
  def foo(args: String*) = println(args)
}
new Foo().foo("hi") // fine
(new Foo(): Test).foo("hi") // AbstractMethodError

This is particularly dangerous, of course, because you have no idea where the error will actually occur...

@scabug
Copy link
Author

scabug commented Nov 11, 2009

Pavol Vaskovic (pali-at-pali.sk) said:
Testcase demonsrating the imprications for overriding var arg methods.

@scabug
Copy link
Author

scabug commented Nov 11, 2009

Pavol Vaskovic (pali-at-pali.sk) said:
I'm seeing this problem with scala 2.7.5.

This is also impacting the ability to override java methods that use varargs. The overriden methods are never invoked, even though the code compiles without errors.

Running the testcase attached above produces:

JavaPrinter: one two three 
JavaPrinter: one two three 
Exception in thread "main" java.lang.AbstractMethodError
at Main.doYourThing(Main.java:12)
at Main.main(Main.java:8)

Expected result:

JavaPrinter: one two three 
InheritingPrinter extends JavaPrinter: one two three 
ScalaPrinter: one two three 

@scabug
Copy link
Author

scabug commented Nov 11, 2009

Pavol Vaskovic (pali-at-pali.sk) said:
Replying to [comment:11 pali@…]:

I'm seeing this problem with scala 2.7.5.
I meant 2.7.6.

@scabug
Copy link
Author

scabug commented Nov 14, 2009

Coda Hale (codahale) said:
Just got bit by this using Scala 2.7.7, and it means I'll be unable to use Scala for this particular project.

@scabug
Copy link
Author

scabug commented Nov 15, 2009

@odersky said:
This is in part a duplicate of #1342. After the fix of #1342 this still gave a compile tyime error, which is now fixed (for 2.8) in r19656.

@scabug
Copy link
Author

scabug commented Nov 16, 2009

Pavol Vaskovic (pali-at-pali.sk) said:
Martin, any thoughts on backporting this fix for 2.7.x ?

@scabug
Copy link
Author

scabug commented Nov 16, 2009

Pavol Vaskovic (pali-at-pali.sk) said:
Verified. [attachment:ticket:1459:VarArgOverride_TestCase.zip Testcase] works fine in r19664.

@scabug
Copy link
Author

scabug commented Nov 29, 2010

joe (barillari) said:
in the "integer" folder, run "run.sh" to trigger the AbstractMethodError.

@scabug
Copy link
Author

scabug commented Nov 29, 2010

joe (barillari) said:
I believe this bug is not completely fixed in 2.8.1.

I just tried hkulmala's example and it ran without a hitch. But when I
changed AbstractBase to a generic type and instantiated it with a
java.lang.Integer, the runtime threw an AbstractMethodError.

Here is AbstractBase.java:

public abstract class AbstractBase<T> {
  abstract void doStuff(T... params);

    void callDoStuff(T... params) {
	doStuff(params);
    }

Concrete.scala:

class Concrete extends AbstractBase[java.lang.Integer] {
  override def doStuff(params:java.lang.Integer*) = println("doStuff invoked")
}

Invoker.scala:

object Invoker {
  def main(args: Array[String]) {
    val impl = new Concrete
    impl.callDoStuff(1,2,3)

  }
}

If I compile and run with:

javac *.java && scalac  *.scala && scala Invoker

The result is:

java.lang.AbstractMethodError: AbstractBase.doStuff([Ljava/lang/Object;)V
	at AbstractBase.callDoStuff(AbstractBase.java:5)
	at Invoker$$.main(Invoker.scala:4)
	at Invoker.main(Invoker.scala)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:616)
	at scala.tools.nsc.util.ScalaClassLoader$$$$anonfun$$run$$1.apply(ScalaClassLoader.scala:81)
	at scala.tools.nsc.util.ScalaClassLoader$$class.asContext(ScalaClassLoader.scala:24)
	at scala.tools.nsc.util.ScalaClassLoader$$URLClassLoader.asContext(ScalaClassLoader.scala:86)
	at scala.tools.nsc.util.ScalaClassLoader$$class.run(ScalaClassLoader.scala:81)
	at scala.tools.nsc.util.ScalaClassLoader$$URLClassLoader.run(ScalaClassLoader.scala:86)
	at scala.tools.nsc.MainGenericRunner$$.main(MainGenericRunner.scala:83)
	at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

Interestingly, if you replace java.lang.Integer with java.lang.Object
and alter the call to callDoStuff accordingly, the code works without a hitch.

For the record, I'm running:

$$ scala -version
javaScala code runner version 2.8.1.final -- Copyright 2002-2010, LAMP/EPFL

$$ java -version
java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.2) (6b18-1.8.2-4ubuntu2)
OpenJDK Server VM (build 16.0-b13, mixed mode)

I have attached a .zip file containing a directory called "object",
which instantiates AbstractBase with a java.lang.Object and works, and
another directory called "integer", which instantiates AbstractBase
with a java.lang.Integer and does not work.

@scabug
Copy link
Author

scabug commented Nov 30, 2010

Charles Beyer (cjbeyer) said:
I believe I'm hitting this bug in 2.8.0 and 2.8.1 final.

The bug isn't triggered if I change the java abstract method definition from package private (no qualifier) to public.

Also, in my program, the java method doesn't have to be abstract. If I have a dumb generic implementation, the java implementation will get called when I call the method from java, while the scala overriding implementation gets called from scala.

So the bug seems to be related to problems with the visibility qualifiers.

@scabug
Copy link
Author

scabug commented Dec 29, 2011

@paulp said (edited on Dec 29, 2011 7:05:35 AM UTC):
This bug is still in trunk. The reason is this "except" business in UnCurry:

 *  - for every repeated Scala parameter `x: T*' --> x: Seq[T].
 *  - for every repeated Java parameter `x: T...' --> x: Array[T], except:
 *    if T is an unbounded abstract type, replace --> x: Array[Object]

Referring to joe's comment above mine, this:

public abstract class AbstractBase<T> {
  abstract void doStuff(T... params);
}

What happens is that after uncurry that method looks like doStuff(Array[Object]), and so the overriding pair checks during erasure which would insert the necessary bridge do not recognize that the methods have the same signature, because the override's parameter is Array[Integer], not Array[Object].

I put a shameful hack here which makes the problem go away in this instance:

https://github.com/paulp/scala/tree/ticket/SI-1459

But I can't commit that so it's only there for reference.

@scabug
Copy link
Author

scabug commented Jul 3, 2012

@paulp said:
Interesting, I didn't realize it at the time but it sounds from my description above that this is intimately related to #3452.

@scabug
Copy link
Author

scabug commented Oct 25, 2012

François-Xavier Thomas (frx) said:
Just ran into it, so any progress on that issue?

@scabug
Copy link
Author

scabug commented Sep 26, 2013

Philippe Sam-Long (pulsation) said:
Looks like I also ran into this issue extending AsyncTask on android:

E/AndroidRuntime( 1320): FATAL EXCEPTION: AsyncTask #1
E/AndroidRuntime( 1320): java.lang.RuntimeException: An error occured while executing doInBackground()
E/AndroidRuntime( 1320):        at android.os.AsyncTask$3.done(AsyncTask.java:299)
E/AndroidRuntime( 1320):        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
E/AndroidRuntime( 1320):        at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
E/AndroidRuntime( 1320):        at java.util.concurrent.FutureTask.run(FutureTask.java:239)
E/AndroidRuntime( 1320):        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
E/AndroidRuntime( 1320):        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
E/AndroidRuntime( 1320):        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
E/AndroidRuntime( 1320):        at java.lang.Thread.run(Thread.java:841)
E/AndroidRuntime( 1320): Caused by: java.lang.AbstractMethodError: abstract method not implemented
E/AndroidRuntime( 1320):        at android.os.AsyncTask.doInBackground(AsyncTask.java)
E/AndroidRuntime( 1320):        at android.os.AsyncTask$2.call(AsyncTask.java:287)
E/AndroidRuntime( 1320):        at java.util.concurrent.FutureTask.run(FutureTask.java:234)
E/AndroidRuntime( 1320):        ... 4 more

Here is a workaround for this specific case: https://www.assembla.com/code/scala-eclipse-toolchain/git/nodes/ad17dd4047ac25b167496db84f4272795eb93c3e/docs/android-examples/android-sdk/Wiktionary/src/com/example/android/wiktionary/MyAsyncTask.java

@scabug
Copy link
Author

scabug commented Oct 28, 2013

Kirill Lastovirya (kirhgoff) said:
Tried to adopt this library https://github.com/Gottox/socket.io-java-client in scala code and faced same issue. Cannot implement interface IOCallback as it has this method in java

void on(java.lang.String s, io.socket.IOAcknowledge ioAcknowledge, java.lang.Object... objects);

and scala compiler gives the following error:
IoClient.scala:20: object creation impossible, since method on in trait IOCallback of type (x$1: String, x$2: io.socket.IOAcknowledge, x$3: <repeated...>[Object])Unit is not defined
[error] socket.connect(new IOCallback {
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed

Tried a lot of variants, most reasonable (but still failing) one is
def on(event: String, ack: IOAcknowledge, params: Object*)

@scabug
Copy link
Author

scabug commented Nov 27, 2014

@jrudolph said:
As I'm currently in the awkward position of dealing with Java Interop and also got hurt by this issue I compiled a cabinet of all the different cases I could think of that show problems wrt @VarArgs methods that still exist in Scala 2.11.4.

See https://github.com/jrudolph/scala-broken-java-varargs/blob/master/src/test/java/example/Test.java

@scabug
Copy link
Author

scabug commented Nov 23, 2015

@skyluc said:
The original problem has been fixed sometime before 2.10.1, but I could the 'AbstractMethodError' problem from Johannes test code.

@scabug
Copy link
Author

scabug commented Dec 22, 2015

Arjun Panday (joune) said:
May I submit a similar use case, summarized in this very simple gist: https://gist.github.com/joune/494f7bba4c6889572bb4

=> Exception in thread "main" java.lang.AbstractMethodError: Impl.h(Ljava/lang/Object;[Ljava/lang/String;)Ljava/lang/String;

It seems to work if I remove either the type parameter or the varargs.

Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
Scala code runner version 2.11.6 -- Copyright 2002-2013, LAMP/EPFL

@scabug
Copy link
Author

scabug commented Jan 12, 2017

@adriaanm said:
scala/scala#5631

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants