JDK-7044892 : JSR 292: API entry points sometimes throw the wrong exceptions or doesn't throw the expected one
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 7
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2011-05-14
  • Updated: 2012-03-22
  • Resolved: 2011-07-18
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
7 b144Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Description
MethodHandles$Lookup.findStatic[S|G]etter throws InternalError if SecurityManager is set.

Minimized test:
===============
$ cat test.java
import java.lang.invoke.*;

public class test {
    public static void main(String[] args) throws Throwable {
        System.setSecurityManager(new SM());

        MethodHandle mh = MethodHandles.lookup().findStaticSetter(
                GetSet.class, "sf1", int.class);
    }
}

class SM extends SecurityManager {
    protected SM() { super(); }
}

class GetSet {
    static int sf1;
}

Minimized test output:
======================
$ javac test.java

$ java -showversion test
java version "1.7.0-ea"
Java(TM) SE Runtime Environment (build 1.7.0-ea-b140)
Java HotSpot(TM) Server VM (build 21.0-b10, mixed mode)

Exception in thread "main" java.lang.InternalError: uncaught exception
        at java.lang.invoke.MethodHandleStatics.uncaughtException(MethodHandleStatics.java:84)
        at java.lang.invoke.MethodHandleImpl$FieldAccessor.staticBase(MethodHandleImpl.java:314)
        at java.lang.invoke.MethodHandleImpl$FieldAccessor.<init>(MethodHandleImpl.java:278)
        at java.lang.invoke.MethodHandleImpl.accessField(MethodHandleImpl.java:241)
        at java.lang.invoke.MethodHandles$Lookup.makeAccessor(MethodHandles.java:1103)
        at java.lang.invoke.MethodHandles$Lookup.makeAccessor(MethodHandles.java:1094)
        at java.lang.invoke.MethodHandles$Lookup.findStaticSetter(MethodHandles.java:785)
        at test.main(test.java:7)
Caused by: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessDeclaredMembers")
        at java.security.AccessControlContext.checkPermission(AccessControlContext.java:366)
        at java.security.AccessController.checkPermission(AccessController.java:555)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
        at java.lang.SecurityManager.checkMemberAccess(SecurityManager.java:1679)
        at java.lang.Class.checkMemberAccess(Class.java:2174)
        at java.lang.Class.getDeclaredField(Class.java:1896)
        at java.lang.invoke.MethodHandleImpl$FieldAccessor.staticBase(MethodHandleImpl.java:311)
        ... 6 more
MethodHandle.invoke[Exact|Generic] doesn't throw UnsupportedOperationException if invoked via java.lang.reflect.Method.invoke.

The javadoc for the MethodHandle invokeExact/invokeGeneric methods states as follows:
  "When this method is observed via the Core Reflection API, it will appear as a single
  native method, taking an object array and returning an object. If this native method
  is invoked directly via Method.invoke ... it will throw an UnsupportedOperationException."

However, InvocationTargetException is thrown in such cases.

Minimized test:
===============
$ cat test.java
import java.lang.reflect.*;
import java.lang.invoke.*;

public class test {
    public static void main(String[] args) throws Throwable {
        MethodHandle target = MethodHandles.lookup().findVirtual(
                MethodHandle.class, "invokeExact",
                MethodType.methodType(Object.class, Object[].class));
        Method method = MethodHandle.class.getDeclaredMethod(
                "invokeExact", Object[].class);
        //Method method = MethodHandle.class.getDeclaredMethod(
        //        "invokeGeneric", Object[].class);

        Object[] params = new Object[1];
        method.invoke(target, params);
    }
}

Minimized test output:
========================
$ javac test.java
$ java -verify test
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at test.main(test.java:15)
Caused by: java.lang.UnsatisfiedLinkError: java.lang.invoke.MethodHandle.invokeExact([Ljava/lang/Object;)Ljava/lang/Object;
        at java.lang.invoke.MethodHandle.invokeExact(Native Method)
        ... 5 more
MethodType.methodType(Class rtype, Class[] ptypes) doesn't throw NPE if ptypes is null.

Minimized test:
===============
$ cat Test.java
import java.lang.invoke.*;
import java.util.Arrays;

public class Test {
    public static void main(String[] args) throws Exception {
        Object[][] data = new Object[][] { 
            { (Class) null, (Class[]) null },
            { (Class) null, new Class[]{Object.class} },
            { Object.class, new Class[]{ null} },
            { Object.class, new Class[]{Object.class, (Class) null} },
            { Object.class, (Class[]) null }
        };

        for (int i = 0; i < data.length; i++) {
            Class<?> rtype = (Class) data[i][0];
            Class<?>[] ptypes = (Class[]) data[i][1]; 
            try {
                MethodType.methodType(rtype, ptypes);
                System.out.println("FAILED: NPE not thrown for " + rtype + ", " + Arrays.toString(ptypes));
            } catch(NullPointerException ok) {
                System.out.println("OK: NPE is thrown for " + rtype + ", " + Arrays.toString(ptypes));
            }
        }
    }
}

Minimized test output:
========================
$ java -showversion -verify Test
java version "1.7.0-ea"
Java(TM) SE Runtime Environment (build 1.7.0-ea-b138)
Java HotSpot(TM) Server VM (build 21.0-b08, mixed mode)

OK: NPE is thrown for null, null
OK: NPE is thrown for null, [class java.lang.Object]
OK: NPE is thrown for class java.lang.Object, [null]
OK: NPE is thrown for class java.lang.Object, [class java.lang.Object, null]
FAILED: NPE not thrown for class java.lang.Object, null
MethodType.fromMethodDescriptorString unexpectedly accepts both "binary names"/"internal form of binary names".

Minimized test:
===============
$ cat Test.java
import java.lang.invoke.*;

public class Test {
    public static void main(String[] args) throws Exception {
        for (String d : new String[] {"(Ljava/lang/Object;)V", "(Ljava.lang.Object;)V"}) {
            MethodType.fromMethodDescriptorString(d, null);
            System.out.println("OK");
        }
    }
}

Minimized test output:
======================
$ java -showversion -verify Test
java version "1.7.0-ea"
Java(TM) SE Runtime Environment (build 1.7.0-ea-b138)
Java HotSpot(TM) Server VM (build 21.0-b08, mixed mode)

OK
OK
MethodHandles.Lookup.find[G|S]etter throws unexpected IllegalAccessException.

Minimized test:
===============
$ cat test.java
import java.lang.invoke.MethodType;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodHandles.Lookup.*;

public class test {

    public static void main(String[] args) throws Throwable {
        lookup().findGetter(TestCls.class, "f2", int.class);
        TestCls c =  new TestCls();
        c.f2 =  1;
    }
}

class TestCls {
    protected int f2 = 2;
}

Minimized test output:
======================
$ java -showversion test
java version "1.7.0-ea"
Java(TM) SE Runtime Environment (build 1.7.0-ea-b140)
Java HotSpot(TM) Server VM (build 21.0-b10, mixed mode)

Exception in thread "main" java.lang.IllegalAccessException: caller class must be a subclass below the method: TestCls.f2/int, from class test
        at java.lang.invoke.MemberName.makeAccessException(MemberName.java:507)
        at java.lang.invoke.MethodHandles$Lookup.restrictReceiver(MethodHandles.java:1078)
        at java.lang.invoke.MethodHandles$Lookup.restrictProtectedReceiver(MethodHandles.java:1072)
        at java.lang.invoke.MethodHandles$Lookup.makeAccessor(MethodHandles.java:1104)
        at java.lang.invoke.MethodHandles$Lookup.makeAccessor(MethodHandles.java:1094)
        at java.lang.invoke.MethodHandles$Lookup.findGetter(MethodHandles.java:724)
        at test.main(test.java:8)
MethodHandle.asCollector throws undocumented ArrayIndexOutOfBoundsException instead of IllegalArgumentException for invalid arrayLength values (negative or >= 256).

Minimized test:
===============
$ cat test.java
import java.lang.invoke.*;
import java.util.Arrays;

public class test {
    public static void main(String[] args) throws Throwable {
        MethodHandle dts = MethodHandles.publicLookup().findStatic(
                Arrays.class,
                "deepToString",
                MethodType.methodType(String.class, Object[].class));
        dts.asCollector(Object[].class, -1);
    }
}

Minimized test output:
======================
$ openjdk7-mlvm/solaris-i586/bin/java -verify test
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
        at sun.invoke.util.ValueConversions.varargsArray(ValueConversions.java:1068)
        at sun.invoke.util.ValueConversions.varargsArray(ValueConversions.java:1144)
        at java.lang.invoke.MethodHandle.asCollector(MethodHandle.java:872)
        at test.main(test.java:10)
MethodHandle.bindTo((Object) null) throws InternalError.

Minimized test:
===============
$ cat test.java
import java.lang.invoke.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;

public class test {

    public static void main(String[] args) throws Throwable {
        MethodHandle target = publicLookup().findVirtual(
                String.class, "toString", methodType(String.class));
        MethodHandle bt = target.bindTo((Object) null);
    }
}

Minimized test output:
======================
$ java -verify test
Exception in thread "main" java.lang.InternalError
        at java.lang.invoke.MethodHandleNatives.init(Native Method)
        at java.lang.invoke.BoundMethodHandle.initTarget(BoundMethodHandle.java:81)
        at java.lang.invoke.BoundMethodHandle.<init>(BoundMethodHandle.java:76)
        at java.lang.invoke.BoundMethodHandle.<init>(BoundMethodHandle.java:61)
        at java.lang.invoke.MethodHandleImpl.bindReceiver(MethodHandleImpl.java:501)
        at java.lang.invoke.MethodHandle.bindTo(MethodHandle.java:1125)
        at test.main(test.java:10)

Comments
EVALUATION http://hg.openjdk.java.net/jdk7/hotspot-comp/hotspot/rev/2848194272f4
18-05-2011

EVALUATION There are a number of bugs like this, where the API rejects an invalid input but throws an undocumented exception. This is a deferrable bug. In a smaller number of cases, the API accepts invalid inputs without throwing an exception. These should be treated as serious problems, because users may begin to rely on the invalid behaviors. These bugs are all fixable by adding error-checking code. The mlvm-dev repo has a patch for the known instances of this problem.
16-05-2011