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
src/hotspot/share
test/hotspot/jtreg/compiler/jsr292/NonInlinedCall

@ -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") \

@ -932,7 +932,7 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
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;

@ -1082,6 +1082,7 @@ Handle SharedRuntime::find_callee_info_helper(JavaThread* thread,
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 @@ Handle SharedRuntime::find_callee_info_helper(JavaThread* thread,
// 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 @@ Handle SharedRuntime::find_callee_info_helper(JavaThread* thread,
break;
}
}
} else {
bc = bytecode.invoke_code();
}
assert(bc != Bytecodes::_illegal, "not initialized");
bool has_receiver = bc != Bytecodes::_invokestatic &&
bc != Bytecodes::_invokedynamic &&
bc != Bytecodes::_invokehandle;

@ -52,10 +52,12 @@ import static jdk.test.lib.Asserts.assertEquals;
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 @@ public class InvokeTest {
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 @@ public class InvokeTest {
@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,15 +158,26 @@ public class InvokeTest {
}
@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);
}
}
@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 {
@ -171,13 +213,33 @@ public class InvokeTest {
// 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(() -> {
for (Object r : recv) {
linkToVirtual(r, r.getClass());
}});
linkToVirtual(new T() {}, T.class);
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() {
@ -190,17 +252,18 @@ public class InvokeTest {
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() {