8203480: IncompatibleClassChangeError thrown at sites linked to default interface methods

Reviewed-by: kvn
This commit is contained in:
Vladimir Ivanov 2018-06-06 23:36:08 +03:00
parent ff7db9b11a
commit ddc42415c0
4 changed files with 96 additions and 24 deletions

View file

@ -53,6 +53,9 @@
diagnostic(bool, StressGCM, false, \ diagnostic(bool, StressGCM, false, \
"Randomize instruction scheduling in GCM") \ "Randomize instruction scheduling in GCM") \
\ \
develop(bool, StressMethodHandleLinkerInlining, false, \
"Stress inlining through method handle linkers") \
\
develop(intx, OptoPrologueNops, 0, \ develop(intx, OptoPrologueNops, 0, \
"Insert this many extra nop instructions " \ "Insert this many extra nop instructions " \
"in the prologue of every nmethod") \ "in the prologue of every nmethod") \

View file

@ -932,7 +932,7 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
speculative_receiver_type = (receiver_type != NULL) ? receiver_type->speculative_type() : NULL; speculative_receiver_type = (receiver_type != NULL) ? receiver_type->speculative_type() : NULL;
} }
CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms, CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms,
true /* allow_inline */, !StressMethodHandleLinkerInlining /* allow_inline */,
PROB_ALWAYS, PROB_ALWAYS,
speculative_receiver_type); speculative_receiver_type);
return cg; return cg;

View file

