JDK-8031502 : JSR292: IncompatibleClassChangeError in LambdaForm for CharSequence.toString() method handle type converter
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: hs25,8,9
  • Priority: P1
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2014-01-10
  • Updated: 2014-10-14
  • Resolved: 2014-01-16
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8 JDK 9
7u71Fixed 8Fixed 9 b02Fixed
Related Reports
Relates :  
Relates :  
Description
Compilation of LambdaForm CharSequence.toString() MH type converter produces incorrect bytecode.

Exception in thread "main" java.lang.IncompatibleClassChangeError: Found interface java.lang.CharSequence, but class was expected
	at java.lang.invoke.LambdaForm$MH/707806938.convert(LambdaForm$MH:1000001)
	at java.lang.invoke.LambdaForm$MH/317983781.invokeExact_MT(LambdaForm$MH:1000010)
	at TestCharSequence.main(TestCharSequence.java:13)

  static java.lang.Object convert(java.lang.Object, java.lang.Object);
    flags: ACC_STATIC
    Code:
      stack=1, locals=2, args_size=2
         0: aload_1       
         1: invokevirtual #16                 // InterfaceMethod java/lang/CharSequence.toString:()Ljava/lang/String;
         4: areturn       

CharSequence is an interface, so invokevirtual fails to invoke toString method and throws ICCE.

The test case passes fine in jdk7.

Filed against hotspot/compiler (and not core-libs/java.lang.invoke), because initial analysis shows that VM returns wrong info when resolving corresponding MemberName:
jdk/src/share/classes/java/lang/invoke/MemberName.java:965
                m = MethodHandleNatives.resolve(m, lookupClass);

before: m = java.lang.CharSequence.toString()String/invokeInterface
after: m = java.lang.CharSequence.toString()String/invokeVirtual

ILW = HLH = P2
I = H = regression against jdk7
L = L = very unlikely: (1) interface on boot class path; (2) overrides a method from Object; (3) MH type is converted; (4) MH invoked enough to be compiled into bytecode 
W = H = none
Comments
Verified with jdk9/b02 by the regression test provided with this fix. Test fail with b01 and pass with b02.
01-04-2014

Thanks, John. I filed JDK-8033465 to track this.
03-02-2014

Good fix, but since JDK-8029507, it can be turned to an assert: assert(!(member.getDeclaringClass().isInterface() && refKind == REF_invokeVirtual));
28-01-2014

http://hg.openjdk.java.net/jdk8/tl/jdk/rev/9e91793fd516 changeset: 8936:901d02c5f76d tag: tip user: vlivanov date: Wed Jan 15 20:48:44 2014 +0400 summary: 8031502: JSR292: IncompatibleClassChangeError in LambdaForm for CharSequence.toString() method handle type converter
16-01-2014

Fix: http://cr.openjdk.java.net/~vlivanov/8031502/webrev.00/
15-01-2014

Release team: Approved for fixing
14-01-2014

ILW = HMH = P1 Impact: High Regression against JDK7 in JSR292 (java.lang.invoke). Incorrectly generated bytecode during LambdaForm compilation. Can partially or completely break affected application (like with Groovy Console [1]). Likelihood: Medium Though the preconditions are very specific (interface on boot class path, overrides a method from Object, method handle type should be converted and corresponding LambdaForm should be compiled into bytecode), at least Groovy is severely affected by this bug (Groovy Console fails to start [1]). Also, it's easy to come up with a simple scripts in Nashorn, Groovy & JRuby which hit this bug. Workaround: High No workaround. Justification: It's a regression against JDK7. Incorrectly generated bytecode can partially or completely break affected application (like with Groovy Console [1]). Though the preconditions are very specific (interface on boot class path, overrides a method from Object, method handle type should be converted and corresponding LambdaForm should be compiled into bytecode), at least Groovy is severely affected by this bug (Groovy Console fails to start [1]). Also, it's easy to come up with a simple scripts in Nashorn, Groovy & JRuby which hit this bug. I suspect (haven't verified though) that Java programs with lambdas can hit this bug as well. Evaluation: The problem is the following: (1) java.lang.CharSequence interface declares abstract method "String toString()"; (2) after 8014013, VM resolves CharSequence::toString()/invokeInterface to CharSequence::toString()/invokeVirtual; (3) during LambdaForm compilation, CharSequence is considered statically invocable (see InvokeBytecodeGenerator::isStaticallyInvocable) and invokevirtual for CharSequence::toString() is issued, which is wrong (invokevirtual throws ICCE if it references an interface); The fix is straightforward: during LambdaForm compilation, switch back from invokevirtual to invokeinterface instruction when invoking method on an interface. Risk Assessment: Very low. The fix is to switch back from invokevirtual to invokeinterface when invoking a method on an interface. It affects only LambdaForm compilation part and is focused on one particular corner case: Object method redeclared in an interface. Testing (in progress): regression test, jdk/test/java/lang/invoke, vm.mlvm.testlist, nashorn (unit tests, test262, octane), jruby, groovy [1] http://jira.codehaus.org/browse/GROOVY-6449
14-01-2014

Changing Likelihood to Medium: though the preconditions are very specific, Groovy is severely affected by this bug (Groovy Console fails to start). ILW = HMH => P1
13-01-2014

-XX:+TraceItables -XX:+Verbose: ... invokeinterface resolved method: caller-class:<NULL>, compile-time-class:java.lang.CharSequence, method:java.lang.CharSequence.toString()Ljava/lang/String;, method_holder:java.lang.CharSequence, access_flags: public abstract invokeinterface selected method: receiver-class:java.lang.CharSequence, resolved-class:java.lang.CharSequence, method:java.lang.CharSequence.toString()Ljava/lang/String;, method_holder:java.lang.CharSequence, access_flags: public abstract ... After method resolution (methodHandles.cpp:666) (gdb) print result $10 = { ... _call_kind = CallInfo::vtable_call, _call_index = 2, ... } _call_kind is vtable_call, because resolved method doesn't have itable index: src/share/vm/interpreter/linkResolver.cpp:1346 // setup result if (!resolved_method->has_itable_index()) { int vtable_index = resolved_method->vtable_index(); assert(vtable_index == sel_method->vtable_index(), "sanity check"); result.set_virtual(resolved_klass, recv_klass, resolved_method, sel_method, vtable_index, CHECK); } else { int itable_index = resolved_method()->itable_index(); result.set_interface(resolved_klass, recv_klass, resolved_method, sel_method, itable_index, CHECK); } Resolved method: {method} - this oop: ... - method holder: 'java/lang/CharSequence' - constants: ... - access: 0x401 public abstract - name: 'toString' - signature: '()Ljava/lang/String;' - max stack: 1 - max locals: 0 - size of params: 1 - method size: 12 - vtable index: 2 ...
10-01-2014

-XX:+TraceMethodHandles relevant output: ... memberName: invokevirtual method_holder::method: java.lang.CharSequence.toString()Ljava/lang/String;, receiver: java.lang.CharSequence, vtableindex: 2, access_flags: public abstract ...
10-01-2014

Most probable culprit: changeset: 5296:b2e698d2276c user: drchase date: Fri Sep 13 22:38:02 2013 -0400 summary: 8014013: CallInfo structure no longer accurately reports the result of a LinkResolver operation
10-01-2014

The bug was introduced in 8b109.
10-01-2014

Initially reported at http://jira.codehaus.org/browse/GROOVY-6449
10-01-2014