8150782: findClass / accessClass throw unexpected exceptions
authormhaupt
Sun, 13 Mar 2016 20:26:29 +0100
changeset 36623 79e0e71cdb20
parent 36622 9406773bccd5
child 36624 c589c89ecc1a
8150782: findClass / accessClass throw unexpected exceptions Reviewed-by: sundar
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
jdk/test/java/lang/invoke/t8150782/TestAccessClass.java
jdk/test/java/lang/invoke/t8150782/TestCls.java
jdk/test/java/lang/invoke/t8150782/TestFindClass.java
jdk/test/java/lang/invoke/t8150782/TestLookup.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Fri Mar 11 17:07:57 2016 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Sun Mar 13 20:26:29 2016 +0100
@@ -99,13 +99,16 @@
      * <p>
      * As a matter of pure convention, the {@linkplain Lookup#lookupClass lookup class}
      * of this lookup object will be {@link java.lang.Object}.
+     * Consequently, the lookup context of this lookup object will be the bootstrap
+     * class loader, which means it cannot find user classes.
      *
      * <p style="font-size:smaller;">
      * <em>Discussion:</em>
      * The lookup class can be changed to any other class {@code C} using an expression of the form
      * {@link Lookup#in publicLookup().in(C.class)}.
      * Since all classes have equal access to public names,
-     * such a change would confer no new access rights.
+     * such a change would confer no new access rights,
+     * but may change the lookup context by virtue of changing the class loader.
      * A public lookup object is always subject to
      * <a href="MethodHandles.Lookup.html#secmgr">security manager checks</a>.
      * Also, it cannot access
@@ -641,6 +644,11 @@
          * then no members, not even public members, will be accessible.
          * (In all other cases, public members will continue to be accessible.)
          * </ul>
+         * <p>
+         * The resulting lookup's capabilities for loading classes
+         * (used during {@link #findClass} invocations)
+         * are determined by the lookup class' loader,
+         * which may change due to this operation.
          *
          * @param requestedLookupClass the desired lookup class for the new lookup object
          * @return a lookup object which reports the desired lookup class
@@ -939,13 +947,17 @@
         /**
          * Looks up a class by name from the lookup context defined by this {@code Lookup} object. The static
          * initializer of the class is not run.
+         * <p>
+         * The lookup context here is determined by the {@linkplain #lookupClass() lookup class}, its class
+         * loader, and the {@linkplain #lookupModes() lookup modes}. In particular, the method first attempts to
+         * load the requested class, and then determines whether the class is accessible to this lookup object.
          *
          * @param targetName the fully qualified name of the class to be looked up.
          * @return the requested class.
          * @exception SecurityException if a security manager is present and it
          *            <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws LinkageError if the linkage fails
-         * @throws ClassNotFoundException if the class does not exist.
+         * @throws ClassNotFoundException if the class cannot be loaded by the lookup class' loader.
          * @throws IllegalAccessException if the class is not accessible, using the allowed access
          * modes.
          * @exception SecurityException if a security manager is present and it
@@ -960,6 +972,9 @@
         /**
          * Determines if a class can be accessed from the lookup context defined by this {@code Lookup} object. The
          * static initializer of the class is not run.
+         * <p>
+         * The lookup context here is determined by the {@linkplain #lookupClass() lookup class} and the
+         * {@linkplain #lookupModes() lookup modes}.
          *
          * @param targetClass the class to be access-checked
          *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/t8150782/TestAccessClass.java	Sun Mar 13 20:26:29 2016 +0100
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @compile TestAccessClass.java TestCls.java
+ * @run testng/othervm -ea -esa test.java.lang.invoke.t8150782.TestAccessClass
+ */
+package test.java.lang.invoke.t8150782;
+
+import java.lang.invoke.*;
+
+import static java.lang.invoke.MethodHandles.*;
+
+import static org.testng.AssertJUnit.*;
+
+import org.testng.annotations.*;
+
+public class TestAccessClass {
+
+    private static boolean initializedClass1;
+
+    private static class Class1 {
+        static {
+            initializedClass1 = true;
+        }
+    }
+
+    @Test
+    public void initializerNotRun() throws IllegalAccessException {
+        lookup().accessClass(Class1.class);
+        assertFalse(initializedClass1);
+    }
+
+    @Test
+    public void returnsSameClass() throws IllegalAccessException, ClassNotFoundException {
+        Class<?> aClass = lookup().accessClass(Class1.class);
+        assertEquals(Class1.class, aClass);
+    }
+
+    @DataProvider
+    Object[][] illegalAccessAccess() {
+        return new Object[][] {
+                {publicLookup(), Class1.class},
+                {publicLookup(), TestCls.getPrivateSIC()}
+        };
+    }
+
+    @Test(dataProvider = "illegalAccessAccess", expectedExceptions = {IllegalAccessException.class})
+    public void illegalAccessExceptionTest(Lookup lookup, Class<?> klass) throws IllegalAccessException, ClassNotFoundException {
+        lookup.accessClass(klass);
+    }
+
+    @Test
+    public void okAccess() throws IllegalAccessException {
+        lookup().accessClass(TestCls.getPrivateSIC());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/t8150782/TestCls.java	Sun Mar 13 20:26:29 2016 +0100
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package test.java.lang.invoke.t8150782;
+
+import static java.lang.invoke.MethodHandles.*;
+
+public class TestCls {
+
+    public static final Lookup LOOKUP = lookup();
+
+    private static class PrivateSIC {}
+    public static Class getPrivateSIC() { return PrivateSIC.class; }
+    public static Lookup getLookupForPrivateSIC() { return lookup(); }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/t8150782/TestFindClass.java	Sun Mar 13 20:26:29 2016 +0100
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @compile TestFindClass.java TestCls.java
+ * @run testng/othervm -ea -esa test.java.lang.invoke.t8150782.TestFindClass
+ */
+package test.java.lang.invoke.t8150782;
+
+import java.lang.invoke.*;
+
+import static java.lang.invoke.MethodHandles.*;
+
+import static org.testng.AssertJUnit.*;
+
+import org.testng.annotations.*;
+
+public class TestFindClass {
+
+    private static final String PACKAGE_PREFIX = "test.java.lang.invoke.t8150782.";
+
+    private static boolean initializedClass1;
+
+    private static class Class1 {
+        static {
+            initializedClass1 = true;
+        }
+    }
+
+    @Test
+    public void initializerNotRun() throws IllegalAccessException, ClassNotFoundException {
+        lookup().findClass(PACKAGE_PREFIX + "TestFindClass$Class1");
+        assertFalse(initializedClass1);
+    }
+
+    @Test
+    public void returnsRequestedClass() throws IllegalAccessException, ClassNotFoundException {
+        Class<?> aClass = lookup().findClass(PACKAGE_PREFIX + "TestFindClass$Class1");
+        assertEquals(Class1.class, aClass);
+    }
+
+    @Test(expectedExceptions = {ClassNotFoundException.class})
+    public void classNotFoundExceptionTest() throws IllegalAccessException, ClassNotFoundException {
+        lookup().findClass(PACKAGE_PREFIX + "TestFindClass$NonExistent");
+    }
+
+    @DataProvider
+    Object[][] illegalAccessFind() {
+        return new Object[][] {
+                {publicLookup(), PACKAGE_PREFIX + "TestFindClass$Class1"},
+                {publicLookup(), PACKAGE_PREFIX + "TestCls$PrivateSIC"}
+        };
+    }
+
+    /**
+     * Assertion: @throws IllegalAccessException if the class is not accessible, using the allowed access modes.
+     */
+    @Test(dataProvider = "illegalAccessFind", expectedExceptions = {ClassNotFoundException.class})
+    public void illegalAccessExceptionTest(Lookup lookup, String className) throws IllegalAccessException, ClassNotFoundException {
+        lookup.findClass(className);
+    }
+
+    @Test
+    public void okAccess() throws IllegalAccessException, ClassNotFoundException {
+        lookup().findClass(PACKAGE_PREFIX + "TestCls$PrivateSIC");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/t8150782/TestLookup.java	Sun Mar 13 20:26:29 2016 +0100
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @run testng/othervm -ea -esa test.java.lang.invoke.t8150782.TestLookup
+ */
+package test.java.lang.invoke.t8150782;
+
+import org.testng.annotations.Test;
+
+import static java.lang.invoke.MethodHandles.*;
+
+import static org.testng.AssertJUnit.*;
+
+public class TestLookup {
+
+    @Test
+    public void testClassLoaderChange() {
+        Lookup lookup = lookup();
+        assertNotNull(lookup.lookupClass().getClassLoader());
+        Lookup lookup2 = lookup.in(Object.class);
+        assertNull(lookup2.lookupClass().getClassLoader());
+    }
+
+    @Test(expectedExceptions = {ClassNotFoundException.class})
+    public void testPublicCannotLoadUserClass() throws IllegalAccessException, ClassNotFoundException {
+        Lookup lookup = publicLookup();
+        lookup.findClass("test.java.lang.invoke.t8150782.TestCls");
+    }
+
+    @Test
+    public void testPublicCanLoadSystemClass() throws IllegalAccessException, ClassNotFoundException {
+        Lookup lookup = publicLookup();
+        lookup.findClass("java.util.HashMap");
+    }
+
+    @Test
+    public void testPublicInChangesClassLoader() {
+        Lookup lookup = publicLookup();
+        assertNull(lookup.lookupClass().getClassLoader());
+        Lookup lookup2 = lookup.in(TestCls.class);
+        assertNotNull(lookup2.lookupClass().getClassLoader());
+    }
+
+}