8203480: IncompatibleClassChangeError thrown at sites linked to default interface methods
authorvlivanov
Wed, 06 Jun 2018 23:36:08 +0300
changeset 50449 acdfeede95ff
parent 50448 db8036093504
child 50450 517f76115e5a
8203480: IncompatibleClassChangeError thrown at sites linked to default interface methods Reviewed-by: kvn
src/hotspot/share/opto/c2_globals.hpp
src/hotspot/share/opto/callGenerator.cpp
src/hotspot/share/runtime/sharedRuntime.cpp
test/hotspot/jtreg/compiler/jsr292/NonInlinedCall/InvokeTest.java
--- a/src/hotspot/share/opto/c2_globals.hpp	Thu Jun 07 17:40:15 2018 +0200
+++ b/src/hotspot/share/opto/c2_globals.hpp	Wed Jun 06 23:36:08 2018 +0300
@@ -53,6 +53,9 @@
   diagnostic(bool, StressGCM, false,                                        \
           "Randomize instruction scheduling in GCM")                        \
                                                                             \
+  develop(bool, StressMethodHandleLinkerInlining, false,                    \
+          "Stress inlining through method handle linkers")                  \
+                                                                            \
   develop(intx, OptoPrologueNops, 0,                                        \
           "Insert this many extra nop instructions "                        \
           "in the prologue of every nmethod")                               \
--- a/src/hotspot/share/opto/callGenerator.cpp	Thu Jun 07 17:40:15 2018 +0200
+++ b/src/hotspot/share/opto/callGenerator.cpp	Wed Jun 06 23:36:08 2018 +0300
@@ -932,7 +932,7 @@
           speculative_receiver_type = (receiver_type != NULL) ? receiver_type->speculative_type() : NULL;
         }
         CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms,
-                                              true /* allow_inline */,
+                                              !StressMethodHandleLinkerInlining /* allow_inline */,
                                               PROB_ALWAYS,
                                               speculative_receiver_type);
         return cg;
--- a/src/hotspot/share/runtime/sharedRuntime.cpp	Thu Jun 07 17:40:15 2018 +0200
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp	Wed Jun 06 23:36:08 2018 +0300
@@ -1082,6 +1082,7 @@
 
   Bytecode_invoke bytecode(caller, bci);
   int bytecode_index = bytecode.index();
+  bc = bytecode.invoke_code();
 
   methodHandle attached_method = extract_attached_method(vfst);
   if (attached_method.not_null()) {
@@ -1095,6 +1096,11 @@
 
       // Adjust invocation mode according to the attached method.
       switch (bc) {
+        case Bytecodes::_invokevirtual:
+          if (attached_method->method_holder()->is_interface()) {
+            bc = Bytecodes::_invokeinterface;
+          }
+          break;
         case Bytecodes::_invokeinterface:
           if (!attached_method->method_holder()->is_interface()) {
             bc = Bytecodes::_invokevirtual;
@@ -1110,10 +1116,10 @@
           break;
       }
     }
-  } else {
-    bc = bytecode.invoke_code();
   }
 
+  assert(bc != Bytecodes::_illegal, "not initialized");
+
   bool has_receiver = bc != Bytecodes::_invokestatic &&
                       bc != Bytecodes::_invokedynamic &&
                       bc != Bytecodes::_invokehandle;
--- a/test/hotspot/jtreg/compiler/jsr292/NonInlinedCall/InvokeTest.java	Thu Jun 07 17:40:15 2018 +0200
+++ b/test/hotspot/jtreg/compiler/jsr292/NonInlinedCall/InvokeTest.java	Wed Jun 06 23:36:08 2018 +0300
@@ -52,10 +52,12 @@
 public class InvokeTest {
     static MethodHandles.Lookup LOOKUP = MethodHandleHelper.IMPL_LOOKUP;
 
-    static final MethodHandle virtualMH;  // invokevirtual   T.f1
-    static final MethodHandle staticMH;   // invokestatic    T.f2
-    static final MethodHandle intfMH;     // invokeinterface I.f1
-    static final MethodHandle specialMH;  // invokespecial   T.f4 T
+    static final MethodHandle virtualMH; // invokevirtual   T.f1
+    static final MethodHandle staticMH;  // invokestatic    T.f2
+    static final MethodHandle intfMH;    // invokeinterface I.f3
+    static final MethodHandle defaultMH; // invokevirtual   T.f3
+    static final MethodHandle specialMH; // invokespecial   T.f4 T
+    static final MethodHandle privateMH; // invokespecial   I.f4 T
     static final MethodHandle basicMH;
 
     static final WhiteBox WB = WhiteBox.getWhiteBox();
@@ -69,7 +71,9 @@
             virtualMH  = LOOKUP.findVirtual(T.class, "f1", mtype);
             staticMH   = LOOKUP.findStatic (T.class, "f2", mtype);
             intfMH     = LOOKUP.findVirtual(I.class, "f3", mtype);
+            defaultMH  = LOOKUP.findVirtual(T.class, "f3", mtype);
             specialMH  = LOOKUP.findSpecial(T.class, "f4", mtype, T.class);
+            privateMH  = LOOKUP.findSpecial(I.class, "f4", mtype, I.class);
             basicMH    = NonInlinedReinvoker.make(staticMH);
         } catch (Exception e) {
             throw new Error(e);
@@ -92,24 +96,51 @@
         @DontInline public Class<?> f3() { if (doDeopt) WB.deoptimizeAll(); return P2.class; }
     }
 
