JDK-8032400 : JSR292: invokeSpecial: InternalError attempting to lookup a method
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 8,9
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2014-01-21
  • Updated: 2014-07-29
  • Resolved: 2014-06-09
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 8 JDK 9
8u20 b20Fixed 9Fixed
Description
Consider the following case:
  public class T1                  { public          int m() { return 2; } }
  public class T2 extends T1 { public static int m() { return 1; } }
  public class T3 extends T2 { public          int m() { return 0; } }

Call site: equivalent of invokespecial T1.m T3

Expected result: T1.m is invoked.

java.lang.InternalError: p1.T1.m()int/invokeSpecial
	at java.lang.invoke.MethodHandles$Lookup.getDirectMethodCommon(MethodHandles.java:1631)
	at java.lang.invoke.MethodHandles$Lookup.getDirectMethodNoSecurityManager(MethodHandles.java:1602)
	at java.lang.invoke.MethodHandles$Lookup.getDirectMethodForConstant(MethodHandles.java:1778)
	at java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:1727)
	at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:442)
	at p1.T3.test(Unknown Source)
	at p1.T3Caller.test(Unknown Source)

Test case is attached.

ILW = HLH = P2
I = H = incorrect result: error instead of successful invocation
L = L = javac rejects to compile the test case; may be a result of separate compilation though 
W = H = no workaround
Comments
Verified with regression test. Platform tested: Linux x64
28-07-2014

T1.m lookup attempt from T3 ends up as T2.m lookup, but it fails since T2 has static method with the same signature. Lookup.getDirectMethodCommon doesn't expect such failure and throws InternalError. JVMS specification states the following: // JVMS 6.5: invokespecial // ... 2. Otherwise, if C is a class and has a superclass, a search // for a declaration of an instance method with the same name and // descriptor as the resolved method is performed, starting with the // direct superclass of C and continuing with the direct superclass // of that class, and so forth, until a match is found or no further // superclasses exist. If a match is found, then it is the method to // be invoked. The fix is to comply with the spec and search upper in the class hierarchy if lookup attempt fails.
06-06-2014

Suggested fix: diff --git a/src/share/classes/java/lang/invoke/MethodHandles.java b/src/share/classes/java/lang/invoke/MethodHandles.java --- a/src/share/classes/java/lang/invoke/MethodHandles.java +++ b/src/share/classes/java/lang/invoke/MethodHandles.java @@ -1612,11 +1612,10 @@ checkSecurityManager(refc, method); assert(!method.isMethodHandleInvoke()); - Class<?> refcAsSuper; if (refKind == REF_invokeSpecial && refc != lookupClass() && !refc.isInterface() && - refc != (refcAsSuper = lookupClass().getSuperclass()) && + refc != lookupClass().getSuperclass() && refc.isAssignableFrom(lookupClass())) { assert(!method.getName().equals("<init>")); // not this code path // Per JVMS 6.5, desc. of invokespecial instruction: @@ -1624,11 +1623,24 @@ // and if our original search was above LC.super, // repeat the search (symbolic lookup) from LC.super. // FIXME: MemberName.resolve should handle this instead. - MemberName m2 = new MemberName(refcAsSuper, - method.getName(), - method.getMethodType(), - REF_invokeSpecial); - m2 = IMPL_NAMES.resolveOrNull(refKind, m2, lookupClassOrNull()); + Class<?> refcAsSuper = lookupClass(); + MemberName m2; + do { + // JVMS 6.5: invokespecial + // ... 2. Otherwise, if C is a class and has a superclass, a search for a declaration of an + // instance method with the same name and descriptor as the resolved method is performed, + // starting with the direct superclass of C and continuing with the direct superclass of that class, + // and so forth, until a match is found or no further superclasses exist. If a match is found, + // then it is the method to be invoked. + refcAsSuper = refcAsSuper.getSuperclass(); + m2 = new MemberName(refcAsSuper, + method.getName(), + method.getMethodType(), + REF_invokeSpecial); + m2 = IMPL_NAMES.resolveOrNull(refKind, m2, lookupClassOrNull()); + } while (m2 == null && // no method is found yet + refc.isAssignableFrom(refcAsSuper) && // looking up until refc + refc != refcAsSuper); // skip iteration above refc if (m2 == null) throw new InternalError(method.toString()); method = m2; refc = refcAsSuper;
30-05-2014