Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8150436

Incorrect invocation mode when linkToInteface linker is eliminated

    Details

    • Subcomponent:
    • Introduced In Build:
      b103
    • Resolved In Build:
      b110
    • CPU:
      x86_64
    • OS:
      generic

      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.

        Issue Links

          Activity

          Hide
          fmatte Fairoz Matte added a comment -
          This issue doesn't exist in Linux, observed only on windows.
          Show
          fmatte Fairoz Matte added a comment - This issue doesn't exist in Linux, observed only on windows.
          Hide
          fmatte Fairoz Matte added a comment -
          Test results
          8u73/74 - Pass
          9 ea b-102 - Pass
          9 ea b-103 - Fail
          9 ea b-106 - Fail

          Introduced in build 103
          Show
          fmatte Fairoz Matte added a comment - Test results 8u73/74 - Pass 9 ea b-102 - Pass 9 ea b-103 - Fail 9 ea b-106 - Fail Introduced in build 103
          Hide
          rodonnel Rory O'Donnell added a comment -
          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!
          Show
          rodonnel Rory O'Donnell added a comment - 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!
          Hide
          fmatte Fairoz Matte added a comment -
          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)
          Show
          fmatte Fairoz Matte added a comment - 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)
          Hide
          hgupdate HG Updates added a comment -
          URL: http://hg.openjdk.java.net/jdk9/hs-comp/hotspot/rev/cb59d649446d
          User: vlivanov
          Date: 2016-02-26 20:10:30 +0000
          Show
          hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk9/hs-comp/hotspot/rev/cb59d649446d User: vlivanov Date: 2016-02-26 20:10:30 +0000
          Hide
          hgupdate HG Updates added a comment -
          URL: http://hg.openjdk.java.net/jdk9/jdk9/hotspot/rev/cb59d649446d
          User: lana
          Date: 2016-03-14 15:54:56 +0000
          Show
          hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk9/jdk9/hotspot/rev/cb59d649446d User: lana Date: 2016-03-14 15:54:56 +0000

            People

            • Assignee:
              vlivanov Vladimir Ivanov
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: