JDK-8209005 : Lookup.unreflectSpecial fails for default methods when Lookup.findSpecial works
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 10.0.2,11,12
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: x86_64
  • Submitted: 2018-08-03
  • Updated: 2019-08-19
  • Resolved: 2019-08-13
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 14
14 b10Fixed
Related Reports
Blocks :  
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
Lookup.unreflectSpecial throws an Exception even when a call to Lookup.findSpecial with parameters extracted from the Method succeed.

This especially happens, when a default method of an interface should be looked up.
Using Lookup.unreflectSpecial() is useful for InvokationHandlers to invoke default methods.

This difference between unreflectSpecial and findSpecial is at least surprising.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
This works:
Try to lookup a MethodHandle for a default method using Lookup.findSpecial with the interface as special caller.

This fails:
Try to lookup a MethodHandle for a default method using Lookup.unreflectSpecial with the interface as special caller.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Lookup.unreflectSpecial is as least as powerful as Lookup.findSpecial.
ACTUAL -
Lookup.unreflectSpecial throws an IllegalAccessException.

Exception in thread "main" java.lang.IllegalAccessException: no private access for invokespecial: interface java.util.Comparator, from bug.UnreflectSpecial (unnamed module @6c629d6e)
	at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:942)
	at java.base/java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:2231)
	at java.base/java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(MethodHandles.java:1795)
	at bug.UnreflectSpecial.unreflectSpecial(UnreflectSpecial.java:34)
	at bug.UnreflectSpecial.main(UnreflectSpecial.java:22)


---------- BEGIN SOURCE ----------
package bug;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.Comparator;

public class UnreflectSpecial {
	 
	private static final Lookup LOOKUP = MethodHandles.lookup();
	
	public static void main(String[] args) throws Throwable {
		Method method = Comparator.class.getMethod("reversed");
		Comparator<Object> cmp = new TestInterf();

		MethodHandle mhFind = findSpecial(method);
		Object findRev = mhFind.invoke(cmp);
		System.out.println(findRev);
		
		MethodHandle mhUnreflect = unreflectSpecial(method);
		Object unreflectRev = mhUnreflect.invoke(cmp);
		System.out.println(unreflectRev);
	}
	
	static MethodHandle findSpecial(Method m) throws ReflectiveOperationException {
		return LOOKUP.findSpecial(m.getDeclaringClass(), m.getName(), 
				MethodType.methodType(m.getReturnType(), m.getParameterTypes()), 
				m.getDeclaringClass());
	}
	
	static MethodHandle unreflectSpecial(Method m) throws ReflectiveOperationException {
		return LOOKUP.unreflectSpecial(m, m.getDeclaringClass());
	}
	
	static class TestInterf implements Comparator<Object> {
		
		public int compare(Object o1, Object o2) {
			return 0;
		}
		
		@Override
		public Comparator<Object> reversed() {
			System.out.println("UnreflectSpecial.TestInterf.reversed()");
			return this;
		}
		
	}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use Lookup.findSpecial instead.

FREQUENCY : always



Comments
URL: https://hg.openjdk.java.net/jdk/jdk/rev/7ba5e49258de User: mchung Date: 2019-08-13 22:49:27 +0000
13-08-2019

This issue also depends on JDK-8173978 where Lookup::in may be called to teleport to another module.
25-07-2019

There is a simple fix, but it is not clear why the code was not originally implemented this way. diff -r 3142a04a2f6d src/java.base/share/classes/java/lang/invoke/MethodHandles.java --- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Mon Aug 06 15:18:57 2018 -0700 +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Tue Aug 07 15:07:22 2018 -0700 @@ -1794,7 +1794,7 @@ * @throws NullPointerException if any argument is null */ public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException { - checkSpecialCaller(specialCaller, null); + checkSpecialCaller(specialCaller, m.getDeclaringClass()); Lookup specialLookup = this.in(specialCaller); MemberName method = new MemberName(m, true); assert(method.isMethod());
07-08-2018

This issue is reproducible 10.0.2 - Fail 11 ea b23 - Fail 12 ea b04 - Fail == -sh-4.2$ /scratch/fairoz/JAVA/jdk12/jdk-12-ea+04/bin/java UnreflectSpecial java.util.Collections$ReverseComparator2@c141d797 Exception in thread "main" java.lang.IllegalAccessException: no private access for invokespecial: interface java.util.Comparator, from UnreflectSpecial (unnamed module @68f7aae2) at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:942) at java.base/java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:2244) at java.base/java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(MethodHandles.java:1797) at UnreflectSpecial.unreflectSpecial(UnreflectSpecial.java:32) at UnreflectSpecial.main(UnreflectSpecial.java:20)
06-08-2018