diff options
author | chibash <chiba@javassist.org> | 2018-09-08 15:47:38 +0900 |
---|---|---|
committer | chibash <chiba@javassist.org> | 2018-09-08 15:47:38 +0900 |
commit | 6320bc4e14350af392b285b1b1ea312673625b21 (patch) | |
tree | 8fd478f2baa4bd75048432ed3474d383131f5f04 /src/main/javassist/util | |
parent | 0958148acbb1b737ca90f78670a4d13c6f321416 (diff) | |
download | javassist-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.java | 92 | ||||
-rw-r--r-- | src/main/javassist/util/proxy/FactoryHelper.java | 17 | ||||
-rw-r--r-- | src/main/javassist/util/proxy/ProxyFactory.java | 18 |
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 { |