mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-21 11:34:38 +02:00
8203480: IncompatibleClassChangeError thrown at sites linked to default interface methods
Reviewed-by: kvn
This commit is contained in:
parent
ff7db9b11a
commit
ddc42415c0
4 changed files with 96 additions and 24 deletions
|
@ -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") \
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue