changeset 14502:89fb05b6e864

Throw IAE if the proxy module can't access interface
author mchung
date Mon, 23 Nov 2015 19:22:22 -0800
parents f1fff0daf868
children 33d87582d833
files src/java.base/share/classes/java/lang/reflect/Proxy.java test/jdk/jigsaw/reflect/Proxy/ProxyClassAccessTest.java test/jdk/jigsaw/reflect/Proxy/ProxyLayerTest.java test/jdk/jigsaw/reflect/Proxy/ProxyModuleMapping.java test/jdk/jigsaw/reflect/Proxy/q/NP.java
diffstat 5 files changed, 134 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/reflect/Proxy.java	Thu Nov 19 14:04:22 2015 -0800
+++ b/src/java.base/share/classes/java/lang/reflect/Proxy.java	Mon Nov 23 19:22:22 2015 -0800
@@ -404,12 +404,6 @@
         }
     }
 
-    private static String packageName(Class<?> c) {
-        String cn = c.getName();
-        int last = cn.lastIndexOf(".");
-        return (last != -1) ? cn.substring(0, last) : "";
-    }
-
     /*
      * a key used for proxy class with 0 implemented interfaces
      */
@@ -567,7 +561,7 @@
                 int flags = intf.getModifiers();
                 if (!Modifier.isPublic(flags)) {
                     accessFlags = Modifier.FINAL;  // non-public, final
-                    String pkg = packageName(intf);
+                    String pkg = intf.getPackageName();
                     if (proxyPkg == null) {
                         proxyPkg = pkg;
                     } else if (!pkg.equals(proxyPkg)) {
@@ -652,7 +646,7 @@
 
 
         private static boolean isExportedType(Class<?> c) {
-            String pn = packageName(c);
+            String pn = c.getPackageName();
             return Modifier.isPublic(c.getModifiers()) && c.getModule().isExported(pn);
         }
 
@@ -745,7 +739,10 @@
          * @throws IllegalArgumentException if it violates the restrictions specified
          *         in {@link Proxy#newProxyInstance}
          */
-        static void validateProxyInterfaces(ClassLoader loader, List<Class<?>> interfaces, Set<Class<?>> refTypes) {
+        static void validateProxyInterfaces(ClassLoader loader,
+                                            List<Class<?>> interfaces,
+                                            Set<Class<?>> refTypes)
+        {
             Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.size());
             for (Class<?> intf : interfaces) {
                 /*
@@ -816,7 +813,7 @@
                 Module m = intf.getModule();
                 if (Modifier.isPublic(intf.getModifiers())) {
                     // module-private types
-                    if (!m.isExported(packageName(intf))) {
+                    if (!m.isExported(intf.getPackageName())) {
                         modulePrivateTypes.put(intf, m);
                     }
                 } else {
@@ -839,7 +836,7 @@
                 // and both have the same package p (so no need to check class loader)
                 if (packagePrivateTypes.size() > 1 &&
                         (packagePrivateTypes.keySet().stream()  // more than one package
-                                 .map(Proxy::packageName).distinct().count() > 1 ||
+                                 .map(Class::getPackageName).distinct().count() > 1 ||
                          packagePrivateTypes.values().stream()  // or more than one module
                                  .distinct().count() > 1)) {
                     throw new IllegalArgumentException(
@@ -847,18 +844,28 @@
                 }
 
                 // all package-private types are in the same module (named or unnamed)
+                Module target = null;
                 for (Module m : packagePrivateTypes.values()) {
-                    if (getLoader(m) == loader) {
-                        // return the module of the package-private interface if
-                        // loader is the module's class loader
-                        return m;
-                    } else {
+                    if (getLoader(m) != loader) {
                         // the specified loader is not the same class loader of the non-public interface
                         throw new IllegalArgumentException(
                                 "non-public interface is not defined by the given loader");
                     }
+                    target = m;
+                }
 
+                // validate if the target module can access all other interfaces
+                for (Class<?> intf : interfaces) {
+                    Module m = intf.getModule();
+                    if (m == target) continue;
+
+                    if (!target.canRead(m) || !m.isExported(intf.getPackageName(), target)) {
+                        throw new IllegalArgumentException(target + " can't access " + intf.getName());
+                    }
                 }
+
+                // return the module of the package-private interface
+                return target;
             }
 
             // all proxy interfaces are public and at least one in a non-exported package
@@ -898,7 +905,7 @@
             if (!target.canRead(m)) {
                 Modules.addReads(target, m);
             }
-            String pn = packageName(c);
+            String pn = c.getPackageName();
             if (!m.isExported(pn, target)) {
                 Modules.addExports(m, pn, target);
             }
@@ -977,10 +984,11 @@
      * must be visible by name through the specified class loader.
      *
      * <li>All non-public interfaces must be in the same package
-     * and module and defined by the specified class loader;
-     * otherwise, it would not be possible for the proxy class to
-     * implement all of the interfaces, regardless of what package it is
-     * defined in.
+     * and module, defined by the specified class loader and
+     * the module of the non-public interfaces can access all of
+     * the interface types; otherwise, it would not be possible for
+     * the proxy class to implement all of the interfaces,
+     * regardless of what package it is defined in.
      *
      * <li>For any set of member methods of the specified interfaces
      * that have the same signature:
--- a/test/jdk/jigsaw/reflect/Proxy/ProxyClassAccessTest.java	Thu Nov 19 14:04:22 2015 -0800
+++ b/test/jdk/jigsaw/reflect/Proxy/ProxyClassAccessTest.java	Mon Nov 23 19:22:22 2015 -0800
@@ -22,6 +22,11 @@
  */
 
 import java.io.File;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleFinder;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Layer;
+import java.lang.reflect.Proxy;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
@@ -37,7 +42,7 @@
  * @test
  * @library ../../lib /lib/testlibrary
  * @modules jdk.compiler
- * @build ProxyClassAccessTest CompilerUtils jdk.testlibrary.*
+ * @build ProxyClassAccessTest q.NP CompilerUtils jdk.testlibrary.*
  * @run testng ProxyClassAccessTest
  * @summary Driver for testing proxy class doesn't have access to
  *          types referenced by proxy interfaces
@@ -78,4 +83,36 @@
 
         assertTrue(exitValue == 0);
     }
+
+    /**
+     * Test unnamed module has no access to other proxy interface
+     */
+    @Test
+    public void testNoReadAccess() throws Exception {
+        ModuleFinder finder = ModuleFinder.of(MODS_DIR);
+        Configuration cf = Configuration
+                .resolve(ModuleFinder.empty(), Layer.boot(), finder, modules).bind();
+
+        ClassLoader loader = new ModuleClassLoader(cf);
+        Layer layer = Layer.create(cf, mn -> loader);
+        Class<?>[] interfaces = new Class<?>[] {
+                Class.forName("p.one.I", false, loader),
+                Class.forName("q.NP", false, loader)     // non-public interface in unnamed module
+        };
+        checkIAE(loader, interfaces);
+    }
+
+    private void checkIAE(ClassLoader loader, Class<?>[] interfaces) {
+        try {
+            Proxy.getProxyClass(loader, interfaces);
+            throw new RuntimeException("Expected IllegalArgumentException thrown");
+        } catch (IllegalArgumentException e) {}
+
+        try {
+            Proxy.newProxyInstance(loader, interfaces,
+                (proxy, m, params) -> { throw new RuntimeException(m.toString()); });
+            throw new RuntimeException("Expected IllegalArgumentException thrown");
+        } catch (IllegalArgumentException e) {}
+    }
+
 }
--- a/test/jdk/jigsaw/reflect/Proxy/ProxyLayerTest.java	Thu Nov 19 14:04:22 2015 -0800
+++ b/test/jdk/jigsaw/reflect/Proxy/ProxyLayerTest.java	Mon Nov 23 19:22:22 2015 -0800
@@ -80,7 +80,8 @@
     public void testProxyInUnnamed() throws Exception {
         ModuleFinder finder = ModuleFinder.of(MODS_DIR);
         Configuration cf = Configuration
-                .resolve(ModuleFinder.empty(), Layer.boot(), finder, modules).bind();
+                .resolve(ModuleFinder.empty(), Layer.boot(), finder, modules)
+                .bind();
 
         ClassLoader loader = new ModuleClassLoader(cf);
         Layer layer = Layer.create(cf, mn -> loader);
@@ -102,6 +103,7 @@
         assertFalse(pkg.isSealed());
         assertEquals(proxyClass.getModule().getLayer(), null);
     }
+
     /**
      * Test proxy implementing interfaces in a Layer and defined in a
      * dynamic module
@@ -130,6 +132,41 @@
         assertEquals(proxyClass.getModule().getLayer(), null);
     }
 
+    /**
+     * Test proxy implementing interfaces that the target module has no access
+     */
+    @Test
+    public void testNoReadAccess() throws Exception {
+        ModuleFinder finder = ModuleFinder.of(MODS_DIR);
+        Configuration cf = Configuration
+                .resolve(ModuleFinder.empty(), Layer.boot(), finder, modules).bind();
+
+        ClassLoader loader = new ModuleClassLoader(cf);
+        Layer layer = Layer.create(cf, mn -> loader);
+
+        assertTrue(layer.findModule("m1").isPresent());
+        assertTrue(layer.findModule("m2").isPresent());
+        assertTrue(layer.findModule("m3").isPresent());
+
+        Class<?>[] interfaces = new Class<?>[] {
+                Class.forName("p.one.I", false, loader),
+                Class.forName("p.two.B", false, loader)   // non-public interface but exported package
+        };
+        checkIAE(loader, interfaces);
+    }
+
+    private void checkIAE(ClassLoader loader, Class<?>[] interfaces) {
+        try {
+            Proxy.getProxyClass(loader, interfaces);
+            throw new RuntimeException("Expected IllegalArgumentException thrown");
+        } catch (IllegalArgumentException e) {}
+
+        try {
+            Proxy.newProxyInstance(loader, interfaces, handler);
+            throw new RuntimeException("Expected IllegalArgumentException thrown");
+        } catch (IllegalArgumentException e) {}
+    }
+
     private final static InvocationHandler handler =
             (proxy, m, params) -> { throw new RuntimeException(m.toString()); };
 
--- a/test/jdk/jigsaw/reflect/Proxy/ProxyModuleMapping.java	Thu Nov 19 14:04:22 2015 -0800
+++ b/test/jdk/jigsaw/reflect/Proxy/ProxyModuleMapping.java	Mon Nov 23 19:22:22 2015 -0800
@@ -26,9 +26,9 @@
 import java.lang.reflect.Module;
 import java.lang.reflect.Proxy;
 
-/* @test
+/*
+ * @test
  * @summary Basic test of proxy module mapping and the access to Proxy class
- *          call Constructor.verifyNewInstanceAccess
  * @modules java.base/sun.invoke
  */
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jigsaw/reflect/Proxy/q/NP.java	Mon Nov 23 19:22:22 2015 -0800
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015, 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.
+ *
+ * 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 q;
+
+interface NP {
+}