JDK-8150436 : Incorrect invocation mode when linkToInteface linker is eliminated
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 9
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: x86_64
  • Submitted: 2016-02-20
  • Updated: 2018-11-14
  • Resolved: 2016-02-26
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.
JDK 9
9 b110Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "9-ea"
Java(TM) SE Runtime Environment (build 9-ea+105-2016-02-11-003336.javare.4433.nc)
Java HotSpot(TM) 64-Bit Server VM (build 9-ea+105-2016-02-11-003336.javare.4433.nc, mixed mode)

FULL OS VERSION :
Microsoft Windows [Version 6.1.7601]
(also happens on Linux)

A DESCRIPTION OF THE PROBLEM :
During the development of a MethodHandle based implementation of Apache Lucene's MMapDirectory internals for the changes in Java 9 build 105+, we encountered the follwoing bug: Java 9 breaks invoking a MethodHandle that uses guardWithTest to invoke a DirectMethodHandle that points to an interface.

THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: No

THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Yes

REGRESSION.  Last worked in version 8u74

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and execute the the attached test code with Java 9 b105 (be sure to name the class and source code "Test.java") for the later java command line to work correctly:


$ javac Test.java
$ java -ea -Xbatch -XX:CompileCommand=exclude,Test$RunnableImpl,run Test

The invocation with "-XX:CompileCommand=exclude,Test$RunnableImpl,run" is important to trigger the bug, because in this testcase the run method usually gets inlined. In the real world scenario with more complex classes this does not happen for sure, so triggering the bug. So we prevent compilation of the interface's run() method by Hotspot.

EXPECTED VERSUS ACTUAL BEHAVIOR :
The test program should pass.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
CompileCommand: exclude Test$RunnableImpl.run
Exception in thread "main" java.lang.IncompatibleClassChangeError: Found class Test$RunnableImpl, but interface was expected
        at Test.main(Test.java:67)

Test.java:67 points to composite_MH.invokeExect().

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.methodType;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles.Lookup;
import java.util.Objects;

public final class Test {
 
  static class RunnableImpl implements Runnable {
    public int count = 0;
    
    @Override
    public void run() {
      count++;
    }
  }
  
  private Test(RunnableImpl impl) {
    this.impl = impl;
  }
  
  private final RunnableImpl impl;
  
  RunnableImpl get() {
    return impl;
  }
  
  public static void main(String... args) throws Throwable {
    /* This test creates a MethodHandle that executes the same code like:
     * 
     * void composite(Test arg) {
     *   Runnable r = (Runnable) arg.get();
     *   if (Objects.nonNull(r)) {
     *     r.run();
     *   } else {
     *     // fake else clause for MethodHandles.guardWithTest():
     *     noop(r);
     *   }
     * }
     */
    
    final Lookup lookup = lookup();
    MethodHandle getter_MH = lookup.findVirtual(Test.class, "get", methodType(RunnableImpl.class));
    // cast to Runnable:
    getter_MH = getter_MH.asType(getter_MH.type().changeReturnType(Runnable.class));
    
    // MH with "invokeinterface"
    MethodHandle run_MH = lookup.findVirtual(Runnable.class, "run", methodType(void.class));

    // MH for null check:
    MethodHandle nonNullTest_MH = lookup.findStatic(Objects.class, "nonNull", methodType(boolean.class, Object.class))
        .asType(methodType(boolean.class, Runnable.class));
    
    // MH to implement fake else check with noop(Runnable):
    MethodHandle noop_MH = dropArguments(constant(Void.class, null).asType(methodType(void.class)), 0, Runnable.class);
    
    // our composite MH doing null check:
    MethodHandle composite_MH = filterReturnValue(getter_MH, guardWithTest(nonNullTest_MH, run_MH, noop_MH));
    
    // invoke our handle in loop executing null/non-null branch alternately:
    Test t1 = new Test(new RunnableImpl());
    Test t2 = new Test(null);
    int iters = 10_000;
    for (int i = 0; i < iters; i++) {
      Test t = (i % 2 == 0) ? t1 : t2;
      composite_MH.invokeExact(t);
    }
    assert t1.get().count == iters / 2; 
  } 
   
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Run program with Java 8 build 74. It passes without problems. It also passes if you don't prevent the interface's run() method to inline/compile.


Comments
Thanks Rory O'Donnell and Uwe Schindler for the comments, I verified in linux too issue exist. -sh-4.1$ /opt/java/jdk-9_ea-106/bin/java -ea -Xbatch -XX:CompileCommand=exclude,"Test$"RunnableImpl,run Test CompileCommand: exclude Test$RunnableImpl.run Exception in thread "main" java.lang.IncompatibleClassChangeError: Found class Test$RunnableImpl, but interface was expected at Test.main(Test.java:66)
23-02-2016

Email from Uwe Schindler , Apache Lucene - bug reporter This issue does not exist on Linux. It could just be that it does not reproduce in the same way! One reason could be, because the "-XX:CompileCommand=exclude,Test$RunnableImpl,run" may need to be quoted on Linux's command line, because of the $ sign inside (which would point to an environment variable named $RunnableImpl)! The issue definitely exists on Linux, too, e.g., see this failed Jenkins run on Linux: http://jenkins.thetaphi.de/job/Lucene-Solr-trunk-Linux/15966/console; this one fails tons of tests with exactly that message!
23-02-2016

Test results 8u73/74 - Pass 9 ea b-102 - Pass 9 ea b-103 - Fail 9 ea b-106 - Fail Introduced in build 103
23-02-2016

This issue doesn't exist in Linux, observed only on windows.
23-02-2016