]> source.dussan.org Git - javassist.git/commitdiff
adds javassist.util.proxy.ProxyFactory#onlyPublicMethods. If true, Javassist uses...
authorchibash <chiba@javassist.org>
Sat, 15 Apr 2017 14:57:23 +0000 (23:57 +0900)
committerchibash <chiba@javassist.org>
Sat, 15 Apr 2017 14:57:23 +0000 (23:57 +0900)
javassist.jar
src/main/javassist/util/proxy/DefineClassHelper.java
src/main/javassist/util/proxy/FactoryHelper.java
src/main/javassist/util/proxy/ProxyFactory.java
src/test/test/javassist/proxy/ProxySimpleTest.java

index 068a36b7ee8cbadf4f51579ee51b0193ac3b2d34..8d9c19822ef33ed873a0faffce8c4d600a06b27b 100644 (file)
Binary files a/javassist.jar and b/javassist.jar differ
index c57499256e35a6307a87cedccbdd3d63bb1f9f6c..97c83800c282c1249c55663b66fb0077b33351e3 100644 (file)
@@ -68,6 +68,15 @@ public class DefineClassHelper {
     /**
      * Loads a class file by a given class loader.
      *
+     * <p>This first tries to use {@code sun.misc.Unsafe} to load a class.
+     * Then it tries to use a {@code protected} method in {@code java.lang.ClassLoader}
+     * via {@code PrivilegedAction}.  Since the latter approach is not available
+     * any longer by default in Java 9 or later, the JVM argument
+     * {@code --add-opens java.base/java.lang=ALL-UNNAMED} must be given to the JVM.
+     * If this JVM argument cannot be given, {@link #toPublicClass(String,byte[])}
+     * should be used instead.
+     * </p>
+     *
      * @param domain        if it is null, a default domain is used.
      * @since 3.22
      */
@@ -75,9 +84,7 @@ public class DefineClassHelper {
                                    ProtectionDomain domain, byte[] bcode)
         throws CannotCompileException
     {
-        if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_9)
-            return toClass2(className, loader, domain, bcode);
-        else {
+        if (ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9)
             if (sunMiscUnsafe != null)
                 try {
                     return sunMiscUnsafe.defineClass(className, bcode, 0, bcode.length,
@@ -85,14 +92,24 @@ public class DefineClassHelper {
                 }
                 catch (Throwable t2) {}
 
-            try {
-                Lookup lookup = MethodHandles.lookup();
-                lookup = lookup.dropLookupMode(java.lang.invoke.MethodHandles.Lookup.PRIVATE);
-                return lookup.defineClass(bcode);
-            }
-            catch (Throwable t) {
-                throw new CannotCompileException(t);
-            }
+        return toClass2(className, loader, domain, bcode);
+    }
+
+    /**
+     * Loads a class file by {@code java.lang.invoke.MethodHandles.Lookup}.
+     *
+     * @since 3.22
+     */
+    static Class<?> toPublicClass(String className, byte[] bcode)
+        throws CannotCompileException
+    {
+        try {
+            Lookup lookup = MethodHandles.lookup();
+            lookup = lookup.dropLookupMode(java.lang.invoke.MethodHandles.Lookup.PRIVATE);
+            return lookup.defineClass(bcode);
+        }
+        catch (Throwable t) {
+            throw new CannotCompileException(t);
         }
     }
 
index 39114657189964c43dbecd481a223b266fb1e1cd..34af79d3d82081a43a0ead567c2aa63e099616a8 100644 (file)
@@ -104,7 +104,7 @@ public class FactoryHelper {
     /**
      * Loads a class file by a given class loader.
      * This method uses a default protection domain for the class
-     * but it may not work with a security manager or a sigend jar file.
+     * but it may not work with a security manager or a signed jar file.
      *
      * @see #toClass(ClassFile,ClassLoader,ProtectionDomain)
      */
@@ -125,7 +125,10 @@ public class FactoryHelper {
     {
         try {
             byte[] b = toBytecode(cf);
-            return DefineClassHelper.toClass(cf.getName(), loader, domain, b);
+            if (ProxyFactory.onlyPublicMethods)
+                return DefineClassHelper.toPublicClass(cf.getName(), b);
+            else
+                return DefineClassHelper.toClass(cf.getName(), loader, domain, b);
         }
         catch (IOException e) {
             throw new CannotCompileException(e);
index 817925852b733f443a174857b7c1195b2add3a6a..35c6c11e3d43237ffc6549caa2ee28e1d5770140 100644 (file)
@@ -173,6 +173,29 @@ public class ProxyFactory {
      */
     private boolean factoryWriteReplace;
 
+    /**
+     * <p>If true, only public/protected methods are forwarded to a proxy object.
+     * The class for that proxy object is loaded by the {@code defineClass} method
+     * in {@code java.lang.invoke.MethodHandles.Lookup}, which is available in
+     * Java 9 or later.  This works even when {@code sun.misc.Unsafe} is not
+     * available for some reasons (it is already deprecated in Java 9).</p>
+     *
+     * <p>To load a class, Javassist first tries to use {@code sun.misc.Unsafe} and,
+     * if not available, it uses a {@code protected} method in {@code java.lang.ClassLoader}
+     * via {@code PrivilegedAction}.  Since the latter approach is not available
+     * any longer by default in Java 9 or later, the JVM argument
+     * {@code --add-opens java.base/java.lang=ALL-UNNAMED} must be given to the JVM
+     * when it is used (because of lack of {@code sun.misc.Unsafe}).
+     * If this argument cannot be given to the JVM, {@code onlyPublicMethods} should
+     * be set to {@code true}.  Javassist will try to load by using
+     * {@code java.lang.invoke.MethodHandles.Lookup}.</p>
+     *
+     * <p>The default value is {@code false}.</p>
+     *
+     * @see DefineClassHelper#toClass(String, ClassLoader, ProtectionDomain, byte[])
+     * @since 3.22
+     */
+    public static boolean onlyPublicMethods = false;
 
     /**
      * If the value of this variable is not null, the class file of
@@ -803,7 +826,7 @@ public class ProxyFactory {
         if (Modifier.isFinal(superClass.getModifiers()))
             throw new RuntimeException(superName + " is final");
         
-        if (basename.startsWith("java."))
+        if (basename.startsWith("java.") || onlyPublicMethods)
             basename = "javassist.util.proxy." + basename.replace('.', '_');
     }
 
index 249a25420eafa22df9cb82914d8e5ba46936a116..bbae9d8d6e85cbb4ad4e896b487393cf696a6dfe 100644 (file)
@@ -1,7 +1,6 @@
 package test.javassist.proxy;
 
 import junit.framework.TestCase;
-import testproxy.ProxyTester.ReadWriteData;
 
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -209,4 +208,72 @@ public class ProxySimpleTest extends TestCase {
     public static class Default4 extends Default3 {
         public int baz() { return super.baz(); }
     }
+
+    public void testPublicProxy() throws Exception {
+        ProxyFactory f = new ProxyFactory();
+        f.writeDirectory = "./proxy";
+        f.setSuperclass(PubProxy.class);
+        Class c = f.createClass();
+        MethodHandler mi = new MethodHandler() {
+            public Object invoke(Object self, Method m, Method proceed,
+                                 Object[] args) throws Throwable {
+                PubProxy.result += args[0].toString();
+                return proceed.invoke(self, args);
+            }
+        };
+        PubProxy.result = "";
+        PubProxy foo = (PubProxy)c.getConstructor().newInstance();
+        ((Proxy)foo).setHandler(mi);
+        foo.foo(1);
+        foo.bar(2);
+        foo.baz(3);
+        assertEquals("c1p2q3r", PubProxy.result);
+    }
+
+    public static class PubProxy {
+        public static String result;
+        public PubProxy() { result += "c"; }
+        PubProxy(int i) { result += "d"; }
+        void foo(int i) { result += "p"; }
+        protected void bar(int i) { result += "q"; }
+        public void baz(int i) { result += "r"; }
+    }
+
+    public void testPublicProxy2() throws Exception {
+        boolean b = ProxyFactory.onlyPublicMethods;
+        ProxyFactory.onlyPublicMethods = true;
+        ProxyFactory f = new ProxyFactory();
+        f.writeDirectory = "./proxy";
+        f.setSuperclass(PubProxy2.class);
+        Class c = f.createClass();
+        MethodHandler mi = new MethodHandler() {
+            public Object invoke(Object self, Method m, Method proceed,
+                                 Object[] args) throws Throwable {
+                PubProxy2.result += args[0].toString();
+                return proceed.invoke(self, args);
+            }
+        };
+
+        PubProxy2.result = "";
+        try {
+            PubProxy2 foo = (PubProxy2)c.getConstructor().newInstance();
+            ((Proxy)foo).setHandler(mi);
+            foo.foo(1);     // mi does not intercept this call.
+            foo.bar(2);
+            foo.baz(3);
+            assertEquals("cp2q3r", PubProxy2.result);
+        }
+        finally {
+            ProxyFactory.onlyPublicMethods = b;
+        }
+    }
+
+    public static class PubProxy2 {
+        public static String result;
+        public PubProxy2() { result += "c"; }
+        PubProxy2(int i) { result += "d"; }
+        void foo(int i) { result += "p"; }
+        protected void bar(int i) { result += "q"; }
+        public void baz(int i) { result += "r"; }
+    }
 }