in other words, java.lang.invoke.MethodHandles.Lookup.tags/rel_3_24_0_rc
.project | .project | ||||
.settings | .settings | ||||
TestLog.xml | TestLog.xml | ||||
*~ |
<p>-version 3.24 | <p>-version 3.24 | ||||
<ul> | <ul> | ||||
<li>Java 11 supports.</li> | |||||
<li>JIRA JASSIST-267.</li> | <li>JIRA JASSIST-267.</li> | ||||
<li>Github PR #218.</li> | <li>Github PR #218.</li> | ||||
</ul> | </ul> |
* the <code>getClassLoader()</code> method. | * the <code>getClassLoader()</code> method. | ||||
* If the program is running on some application | * If the program is running on some application | ||||
* server, the context class loader might be inappropriate to load the | * server, the context class loader might be inappropriate to load the | ||||
* class. | |||||
* class.</p> | |||||
* | * | ||||
* <p>This method is provided for convenience. If you need more | * <p>This method is provided for convenience. If you need more | ||||
* complex functionality, you should write your own class loader. | * complex functionality, you should write your own class loader. | ||||
* | * | ||||
* <p><b>Warining:</b> A Class object returned by this method may not | |||||
* <p><b>Warining:</b> | |||||
* This method should not be used in Java 11 or later. | |||||
* Use {@link #toClass(CtClass,Class)}. | |||||
* </p> | |||||
* | |||||
* <p><b>Warining:</b> | |||||
* A Class object returned by this method may not | |||||
* work with a security manager or a signed jar file because a | * work with a security manager or a signed jar file because a | ||||
* protection domain is not specified. | |||||
* protection domain is not specified.</p> | |||||
* | * | ||||
* @see #toClass(CtClass, java.lang.ClassLoader, ProtectionDomain) | |||||
* @see #toCalss(CtClass,Class) | |||||
* @see #toClass(CtClass,Class,java.lang.ClassLoader,ProtectionDomain) | |||||
* @see #getClassLoader() | * @see #getClassLoader() | ||||
*/ | */ | ||||
public Class toClass(CtClass clazz) throws CannotCompileException { | public Class toClass(CtClass clazz) throws CannotCompileException { | ||||
/** | /** | ||||
* Converts the class to a <code>java.lang.Class</code> object. | * Converts the class to a <code>java.lang.Class</code> object. | ||||
* Do not override this method any more at a subclass because | * Do not override this method any more at a subclass because | ||||
* <code>toClass(CtClass)</code> never calls this method. | |||||
* {@link #toClass(CtClass)} will never calls this method. | |||||
* | * | ||||
* <p><b>Warining:</b> A Class object returned by this method may not | * <p><b>Warining:</b> A Class object returned by this method may not | ||||
* work with a security manager or a signed jar file because a | * work with a security manager or a signed jar file because a | ||||
* protection domain is not specified. | * protection domain is not specified. | ||||
* | * | ||||
* @deprecated Replaced by {@link #toClass(CtClass,ClassLoader,ProtectionDomain)}. | |||||
* @deprecated Replaced by {@link #toClass(CtClass,Class,ClassLoader,ProtectionDomain)}. | |||||
* A subclass of <code>ClassPool</code> that has been | * A subclass of <code>ClassPool</code> that has been | ||||
* overriding this method should be modified. It should override | * overriding this method should be modified. It should override | ||||
* {@link #toClass(CtClass,ClassLoader,ProtectionDomain)}. | |||||
* {@link #toClass(CtClass,Class,ClassLoader,ProtectionDomain)}. | |||||
*/ | */ | ||||
public Class toClass(CtClass ct, ClassLoader loader) | public Class toClass(CtClass ct, ClassLoader loader) | ||||
throws CannotCompileException | throws CannotCompileException | ||||
{ | { | ||||
return toClass(ct, loader, null); | |||||
return toClass(ct, null, loader, null); | |||||
} | } | ||||
/** | /** | ||||
* loaded by the given class loader to construct a | * loaded by the given class loader to construct a | ||||
* <code>java.lang.Class</code> object. Since a private method | * <code>java.lang.Class</code> object. Since a private method | ||||
* on the class loader is invoked through the reflection API, | * on the class loader is invoked through the reflection API, | ||||
* the caller must have permissions to do that. | |||||
* the caller must have permissions to do that.</p> | |||||
* | * | ||||
* <p>An easy way to obtain <code>ProtectionDomain</code> object is | * <p>An easy way to obtain <code>ProtectionDomain</code> object is | ||||
* to call <code>getProtectionDomain()</code> | * to call <code>getProtectionDomain()</code> | ||||
* class belongs to. | * class belongs to. | ||||
* | * | ||||
* <p>This method is provided for convenience. If you need more | * <p>This method is provided for convenience. If you need more | ||||
* complex functionality, you should write your own class loader. | |||||
* complex functionality, you should write your own class loader.</p> | |||||
* | * | ||||
* @param ct the class converted into {@code java.lang.Class}. | |||||
* @param loader the class loader used to load this class. | * @param loader the class loader used to load this class. | ||||
* For example, the loader returned by | * For example, the loader returned by | ||||
* <code>getClassLoader()</code> can be used | * <code>getClassLoader()</code> can be used | ||||
* | * | ||||
* @see #getClassLoader() | * @see #getClassLoader() | ||||
* @since 3.3 | * @since 3.3 | ||||
* @deprecated Replaced by {@link #toClass(CtClass,Class,ClassLoader,ProtectionDomain)}. | |||||
*/ | */ | ||||
public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain) | public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain) | ||||
throws CannotCompileException | throws CannotCompileException | ||||
{ | |||||
return toClass(ct, null, loader, domain); | |||||
} | |||||
/** | |||||
* Converts the class to a <code>java.lang.Class</code> object. | |||||
* Once this method is called, further modifications are not allowed | |||||
* any more. | |||||
* | |||||
* <p>This method is available in Java 9 or later. | |||||
* It loads the class | |||||
* by using {@code java.lang.invoke.MethodHandles} with {@code neighbor}. | |||||
* </p> | |||||
* | |||||
* @param ct the class converted into {@code java.lang.Class}. | |||||
* @param neighbor a class belonging to the same package that | |||||
* the converted class belongs to. | |||||
* @since 3.24 | |||||
*/ | |||||
public Class<?> toClass(CtClass ct, Class<?> neighbor) | |||||
throws CannotCompileException | |||||
{ | |||||
try { | |||||
return javassist.util.proxy.DefineClassHelper.toClass(neighbor, | |||||
ct.toBytecode()); | |||||
} | |||||
catch (IOException e) { | |||||
throw new CannotCompileException(e); | |||||
} | |||||
} | |||||
/** | |||||
* Converts the class to a <code>java.lang.Class</code> object. | |||||
* Once this method is called, further modifications are not allowed | |||||
* any more. | |||||
* | |||||
* <p>This method is available in Java 9 or later. | |||||
* It loads the class | |||||
* by using the given {@code java.lang.invoke.MethodHandles.Lookup}. | |||||
* </p> | |||||
* | |||||
* @param ct the class converted into {@code java.lang.Class}. | |||||
* @since 3.24 | |||||
*/ | |||||
public Class<?> toClass(CtClass ct, | |||||
java.lang.invoke.MethodHandles.Lookup lookup) | |||||
throws CannotCompileException | |||||
{ | |||||
try { | |||||
return javassist.util.proxy.DefineClassHelper.toClass(lookup, | |||||
ct.toBytecode()); | |||||
} | |||||
catch (IOException e) { | |||||
throw new CannotCompileException(e); | |||||
} | |||||
} | |||||
/** | |||||
* Converts the class to a <code>java.lang.Class</code> object. | |||||
* Once this method is called, further modifications are not allowed | |||||
* any more. | |||||
* | |||||
* <p>When the JVM is Java 11 or later, this method loads the class | |||||
* by using {@code java.lang.invoke.MethodHandles} with {@code neighbor}. | |||||
* The other arguments {@code loader} and {@code domain} are not used; | |||||
* so they can be null. | |||||
* </p> | |||||
* | |||||
* <p>Otherwise, or when {@code neighbor} is null, | |||||
* the class file represented by the given <code>CtClass</code> is | |||||
* loaded by the given class loader to construct a | |||||
* <code>java.lang.Class</code> object. Since a private method | |||||
* on the class loader is invoked through the reflection API, | |||||
* the caller must have permissions to do that. | |||||
* | |||||
* <p>An easy way to obtain <code>ProtectionDomain</code> object is | |||||
* to call <code>getProtectionDomain()</code> | |||||
* in <code>java.lang.Class</code>. It returns the domain that the | |||||
* class belongs to. | |||||
* | |||||
* <p>If your program is for only Java 9 or later, don't use this method. | |||||
* Use {@link #toClass(CtClass,Class)} or | |||||
* {@link #toClass(CtClass)CtClass,java.lang.invoke.MethodHandles.Lookup)}. | |||||
* </p> | |||||
* | |||||
* @param ct the class converted into {@code java.lang.Class}. | |||||
* @param neighbor a class belonging to the same package that | |||||
* the converted class belongs to. | |||||
* It can be null. | |||||
* @param loader the class loader used to load this class. | |||||
* For example, the loader returned by | |||||
* <code>getClassLoader()</code> can be used | |||||
* for this parameter. | |||||
* @param domain the protection domain for the class. | |||||
* If it is null, the default domain created | |||||
* by <code>java.lang.ClassLoader</code> is used. | |||||
* | |||||
* @see #getClassLoader() | |||||
* @since 3.24 | |||||
*/ | |||||
public Class toClass(CtClass ct, Class<?> neighbor, ClassLoader loader, | |||||
ProtectionDomain domain) | |||||
throws CannotCompileException | |||||
{ | { | ||||
try { | try { | ||||
return javassist.util.proxy.DefineClassHelper.toClass(ct.getName(), | return javassist.util.proxy.DefineClassHelper.toClass(ct.getName(), | ||||
loader, domain, ct.toBytecode()); | |||||
neighbor, loader, domain, ct.toBytecode()); | |||||
} | } | ||||
catch (IOException e) { | catch (IOException e) { | ||||
throw new CannotCompileException(e); | throw new CannotCompileException(e); |
* work with a security manager or a signed jar file because a | * work with a security manager or a signed jar file because a | ||||
* protection domain is not specified. | * protection domain is not specified. | ||||
* | * | ||||
* @see #toClass(java.lang.ClassLoader,ProtectionDomain) | |||||
* @see #toClass(java.lang.invoke.MethodHandles.Lookup) | |||||
* @see #toClass(Class) | |||||
* @see ClassPool#toClass(CtClass) | * @see ClassPool#toClass(CtClass) | ||||
*/ | */ | ||||
public Class<?> toClass() throws CannotCompileException { | public Class<?> toClass() throws CannotCompileException { | ||||
return getClassPool().toClass(this); | return getClassPool().toClass(this); | ||||
} | } | ||||
/** | |||||
* Converts this class to a <code>java.lang.Class</code> object. | |||||
* Once this method is called, further modifications are not | |||||
* allowed any more. | |||||
* | |||||
* <p>This method is provided for convenience. If you need more | |||||
* complex functionality, you should write your own class loader. | |||||
* | |||||
* <p>Note: this method calls <code>toClass()</code> | |||||
* in <code>ClassPool</code>. | |||||
* | |||||
* <p><b>Warining:</b> A Class object returned by this method may not | |||||
* work with a security manager or a signed jar file because a | |||||
* protection domain is not specified. | |||||
* | |||||
* @param neighbor A class belonging to the same package that this | |||||
* class belongs to. It is used to load the class. | |||||
* @see ClassPool#toClass(CtClass,Class) | |||||
* @since 3.24 | |||||
*/ | |||||
public Class<?> toClass(Class<?> neighbor) throws CannotCompileException | |||||
{ | |||||
return getClassPool().toClass(this, neighbor); | |||||
} | |||||
/** | |||||
* Converts this class to a <code>java.lang.Class</code> object. | |||||
* Once this method is called, further modifications are not | |||||
* allowed any more. | |||||
* | |||||
* <p>This method is provided for convenience. If you need more | |||||
* complex functionality, you should write your own class loader. | |||||
* | |||||
* <p>Note: this method calls <code>toClass()</code> | |||||
* in <code>ClassPool</code>. | |||||
* | |||||
* <p><b>Warining:</b> A Class object returned by this method may not | |||||
* work with a security manager or a signed jar file because a | |||||
* protection domain is not specified. | |||||
* | |||||
* @param lookup used when loading the class. It has to have | |||||
* an access right to define a new class. | |||||
* @see ClassPool#toClass(CtClass,java.lang.invoke.MethodHandles.Lookup) | |||||
* @since 3.24 | |||||
*/ | |||||
public Class<?> toClass(java.lang.invoke.MethodHandles.Lookup lookup) | |||||
throws CannotCompileException | |||||
{ | |||||
return getClassPool().toClass(this, lookup); | |||||
} | |||||
/** | /** | ||||
* Converts this class to a <code>java.lang.Class</code> object. | * Converts this class to a <code>java.lang.Class</code> object. | ||||
* Once this method is called, further modifications are not allowed | * Once this method is called, further modifications are not allowed | ||||
if (loader == null) | if (loader == null) | ||||
loader = cp.getClassLoader(); | loader = cp.getClassLoader(); | ||||
return cp.toClass(this, loader, domain); | |||||
return cp.toClass(this, null, loader, domain); | |||||
} | } | ||||
/** | /** | ||||
public final Class<?> toClass(ClassLoader loader) | public final Class<?> toClass(ClassLoader loader) | ||||
throws CannotCompileException | throws CannotCompileException | ||||
{ | { | ||||
return getClassPool().toClass(this, loader); | |||||
return getClassPool().toClass(this, null, loader, null); | |||||
} | } | ||||
/** | /** | ||||
* @see ClassPool#doPruning | * @see ClassPool#doPruning | ||||
* | * | ||||
* @see #toBytecode() | * @see #toBytecode() | ||||
* @see #toClass() | |||||
* @see #toClass(Class) | |||||
* @see #writeFile() | * @see #writeFile() | ||||
* @see #instrument(CodeConverter) | * @see #instrument(CodeConverter) | ||||
* @see #instrument(ExprEditor) | * @see #instrument(ExprEditor) |
package javassist; | package javassist; | ||||
import java.io.IOException; | |||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.lang.reflect.InvocationTargetException; | import java.lang.reflect.InvocationTargetException; | ||||
import java.security.ProtectionDomain; | import java.security.ProtectionDomain; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.Hashtable; | |||||
import java.util.HashMap; | |||||
import java.util.Vector; | import java.util.Vector; | ||||
import javassist.bytecode.ClassFile; | import javassist.bytecode.ClassFile; | ||||
* @see javassist.Translator | * @see javassist.Translator | ||||
*/ | */ | ||||
public class Loader extends ClassLoader { | public class Loader extends ClassLoader { | ||||
private Hashtable<String,ClassLoader> notDefinedHere; // must be atomic. | |||||
/** | |||||
* A simpler class loader. | |||||
* This is a class loader that exposes the protected {@code defineClass()} method | |||||
* declared in {@code java.lang.ClassLoader}. It provides a method similar to | |||||
* {@code CtClass#toClass()}. | |||||
* | |||||
* <p>When loading a class, this class loader delegates the work to the | |||||
* parent class loader unless the loaded classes are explicitly given | |||||
* by {@link #invokeDefineClass(CtClass)}. | |||||
* Note that a class {@code Foo} loaded by this class loader is | |||||
* different from the class with the same name {@code Foo} but loaded by | |||||
* another class loader. This is Java's naming rule. | |||||
* </p> | |||||
* | |||||
* @since 3.24 | |||||
*/ | |||||
public static class Simple extends ClassLoader { | |||||
/** | |||||
* Constructs a class loader. | |||||
*/ | |||||
public Simple() {} | |||||
/** | |||||
* Constructs a class loader. | |||||
* @param parent the parent class loader. | |||||
*/ | |||||
public Simple(ClassLoader parent) { | |||||
super(parent); | |||||
} | |||||
/** | |||||
* Invokes the protected {@code defineClass()} in {@code ClassLoader}. | |||||
* It converts the given {@link CtClass} object into a {@code java.lang.Class} object. | |||||
*/ | |||||
public Class<?> invokeDefineClass(CtClass cc) throws IOException, CannotCompileException { | |||||
byte[] code = cc.toBytecode(); | |||||
return defineClass(cc.getName(), code, 0, code.length); | |||||
} | |||||
} | |||||
private HashMap<String,ClassLoader> notDefinedHere; // must be atomic. | |||||
private Vector<String> notDefinedPackages; // must be atomic. | private Vector<String> notDefinedPackages; // must be atomic. | ||||
private ClassPool source; | private ClassPool source; | ||||
private Translator translator; | private Translator translator; | ||||
} | } | ||||
private void init(ClassPool cp) { | private void init(ClassPool cp) { | ||||
notDefinedHere = new Hashtable<String,ClassLoader>(); | |||||
notDefinedHere = new HashMap<String,ClassLoader>(); | |||||
notDefinedPackages = new Vector<String>(); | notDefinedPackages = new Vector<String>(); | ||||
source = cp; | source = cp; | ||||
translator = null; | translator = null; |
public class DefineClassHelper { | public class DefineClassHelper { | ||||
private static abstract class Helper { | 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) | 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 { | private static class Java9 extends Helper { | ||||
} | } | ||||
@Override | @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) | ClassLoader loader, ProtectionDomain protectionDomain) | ||||
throws ClassFormatError | throws ClassFormatError | ||||
{ | { | ||||
} | } | ||||
@Override | @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) | ClassLoader loader, ProtectionDomain protectionDomain) | ||||
throws ClassFormatError | throws ClassFormatError | ||||
{ | { | ||||
} | } | ||||
@Override | @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) | 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."); | throw new IllegalAccessError("Access denied for caller."); | ||||
try { | try { | ||||
SecurityActions.setAccessible(defineClass, true); | SecurityActions.setAccessible(defineClass, true); | ||||
} catch (Throwable e) { | } catch (Throwable e) { | ||||
if (e instanceof ClassFormatError) throw (ClassFormatError) e; | if (e instanceof ClassFormatError) throw (ClassFormatError) e; | ||||
if (e instanceof RuntimeException) throw (RuntimeException) e; | if (e instanceof RuntimeException) throw (RuntimeException) e; | ||||
throw new ClassFormatError(e.getMessage()); | |||||
throw new CannotCompileException(e); | |||||
} | } | ||||
finally { | finally { | ||||
SecurityActions.setAccessible(defineClass, false); | SecurityActions.setAccessible(defineClass, false); | ||||
// Java 11+ removed sun.misc.Unsafe.defineClass, so we fallback to invoking defineClass on | // 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 | // ClassLoader until we have an implementation that uses MethodHandles.Lookup.defineClass | ||||
private static final Helper privileged = ClassFile.MAJOR_VERSION > ClassFile.JAVA_10 | private static final Helper privileged = ClassFile.MAJOR_VERSION > ClassFile.JAVA_10 | ||||
? new JavaOther() | |||||
? new Java11() | |||||
: ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9 | : ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9 | ||||
? new Java9() | ? new Java9() | ||||
: ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7 ? new Java7() : new JavaOther(); | : ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7 ? new Java7() : new JavaOther(); | ||||
/** | /** | ||||
* Loads a class file by a given class loader. | * 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} | * Then it tries to use a {@code protected} method in {@code java.lang.ClassLoader} | ||||
* via {@code PrivilegedAction}. Since the latter approach is not available | * via {@code PrivilegedAction}. Since the latter approach is not available | ||||
* any longer by default in Java 9 or later, the JVM argument | * any longer by default in Java 9 or later, the JVM argument | ||||
* should be used instead. | * should be used instead. | ||||
* </p> | * </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. | * @param domain if it is null, a default domain is used. | ||||
* @parma bcode the bytecode for the loaded class. | |||||
* @since 3.22 | * @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) | ProtectionDomain domain, byte[] bcode) | ||||
throws CannotCompileException | throws CannotCompileException | ||||
{ | { | ||||
try { | 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) { | catch (RuntimeException e) { | ||||
throw e; | throw e; | ||||
} | } | ||||
catch (CannotCompileException e) { | |||||
throw e; | |||||
} | |||||
catch (ClassFormatError e) { | catch (ClassFormatError e) { | ||||
Throwable t = e.getCause(); | Throwable t = e.getCause(); | ||||
throw new CannotCompileException(t == null ? e : t); | throw new CannotCompileException(t == null ? e : t); | ||||
} | } | ||||
} | } | ||||
/** | |||||
* 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}. | * Loads a class file by {@code java.lang.invoke.MethodHandles.Lookup}. | ||||
* | * |
* This method uses a default protection domain for the class | * This method uses a default protection domain for the class | ||||
* but it may not work with a security manager or a signed jar file. | * 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) | public static Class<?> toClass(ClassFile cf, ClassLoader loader) | ||||
throws CannotCompileException | throws CannotCompileException | ||||
{ | { | ||||
return toClass(cf, loader, null); | |||||
return toClass(cf, null, loader, null); | |||||
} | } | ||||
/** | /** | ||||
* Loads a class file by a given class loader. | * 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. | * @param domain if it is null, a default domain is used. | ||||
* @since 3.3 | * @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 | throws CannotCompileException | ||||
{ | { | ||||
try { | try { | ||||
byte[] b = toBytecode(cf); | byte[] b = toBytecode(cf); | ||||
if (ProxyFactory.onlyPublicMethods) | if (ProxyFactory.onlyPublicMethods) | ||||
return DefineClassHelper.toPublicClass(cf.getName(), b); | 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) { | catch (IOException e) { | ||||
throw new CannotCompileException(e); | throw new CannotCompileException(e); |
* | * | ||||
* <p>The default value is {@code false}.</p> | * <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 | * @since 3.22 | ||||
*/ | */ | ||||
public static boolean onlyPublicMethods = false; | public static boolean onlyPublicMethods = false; | ||||
if (writeDirectory != null) | if (writeDirectory != null) | ||||
FactoryHelper.writeFile(cf, writeDirectory); | FactoryHelper.writeFile(cf, writeDirectory); | ||||
thisClass = FactoryHelper.toClass(cf, cl, getDomain()); | |||||
thisClass = FactoryHelper.toClass(cf, getClassInTheSamePackage(), cl, getDomain()); | |||||
setField(FILTER_SIGNATURE_FIELD, signature); | setField(FILTER_SIGNATURE_FIELD, signature); | ||||
// legacy behaviour : we only set the default interceptor static field if we are not using the cache | // legacy behaviour : we only set the default interceptor static field if we are not using the cache | ||||
if (!factoryUseCache) { | if (!factoryUseCache) { | ||||
} | } | ||||
/** | |||||
* 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) { | private void setField(String fieldName, Object value) { | ||||
if (thisClass != null && value != null) | if (thisClass != null && value != null) | ||||
try { | try { |
/* | |||||
* This is used as a capability for running CtClass#toClass(). | |||||
*/ | |||||
public class DefineClassCapability { | |||||
} |
package javassist; | package javassist; | ||||
import junit.framework.*; | import junit.framework.*; | ||||
import test1.DefineClassCapability; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
ctInterface.stopPruning(true); | ctInterface.stopPruning(true); | ||||
ctInterface.writeFile(); | ctInterface.writeFile(); | ||||
ctInterface.toClass(); | |||||
targetCtClass.toClass(); | |||||
ctInterface.toClass(DefineClassCapability.class); | |||||
targetCtClass.toClass(DefineClassCapability.class); | |||||
} | } | ||||
public void testDispatch() throws Exception { | public void testDispatch() throws Exception { | ||||
suite.addTestSuite(javassist.SetterTest.class); | suite.addTestSuite(javassist.SetterTest.class); | ||||
suite.addTestSuite(javassist.bytecode.InsertGap0.class); | suite.addTestSuite(javassist.bytecode.InsertGap0.class); | ||||
suite.addTestSuite(javassist.tools.reflect.LoaderTest.class); | suite.addTestSuite(javassist.tools.reflect.LoaderTest.class); | ||||
suite.addTestSuite(javassist.tools.CallbackTest.class); | |||||
suite.addTestSuite(testproxy.ProxyTester.class); | suite.addTestSuite(testproxy.ProxyTester.class); | ||||
suite.addTestSuite(testproxy.ProxyFactoryPerformanceTest.class); // remove? | suite.addTestSuite(testproxy.ProxyFactoryPerformanceTest.class); // remove? | ||||
suite.addTestSuite(javassist.proxyfactory.ProxyFactoryTest.class); | suite.addTestSuite(javassist.proxyfactory.ProxyFactoryTest.class); |
import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
import javassist.expr.*; | import javassist.expr.*; | ||||
import test2.DefineClassCapability; | |||||
@SuppressWarnings({"rawtypes","unused"}) | @SuppressWarnings({"rawtypes","unused"}) | ||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING) | @FixMethodOrder(MethodSorters.NAME_ASCENDING) | ||||
public void testToClass() throws Exception { | public void testToClass() throws Exception { | ||||
ClassPool cp = ClassPool.getDefault(); | ClassPool cp = ClassPool.getDefault(); | ||||
CtClass cc = cp.makeClass("test2.ToClassTest"); | CtClass cc = cp.makeClass("test2.ToClassTest"); | ||||
Class c = cc.toClass(); | |||||
Class c = cc.toClass(DefineClassCapability.class); | |||||
assertEquals(getClass().getClassLoader(), c.getClassLoader()); | assertEquals(getClass().getClassLoader(), c.getClassLoader()); | ||||
} | } | ||||
System.out.println("Num Annotation : " +ans.length); | System.out.println("Num Annotation : " +ans.length); | ||||
// c.debugWriteFile(); | // c.debugWriteFile(); | ||||
Class newclass = c.toClass(); | |||||
Class newclass = c.toClass(DefineClassCapability.class); | |||||
java.lang.annotation.Annotation[] anns = newclass.getAnnotations(); | java.lang.annotation.Annotation[] anns = newclass.getAnnotations(); | ||||
System.out.println("Num NewClass Annotation : " +anns.length); | System.out.println("Num NewClass Annotation : " +anns.length); | ||||
assertEquals(ans.length, anns.length); | assertEquals(ans.length, anns.length); | ||||
CtMethod m2 = cc2.getDeclaredMethod("getX"); | CtMethod m2 = cc2.getDeclaredMethod("getX"); | ||||
copyAnnotations(m1, m2); | copyAnnotations(m1, m2); | ||||
cc2.getClassFile(); | cc2.getClassFile(); | ||||
Class clazz = cc2.toClass(); | |||||
Class clazz = cc2.toClass(DefineClassCapability.class); | |||||
java.lang.reflect.Method m = clazz.getDeclaredMethod("getX", new Class[0]); | java.lang.reflect.Method m = clazz.getDeclaredMethod("getX", new Class[0]); | ||||
assertEquals(1, m.getAnnotations().length); | assertEquals(1, m.getAnnotations().length); | ||||
test3.VisibleAnno a = m.getAnnotation(test3.VisibleAnno.class); | test3.VisibleAnno a = m.getAnnotation(test3.VisibleAnno.class); | ||||
cc.addField(fobj, CtField.Initializer.constant("bar")); | cc.addField(fobj, CtField.Initializer.constant("bar")); | ||||
cc.writeFile(); | cc.writeFile(); | ||||
Class clazz = cc.toClass(); | |||||
Class clazz = cc.toClass(DefineClassCapability.class); | |||||
assertEquals(2L, clazz.getField("j").getLong(null)); | assertEquals(2L, clazz.getField("j").getLong(null)); | ||||
assertEquals(3, clazz.getField("i").getInt(null)); | assertEquals(3, clazz.getField("i").getInt(null)); | ||||
assertEquals(4, clazz.getField("s").getShort(null)); | assertEquals(4, clazz.getField("s").getShort(null)); | ||||
sb.append("}"); | sb.append("}"); | ||||
ctc.addMethod(CtNewMethod.make(sb.toString(), ctc)); | ctc.addMethod(CtNewMethod.make(sb.toString(), ctc)); | ||||
ctc.debugWriteFile(); | ctc.debugWriteFile(); | ||||
ctc.toClass().getConstructor().newInstance(); | |||||
ctc.toClass(DefineClassCapability.class).getConstructor().newInstance(); | |||||
} | } | ||||
// JIRA-83 | // JIRA-83 |
addDeadCode(newClass, "public boolean evaluate7(){ return !true; }"); | addDeadCode(newClass, "public boolean evaluate7(){ return !true; }"); | ||||
newClass.debugWriteFile(); | newClass.debugWriteFile(); | ||||
Class<?> cClass = newClass.toClass(); | |||||
Class<?> cClass = newClass.toClass(test4.DefineClassCapability.class); | |||||
Object o = cClass.getConstructor().newInstance(); | Object o = cClass.getConstructor().newInstance(); | ||||
java.lang.reflect.Method m = cClass.getMethod("evaluate"); | java.lang.reflect.Method m = cClass.getMethod("evaluate"); | ||||
m.invoke(o); | m.invoke(o); | ||||
attr.setAnnotation(a); | attr.setAnnotation(a); | ||||
m.getMethodInfo().addAttribute(attr); | m.getMethodInfo().addAttribute(attr); | ||||
cc.writeFile(); | cc.writeFile(); | ||||
anno.toClass(test4.DefineClassCapability.class); | |||||
Class<?> rc = ((java.lang.annotation.Annotation)m.getAnnotations()[0]).annotationType(); | Class<?> rc = ((java.lang.annotation.Annotation)m.getAnnotations()[0]).annotationType(); | ||||
assertEquals(anno.getName(), rc.getName()); | assertEquals(anno.getName(), rc.getName()); | ||||
} | } |
"}"); | "}"); | ||||
System.out.println(src); | System.out.println(src); | ||||
badClass.addMethod(CtMethod.make(src, badClass)); | badClass.addMethod(CtMethod.make(src, badClass)); | ||||
Class clazzz = badClass.toClass(); | |||||
Class clazzz = badClass.toClass(Class.forName("DefineClassCapability")); | |||||
Object obj = clazzz.getConstructor().newInstance(); // <-- falls here | Object obj = clazzz.getConstructor().newInstance(); // <-- falls here | ||||
} | } | ||||
public void testNestHostAttributeCopy() throws Exception { | public void testNestHostAttributeCopy() throws Exception { | ||||
CtClass cc = sloader.get("test5.NestHost2$Foo"); | CtClass cc = sloader.get("test5.NestHost2$Foo"); | ||||
cc.getClassFile().compact(); | cc.getClassFile().compact(); | ||||
cc.toClass(); | |||||
cc.toClass(test5.DefineClassCapability.class); | |||||
} | } | ||||
} | } |
package javassist; | |||||
import java.lang.reflect.Method; | |||||
import junit.framework.TestCase; | |||||
@SuppressWarnings({"rawtypes","unchecked"}) | |||||
public class SetterTest extends TestCase { | |||||
ClassPool pool; | |||||
public SetterTest(String name) { | |||||
super(name); | |||||
} | |||||
protected void setUp() throws Exception { | |||||
super.setUp(); | |||||
pool = ClassPool.getDefault(); | |||||
} | |||||
/** | |||||
* Tests a getter only on a field without a Modifier. | |||||
* | |||||
* @throws Exception | |||||
*/ | |||||
public void testFieldGetter() throws Exception { | |||||
CtClass clazz = pool.makeClass("HasFieldGetter"); | |||||
clazz.setSuperclass(pool.get("java.lang.Object")); | |||||
CtField field = new CtField(CtClass.booleanType, "broken", clazz); | |||||
clazz.addField(field, "true"); | |||||
clazz.addMethod(CtNewMethod.getter("isBroken", field)); | |||||
Class _class = clazz.toClass(); | |||||
Object object = _class.getConstructor().newInstance(); | |||||
check(_class, object, true); | |||||
} | |||||
/** | |||||
* Tests a getter and a setter on a field without a Modifier. | |||||
* | |||||
* @throws Exception | |||||
*/ | |||||
public void testFieldGetterSetter() throws Exception { | |||||
CtClass clazz = pool.makeClass("HasFieldGetterSetter"); | |||||
clazz.setSuperclass(pool.get("java.lang.Object")); | |||||
CtField field = new CtField(CtClass.booleanType, "broken", clazz); | |||||
clazz.addField(field, "true"); | |||||
clazz.addMethod(CtNewMethod.getter("isBroken", field)); | |||||
clazz.addMethod(CtNewMethod.setter("setBroken", field)); | |||||
Class _class = clazz.toClass(); | |||||
Object object = _class.getConstructor().newInstance(); | |||||
set(_class, object, false); | |||||
check(_class, object, false); | |||||
} | |||||
/** | |||||
* Tests a getter only on a field with Modifier.STATIC. | |||||
* | |||||
* @throws Exception | |||||
*/ | |||||
public void testStaticFieldGetter() throws Exception { | |||||
CtClass clazz = pool.makeClass("HasStaticFieldGetter"); | |||||
clazz.setSuperclass(pool.get("java.lang.Object")); | |||||
CtField field = new CtField(CtClass.booleanType, "broken", clazz); | |||||
field.setModifiers(Modifier.STATIC); | |||||
clazz.addField(field, "true"); | |||||
clazz.addMethod(CtNewMethod.getter("isBroken", field)); | |||||
Class _class = clazz.toClass(); | |||||
Object object = _class.getConstructor().newInstance(); | |||||
check(_class, object, true); | |||||
} | |||||
/** | |||||
* Tests a getter and setter on a field with Modifier.STATIC. | |||||
* | |||||
* @throws Exception | |||||
*/ | |||||
public void testStaticFieldGetterSetter() throws Exception { | |||||
CtClass clazz = pool.makeClass("HasStaticFieldGetterSetter"); | |||||
clazz.setSuperclass(pool.get("java.lang.Object")); | |||||
CtField field = new CtField(CtClass.booleanType, "broken", clazz); | |||||
field.setModifiers(Modifier.STATIC); | |||||
clazz.addField(field, "true"); | |||||
clazz.addMethod(CtNewMethod.getter("isBroken", field)); | |||||
clazz.addMethod(CtNewMethod.setter("setBroken", field)); | |||||
Class _class = clazz.toClass(); | |||||
Object object = _class.getConstructor().newInstance(); | |||||
set(_class, object, false); | |||||
check(_class, object, false); | |||||
} | |||||
private void check(Class _class, Object object, boolean shouldBe) | |||||
throws Exception | |||||
{ | |||||
Method method = _class.getMethod("isBroken", new Class[] {}); | |||||
Boolean result = (Boolean) method.invoke(object, new Object[] {}); | |||||
assertEquals("boolean is wrong value", | |||||
shouldBe, result.booleanValue()); | |||||
} | |||||
private void set(Class _class, Object object, boolean willBe) | |||||
throws Exception | |||||
{ | |||||
Method method = _class.getMethod("setBroken", | |||||
new Class[] {Boolean.TYPE}); | |||||
method.invoke(object, new Object[] { Boolean.valueOf(willBe)}); | |||||
} | |||||
} | |||||
package javassist; | |||||
import java.lang.reflect.Method; | |||||
import junit.framework.TestCase; | |||||
@SuppressWarnings({"rawtypes","unchecked"}) | |||||
public class SetterTest extends TestCase { | |||||
ClassPool pool; | |||||
Class<?> capability; | |||||
public SetterTest(String name) { | |||||
super(name); | |||||
} | |||||
protected void setUp() throws Exception { | |||||
super.setUp(); | |||||
pool = ClassPool.getDefault(); | |||||
capability = Class.forName("DefineClassCapability"); | |||||
} | |||||
/** | |||||
* Tests a getter only on a field without a Modifier. | |||||
* | |||||
* @throws Exception | |||||
*/ | |||||
public void testFieldGetter() throws Exception { | |||||
CtClass clazz = pool.makeClass("HasFieldGetter"); | |||||
clazz.setSuperclass(pool.get("java.lang.Object")); | |||||
CtField field = new CtField(CtClass.booleanType, "broken", clazz); | |||||
clazz.addField(field, "true"); | |||||
clazz.addMethod(CtNewMethod.getter("isBroken", field)); | |||||
Class _class = clazz.toClass(capability); | |||||
Object object = _class.getConstructor().newInstance(); | |||||
check(_class, object, true); | |||||
} | |||||
/** | |||||
* Tests a getter and a setter on a field without a Modifier. | |||||
* | |||||
* @throws Exception | |||||
*/ | |||||
public void testFieldGetterSetter() throws Exception { | |||||
CtClass clazz = pool.makeClass("HasFieldGetterSetter"); | |||||
clazz.setSuperclass(pool.get("java.lang.Object")); | |||||
CtField field = new CtField(CtClass.booleanType, "broken", clazz); | |||||
clazz.addField(field, "true"); | |||||
clazz.addMethod(CtNewMethod.getter("isBroken", field)); | |||||
clazz.addMethod(CtNewMethod.setter("setBroken", field)); | |||||
Class _class = clazz.toClass(capability); | |||||
Object object = _class.getConstructor().newInstance(); | |||||
set(_class, object, false); | |||||
check(_class, object, false); | |||||
} | |||||
/** | |||||
* Tests a getter only on a field with Modifier.STATIC. | |||||
* | |||||
* @throws Exception | |||||
*/ | |||||
public void testStaticFieldGetter() throws Exception { | |||||
CtClass clazz = pool.makeClass("HasStaticFieldGetter"); | |||||
clazz.setSuperclass(pool.get("java.lang.Object")); | |||||
CtField field = new CtField(CtClass.booleanType, "broken", clazz); | |||||
field.setModifiers(Modifier.STATIC); | |||||
clazz.addField(field, "true"); | |||||
clazz.addMethod(CtNewMethod.getter("isBroken", field)); | |||||
Class _class = clazz.toClass(capability); | |||||
Object object = _class.getConstructor().newInstance(); | |||||
check(_class, object, true); | |||||
} | |||||
/** | |||||
* Tests a getter and setter on a field with Modifier.STATIC. | |||||
* | |||||
* @throws Exception | |||||
*/ | |||||
public void testStaticFieldGetterSetter() throws Exception { | |||||
CtClass clazz = pool.makeClass("HasStaticFieldGetterSetter"); | |||||
clazz.setSuperclass(pool.get("java.lang.Object")); | |||||
CtField field = new CtField(CtClass.booleanType, "broken", clazz); | |||||
field.setModifiers(Modifier.STATIC); | |||||
clazz.addField(field, "true"); | |||||
clazz.addMethod(CtNewMethod.getter("isBroken", field)); | |||||
clazz.addMethod(CtNewMethod.setter("setBroken", field)); | |||||
Class _class = clazz.toClass(capability); | |||||
Object object = _class.getConstructor().newInstance(); | |||||
set(_class, object, false); | |||||
check(_class, object, false); | |||||
} | |||||
private void check(Class _class, Object object, boolean shouldBe) | |||||
throws Exception | |||||
{ | |||||
Method method = _class.getMethod("isBroken", new Class[] {}); | |||||
Boolean result = (Boolean) method.invoke(object, new Object[] {}); | |||||
assertEquals("boolean is wrong value", | |||||
shouldBe, result.booleanValue()); | |||||
} | |||||
private void set(Class _class, Object object, boolean willBe) | |||||
throws Exception | |||||
{ | |||||
Method method = _class.getMethod("setBroken", | |||||
new Class[] {Boolean.TYPE}); | |||||
method.invoke(object, new Object[] { Boolean.valueOf(willBe)}); | |||||
} | |||||
} |
cc.addField(new CtField(CtClass.intType, "i", cc), "++counter"); | cc.addField(new CtField(CtClass.intType, "i", cc), "++counter"); | ||||
boolean p = cc.stopPruning(true); | boolean p = cc.stopPruning(true); | ||||
cc.writeFile(); | cc.writeFile(); | ||||
Class c = cc.toClass(); | |||||
Class c = cc.toClass(ClassFile.class); | |||||
cc.stopPruning(p); | cc.stopPruning(p); | ||||
Object obj = c.getConstructor().newInstance(); | Object obj = c.getConstructor().newInstance(); | ||||
cc.addField(new CtField(CtClass.intType, "i", cc), "++counter"); | cc.addField(new CtField(CtClass.intType, "i", cc), "++counter"); | ||||
boolean p = cc.stopPruning(true); | boolean p = cc.stopPruning(true); | ||||
cc.writeFile(); | cc.writeFile(); | ||||
Class c = cc.toClass(); | |||||
Class c = cc.toClass(ClassFile.class); | |||||
cc.stopPruning(p); | cc.stopPruning(p); | ||||
Object obj = c.getConstructor().newInstance(); | Object obj = c.getConstructor().newInstance(); |
import javassist.*; | import javassist.*; | ||||
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
import test.javassist.tools.DummyClass; | import test.javassist.tools.DummyClass; | ||||
import test.javassist.tools.DefineClassCapability; | |||||
import static javassist.tools.Callback.*; | import static javassist.tools.Callback.*; | ||||
}); | }); | ||||
// Change class and invoke method; | // Change class and invoke method; | ||||
classToChange.toClass(); | |||||
classToChange.toClass(DefineClassCapability.class); | |||||
new DummyClass().dummyMethod(); | new DummyClass().dummyMethod(); | ||||
} | } | ||||
} | } |
package test.javassist; | |||||
/* | |||||
* This is used as a capability for running CtClass#toClass(). | |||||
*/ | |||||
public class DefineClassCapability { | |||||
} |
package test.javassist.convert; | package test.javassist.convert; | ||||
import java.net.URL; | |||||
import java.net.URLClassLoader; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.Map; | import java.util.Map; | ||||
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
public class ArrayAccessReplaceTest extends TestCase { | public class ArrayAccessReplaceTest extends TestCase { | ||||
private static SimpleInterface simple; | |||||
private static SimpleInterface simple = null; | |||||
public void setUp() throws Exception { | public void setUp() throws Exception { | ||||
ClassPool pool = new ClassPool(true); | ClassPool pool = new ClassPool(true); | ||||
converter.replaceArrayAccess(echoClass, new CodeConverter.DefaultArrayAccessReplacementMethodNames()); | converter.replaceArrayAccess(echoClass, new CodeConverter.DefaultArrayAccessReplacementMethodNames()); | ||||
simpleClass.instrument(converter); | simpleClass.instrument(converter); | ||||
//simpleClass.writeFile("/tmp"); | //simpleClass.writeFile("/tmp"); | ||||
simple = (SimpleInterface) simpleClass.toClass(new URLClassLoader(new URL[0], getClass().getClassLoader()), Class.class.getProtectionDomain()).getConstructor().newInstance(); | |||||
simple = (SimpleInterface)new javassist.Loader.Simple().invokeDefineClass(simpleClass) | |||||
.getConstructor().newInstance(); | |||||
} | } | ||||
public void testComplex() throws Exception { | public void testComplex() throws Exception { | ||||
CodeConverter converter = new CodeConverter(); | CodeConverter converter = new CodeConverter(); | ||||
converter.replaceArrayAccess(clazz, new CodeConverter.DefaultArrayAccessReplacementMethodNames()); | converter.replaceArrayAccess(clazz, new CodeConverter.DefaultArrayAccessReplacementMethodNames()); | ||||
clazz.instrument(converter); | clazz.instrument(converter); | ||||
ComplexInterface instance = (ComplexInterface) clazz.toClass(new URLClassLoader(new URL[0], getClass().getClassLoader()), Class.class.getProtectionDomain()).getConstructor().newInstance(); | |||||
ComplexInterface instance | |||||
= (ComplexInterface)new javassist.Loader.Simple().invokeDefineClass(clazz) | |||||
.getConstructor().newInstance(); | |||||
assertEquals(Integer.valueOf(5), instance.complexRead(4)); | assertEquals(Integer.valueOf(5), instance.complexRead(4)); | ||||
} | } | ||||
// now create a proxyfactory and use it to create a proxy | // now create a proxyfactory and use it to create a proxy | ||||
ProxyFactory factory = new ProxyFactory(); | ProxyFactory factory = new ProxyFactory(); | ||||
Class javaTargetClass = classPool.toClass(ctTargetClass); | |||||
Class javaHandlerClass = classPool.toClass(ctHandlerClass); | |||||
Class javaFilterClass = classPool.toClass(ctFilterClass); | |||||
Class javaTargetClass = classPool.toClass(ctTargetClass, test.javassist.DefineClassCapability.class); | |||||
Class javaHandlerClass = classPool.toClass(ctHandlerClass, test.javassist.DefineClassCapability.class); | |||||
Class javaFilterClass = classPool.toClass(ctFilterClass, test.javassist.DefineClassCapability.class); | |||||
MethodHandler handler= (MethodHandler)javaHandlerClass.getConstructor().newInstance(); | MethodHandler handler= (MethodHandler)javaHandlerClass.getConstructor().newInstance(); | ||||
MethodFilter filter = (MethodFilter)javaFilterClass.getConstructor().newInstance(); | MethodFilter filter = (MethodFilter)javaFilterClass.getConstructor().newInstance(); |
package test.javassist.proxy; | |||||
import static org.hamcrest.Matchers.arrayWithSize; | |||||
import static org.hamcrest.Matchers.both; | |||||
import static org.hamcrest.Matchers.endsWith; | |||||
import static org.hamcrest.Matchers.equalTo; | |||||
import static org.hamcrest.Matchers.instanceOf; | |||||
import static org.hamcrest.Matchers.not; | |||||
import static org.hamcrest.Matchers.notNullValue; | |||||
import static org.hamcrest.Matchers.startsWith; | |||||
import static org.hamcrest.Matchers.stringContainsInOrder; | |||||
import static org.junit.Assert.assertThat; | |||||
import static org.junit.Assert.assertTrue; | |||||
import java.lang.reflect.Constructor; | |||||
import java.lang.reflect.Field; | |||||
import java.lang.reflect.InvocationTargetException; | |||||
import java.lang.reflect.Method; | |||||
import java.lang.reflect.Modifier; | |||||
import java.security.ProtectionDomain; | |||||
import java.util.Arrays; | |||||
import org.junit.Rule; | |||||
import org.junit.Test; | |||||
import org.junit.rules.ExpectedException; | |||||
import javassist.ClassPool; | |||||
import javassist.CtClass; | |||||
import javassist.util.proxy.DefineClassHelper; | |||||
public class TestSecuredPrivileged { | |||||
public TestSecuredPrivileged() { | |||||
} | |||||
@Rule | |||||
public ExpectedException thrown = ExpectedException.none(); | |||||
/** | |||||
* Test proves that you cannot even access members with | |||||
* private static and final modifiers. */ | |||||
@Test | |||||
public void testDefinedHelperPrivilegedFieldVisibility() { | |||||
try { | |||||
Field privi = DefineClassHelper.class.getDeclaredField("privileged"); | |||||
assertTrue(Modifier.isStatic(privi.getModifiers())); | |||||
thrown.expectCause(instanceOf(IllegalAccessException.class)); | |||||
thrown.expectMessage(both(stringContainsInOrder(Arrays.asList("cannot access a member"))) | |||||
.and(stringContainsInOrder(Arrays.asList("with modifiers \"private static final".split("", 1))))); | |||||
privi.get(null); | |||||
} catch(Throwable t) { | |||||
throw new RuntimeException(t); | |||||
} | |||||
} | |||||
/** | |||||
* Test proves that the default enum constant is a class and specifically | |||||
* auto selected for Java 9. */ | |||||
@Test | |||||
public void testDefinedHelperPrivilegedField() { | |||||
try { | |||||
Field privi = DefineClassHelper.class.getDeclaredField("privileged"); | |||||
assertTrue(Modifier.isStatic(privi.getModifiers())); | |||||
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); | |||||
con.setAccessible(true); | |||||
DefineClassHelper inst = con.newInstance(); | |||||
assertThat(inst, instanceOf(DefineClassHelper.class)); | |||||
privi.setAccessible(true); | |||||
Object p = privi.get(inst); | |||||
assertThat(""+p, equalTo("JAVA_9")); | |||||
assertThat(p.getClass().getName(), endsWith("SecuredPrivileged$1")); | |||||
} catch(Throwable t) { | |||||
throw new RuntimeException(t); | |||||
} | |||||
} | |||||
/** | |||||
* Test proves that caller class security is enforced and works | |||||
* as expected. */ | |||||
@Test | |||||
public void testDefinedHelperPrivilegedFieldMethodAccessDenied() { | |||||
try { | |||||
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); | |||||
con.setAccessible(true); | |||||
DefineClassHelper inst = con.newInstance(); | |||||
Field privi = DefineClassHelper.class.getDeclaredField("privileged"); | |||||
privi.setAccessible(true); | |||||
Object priviInst = privi.get(inst); | |||||
Method defineClass = priviInst.getClass().getDeclaredMethod( | |||||
"defineClass", new Class[] { | |||||
String.class, byte[].class, int.class, int.class, | |||||
ClassLoader.class, ProtectionDomain.class | |||||
}); | |||||
assertThat(defineClass, notNullValue()); | |||||
defineClass.setAccessible(true); | |||||
assertThat(defineClass.getName(), equalTo("defineClass")); | |||||
assertTrue(defineClass.canAccess(priviInst)); | |||||
ClassPool cp = ClassPool.getDefault(); | |||||
CtClass c = cp.makeClass("a.b.C"); | |||||
byte[] bc = c.toBytecode(); | |||||
thrown.expectCause(instanceOf(IllegalAccessError.class)); | |||||
thrown.expectMessage(equalTo("java.lang.IllegalAccessError: Access denied for caller.")); | |||||
@SuppressWarnings("unused") | |||||
Object res = defineClass.invoke(priviInst, new Object[] { | |||||
c.getName(), bc, 0, bc.length, new ClassLoader() {}, | |||||
ClassLoader.class.getProtectionDomain() | |||||
}); | |||||
} catch(InvocationTargetException t) { | |||||
throw new RuntimeException(t.getTargetException()); | |||||
} catch(Throwable t) { throw new RuntimeException(t); } | |||||
} | |||||
/** | |||||
* Test proves that we do have 3 enum constants in the private static | |||||
* inner class. */ | |||||
@Test | |||||
public void testDefinedHelperEnumClass() { | |||||
try { | |||||
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); | |||||
con.setAccessible(true); | |||||
assertThat(DefineClassHelper.class.getDeclaredClasses(), arrayWithSize(1)); | |||||
Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; | |||||
assertTrue(secPriv.isEnum()); | |||||
assertThat(secPriv.getEnumConstants(), arrayWithSize(3)); | |||||
assertThat(""+secPriv.getEnumConstants()[0], equalTo("JAVA_9")); | |||||
assertThat(""+secPriv.getEnumConstants()[1], equalTo("JAVA_7")); | |||||
assertThat(""+secPriv.getEnumConstants()[2], equalTo("JAVA_OTHER")); | |||||
} catch (Throwable t) {t.printStackTrace();} | |||||
} | |||||
/** | |||||
* Test proves that you cannot modify private static final reference even | |||||
* with setAccessible(true). */ | |||||
@Test | |||||
public void testDefinedHelperCannotSetPrivileged() { | |||||
try { | |||||
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); | |||||
con.setAccessible(true); | |||||
DefineClassHelper inst = con.newInstance(); | |||||
Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; | |||||
Object J7 = secPriv.getEnumConstants()[1]; | |||||
Field privi = DefineClassHelper.class.getDeclaredField("privileged"); | |||||
privi.setAccessible(true); | |||||
thrown.expectCause(instanceOf(IllegalAccessException.class)); | |||||
thrown.expectMessage(startsWith("java.lang.IllegalAccessException: Can not set static final")); | |||||
privi.set(inst, J7); | |||||
} catch (Throwable t) {throw new RuntimeException(t);} | |||||
} | |||||
/** | |||||
* Test proves that you can achieve the impossible and modify private | |||||
* static final class reference without an instance. Now we can Mock | |||||
* test JDK 6 to 8 functionality */ | |||||
@Test | |||||
public void testDefinedHelperSetPrivilegedToJava7() { | |||||
try { | |||||
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); | |||||
con.setAccessible(true); | |||||
DefineClassHelper inst = con.newInstance(); | |||||
Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; | |||||
Object J9 = secPriv.getEnumConstants()[0]; | |||||
Object J7 = secPriv.getEnumConstants()[1]; | |||||
Field privi = DefineClassHelper.class.getDeclaredField("privileged"); | |||||
privi.setAccessible(true); | |||||
Object privInst = privi.get(inst); | |||||
Field unsf = privInst.getClass().getDeclaredField("sunMiscUnsafe"); | |||||
unsf.setAccessible(true); | |||||
Object refu = unsf.get(privInst); | |||||
Field tuf = refu.getClass().getDeclaredField("sunMiscUnsafeTheUnsafe"); | |||||
tuf.setAccessible(true); | |||||
Object tu = tuf.get(refu); | |||||
Method tu_call = tu.getClass().getMethod("call", new Class<?>[] {String.class, Object[].class}); | |||||
tu_call.setAccessible(true); | |||||
long offset = (Long) tu_call.invoke(tu, new Object[] {"staticFieldOffset", new Object[] {privi}}); | |||||
tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J7}}); | |||||
Object p = privi.get(inst); | |||||
assertThat(""+p, equalTo("JAVA_7")); | |||||
assertThat(p.getClass().getName(), endsWith("SecuredPrivileged$2")); | |||||
tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J9}}); | |||||
} catch (Throwable t) {t.printStackTrace();} | |||||
} | |||||
/** | |||||
* Test proves that Java 7+ MethodHandle defineClass (or DefineClassHelper.toClass) | |||||
* works as expected. */ | |||||
@Test | |||||
public void testDefinedHelperJava7ToClass() { | |||||
try { | |||||
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); | |||||
con.setAccessible(true); | |||||
DefineClassHelper inst = con.newInstance(); | |||||
Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; | |||||
Object J9 = secPriv.getEnumConstants()[0]; | |||||
Object J7 = secPriv.getEnumConstants()[1]; | |||||
Field privi = DefineClassHelper.class.getDeclaredField("privileged"); | |||||
privi.setAccessible(true); | |||||
Object privInst = privi.get(inst); | |||||
Field unsf = privInst.getClass().getDeclaredField("sunMiscUnsafe"); | |||||
unsf.setAccessible(true); | |||||
Object refu = unsf.get(privInst); | |||||
Field tuf = refu.getClass().getDeclaredField("sunMiscUnsafeTheUnsafe"); | |||||
tuf.setAccessible(true); | |||||
Object tu = tuf.get(refu); | |||||
Method tu_call = tu.getClass().getMethod("call", new Class<?>[] {String.class, Object[].class}); | |||||
tu_call.setAccessible(true); | |||||
long offset = (Long) tu_call.invoke(tu, new Object[] {"staticFieldOffset", new Object[] {privi}}); | |||||
tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J7}}); | |||||
ClassPool cp = ClassPool.getDefault(); | |||||
CtClass c = cp.makeClass("a.b.J7"); | |||||
byte[] bc = c.toBytecode(); | |||||
Class<?> bcCls = DefineClassHelper.toClass("a.b.J7", new ClassLoader() {}, null, bc); | |||||
assertThat(bcCls.getName(), equalTo("a.b.J7")); | |||||
assertThat(bcCls.getDeclaredConstructor().newInstance(), | |||||
not(equalTo(bcCls.getDeclaredConstructor().newInstance()))); | |||||
tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J9}}); | |||||
} catch (Throwable t) {t.printStackTrace();} | |||||
} | |||||
/** | |||||
* Test proves that Java 6 reflection method defineClass (or DefineClassHelper.toClass) | |||||
* works as expected. */ | |||||
@Test | |||||
public void testDefinedHelperJavaOtherToClass() { | |||||
try { | |||||
Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); | |||||
con.setAccessible(true); | |||||
DefineClassHelper inst = con.newInstance(); | |||||
Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; | |||||
Object J9 = secPriv.getEnumConstants()[0]; | |||||
Object JO = secPriv.getEnumConstants()[2]; | |||||
Field privi = DefineClassHelper.class.getDeclaredField("privileged"); | |||||
privi.setAccessible(true); | |||||
Object privInst = privi.get(inst); | |||||
Field unsf = privInst.getClass().getDeclaredField("sunMiscUnsafe"); | |||||
unsf.setAccessible(true); | |||||
Object refu = unsf.get(privInst); | |||||
Field tuf = refu.getClass().getDeclaredField("sunMiscUnsafeTheUnsafe"); | |||||
tuf.setAccessible(true); | |||||
Object tu = tuf.get(refu); | |||||
Method tu_call = tu.getClass().getMethod("call", new Class<?>[] {String.class, Object[].class}); | |||||
tu_call.setAccessible(true); | |||||
long offset = (Long) tu_call.invoke(tu, new Object[] {"staticFieldOffset", new Object[] {privi}}); | |||||
tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, JO}}); | |||||
ClassPool cp = ClassPool.getDefault(); | |||||
CtClass c = cp.makeClass("a.b.JO"); | |||||
byte[] bc = c.toBytecode(); | |||||
Class<?> bcCls = DefineClassHelper.toClass("a.b.JO", new ClassLoader() {}, null, bc); | |||||
assertThat(bcCls.getName(), equalTo("a.b.JO")); | |||||
assertThat(bcCls.getDeclaredConstructor().newInstance(), | |||||
not(equalTo(bcCls.getDeclaredConstructor().newInstance()))); | |||||
tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J9}}); | |||||
} catch (Throwable t) {t.printStackTrace();} | |||||
} | |||||
/** | |||||
* Test proves that default Java 9 defineClass (or DefineClassHelper.toClass) | |||||
* works as expected. */ | |||||
@Test | |||||
public void testDefinedHelperDefaultToClass() { | |||||
try { | |||||
ClassPool cp = ClassPool.getDefault(); | |||||
CtClass c = cp.makeClass("a.b.D"); | |||||
byte[] bc = c.toBytecode(); | |||||
Class<?> bcCls = DefineClassHelper.toClass("a.b.D", new ClassLoader() {}, null, bc); | |||||
assertThat(bcCls.getName(), equalTo("a.b.D")); | |||||
assertThat(bcCls.getDeclaredConstructor().newInstance(), | |||||
not(equalTo(bcCls.getDeclaredConstructor().newInstance()))); | |||||
} catch (Throwable t) {t.printStackTrace();} | |||||
} | |||||
} |
/* | |||||
* This is used as a capability for running CtClass#toClass(). | |||||
*/ | |||||
package test.javassist.tools; | |||||
public class DefineClassCapability { | |||||
} |
/* | |||||
* This is used as a capability for running CtClass#toClass(). | |||||
*/ | |||||
package test1; | |||||
public class DefineClassCapability { | |||||
} |
/* | |||||
* This is used as a capability for running CtClass#toClass(). | |||||
*/ | |||||
package test2; | |||||
public class DefineClassCapability { | |||||
} |
/* | |||||
* This is used as a capability for running CtClass#toClass(). | |||||
*/ | |||||
package test3; | |||||
public class DefineClassCapability { | |||||
} |
/* | |||||
* This is used as a capability for running CtClass#toClass(). | |||||
*/ | |||||
package test4; | |||||
public class DefineClassCapability { | |||||
} |
/* | |||||
* This is used as a capability for running CtClass#toClass(). | |||||
*/ | |||||
package test5; | |||||
public class DefineClassCapability { | |||||
} |