-    static interface I {
+    interface I {
         @DontInline default Class<?> f3() { if (doDeopt) WB.deoptimizeAll(); return I.class; }
+        @DontInline private Class<?> f4() { if (doDeopt) WB.deoptimizeAll(); return I.class; }
     }
 
+    interface J1 extends I {
+        @DontInline default Class<?> f3() { if (doDeopt) WB.deoptimizeAll(); return J1.class; }
+    }
+
+    interface J2 extends I {
+        @DontInline default Class<?> f3() { if (doDeopt) WB.deoptimizeAll(); return J2.class; }
+    }
+
+    interface J3 extends I {
+        @DontInline default Class<?> f3() { if (doDeopt) WB.deoptimizeAll(); return J3.class; }
+    }
+
+    static class Q1 extends T implements J1 {}
+    static class Q2 extends T implements J2 {}
+    static class Q3 extends T implements J3 {}
+
     @DontInline
-    static void linkToVirtual(Object obj, Class<?> extecpted) {
+    static void linkToVirtual(T recv, Class<?> expected) {
         try {
-            Class<?> cls = (Class<?>)virtualMH.invokeExact((T)obj);
-            assertEquals(cls, obj.getClass());
+            Class<?> cls = (Class<?>)virtualMH.invokeExact(recv);
+            assertEquals(cls, expected);
         } catch (Throwable e) {
             throw new Error(e);
         }
     }
 
     @DontInline
-    static void linkToInterface(Object obj, Class<?> expected) {
+    static void linkToVirtualDefault(T recv, Class<?> expected) {
         try {
-            Class<?> cls = (Class<?>)intfMH.invokeExact((I)obj);
+            Class<?> cls = (Class<?>)defaultMH.invokeExact(recv);
+            assertEquals(cls, expected);
+        } catch (Throwable e) {
+            throw new Error(e);
+        }
+    }
+
+    @DontInline
+    static void linkToInterface(I recv, Class<?> expected) {
+        try {
+            Class<?> cls = (Class<?>)intfMH.invokeExact(recv);
             assertEquals(cls, expected);
         } catch (Throwable e) {
             throw new Error(e);
@@ -127,9 +158,9 @@
     }
 
     @DontInline
-    static void linkToSpecial(Object obj, Class<?> expected) {
+    static void linkToSpecial(T recv, Class<?> expected) {
         try {
-            Class<?> cls = (Class<?>)specialMH.invokeExact((T)obj);
+            Class<?> cls = (Class<?>)specialMH.invokeExact(recv);
             assertEquals(cls, expected);
         } catch (Throwable e) {
             throw new Error(e);
@@ -137,6 +168,17 @@
     }
 
     @DontInline
+    static void linkToSpecialIntf(I recv, Class<?> expected) {
+        try {
+            Class<?> cls = (Class<?>)privateMH.invokeExact(recv);
+            assertEquals(cls, expected);
+        } catch (Throwable e) {
+            throw new Error(e);
+        }
+    }
+
+
+    @DontInline
     static void invokeBasic() {
         try {
             Class<?> cls = (Class<?>)MethodHandleHelper.invokeBasicL(basicMH);
@@ -171,13 +213,33 @@
 
         // Monomorphic case (optimized virtual call)
         run(() -> linkToVirtual(new T(), T.class));
+        run(() -> linkToVirtualDefault(new T(), I.class));
 
-        // Megamorphic case (virtual call)
-        Object[] recv = new Object[] { new T(), new P1(), new P2() };
+        // Megamorphic case (optimized virtual call)
+        run(() -> {
+            linkToVirtual(new T() {}, T.class);
+            linkToVirtual(new T() {}, T.class);
+            linkToVirtual(new T() {}, T.class);
+        });
+
         run(() -> {
-            for (Object r : recv) {
-                linkToVirtual(r, r.getClass());
-            }});
+            linkToVirtualDefault(new T(){}, I.class);
+            linkToVirtualDefault(new T(){}, I.class);
+            linkToVirtualDefault(new T(){}, I.class);
+        });
+
+        // Megamorphic case (virtual call), multiple implementations
+        run(() -> {
+            linkToVirtual(new T(),  T.class);
+            linkToVirtual(new P1(), P1.class);
+            linkToVirtual(new P2(), P2.class);
+        });
+
+        run(() -> {
+            linkToVirtualDefault(new Q1(), J1.class);
+            linkToVirtualDefault(new Q2(), J2.class);
+            linkToVirtualDefault(new Q3(), J3.class);
+        });
     }
 
     static void testInterface() {
@@ -190,17 +252,18 @@
         run(() -> linkToInterface(new T(), I.class));
 
         // Megamorphic case (virtual call)
-        Object[][] recv = new Object[][] {{new T(), I.class}, {new P1(), P1.class}, {new P2(), P2.class}};
         run(() -> {
-            for (Object[] r : recv) {
-                linkToInterface(r[0], (Class<?>)r[1]);
-            }});
+            linkToInterface(new T(),  I.class);
+            linkToInterface(new P1(), P1.class);
+            linkToInterface(new P2(), P2.class);
+        });
     }
 
     static void testSpecial() {
         System.out.println("linkToSpecial");
         // Monomorphic case (optimized virtual call)
         run(() -> linkToSpecial(new T(), T.class));
+        run(() -> linkToSpecialIntf(new T(), I.class));
     }
 
     static void testStatic() {