aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/javassist/util
diff options
context:
space:
mode:
authorchibash <chiba@javassist.org>2018-09-08 15:47:38 +0900
committerchibash <chiba@javassist.org>2018-09-08 15:47:38 +0900
commit6320bc4e14350af392b285b1b1ea312673625b21 (patch)
tree8fd478f2baa4bd75048432ed3474d383131f5f04 /src/main/javassist/util
parent0958148acbb1b737ca90f78670a4d13c6f321416 (diff)
downloadjavassist-6320bc4e14350af392b285b1b1ea312673625b21.tar.gz
javassist-6320bc4e14350af392b285b1b1ea312673625b21.zip
changes CtClass#toClass() and ClassPool#toClass() etc. to support Java 11,
in other words, java.lang.invoke.MethodHandles.Lookup.
Diffstat (limited to 'src/main/javassist/util')
-rw-r--r--src/main/javassist/util/proxy/DefineClassHelper.java92
-rw-r--r--src/main/javassist/util/proxy/FactoryHelper.java17
-rw-r--r--src/main/javassist/util/proxy/ProxyFactory.java18
3 files changed, 109 insertions, 18 deletions
diff --git a/src/main/javassist/util/proxy/DefineClassHelper.java b/src/main/javassist/util/proxy/DefineClassHelper.java
index bc6866ce..dea53670 100644
--- a/src/main/javassist/util/proxy/DefineClassHelper.java
+++ b/src/main/javassist/util/proxy/DefineClassHelper.java
@@ -34,9 +34,24 @@ import javassist.bytecode.ClassFile;
public class DefineClassHelper {
private static abstract class Helper {
- abstract Class<?> defineClass(String name, byte[] b, int off, int len,
+ abstract Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor,
ClassLoader loader, ProtectionDomain protectionDomain)
- throws ClassFormatError;
+ throws ClassFormatError, CannotCompileException;
+ }
+
+ private static class Java11 extends JavaOther {
+ Class<?> defineClass(String name, byte[] bcode, int off, int len, Class<?> neighbor,
+ ClassLoader loader, ProtectionDomain protectionDomain)
+ throws ClassFormatError, CannotCompileException
+ {
+ if (neighbor != null)
+ return toClass(neighbor, bcode);
+ else {
+ // Lookup#defineClass() is not available. So fallback to invoking defineClass on
+ // ClassLoader, which causes a warning message.
+ return super.defineClass(name, bcode, off, len, neighbor, loader, protectionDomain);
+ }
+ }
}
private static class Java9 extends Helper {
@@ -119,7 +134,7 @@ public class DefineClassHelper {
}
@Override
- Class<?> defineClass(String name, byte[] b, int off, int len,
+ Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor,
ClassLoader loader, ProtectionDomain protectionDomain)
throws ClassFormatError
{
@@ -152,7 +167,7 @@ public class DefineClassHelper {
}
@Override
- Class<?> defineClass(String name, byte[] b, int off, int len,
+ Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor,
ClassLoader loader, ProtectionDomain protectionDomain)
throws ClassFormatError
{
@@ -187,11 +202,12 @@ public class DefineClassHelper {
}
@Override
- Class<?> defineClass(String name, byte[] b, int off, int len,
+ Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor,
ClassLoader loader, ProtectionDomain protectionDomain)
- throws ClassFormatError
+ throws ClassFormatError, CannotCompileException
{
- if (stack.getCallerClass() != DefineClassHelper.class)
+ Class<?> klass = stack.getCallerClass();
+ if (klass != DefineClassHelper.class && klass != this.getClass())
throw new IllegalAccessError("Access denied for caller.");
try {
SecurityActions.setAccessible(defineClass, true);
@@ -201,7 +217,7 @@ public class DefineClassHelper {
} catch (Throwable e) {
if (e instanceof ClassFormatError) throw (ClassFormatError) e;
if (e instanceof RuntimeException) throw (RuntimeException) e;
- throw new ClassFormatError(e.getMessage());
+ throw new CannotCompileException(e);
}
finally {
SecurityActions.setAccessible(defineClass, false);
@@ -212,7 +228,7 @@ public class DefineClassHelper {
// Java 11+ removed sun.misc.Unsafe.defineClass, so we fallback to invoking defineClass on
// ClassLoader until we have an implementation that uses MethodHandles.Lookup.defineClass
private static final Helper privileged = ClassFile.MAJOR_VERSION > ClassFile.JAVA_10
- ? new JavaOther()
+ ? new Java11()
: ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9
? new Java9()
: ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7 ? new Java7() : new JavaOther();
@@ -220,7 +236,9 @@ 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.
+ * <p>This first tries to use {@code java.lang.invoke.MethodHandle} to load a class.
+ * Otherwise, or if {@code neighbor} is null,
+ * this 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
@@ -229,19 +247,28 @@ public class DefineClassHelper {
* should be used instead.
* </p>
*
+ * @param className the name of the loaded class.
+ * @param neighbor the class contained in the same package as the loaded class.
+ * @param loader the class loader. It can be null if {@code neighbor} is not null
+ * and the JVM is Java 11 or later.
* @param domain if it is null, a default domain is used.
+ * @parma bcode the bytecode for the loaded class.
* @since 3.22
*/
- public static Class<?> toClass(String className, ClassLoader loader,
+ public static Class<?> toClass(String className, Class<?> neighbor, ClassLoader loader,
ProtectionDomain domain, byte[] bcode)
throws CannotCompileException
{
try {
- return privileged.defineClass(className, bcode, 0, bcode.length, loader, domain);
+ return privileged.defineClass(className, bcode, 0, bcode.length,
+ neighbor, loader, domain);
}
catch (RuntimeException e) {
throw e;
}
+ catch (CannotCompileException e) {
+ throw e;
+ }
catch (ClassFormatError e) {
Throwable t = e.getCause();
throw new CannotCompileException(t == null ? e : t);
@@ -251,6 +278,47 @@ public class DefineClassHelper {
}
}
+
+ /**
+ * Loads a class file by {@code java.lang.invoke.MethodHandles.Lookup}.
+ * It is obtained by using {@code neighbor}.
+ *
+ * @param neighbor a class belonging to the same package that the loaded
+ * class belogns to.
+ * @param bcode the bytecode.
+ * @since 3.24
+ */
+ public static Class<?> toClass(Class<?> neighbor, byte[] bcode)
+ throws CannotCompileException
+ {
+ try {
+ Lookup lookup = MethodHandles.lookup();
+ Lookup prvlookup = MethodHandles.privateLookupIn(neighbor, lookup);
+ return prvlookup.defineClass(bcode);
+ } catch (IllegalAccessException | IllegalArgumentException e) {
+ throw new CannotCompileException(e.getMessage() + ": " + neighbor.getName()
+ + " has no permission to define the class");
+ }
+ }
+
+ /**
+ * Loads a class file by {@code java.lang.invoke.MethodHandles.Lookup}.
+ * It can be obtained by {@code MethodHandles.lookup()} called from
+ * somewhere in the package that the loaded class belongs to.
+ *
+ * @param bcode the bytecode.
+ * @since 3.24
+ */
+ public static Class<?> toClass(Lookup lookup, byte[] bcode)
+ throws CannotCompileException
+ {
+ try {
+ return lookup.defineClass(bcode);
+ } catch (IllegalAccessException | IllegalArgumentException e) {
+ throw new CannotCompileException(e.getMessage());
+ }
+ }
+
/**
* Loads a class file by {@code java.lang.invoke.MethodHandles.Lookup}.
*
diff --git a/src/main/javassist/util/proxy/FactoryHelper.java b/src/main/javassist/util/proxy/FactoryHelper.java
index 1fb63b65..38fcbf42 100644
--- a/src/main/javassist/util/proxy/FactoryHelper.java
+++ b/src/main/javassist/util/proxy/FactoryHelper.java
@@ -104,28 +104,37 @@ public class FactoryHelper {
* This method uses a default protection domain for the class
* but it may not work with a security manager or a signed jar file.
*
- * @see #toClass(ClassFile,ClassLoader,ProtectionDomain)
+ * @see #toClass(ClassFile,Class,ClassLoader,ProtectionDomain)
+ * @deprecated
*/
public static Class<?> toClass(ClassFile cf, ClassLoader loader)
throws CannotCompileException
{
- return toClass(cf, loader, null);
+ return toClass(cf, null, loader, null);
}
/**
* Loads a class file by a given class loader.
*
+ * @param neighbor a class belonging to the same package that
+ * the loaded class belongs to.
+ * It can be null.
+ * @param loader The class loader. It can be null if {@code neighbor}
+ * is not null.
* @param domain if it is null, a default domain is used.
* @since 3.3
*/
- public static Class<?> toClass(ClassFile cf, ClassLoader loader, ProtectionDomain domain)
+ public static Class<?> toClass(ClassFile cf, Class<?> neighbor,
+ ClassLoader loader, ProtectionDomain domain)
throws CannotCompileException
{
try {
byte[] b = toBytecode(cf);
if (ProxyFactory.onlyPublicMethods)
return DefineClassHelper.toPublicClass(cf.getName(), b);
- return DefineClassHelper.toClass(cf.getName(), loader, domain, b);
+ else
+ return DefineClassHelper.toClass(cf.getName(), neighbor,
+ loader, domain, b);
}
catch (IOException e) {
throw new CannotCompileException(e);
diff --git a/src/main/javassist/util/proxy/ProxyFactory.java b/src/main/javassist/util/proxy/ProxyFactory.java
index d3fcddbd..07b0e161 100644
--- a/src/main/javassist/util/proxy/ProxyFactory.java
+++ b/src/main/javassist/util/proxy/ProxyFactory.java
@@ -213,7 +213,7 @@ public class ProxyFactory {
*
* <p>The default value is {@code false}.</p>
*
- * @see DefineClassHelper#toClass(String, ClassLoader, ProtectionDomain, byte[])
+ * @see DefineClassHelper#toClass(String, Class<?>, ClassLoader, ProtectionDomain, byte[])
* @since 3.22
*/
public static boolean onlyPublicMethods = false;
@@ -549,7 +549,7 @@ public class ProxyFactory {
if (writeDirectory != null)
FactoryHelper.writeFile(cf, writeDirectory);
- thisClass = FactoryHelper.toClass(cf, cl, getDomain());
+ thisClass = FactoryHelper.toClass(cf, getClassInTheSamePackage(), cl, getDomain());
setField(FILTER_SIGNATURE_FIELD, signature);
// legacy behaviour : we only set the default interceptor static field if we are not using the cache
if (!factoryUseCache) {
@@ -562,6 +562,20 @@ public class ProxyFactory {
}
+ /**
+ * Obtains a class belonging to the same package that the created
+ * proxy class belongs to. It is used to obtain an appropriate
+ * {@code java.lang.invoke.MethodHandles.Lookup}.
+ */
+ private Class<?> getClassInTheSamePackage() {
+ if (superClass != null && superClass != OBJECT_TYPE)
+ return superClass;
+ else if (interfaces != null && interfaces.length > 0)
+ return interfaces[0];
+ else
+ return this.getClass(); // maybe wrong?
+ }
+
private void setField(String fieldName, Object value) {
if (thisClass != null && value != null)
try {