@ -1082,6 +1082,7 @@ Handle SharedRuntime::find_callee_info_helper(JavaThread* thread,
Bytecode_invoke bytecode(caller, bci); Bytecode_invoke bytecode(caller, bci);
int bytecode_index = bytecode.index(); int bytecode_index = bytecode.index();
bc = bytecode.invoke_code();
methodHandle attached_method = extract_attached_method(vfst); methodHandle attached_method = extract_attached_method(vfst);
if (attached_method.not_null()) { if (attached_method.not_null()) {
@ -1095,6 +1096,11 @@ Handle SharedRuntime::find_callee_info_helper(JavaThread* thread,
// Adjust invocation mode according to the attached method. // Adjust invocation mode according to the attached method.
switch (bc) { switch (bc) {
case Bytecodes::_invokevirtual:
if (attached_method->method_holder()->is_interface()) {
bc = Bytecodes::_invokeinterface;
}
break;
case Bytecodes::_invokeinterface: case Bytecodes::_invokeinterface:
if (!attached_method->method_holder()->is_interface()) { if (!attached_method->method_holder()->is_interface()) {
bc = Bytecodes::_invokevirtual; bc = Bytecodes::_invokevirtual;
@ -1110,10 +1116,10 @@ Handle SharedRuntime::find_callee_info_helper(JavaThread* thread,
break; break;
} }
} }
} else {
bc = bytecode.invoke_code();
} }
assert(bc != Bytecodes::_illegal, "not initialized");
bool has_receiver = bc != Bytecodes::_invokestatic && bool has_receiver = bc != Bytecodes::_invokestatic &&
bc != Bytecodes::_invokedynamic && bc != Bytecodes::_invokedynamic &&
bc != Bytecodes::_invokehandle; bc != Bytecodes::_invokehandle;

View file

@ -54,8 +54,10 @@ public class InvokeTest {
static final MethodHandle virtualMH; // invokevirtual T.f1 static final MethodHandle virtualMH; // invokevirtual T.f1
static final MethodHandle staticMH; // invokestatic T.f2 static final MethodHandle staticMH; // invokestatic T.f2
static final MethodHandle intfMH; // invokeinterface I.f1 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 specialMH; // invokespecial T.f4 T
static final MethodHandle privateMH; // invokespecial I.f4 T
static final MethodHandle basicMH; static final MethodHandle basicMH;
static final WhiteBox WB = WhiteBox.getWhiteBox(); static final WhiteBox WB = WhiteBox.getWhiteBox();
@ -69,7 +71,9 @@ public class InvokeTest {
virtualMH = LOOKUP.findVirtual(T.class, "f1", mtype); virtualMH = LOOKUP.findVirtual(T.class, "f1", mtype);
staticMH = LOOKUP.findStatic (T.class, "f2", mtype); staticMH = LOOKUP.findStatic (T.class, "f2", mtype);
intfMH = LOOKUP.findVirtual(I.class, "f3", mtype); intfMH = LOOKUP.findVirtual(I.class, "f3", mtype);
defaultMH = LOOKUP.findVirtual(T.class, "f3", mtype);
specialMH = LOOKUP.findSpecial(T.class, "f4", mtype, T.class); specialMH = LOOKUP.findSpecial(T.class, "f4", mtype, T.class);
privateMH = LOOKUP.findSpecial(I.class, "f4", mtype, I.class);
basicMH = NonInlinedReinvoker.make(staticMH); basicMH = NonInlinedReinvoker.make(staticMH);
} catch (Exception e) { } catch (Exception e) {
throw new Error(e); throw new Error(e);
@ -92,24 +96,51 @@ public class InvokeTest {
@DontInline public Class<?> f3() { if (doDeopt) WB.deoptimizeAll(); return P2.class; } @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 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 @DontInline
static void linkToVirtual(Object obj, Class<?> extecpted) { static void linkToVirtual(T recv, Class<?> expected) {
try { try {
Class<?> cls = (Class<?>)virtualMH.invokeExact((T)obj); Class<?> cls = (Class<?>)virtualMH.invokeExact(recv);
assertEquals(cls, obj.getClass()); assertEquals(cls, expected);
} catch (Throwable e) { } catch (Throwable e) {
throw new Error(e); throw new Error(e);
} }
} }
@DontInline @DontInline
static void linkToInterface(Object obj, Class<?> expected) { static void linkToVirtualDefault(T recv, Class<?> expected) {
try { 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); assertEquals(cls, expected);
} catch (Throwable e) { } catch (Throwable e) {
throw new Error(e); throw new Error(e);
@ -127,15 +158,26 @@ public class InvokeTest {
} }
@DontInline @DontInline
static void linkToSpecial(Object obj, Class<?> expected) { static void linkToSpecial(T recv, Class<?> expected) {
try { try {
Class<?> cls = (Class<?>)specialMH.invokeExact((T)obj); Class<?> cls = (Class<?>)specialMH.invokeExact(recv);
assertEquals(cls, expected); assertEquals(cls, expected);
} catch (Throwable e) { } catch (Throwable e) {
throw new Error(e); throw new Error(e);
} }
} }
@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 @DontInline
static void invokeBasic() { static void invokeBasic() {
try { try {
@ -171,13 +213,33 @@ public class InvokeTest {
// Monomorphic case (optimized virtual call) // Monomorphic case (optimized virtual call)
run(() -> linkToVirtual(new T(), T.class)); run(() -> linkToVirtual(new T(), T.class));
run(() -> linkToVirtualDefault(new T(), I.class));
// Megamorphic case (virtual call) // Megamorphic case (optimized virtual call)
Object[] recv = new Object[] { new T(), new P1(), new P2() };
run(() -> { run(() -> {
for (Object r : recv) { linkToVirtual(new T() {}, T.class);
linkToVirtual(r, r.getClass()); linkToVirtual(new T() {}, T.class);
}}); linkToVirtual(new T() {}, T.class);
});
run(() -> {
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() { static void testInterface() {
@ -190,17 +252,18 @@ public class InvokeTest {
run(() -> linkToInterface(new T(), I.class)); run(() -> linkToInterface(new T(), I.class));
// Megamorphic case (virtual call) // Megamorphic case (virtual call)
Object[][] recv = new Object[][] {{new T(), I.class}, {new P1(), P1.class}, {new P2(), P2.class}};
run(() -> { run(() -> {
for (Object[] r : recv) { linkToInterface(new T(), I.class);
linkToInterface(r[0], (Class<?>)r[1]); linkToInterface(new P1(), P1.class);
}}); linkToInterface(new P2(), P2.class);
});
} }
static void testSpecial() { static void testSpecial() {
System.out.println("linkToSpecial"); System.out.println("linkToSpecial");
// Monomorphic case (optimized virtual call) // Monomorphic case (optimized virtual call)
run(() -> linkToSpecial(new T(), T.class)); run(() -> linkToSpecial(new T(), T.class));
run(() -> linkToSpecialIntf(new T(), I.class));
} }
static void testStatic() { static void testStatic() {