/**
* 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
*/
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,
}
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);
}
}
/**
* 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)
*/
{
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);
*/
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
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('.', '_');
}
package test.javassist.proxy;
import junit.framework.TestCase;
-import testproxy.ProxyTester.ReadWriteData;
import java.io.FileInputStream;
import java.io.FileOutputStream;
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"; }
+ }
}