Sync with jboss-javassist/javassist.tags/rel_3_25_0_ga
@@ -12,4 +12,4 @@ tmp/ | |||
.project | |||
.settings | |||
TestLog.xml | |||
*~ |
@@ -281,9 +281,17 @@ see javassist.Dump. | |||
<h2>Changes</h2> | |||
<p>-version 3.24 on November 1, 2018 | |||
<ul> | |||
<li>Java 11 supports.</li> | |||
<li>JIRA JASSIST-267.</li> | |||
<li>Github PR #218.</li> | |||
</ul> | |||
</p> | |||
<p>-version 3.23.1 on July 2, 2018 | |||
<ul> | |||
<li>Github pull issue #171.</li> | |||
<li>Github PR #171.</li> | |||
</ul> | |||
</p> | |||
@@ -862,4 +870,4 @@ and all other contributors for their contributions. | |||
(Email: <tt>chiba@javassist.org</tt>) | |||
</body> | |||
</html> | |||
</html> |
@@ -6,7 +6,7 @@ | |||
<project name="javassist" default="jar" basedir="."> | |||
<property name="dist-version" value="javassist-3.23.1-GA"/> | |||
<property name="dist-version" value="javassist-3.24-GA"/> | |||
<property environment="env"/> | |||
<property name="target.jar" value="javassist.jar"/> | |||
@@ -74,13 +74,13 @@ | |||
</javac> | |||
</target> | |||
<target name="compile16" depends="prepare"> | |||
<target name="compile18" depends="prepare"> | |||
<javac srcdir="${src.dir}" | |||
destdir="${build.classes.dir}" | |||
debug="on" | |||
deprecation="on" | |||
source="1.6" | |||
target="1.6" | |||
source="1.8" | |||
target="1.8" | |||
optimize="off" | |||
includeantruntime="true" | |||
includes="**"> | |||
@@ -181,7 +181,7 @@ | |||
to ${build.classes.dir}.</echo> | |||
</target> | |||
<target name="jar" depends="compile16"> | |||
<target name="jar" depends="compile18"> | |||
<jar jarfile="${target.jar}" update="true" manifest="${src.dir}/META-INF/MANIFEST.MF"> | |||
<fileset dir="${build.classes.dir}"> | |||
<include name="**/*.class"/> |
@@ -7,7 +7,7 @@ | |||
Javassist (JAVA programming ASSISTant) makes Java bytecode manipulation | |||
simple. It is a class library for editing bytecodes in Java. | |||
</description> | |||
<version>3.23.1-GA</version> | |||
<version>3.24.0-GA</version> | |||
<name>Javassist</name> | |||
<url>http://www.javassist.org/</url> | |||
@@ -151,10 +151,10 @@ | |||
<artifactId>maven-compiler-plugin</artifactId> | |||
<version>3.2</version> | |||
<configuration> | |||
<source>1.6</source> | |||
<target>1.6</target> | |||
<testSource>1.9</testSource> | |||
<testTarget>1.9</testTarget> | |||
<source>1.8</source> | |||
<target>1.8</target> | |||
<testSource>11</testSource> | |||
<testTarget>11</testTarget> | |||
<testCompilerArgument>-parameters</testCompilerArgument> | |||
</configuration> | |||
</plugin> | |||
@@ -202,9 +202,14 @@ | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-javadoc-plugin</artifactId> | |||
<version>3.0.0-M1</version> | |||
<version>3.0.1</version> | |||
<configuration> | |||
<attach>true</attach> | |||
<excludePackageNames>javassist.compiler:javassist.convert:javassist.scopedpool:javassist.bytecode.stackmap</excludePackageNames> | |||
<bottom><![CDATA[<i>Javassist, a Java-bytecode translator toolkit.<br> | |||
Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.</i>]]></bottom> | |||
<show>public</show> | |||
<nohelp>true</nohelp> | |||
</configuration> | |||
</plugin> | |||
<plugin> |
@@ -1,4 +1,5 @@ | |||
Specification-Title: Javassist | |||
Specification-Vendor: Shigeru Chiba, www.javassist.org | |||
Specification-Version: 3.23.1-GA | |||
Specification-Version: 3.24.0-GA | |||
Main-Class: javassist.CtClass | |||
Automatic-Module-Name: org.javassist |
@@ -1023,16 +1023,23 @@ public class ClassPool { | |||
* the <code>getClassLoader()</code> method. | |||
* If the program is running on some application | |||
* 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 | |||
* 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 | |||
* protection domain is not specified. | |||
* protection domain is not specified.</p> | |||
* | |||
* @see #toClass(CtClass, java.lang.ClassLoader, ProtectionDomain) | |||
* @see #toClass(CtClass,Class) | |||
* @see #toClass(CtClass,Class,java.lang.ClassLoader,ProtectionDomain) | |||
* @see #getClassLoader() | |||
*/ | |||
public Class toClass(CtClass clazz) throws CannotCompileException { | |||
@@ -1066,21 +1073,21 @@ public class ClassPool { | |||
/** | |||
* Converts the class to a <code>java.lang.Class</code> object. | |||
* 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 | |||
* work with a security manager or a signed jar file because a | |||
* 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 | |||
* 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) | |||
throws CannotCompileException | |||
{ | |||
return toClass(ct, loader, null); | |||
return toClass(ct, null, loader, null); | |||
} | |||
/** | |||
@@ -1092,7 +1099,7 @@ public class ClassPool { | |||
* 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. | |||
* the caller must have permissions to do that.</p> | |||
* | |||
* <p>An easy way to obtain <code>ProtectionDomain</code> object is | |||
* to call <code>getProtectionDomain()</code> | |||
@@ -1100,8 +1107,9 @@ public class ClassPool { | |||
* class belongs to. | |||
* | |||
* <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. | |||
* For example, the loader returned by | |||
* <code>getClassLoader()</code> can be used | |||
@@ -1112,13 +1120,117 @@ public class ClassPool { | |||
* | |||
* @see #getClassLoader() | |||
* @since 3.3 | |||
* @deprecated Replaced by {@link #toClass(CtClass,Class,ClassLoader,ProtectionDomain)}. | |||
*/ | |||
public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain) | |||
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,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 { | |||
return javassist.util.proxy.DefineClassHelper.toClass(ct.getName(), | |||
loader, domain, ct.toBytecode()); | |||
neighbor, loader, domain, ct.toBytecode()); | |||
} | |||
catch (IOException e) { | |||
throw new CannotCompileException(e); |
@@ -159,7 +159,9 @@ final class JarClassPath implements ClassPath { | |||
URL jarURL = find(classname); | |||
if (null != jarURL) | |||
try { | |||
return jarURL.openConnection().getInputStream(); | |||
java.net.URLConnection con = jarURL.openConnection(); | |||
con.setUseCaches(false); | |||
return con.getInputStream(); | |||
} | |||
catch (IOException e) { | |||
throw new NotFoundException("broken jar file?: " |
@@ -69,7 +69,7 @@ public abstract class CtClass { | |||
/** | |||
* The version number of this release. | |||
*/ | |||
public static final String version = "3.23.1-GA"; | |||
public static final String version = "3.24.0-GA"; | |||
/** | |||
* Prints the version number and the copyright notice. | |||
@@ -1261,21 +1261,86 @@ public abstract class CtClass { | |||
* server, the context class loader might be inappropriate to load the | |||
* class. | |||
* | |||
* <p><b>Warning:</b> In Java 11 or later, the call to this method will | |||
* print a warning message:</p> | |||
* <blockquote><pre> | |||
* WARNING: An illegal reflective access operation has occurred | |||
* WARNING: Illegal reflective access by javassist.util.proxy.SecurityActions$3 ... | |||
* WARNING: Please consider reporting this to the maintainers of javassist.util.proxy.SecurityActions$3 | |||
* WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations | |||
* WARNING: All illegal access operations will be denied in a future release | |||
* </pre></blockquote> | |||
* <p>To avoid this message, use {@link #toClass(Class)} | |||
* or {@link #toClass(java.lang.invoke.MethodHandles.Lookup)}. | |||
* {@link #toClass()} will be unavailable in a future release. | |||
* </p> | |||
* | |||
* <p><b>Warning:</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.</p> | |||
* | |||
* <p>Note: this method calls <code>toClass()</code> | |||
* in <code>ClassPool</code>.</p> | |||
* | |||
* @see #toClass(java.lang.invoke.MethodHandles.Lookup) | |||
* @see #toClass(Class) | |||
* @see ClassPool#toClass(CtClass) | |||
*/ | |||
public Class<?> toClass() throws CannotCompileException { | |||
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. You should use | |||
* {@code toClass(Lookup)} for better compatibility with the | |||
* module system. | |||
* | |||
* <p>Note: this method calls <code>toClass()</code> | |||
* in <code>ClassPool</code>. | |||
* | |||
* <p><b>Warning:</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) | |||
* @see #toClass(java.lang.invoke.MethodHandles.Lookup) | |||
* @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 | |||
* <p><b>Warning:</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. | |||
* | |||
* @see #toClass(java.lang.ClassLoader,ProtectionDomain) | |||
* @see ClassPool#toClass(CtClass) | |||
* @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() throws CannotCompileException { | |||
return getClassPool().toClass(this); | |||
public Class<?> toClass(java.lang.invoke.MethodHandles.Lookup lookup) | |||
throws CannotCompileException | |||
{ | |||
return getClassPool().toClass(this, lookup); | |||
} | |||
/** | |||
@@ -1316,13 +1381,13 @@ public abstract class CtClass { | |||
if (loader == null) | |||
loader = cp.getClassLoader(); | |||
return cp.toClass(this, loader, domain); | |||
return cp.toClass(this, null, loader, domain); | |||
} | |||
/** | |||
* Converts this class to a <code>java.lang.Class</code> object. | |||
* | |||
* <p><b>Warining:</b> A Class object returned by this method may not | |||
* <p><b>Warning:</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. | |||
* | |||
@@ -1332,7 +1397,7 @@ public abstract class CtClass { | |||
public final Class<?> toClass(ClassLoader loader) | |||
throws CannotCompileException | |||
{ | |||
return getClassPool().toClass(this, loader); | |||
return getClassPool().toClass(this, null, loader, null); | |||
} | |||
/** | |||
@@ -1405,7 +1470,7 @@ public abstract class CtClass { | |||
* @see ClassPool#doPruning | |||
* | |||
* @see #toBytecode() | |||
* @see #toClass() | |||
* @see #toClass(Class) | |||
* @see #writeFile() | |||
* @see #instrument(CodeConverter) | |||
* @see #instrument(ExprEditor) |
@@ -1526,7 +1526,7 @@ class CtClassType extends CtClass { | |||
ClassFile cf = getClassFile2(); | |||
ConstPool cp = cf.getConstPool(); | |||
List<MethodInfo> methods = cf.getMethods(); | |||
for (MethodInfo minfo:methods) | |||
for (MethodInfo minfo: methods.toArray(new MethodInfo[methods.size()])) | |||
converter.doit(this, minfo, cp); | |||
} | |||
@@ -1537,7 +1537,7 @@ class CtClassType extends CtClass { | |||
checkModify(); | |||
ClassFile cf = getClassFile2(); | |||
List<MethodInfo> methods = cf.getMethods(); | |||
for (MethodInfo minfo:methods) | |||
for (MethodInfo minfo: methods.toArray(new MethodInfo[methods.size()])) | |||
editor.doit(this, minfo); | |||
} | |||
@@ -16,11 +16,12 @@ | |||
package javassist; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.lang.reflect.InvocationTargetException; | |||
import java.security.ProtectionDomain; | |||
import java.util.Arrays; | |||
import java.util.Hashtable; | |||
import java.util.HashMap; | |||
import java.util.Vector; | |||
import javassist.bytecode.ClassFile; | |||
@@ -138,7 +139,48 @@ import javassist.bytecode.ClassFile; | |||
* @see javassist.Translator | |||
*/ | |||
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 ClassPool source; | |||
private Translator translator; | |||
@@ -186,7 +228,7 @@ public class Loader extends ClassLoader { | |||
} | |||
private void init(ClassPool cp) { | |||
notDefinedHere = new Hashtable<String,ClassLoader>(); | |||
notDefinedHere = new HashMap<String,ClassLoader>(); | |||
notDefinedPackages = new Vector<String>(); | |||
source = cp; | |||
translator = null; |
@@ -46,7 +46,7 @@ public class Modifier { | |||
public static final int ENUM = AccessFlag.ENUM; | |||
/** | |||
* Returns true if the modifiers include the <tt>public</tt> | |||
* Returns true if the modifiers include the <code>public</code> | |||
* modifier. | |||
*/ | |||
public static boolean isPublic(int mod) { | |||
@@ -54,7 +54,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>private</tt> | |||
* Returns true if the modifiers include the <code>private</code> | |||
* modifier. | |||
*/ | |||
public static boolean isPrivate(int mod) { | |||
@@ -62,7 +62,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>protected</tt> | |||
* Returns true if the modifiers include the <code>protected</code> | |||
* modifier. | |||
*/ | |||
public static boolean isProtected(int mod) { | |||
@@ -71,14 +71,14 @@ public class Modifier { | |||
/** | |||
* Returns true if the modifiers do not include either | |||
* <tt>public</tt>, <tt>protected</tt>, or <tt>private</tt>. | |||
* <code>public</code>, <code>protected</code>, or <code>private</code>. | |||
*/ | |||
public static boolean isPackage(int mod) { | |||
return (mod & (PUBLIC | PRIVATE | PROTECTED)) == 0; | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>static</tt> | |||
* Returns true if the modifiers include the <code>static</code> | |||
* modifier. | |||
*/ | |||
public static boolean isStatic(int mod) { | |||
@@ -86,7 +86,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>final</tt> | |||
* Returns true if the modifiers include the <code>final</code> | |||
* modifier. | |||
*/ | |||
public static boolean isFinal(int mod) { | |||
@@ -94,7 +94,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>synchronized</tt> | |||
* Returns true if the modifiers include the <code>synchronized</code> | |||
* modifier. | |||
*/ | |||
public static boolean isSynchronized(int mod) { | |||
@@ -102,7 +102,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>volatile</tt> | |||
* Returns true if the modifiers include the <code>volatile</code> | |||
* modifier. | |||
*/ | |||
public static boolean isVolatile(int mod) { | |||
@@ -110,7 +110,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>transient</tt> | |||
* Returns true if the modifiers include the <code>transient</code> | |||
* modifier. | |||
*/ | |||
public static boolean isTransient(int mod) { | |||
@@ -118,7 +118,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>native</tt> | |||
* Returns true if the modifiers include the <code>native</code> | |||
* modifier. | |||
*/ | |||
public static boolean isNative(int mod) { | |||
@@ -126,7 +126,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>interface</tt> | |||
* Returns true if the modifiers include the <code>interface</code> | |||
* modifier. | |||
*/ | |||
public static boolean isInterface(int mod) { | |||
@@ -134,7 +134,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>annotation</tt> | |||
* Returns true if the modifiers include the <code>annotation</code> | |||
* modifier. | |||
* | |||
* @since 3.2 | |||
@@ -144,7 +144,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>enum</tt> | |||
* Returns true if the modifiers include the <code>enum</code> | |||
* modifier. | |||
* | |||
* @since 3.2 | |||
@@ -154,7 +154,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>abstract</tt> | |||
* Returns true if the modifiers include the <code>abstract</code> | |||
* modifier. | |||
*/ | |||
public static boolean isAbstract(int mod) { | |||
@@ -162,7 +162,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>strictfp</tt> | |||
* Returns true if the modifiers include the <code>strictfp</code> | |||
* modifier. | |||
*/ | |||
public static boolean isStrict(int mod) { | |||
@@ -170,7 +170,7 @@ public class Modifier { | |||
} | |||
/** | |||
* Returns true if the modifiers include the <tt>varargs</tt> | |||
* Returns true if the modifiers include the <code>varargs</code> | |||
* (variable number of arguments) modifier. | |||
*/ | |||
public static boolean isVarArgs(int mod) { |
@@ -108,6 +108,10 @@ public class AttributeInfo { | |||
/* Note that the names of Annotations attributes begin with 'R'. */ | |||
if (nameStr.equals(MethodParametersAttribute.tag)) | |||
return new MethodParametersAttribute(cp, name, in); | |||
else if (nameStr.equals(NestHostAttribute.tag)) | |||
return new NestHostAttribute(cp, name, in); | |||
else if (nameStr.equals(NestMembersAttribute.tag)) | |||
return new NestMembersAttribute(cp, name, in); | |||
else if (nameStr.equals(AnnotationsAttribute.visibleTag) | |||
|| nameStr.equals(AnnotationsAttribute.invisibleTag)) | |||
// RuntimeVisibleAnnotations or RuntimeInvisibleAnnotations |
@@ -0,0 +1,67 @@ | |||
/* | |||
* Javassist, a Java-bytecode translator toolkit. | |||
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. | |||
* | |||
* The contents of this file are subject to the Mozilla Public License Version | |||
* 1.1 (the "License"); you may not use this file except in compliance with | |||
* the License. Alternatively, the contents of this file may be used under | |||
* the terms of the GNU Lesser General Public License Version 2.1 or later, | |||
* or the Apache License Version 2.0. | |||
* | |||
* Software distributed under the License is distributed on an "AS IS" basis, | |||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||
* for the specific language governing rights and limitations under the | |||
* License. | |||
*/ | |||
package javassist.bytecode; | |||
import java.io.DataInputStream; | |||
import java.io.IOException; | |||
import java.util.Map; | |||
/** | |||
* <code>NestHost_attribute</code>. | |||
* It was introduced by JEP-181. See JVMS 4.7.28 for the specification. | |||
* | |||
* @since 3.24 | |||
*/ | |||
public class NestHostAttribute extends AttributeInfo { | |||
/** | |||
* The name of this attribute <code>"NestHost"</code>. | |||
*/ | |||
public static final String tag = "NestHost"; | |||
NestHostAttribute(ConstPool cp, int n, DataInputStream in) throws IOException { | |||
super(cp, n, in); | |||
} | |||
private NestHostAttribute(ConstPool cp, int hostIndex) { | |||
super(cp, tag, new byte[2]); | |||
ByteArray.write16bit(hostIndex, get(), 0); | |||
} | |||
/** | |||
* Makes a copy. Class names are replaced according to the | |||
* given <code>Map</code> object. | |||
* | |||
* @param newCp the constant pool table used by the new copy. | |||
* @param classnames pairs of replaced and substituted | |||
* class names. | |||
*/ | |||
@Override | |||
public AttributeInfo copy(ConstPool newCp, Map<String, String> classnames) { | |||
int hostIndex = ByteArray.readU16bit(get(), 0); | |||
int newHostIndex = getConstPool().copy(hostIndex, newCp, classnames); | |||
return new NestHostAttribute(newCp, newHostIndex); | |||
} | |||
/** | |||
* Returns <code>host_class_index</code>. The constant pool entry | |||
* at this entry is a <code>CONSTANT_Class_info</code> structure. | |||
* @return the value of <code>host_class_index</code>. | |||
*/ | |||
public int hostClassIndex() { | |||
return ByteArray.readU16bit(info, 0); | |||
} | |||
} |
@@ -0,0 +1,88 @@ | |||
/* | |||
* Javassist, a Java-bytecode translator toolkit. | |||
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. | |||
* | |||
* The contents of this file are subject to the Mozilla Public License Version | |||
* 1.1 (the "License"); you may not use this file except in compliance with | |||
* the License. Alternatively, the contents of this file may be used under | |||
* the terms of the GNU Lesser General Public License Version 2.1 or later, | |||
* or the Apache License Version 2.0. | |||
* | |||
* Software distributed under the License is distributed on an "AS IS" basis, | |||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |||
* for the specific language governing rights and limitations under the | |||
* License. | |||
*/ | |||
package javassist.bytecode; | |||
import java.io.DataInputStream; | |||
import java.io.IOException; | |||
import java.util.Map; | |||
/** | |||
* <code>NestMembers_attribute</code>. | |||
* It was introduced by JEP-181. See JVMS 4.7.29 for the specification. | |||
* | |||
* @since 3.24 | |||
*/ | |||
public class NestMembersAttribute extends AttributeInfo { | |||
/** | |||
* The name of this attribute <code>"NestMembers"</code>. | |||
*/ | |||
public static final String tag = "NestMembers"; | |||
NestMembersAttribute(ConstPool cp, int n, DataInputStream in) throws IOException { | |||
super(cp, n, in); | |||
} | |||
private NestMembersAttribute(ConstPool cp, byte[] info) { | |||
super(cp, tag, info); | |||
} | |||
/** | |||
* Makes a copy. Class names are replaced according to the | |||
* given <code>Map</code> object. | |||
* | |||
* @param newCp the constant pool table used by the new copy. | |||
* @param classnames pairs of replaced and substituted | |||
* class names. | |||
*/ | |||
@Override | |||
public AttributeInfo copy(ConstPool newCp, Map<String, String> classnames) { | |||
byte[] src = get(); | |||
byte[] dest = new byte[src.length]; | |||
ConstPool cp = getConstPool(); | |||
int n = ByteArray.readU16bit(src, 0); | |||
ByteArray.write16bit(n, dest, 0); | |||
for (int i = 0, j = 2; i < n; ++i, j += 2) { | |||
int index = ByteArray.readU16bit(src, j); | |||
int newIndex = cp.copy(index, newCp, classnames); | |||
ByteArray.write16bit(newIndex, dest, j); | |||
} | |||
return new NestMembersAttribute(newCp, dest); | |||
} | |||
/** | |||
* Returns <code>number_of_classes</code>. | |||
* @return the number of the classes recorded in this attribute. | |||
*/ | |||
public int numberOfClasses() { | |||
return ByteArray.readU16bit(info, 0); | |||
} | |||
/** Returns <code>classes[index]</code>. | |||
* | |||
* @param index the index into <code>classes</code>. | |||
* @return the value at the given index in the <code>classes</code> array. | |||
* It is an index into the constant pool. | |||
* The constant pool entry at the returned index is a | |||
* <code>CONSTANT_Class_info</code> structure. | |||
*/ | |||
public int memberClass(int index) { | |||
return ByteArray.readU16bit(info, index * 2 + 2); | |||
} | |||
} |
@@ -35,7 +35,7 @@ import javassist.bytecode.Opcode; | |||
* <p>If <code>instrument()</code> is called in | |||
* <code>CtMethod</code>, the method body is scanned from the beginning | |||
* to the end. | |||
* Whenever an expression, such as a method call and a <tt>new</tt> | |||
* Whenever an expression, such as a method call and a <code>new</code> | |||
* expression (object creation), | |||
* is found, <code>edit()</code> is called in <code>ExprEdit</code>. | |||
* <code>edit()</code> can inspect and modify the given expression. | |||
@@ -259,10 +259,10 @@ public class ExprEditor { | |||
} | |||
/** | |||
* Edits a <tt>new</tt> expression (overridable). | |||
* Edits a <code>new</code> expression (overridable). | |||
* The default implementation performs nothing. | |||
* | |||
* @param e the <tt>new</tt> expression creating an object. | |||
* @param e the <code>new</code> expression creating an object. | |||
*/ | |||
public void edit(NewExpr e) throws CannotCompileException {} | |||
@@ -270,7 +270,7 @@ public class ExprEditor { | |||
* Edits an expression for array creation (overridable). | |||
* The default implementation performs nothing. | |||
* | |||
* @param a the <tt>new</tt> expression for creating an array. | |||
* @param a the <code>new</code> expression for creating an array. | |||
* @throws CannotCompileException | |||
*/ | |||
public void edit(NewArray a) throws CannotCompileException {} |
@@ -92,9 +92,9 @@ public class NewArray extends Expr { | |||
/** | |||
* Returns the type of array components. If the created array is | |||
* a two-dimensional array of <tt>int</tt>, | |||
* a two-dimensional array of <code>int</code>, | |||
* the type returned by this method is | |||
* not <tt>int[]</tt> but <tt>int</tt>. | |||
* not <code>int[]</code> but <code>int</code>. | |||
*/ | |||
public CtClass getComponentType() throws NotFoundException { | |||
if (opcode == Opcode.NEWARRAY) { |
@@ -38,7 +38,7 @@ import javassist.compiler.ProceedHandler; | |||
import javassist.compiler.ast.ASTList; | |||
/** | |||
* Object creation (<tt>new</tt> expression). | |||
* Object creation (<code>new</code> expression). | |||
*/ | |||
public class NewExpr extends Expr { | |||
String newTypeName; | |||
@@ -69,7 +69,7 @@ public class NewExpr extends Expr { | |||
} */ | |||
/** | |||
* Returns the method or constructor containing the <tt>new</tt> | |||
* Returns the method or constructor containing the <code>new</code> | |||
* expression represented by this object. | |||
*/ | |||
@Override | |||
@@ -77,7 +77,7 @@ public class NewExpr extends Expr { | |||
/** | |||
* Returns the line number of the source line containing the | |||
* <tt>new</tt> expression. | |||
* <code>new</code> expression. | |||
* | |||
* @return -1 if this information is not available. | |||
*/ | |||
@@ -87,7 +87,7 @@ public class NewExpr extends Expr { | |||
} | |||
/** | |||
* Returns the source file containing the <tt>new</tt> expression. | |||
* Returns the source file containing the <code>new</code> expression. | |||
* | |||
* @return null if this information is not available. | |||
*/ | |||
@@ -173,7 +173,7 @@ public class NewExpr extends Expr { | |||
} | |||
/** | |||
* Replaces the <tt>new</tt> expression with the bytecode derived from | |||
* Replaces the <code>new</code> expression with the bytecode derived from | |||
* the given source text. | |||
* | |||
* <p>$0 is available but the value is null. |
@@ -31,7 +31,7 @@ import java.util.concurrent.ConcurrentHashMap; | |||
* This Map will remove entries when the value in the map has been cleaned from | |||
* garbage collection | |||
* | |||
* @version <tt>$Revision: 1.4 $</tt> | |||
* @version <code>$Revision: 1.4 $</code> | |||
* @author <a href="mailto:bill@jboss.org">Bill Burke</a> | |||
*/ | |||
public class SoftValueHashMap<K,V> implements Map<K,V> { | |||
@@ -133,10 +133,10 @@ public class SoftValueHashMap<K,V> implements Map<K,V> { | |||
/** | |||
* Constructs a new <code>WeakHashMap</code> with the same mappings as the | |||
* specified <tt>Map</tt>. The <code>WeakHashMap</code> is created with | |||
* specified <code>Map</code>. The <code>WeakHashMap</code> is created with | |||
* an initial capacity of twice the number of mappings in the specified map | |||
* or 11 (whichever is greater), and a default load factor, which is | |||
* <tt>0.75</tt>. | |||
* <code>0.75</code>. | |||
* | |||
* @param t the map whose mappings are to be placed in this map. | |||
*/ |
@@ -31,199 +31,214 @@ import javassist.bytecode.ClassFile; | |||
* | |||
* @since 3.22 | |||
*/ | |||
public class DefineClassHelper | |||
{ | |||
public class DefineClassHelper { | |||
private static enum SecuredPrivileged | |||
{ | |||
JAVA_9 { | |||
final class ReferencedUnsafe | |||
{ | |||
private final SecurityActions.TheUnsafe sunMiscUnsafeTheUnsafe; | |||
private final MethodHandle defineClass; | |||
ReferencedUnsafe(SecurityActions.TheUnsafe usf, MethodHandle meth) | |||
{ | |||
this.sunMiscUnsafeTheUnsafe = usf; | |||
this.defineClass = meth; | |||
} | |||
private static abstract class Helper { | |||
abstract Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor, | |||
ClassLoader loader, ProtectionDomain protectionDomain) | |||
throws ClassFormatError, CannotCompileException; | |||
} | |||
Class<?> defineClass(String name, byte[] b, int off, int len, | |||
ClassLoader loader, ProtectionDomain protectionDomain) | |||
throws ClassFormatError { | |||
try { | |||
if (getCallerClass.invoke(stack) != SecuredPrivileged.JAVA_9.getClass()) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
} catch (Exception e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
try { | |||
return (Class<?>) defineClass.invokeWithArguments( | |||
sunMiscUnsafeTheUnsafe.theUnsafe, | |||
name, b, off, len, loader, protectionDomain); | |||
} catch (Throwable e) { | |||
if (e instanceof RuntimeException) throw (RuntimeException) e; | |||
if (e instanceof ClassFormatError) throw (ClassFormatError) e; | |||
throw new ClassFormatError(e.getMessage()); | |||
} | |||
} | |||
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 final Object stack; | |||
private final Method getCallerClass; | |||
{ | |||
Class<?> stackWalkerClass = null; | |||
try { | |||
stackWalkerClass = Class.forName("java.lang.StackWalker"); | |||
} catch (ClassNotFoundException e) { | |||
// Skip initialization when the class doesn't exist i.e. we are on JDK < 9 | |||
} | |||
if (stackWalkerClass != null) { | |||
try { | |||
Class<?> optionClass = Class.forName("java.lang.StackWalker$Option"); | |||
stack = stackWalkerClass.getMethod("getInstance", optionClass) | |||
// The first one is RETAIN_CLASS_REFERENCE | |||
.invoke(null, optionClass.getEnumConstants()[0]); | |||
getCallerClass = stackWalkerClass.getMethod("getCallerClass"); | |||
} catch (Throwable e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
} else { | |||
stack = null; | |||
getCallerClass = null; | |||
} | |||
} | |||
} | |||
private static class Java9 extends Helper { | |||
final class ReferencedUnsafe { | |||
private final SecurityActions.TheUnsafe sunMiscUnsafeTheUnsafe; | |||
private final MethodHandle defineClass; | |||
ReferencedUnsafe(SecurityActions.TheUnsafe usf, MethodHandle meth) { | |||
this.sunMiscUnsafeTheUnsafe = usf; | |||
this.defineClass = meth; | |||
} | |||
private final ReferencedUnsafe sunMiscUnsafe = getReferencedUnsafe(); | |||
private final ReferencedUnsafe getReferencedUnsafe() | |||
Class<?> defineClass(String name, byte[] b, int off, int len, | |||
ClassLoader loader, ProtectionDomain protectionDomain) | |||
throws ClassFormatError | |||
{ | |||
try { | |||
if (null != SecuredPrivileged.JAVA_9 | |||
&& getCallerClass.invoke(stack) != this.getClass()) | |||
if (getCallerClass.invoke(stack) != Java9.class) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
} catch (Exception e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
try { | |||
SecurityActions.TheUnsafe usf = SecurityActions.getSunMiscUnsafeAnonymously(); | |||
List<Method> defineClassMethod = usf.methods.get("defineClass"); | |||
// On Java 11+ the defineClass method does not exist anymore | |||
if (null == defineClassMethod) | |||
return null; | |||
MethodHandle meth = MethodHandles.lookup() | |||
.unreflect(defineClassMethod.get(0)); | |||
return new ReferencedUnsafe(usf, meth); | |||
return (Class<?>) defineClass.invokeWithArguments( | |||
sunMiscUnsafeTheUnsafe.theUnsafe, | |||
name, b, off, len, loader, protectionDomain); | |||
} catch (Throwable e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
if (e instanceof RuntimeException) throw (RuntimeException) e; | |||
if (e instanceof ClassFormatError) throw (ClassFormatError) e; | |||
throw new ClassFormatError(e.getMessage()); | |||
} | |||
} | |||
} | |||
@Override | |||
public Class<?> defineClass(String name, byte[] b, int off, int len, | |||
ClassLoader loader, ProtectionDomain protectionDomain) | |||
throws ClassFormatError | |||
{ | |||
private final Object stack; | |||
private final Method getCallerClass; | |||
private final ReferencedUnsafe sunMiscUnsafe = getReferencedUnsafe(); | |||
Java9 () { | |||
Class<?> stackWalkerClass = null; | |||
try { | |||
stackWalkerClass = Class.forName("java.lang.StackWalker"); | |||
} catch (ClassNotFoundException e) { | |||
// Skip initialization when the class doesn't exist i.e. we are on JDK < 9 | |||
} | |||
if (stackWalkerClass != null) { | |||
try { | |||
if (getCallerClass.invoke(stack) != DefineClassHelper.class) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
} catch (Exception e) { | |||
Class<?> optionClass = Class.forName("java.lang.StackWalker$Option"); | |||
stack = stackWalkerClass.getMethod("getInstance", optionClass) | |||
// The first one is RETAIN_CLASS_REFERENCE | |||
.invoke(null, optionClass.getEnumConstants()[0]); | |||
getCallerClass = stackWalkerClass.getMethod("getCallerClass"); | |||
} catch (Throwable e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
return sunMiscUnsafe.defineClass(name, b, off, len, loader, | |||
protectionDomain); | |||
} else { | |||
stack = null; | |||
getCallerClass = null; | |||
} | |||
}, | |||
JAVA_7 { | |||
private final SecurityActions stack = SecurityActions.stack; | |||
private final MethodHandle defineClass = getDefineClassMethodHandle(); | |||
private final MethodHandle getDefineClassMethodHandle() | |||
{ | |||
if (null != SecuredPrivileged.JAVA_7 | |||
&& stack.getCallerClass() != this.getClass()) | |||
} | |||
private final ReferencedUnsafe getReferencedUnsafe() { | |||
try { | |||
if (privileged != null && getCallerClass.invoke(stack) != this.getClass()) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
return SecurityActions.getMethodHandle(ClassLoader.class, | |||
"defineClass", new Class[] { | |||
} catch (Exception e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
try { | |||
SecurityActions.TheUnsafe usf = SecurityActions.getSunMiscUnsafeAnonymously(); | |||
List<Method> defineClassMethod = usf.methods.get("defineClass"); | |||
// On Java 11+ the defineClass method does not exist anymore | |||
if (null == defineClassMethod) | |||
return null; | |||
MethodHandle meth = MethodHandles.lookup().unreflect(defineClassMethod.get(0)); | |||
return new ReferencedUnsafe(usf, meth); | |||
} catch (Throwable e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
} | |||
@Override | |||
Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor, | |||
ClassLoader loader, ProtectionDomain protectionDomain) | |||
throws ClassFormatError | |||
{ | |||
try { | |||
if (getCallerClass.invoke(stack) != DefineClassHelper.class) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
} catch (Exception e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
return sunMiscUnsafe.defineClass(name, b, off, len, loader, | |||
protectionDomain); | |||
} | |||
} | |||
private static class Java7 extends Helper { | |||
private final SecurityActions stack = SecurityActions.stack; | |||
private final MethodHandle defineClass = getDefineClassMethodHandle(); | |||
private final MethodHandle getDefineClassMethodHandle() { | |||
if (privileged != null && stack.getCallerClass() != this.getClass()) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
return SecurityActions.getMethodHandle(ClassLoader.class, "defineClass", | |||
new Class[] { | |||
String.class, byte[].class, int.class, int.class, | |||
ProtectionDomain.class | |||
}); | |||
} catch (NoSuchMethodException e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
} | |||
} | |||
@Override | |||
protected Class<?> defineClass(String name, byte[] b, int off, int len, | |||
ClassLoader loader, ProtectionDomain protectionDomain) throws ClassFormatError | |||
{ | |||
if (stack.getCallerClass() != DefineClassHelper.class) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
return (Class<?>) defineClass.invokeWithArguments( | |||
@Override | |||
Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor, | |||
ClassLoader loader, ProtectionDomain protectionDomain) | |||
throws ClassFormatError | |||
{ | |||
if (stack.getCallerClass() != DefineClassHelper.class) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
return (Class<?>) defineClass.invokeWithArguments( | |||
loader, name, b, off, len, protectionDomain); | |||
} catch (Throwable e) { | |||
if (e instanceof RuntimeException) throw (RuntimeException) e; | |||
if (e instanceof ClassFormatError) throw (ClassFormatError) e; | |||
throw new ClassFormatError(e.getMessage()); | |||
} | |||
} catch (Throwable e) { | |||
if (e instanceof RuntimeException) throw (RuntimeException) e; | |||
if (e instanceof ClassFormatError) throw (ClassFormatError) e; | |||
throw new ClassFormatError(e.getMessage()); | |||
} | |||
}, | |||
JAVA_OTHER { | |||
private final Method defineClass = getDefineClassMethod(); | |||
private final SecurityActions stack = SecurityActions.stack; | |||
private final Method getDefineClassMethod() { | |||
if (null != SecuredPrivileged.JAVA_OTHER | |||
&& stack.getCallerClass() != this.getClass()) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
return SecurityActions.getDeclaredMethod(ClassLoader.class, | |||
"defineClass", new Class[] { | |||
} | |||
} | |||
private static class JavaOther extends Helper { | |||
private final Method defineClass = getDefineClassMethod(); | |||
private final SecurityActions stack = SecurityActions.stack; | |||
private final Method getDefineClassMethod() { | |||
if (privileged != null && stack.getCallerClass() != this.getClass()) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
return SecurityActions.getDeclaredMethod(ClassLoader.class, "defineClass", | |||
new Class[] { | |||
String.class, byte[].class, int.class, int.class, ProtectionDomain.class | |||
}); | |||
} catch (NoSuchMethodException e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
} catch (NoSuchMethodException e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
} | |||
@Override | |||
protected Class<?> defineClass(String name, byte[] b, int off, int len, | |||
ClassLoader loader, ProtectionDomain protectionDomain) throws ClassFormatError | |||
{ | |||
if (stack.getCallerClass() != DefineClassHelper.class) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
SecurityActions.setAccessible(defineClass, true); | |||
return (Class<?>) defineClass.invoke(loader, new Object[] { | |||
@Override | |||
Class<?> defineClass(String name, byte[] b, int off, int len, Class<?> neighbor, | |||
ClassLoader loader, ProtectionDomain protectionDomain) | |||
throws ClassFormatError, CannotCompileException | |||
{ | |||
Class<?> klass = stack.getCallerClass(); | |||
if (klass != DefineClassHelper.class && klass != this.getClass()) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
SecurityActions.setAccessible(defineClass, true); | |||
return (Class<?>) defineClass.invoke(loader, new Object[] { | |||
name, b, off, len, protectionDomain | |||
}); | |||
} catch (Throwable e) { | |||
if (e instanceof ClassFormatError) throw (ClassFormatError) e; | |||
if (e instanceof RuntimeException) throw (RuntimeException) e; | |||
throw new ClassFormatError(e.getMessage()); | |||
} | |||
finally { | |||
SecurityActions.setAccessible(defineClass, false); | |||
} | |||
}); | |||
} catch (Throwable e) { | |||
if (e instanceof ClassFormatError) throw (ClassFormatError) e; | |||
if (e instanceof RuntimeException) throw (RuntimeException) e; | |||
throw new CannotCompileException(e); | |||
} | |||
}; | |||
protected abstract Class<?> defineClass(String name, byte[] b, int off, int len, | |||
ClassLoader loader, ProtectionDomain protectionDomain) throws ClassFormatError; | |||
finally { | |||
SecurityActions.setAccessible(defineClass, false); | |||
} | |||
} | |||
} | |||
// 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 SecuredPrivileged privileged = ClassFile.MAJOR_VERSION > ClassFile.JAVA_10 | |||
? SecuredPrivileged.JAVA_OTHER | |||
private static final Helper privileged = ClassFile.MAJOR_VERSION > ClassFile.JAVA_10 | |||
? new Java11() | |||
: ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9 | |||
? SecuredPrivileged.JAVA_9 | |||
: ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7 | |||
? SecuredPrivileged.JAVA_7 | |||
: SecuredPrivileged.JAVA_OTHER; | |||
? new Java9() | |||
: ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7 ? new Java7() : new JavaOther(); | |||
/** | |||
* 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 | |||
@@ -232,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. | |||
* @param 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); | |||
@@ -254,6 +278,48 @@ 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 { | |||
DefineClassHelper.class.getModule().addReads(neighbor.getModule()); | |||
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}. | |||
* |
@@ -32,110 +32,113 @@ import javassist.bytecode.ClassFile; | |||
*/ | |||
public class DefinePackageHelper | |||
{ | |||
private static enum SecuredPrivileged | |||
{ | |||
JAVA_9 { | |||
// definePackage has been discontinued for JAVA 9 | |||
@Override | |||
protected Package definePackage(ClassLoader loader, String name, String specTitle, | |||
private static abstract class Helper { | |||
abstract Package definePackage(ClassLoader loader, String name, String specTitle, | |||
String specVersion, String specVendor, String implTitle, String implVersion, | |||
String implVendor, URL sealBase) | |||
throws IllegalArgumentException; | |||
} | |||
private static class Java9 extends Helper { | |||
// definePackage has been discontinued for JAVA 9 | |||
@Override | |||
Package definePackage(ClassLoader loader, String name, String specTitle, | |||
String specVersion, String specVendor, String implTitle, String implVersion, | |||
String implVendor, URL sealBase) throws IllegalArgumentException | |||
{ | |||
throw new RuntimeException("define package has been disabled for jigsaw"); | |||
} | |||
}, | |||
JAVA_7 { | |||
private final SecurityActions stack = SecurityActions.stack; | |||
private final MethodHandle definePackage = getDefinePackageMethodHandle(); | |||
private MethodHandle getDefinePackageMethodHandle() | |||
{ | |||
if (stack.getCallerClass() != this.getClass()) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
return SecurityActions.getMethodHandle(ClassLoader.class, | |||
String implVendor, URL sealBase) | |||
throws IllegalArgumentException | |||
{ | |||
throw new RuntimeException("define package has been disabled for jigsaw"); | |||
} | |||
}; | |||
private static class Java7 extends Helper { | |||
private final SecurityActions stack = SecurityActions.stack; | |||
private final MethodHandle definePackage = getDefinePackageMethodHandle(); | |||
private MethodHandle getDefinePackageMethodHandle() { | |||
if (stack.getCallerClass() != this.getClass()) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
return SecurityActions.getMethodHandle(ClassLoader.class, | |||
"definePackage", new Class[] { | |||
String.class, String.class, String.class, String.class, | |||
String.class, String.class, String.class, URL.class | |||
}); | |||
} catch (NoSuchMethodException e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
} catch (NoSuchMethodException e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
} | |||
@Override | |||
protected Package definePackage(ClassLoader loader, String name, String specTitle, | |||
@Override | |||
Package definePackage(ClassLoader loader, String name, String specTitle, | |||
String specVersion, String specVendor, String implTitle, String implVersion, | |||
String implVendor, URL sealBase) throws IllegalArgumentException { | |||
if (stack.getCallerClass() != DefinePackageHelper.class) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
return (Package) definePackage.invokeWithArguments(loader, name, specTitle, | |||
String implVendor, URL sealBase) | |||
throws IllegalArgumentException | |||
{ | |||
if (stack.getCallerClass() != DefinePackageHelper.class) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
return (Package) definePackage.invokeWithArguments(loader, name, specTitle, | |||
specVersion, specVendor, implTitle, implVersion, implVendor, sealBase); | |||
} catch (Throwable e) { | |||
if (e instanceof IllegalArgumentException) throw (IllegalArgumentException) e; | |||
if (e instanceof RuntimeException) throw (RuntimeException) e; | |||
} | |||
return null; | |||
} catch (Throwable e) { | |||
if (e instanceof IllegalArgumentException) throw (IllegalArgumentException) e; | |||
if (e instanceof RuntimeException) throw (RuntimeException) e; | |||
} | |||
}, | |||
JAVA_OTHER { | |||
private final SecurityActions stack = SecurityActions.stack; | |||
private final Method definePackage = getDefinePackageMethod(); | |||
private Method getDefinePackageMethod() | |||
{ | |||
if (stack.getCallerClass() != this.getClass()) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
return SecurityActions.getDeclaredMethod(ClassLoader.class, | |||
return null; | |||
} | |||
} | |||
private static class JavaOther extends Helper { | |||
private final SecurityActions stack = SecurityActions.stack; | |||
private final Method definePackage = getDefinePackageMethod(); | |||
private Method getDefinePackageMethod() { | |||
if (stack.getCallerClass() != this.getClass()) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
return SecurityActions.getDeclaredMethod(ClassLoader.class, | |||
"definePackage", new Class[] { | |||
String.class, String.class, String.class, String.class, | |||
String.class, String.class, String.class, URL.class | |||
}); | |||
} catch (NoSuchMethodException e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
} catch (NoSuchMethodException e) { | |||
throw new RuntimeException("cannot initialize", e); | |||
} | |||
} | |||
@Override | |||
protected Package definePackage(ClassLoader loader, String name, String specTitle, | |||
@Override | |||
Package definePackage(ClassLoader loader, String name, String specTitle, | |||
String specVersion, String specVendor, String implTitle, String implVersion, | |||
String implVendor, URL sealBase) throws IllegalArgumentException | |||
{ | |||
if (stack.getCallerClass() != DefinePackageHelper.class) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
definePackage.setAccessible(true); | |||
return (Package) definePackage.invoke(loader, new Object[] { | |||
String implVendor, URL sealBase) | |||
throws IllegalArgumentException | |||
{ | |||
if (stack.getCallerClass() != DefinePackageHelper.class) | |||
throw new IllegalAccessError("Access denied for caller."); | |||
try { | |||
definePackage.setAccessible(true); | |||
return (Package) definePackage.invoke(loader, new Object[] { | |||
name, specTitle, specVersion, specVendor, implTitle, | |||
implVersion, implVendor, sealBase | |||
}); | |||
} catch (Throwable e) { | |||
if (e instanceof InvocationTargetException) { | |||
Throwable t = ((InvocationTargetException) e).getTargetException(); | |||
if (t instanceof IllegalArgumentException) | |||
throw (IllegalArgumentException) t; | |||
} | |||
if (e instanceof RuntimeException) throw (RuntimeException) e; | |||
} | |||
finally { | |||
definePackage.setAccessible(false); | |||
} catch (Throwable e) { | |||
if (e instanceof InvocationTargetException) { | |||
Throwable t = ((InvocationTargetException) e).getTargetException(); | |||
if (t instanceof IllegalArgumentException) | |||
throw (IllegalArgumentException) t; | |||
} | |||
return null; | |||
if (e instanceof RuntimeException) throw (RuntimeException) e; | |||
} | |||
}; | |||
protected abstract Package definePackage(ClassLoader loader, String name, String specTitle, | |||
String specVersion, String specVendor, String implTitle, String implVersion, | |||
String implVendor, URL sealBase) throws IllegalArgumentException; | |||
} | |||
private static final SecuredPrivileged privileged = ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9 | |||
? SecuredPrivileged.JAVA_9 | |||
: ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7 | |||
? SecuredPrivileged.JAVA_7 | |||
: SecuredPrivileged.JAVA_OTHER; | |||
finally { | |||
definePackage.setAccessible(false); | |||
} | |||
return null; | |||
} | |||
}; | |||
private static final Helper privileged | |||
= ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9 | |||
? new Java9() : ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7 | |||
? new Java7() : new JavaOther(); | |||
/** | |||
* Defines a new package. If the package is already defined, this method |
@@ -104,28 +104,55 @@ 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); | |||
} | |||
} | |||
/** | |||
* Loads a class file by a given lookup. | |||
* | |||
* @param lookup used to define the class. | |||
* @since 3.24 | |||
*/ | |||
public static Class<?> toClass(ClassFile cf, java.lang.invoke.MethodHandles.Lookup lookup) | |||
throws CannotCompileException | |||
{ | |||
try { | |||
byte[] b = toBytecode(cf); | |||
return DefineClassHelper.toClass(lookup, b); | |||
} | |||
catch (IOException e) { | |||
throw new CannotCompileException(e); |
@@ -35,6 +35,7 @@ import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.WeakHashMap; | |||
import java.lang.invoke.MethodHandles.Lookup; | |||
import javassist.CannotCompileException; | |||
import javassist.bytecode.AccessFlag; | |||
@@ -213,7 +214,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; | |||
@@ -436,43 +437,92 @@ public class ProxyFactory { | |||
/** | |||
* Generates a proxy class using the current filter. | |||
* The module or package where a proxy class is created | |||
* has to be opened to this package or the Javassist module. | |||
* | |||
* @see #createClass(Lookup) | |||
*/ | |||
public Class<?> createClass() { | |||
if (signature == null) { | |||
computeSignature(methodFilter); | |||
} | |||
return createClass1(); | |||
return createClass1(null); | |||
} | |||
/** | |||
* Generates a proxy class using the supplied filter. | |||
* The module or package where a proxy class is created | |||
* has to be opened to this package or the Javassist module. | |||
*/ | |||
public Class<?> createClass(MethodFilter filter) { | |||
computeSignature(filter); | |||
return createClass1(); | |||
return createClass1(null); | |||
} | |||
/** | |||
* Generates a proxy class with a specific signature. | |||
* access is package local so ProxyObjectInputStream can use this | |||
* @param signature | |||
* @return | |||
*/ | |||
Class<?> createClass(byte[] signature) | |||
{ | |||
installSignature(signature); | |||
return createClass1(); | |||
return createClass1(null); | |||
} | |||
/** | |||
* Generates a proxy class using the current filter. | |||
* | |||
* @param lookup used for loading the proxy class. | |||
* It needs an appropriate right to invoke {@code defineClass} | |||
* for the proxy class. | |||
* @since 3.24 | |||
*/ | |||
public Class<?> createClass(Lookup lookup) { | |||
if (signature == null) { | |||
computeSignature(methodFilter); | |||
} | |||
return createClass1(lookup); | |||
} | |||
/** | |||
* Generates a proxy class using the supplied filter. | |||
* | |||
* @param lookup used for loading the proxy class. | |||
* It needs an appropriate right to invoke {@code defineClass} | |||
* for the proxy class. | |||
* @param filter the filter. | |||
* @since 3.24 | |||
*/ | |||
public Class<?> createClass(Lookup lookup, MethodFilter filter) { | |||
computeSignature(filter); | |||
return createClass1(lookup); | |||
} | |||
private Class<?> createClass1() { | |||
/** | |||
* Generates a proxy class with a specific signature. | |||
* access is package local so ProxyObjectInputStream can use this. | |||
* | |||
* @param lookup used for loading the proxy class. | |||
* It needs an appropriate right to invoke {@code defineClass} | |||
* for the proxy class. | |||
* @param signature the signature. | |||
*/ | |||
Class<?> createClass(Lookup lookup, byte[] signature) | |||
{ | |||
installSignature(signature); | |||
return createClass1(lookup); | |||
} | |||
private Class<?> createClass1(Lookup lookup) { | |||
Class<?> result = thisClass; | |||
if (result == null) { | |||
ClassLoader cl = getClassLoader(); | |||
synchronized (proxyCache) { | |||
if (factoryUseCache) | |||
createClass2(cl); | |||
createClass2(cl, lookup); | |||
else | |||
createClass3(cl); | |||
createClass3(cl, lookup); | |||
result = thisClass; | |||
// don't retain any unwanted references | |||
@@ -512,7 +562,7 @@ public class ProxyFactory { | |||
return sbuf.toString(); | |||
} | |||
private void createClass2(ClassLoader cl) { | |||
private void createClass2(ClassLoader cl, Lookup lookup) { | |||
String key = getKey(superClass, interfaces, signature, factoryWriteReplace); | |||
/* | |||
* Excessive concurrency causes a large memory footprint and slows the | |||
@@ -534,13 +584,13 @@ public class ProxyFactory { | |||
return; | |||
} | |||
} | |||
createClass3(cl); | |||
createClass3(cl, lookup); | |||
details = new ProxyDetails(signature, thisClass, factoryWriteReplace); | |||
cacheForTheLoader.put(key, details); | |||
// } | |||
} | |||
private void createClass3(ClassLoader cl) { | |||
private void createClass3(ClassLoader cl, Lookup lookup) { | |||
// we need a new class so we need a new class name | |||
allocateClassName(); | |||
@@ -549,7 +599,11 @@ public class ProxyFactory { | |||
if (writeDirectory != null) | |||
FactoryHelper.writeFile(cf, writeDirectory); | |||
thisClass = FactoryHelper.toClass(cf, cl, getDomain()); | |||
if (lookup == null) | |||
thisClass = FactoryHelper.toClass(cf, getClassInTheSamePackage(), cl, getDomain()); | |||
else | |||
thisClass = FactoryHelper.toClass(cf, lookup); | |||
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 +616,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 { | |||
@@ -1186,8 +1254,8 @@ public class ProxyFactory { | |||
// put the method to the cache, retrieve previous definition (if any) | |||
Method oldMethod = hash.put(key, m); | |||
// JIRA JASSIST-244 | |||
// ignore a bridge method with the same signature that the overridden one has. | |||
// JIRA JASSIST-244, 267 | |||
// ignore a bridge method to a method declared in a non-public class. | |||
if (null != oldMethod && isBridge(m) | |||
&& !Modifier.isPublic(oldMethod.getDeclaringClass().getModifiers()) | |||
&& !Modifier.isAbstract(oldMethod.getModifiers()) && !isDuplicated(i, methods)) |
@@ -36,6 +36,7 @@ import javassist.bytecode.ClassFile; | |||
class SecurityActions extends SecurityManager | |||
{ | |||
public static final SecurityActions stack = new SecurityActions(); | |||
/** | |||
* Since Java 9 abruptly removed <code>Reflection.getCallerClass()</code> | |||
* in favour of <code>StackWalker</code> we are left having to find a | |||
@@ -46,14 +47,13 @@ class SecurityActions extends SecurityManager | |||
* functional across all versions, for now. | |||
* | |||
* @return represents the declaring class of the method that invoked | |||
* the method that called this or idx 2 on the stack trace. | |||
* @since 3.23 */ | |||
public Class<?> getCallerClass() | |||
{ | |||
* the method that called this or index 2 on the stack trace. | |||
* @since 3.23 | |||
*/ | |||
public Class<?> getCallerClass() { | |||
return getClassContext()[2]; | |||
} | |||
static Method[] getDeclaredMethods(final Class<?> clazz) | |||
{ | |||
if (System.getSecurityManager() == null) |
@@ -0,0 +1,5 @@ | |||
/* | |||
* This is used as a capability for running CtClass#toClass(). | |||
*/ | |||
public class DefineClassCapability { | |||
} |
@@ -1,6 +1,8 @@ | |||
package javassist; | |||
import junit.framework.*; | |||
import test1.DefineClassCapability; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.InputStream; | |||
@@ -743,8 +745,8 @@ public class JvstTest extends JvstTestRoot { | |||
ctInterface.stopPruning(true); | |||
ctInterface.writeFile(); | |||
ctInterface.toClass(); | |||
targetCtClass.toClass(); | |||
ctInterface.toClass(DefineClassCapability.class); | |||
targetCtClass.toClass(DefineClassCapability.class); | |||
} | |||
public void testDispatch() throws Exception { | |||
@@ -1161,6 +1163,7 @@ public class JvstTest extends JvstTestRoot { | |||
suite.addTestSuite(javassist.SetterTest.class); | |||
suite.addTestSuite(javassist.bytecode.InsertGap0.class); | |||
suite.addTestSuite(javassist.tools.reflect.LoaderTest.class); | |||
suite.addTestSuite(javassist.tools.CallbackTest.class); | |||
suite.addTestSuite(testproxy.ProxyTester.class); | |||
suite.addTestSuite(testproxy.ProxyFactoryPerformanceTest.class); // remove? | |||
suite.addTestSuite(javassist.proxyfactory.ProxyFactoryTest.class); |
@@ -9,6 +9,7 @@ import org.junit.runners.MethodSorters; | |||
import java.lang.reflect.Method; | |||
import javassist.expr.*; | |||
import test2.DefineClassCapability; | |||
@SuppressWarnings({"rawtypes","unused"}) | |||
@FixMethodOrder(MethodSorters.NAME_ASCENDING) | |||
@@ -709,7 +710,7 @@ public class JvstTest2 extends JvstTestRoot { | |||
public void testToClass() throws Exception { | |||
ClassPool cp = ClassPool.getDefault(); | |||
CtClass cc = cp.makeClass("test2.ToClassTest"); | |||
Class c = cc.toClass(); | |||
Class c = cc.toClass(DefineClassCapability.class); | |||
assertEquals(getClass().getClassLoader(), c.getClassLoader()); | |||
} | |||
@@ -340,7 +340,7 @@ public class JvstTest3 extends JvstTestRoot { | |||
System.out.println("Num Annotation : " +ans.length); | |||
// c.debugWriteFile(); | |||
Class newclass = c.toClass(); | |||
Class newclass = c.toClass(DefineClassCapability.class); | |||
java.lang.annotation.Annotation[] anns = newclass.getAnnotations(); | |||
System.out.println("Num NewClass Annotation : " +anns.length); | |||
assertEquals(ans.length, anns.length); | |||
@@ -737,7 +737,7 @@ public class JvstTest3 extends JvstTestRoot { | |||
CtMethod m2 = cc2.getDeclaredMethod("getX"); | |||
copyAnnotations(m1, m2); | |||
cc2.getClassFile(); | |||
Class clazz = cc2.toClass(); | |||
Class clazz = cc2.toClass(DefineClassCapability.class); | |||
java.lang.reflect.Method m = clazz.getDeclaredMethod("getX", new Class[0]); | |||
assertEquals(1, m.getAnnotations().length); | |||
test3.VisibleAnno a = m.getAnnotation(test3.VisibleAnno.class); | |||
@@ -790,7 +790,7 @@ public class JvstTest3 extends JvstTestRoot { | |||
cc.addField(fobj, CtField.Initializer.constant("bar")); | |||
cc.writeFile(); | |||
Class clazz = cc.toClass(); | |||
Class clazz = cc.toClass(DefineClassCapability.class); | |||
assertEquals(2L, clazz.getField("j").getLong(null)); | |||
assertEquals(3, clazz.getField("i").getInt(null)); | |||
assertEquals(4, clazz.getField("s").getShort(null)); | |||
@@ -1108,7 +1108,7 @@ public class JvstTest3 extends JvstTestRoot { | |||
sb.append("}"); | |||
ctc.addMethod(CtNewMethod.make(sb.toString(), ctc)); | |||
ctc.debugWriteFile(); | |||
ctc.toClass().getConstructor().newInstance(); | |||
ctc.toClass(DefineClassCapability.class).getConstructor().newInstance(); | |||
} | |||
// JIRA-83 |
@@ -1054,7 +1054,7 @@ public class JvstTest4 extends JvstTestRoot { | |||
addDeadCode(newClass, "public boolean evaluate7(){ return !true; }"); | |||
newClass.debugWriteFile(); | |||
Class<?> cClass = newClass.toClass(); | |||
Class<?> cClass = newClass.toClass(test4.DefineClassCapability.class); | |||
Object o = cClass.getConstructor().newInstance(); | |||
java.lang.reflect.Method m = cClass.getMethod("evaluate"); | |||
m.invoke(o); | |||
@@ -1112,6 +1112,7 @@ public class JvstTest4 extends JvstTestRoot { | |||
attr.setAnnotation(a); | |||
m.getMethodInfo().addAttribute(attr); | |||
cc.writeFile(); | |||
anno.toClass(test4.DefineClassCapability.class); | |||
Class<?> rc = ((java.lang.annotation.Annotation)m.getAnnotations()[0]).annotationType(); | |||
assertEquals(anno.getName(), rc.getName()); | |||
} |
@@ -9,6 +9,8 @@ import javassist.bytecode.AttributeInfo; | |||
import javassist.bytecode.ClassFile; | |||
import javassist.bytecode.ConstPool; | |||
import javassist.bytecode.InnerClassesAttribute; | |||
import javassist.bytecode.NestHostAttribute; | |||
import javassist.bytecode.NestMembersAttribute; | |||
import javassist.expr.ExprEditor; | |||
import javassist.expr.Handler; | |||
import javassist.expr.MethodCall; | |||
@@ -221,7 +223,7 @@ public class JvstTest5 extends JvstTestRoot { | |||
"}"); | |||
System.out.println(src); | |||
badClass.addMethod(CtMethod.make(src, badClass)); | |||
Class clazzz = badClass.toClass(); | |||
Class clazzz = badClass.toClass(Class.forName("DefineClassCapability")); | |||
Object obj = clazzz.getConstructor().newInstance(); // <-- falls here | |||
} | |||
@@ -416,4 +418,39 @@ public class JvstTest5 extends JvstTestRoot { | |||
Object obj = make(cc.getName()); | |||
assertEquals(1, invoke(obj, "test")); | |||
} | |||
public void testNestHostAttribute() throws Exception { | |||
CtClass cc = sloader.get("test5.NestHost$Foo"); | |||
ClassFile cf = cc.getClassFile(); | |||
NestHostAttribute attr = (NestHostAttribute)cf.getAttribute(NestHostAttribute.tag); | |||
assertEquals(test5.NestHost.class.getName(), | |||
cf.getConstPool().getClassInfo(attr.hostClassIndex())); | |||
} | |||
public void testNestMembersAttribute() throws Exception { | |||
CtClass cc = sloader.get("test5.NestHost"); | |||
ClassFile cf = cc.getClassFile(); | |||
NestMembersAttribute attr = (NestMembersAttribute)cf.getAttribute(NestMembersAttribute.tag); | |||
assertEquals(2, attr.numberOfClasses()); | |||
String[] names = new String[2]; | |||
for (int i = 0; i < 2; i++) | |||
names[i] = cf.getConstPool().getClassInfo(attr.memberClass(i)); | |||
assertFalse(names[0].equals(names[1])); | |||
assertTrue(names[0].equals("test5.NestHost$Foo") || names[0].equals("test5.NestHost$Bar")); | |||
assertTrue(names[1].equals("test5.NestHost$Foo") || names[1].equals("test5.NestHost$Bar")); | |||
} | |||
public void testNestMembersAttributeCopy() throws Exception { | |||
CtClass cc = sloader.get("test5.NestHost2"); | |||
cc.getClassFile().compact(); | |||
cc.writeFile(); | |||
make(cc.getName()); | |||
} | |||
public void testNestHostAttributeCopy() throws Exception { | |||
CtClass cc = sloader.get("test5.NestHost2$Foo"); | |||
cc.getClassFile().compact(); | |||
cc.toClass(test5.DefineClassCapability.class); | |||
} | |||
} |
@@ -1,112 +1,114 @@ | |||
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)}); | |||
} | |||
} |
@@ -175,7 +175,7 @@ public final class InsertGap0 extends JvstTestRoot { | |||
cc.addField(new CtField(CtClass.intType, "i", cc), "++counter"); | |||
boolean p = cc.stopPruning(true); | |||
cc.writeFile(); | |||
Class c = cc.toClass(); | |||
Class c = cc.toClass(ClassFile.class); | |||
cc.stopPruning(p); | |||
Object obj = c.getConstructor().newInstance(); | |||
@@ -194,7 +194,7 @@ public final class InsertGap0 extends JvstTestRoot { | |||
cc.addField(new CtField(CtClass.intType, "i", cc), "++counter"); | |||
boolean p = cc.stopPruning(true); | |||
cc.writeFile(); | |||
Class c = cc.toClass(); | |||
Class c = cc.toClass(ClassFile.class); | |||
cc.stopPruning(p); | |||
Object obj = c.getConstructor().newInstance(); |
@@ -3,6 +3,7 @@ package javassist.tools; | |||
import javassist.*; | |||
import junit.framework.TestCase; | |||
import test.javassist.tools.DummyClass; | |||
import test.javassist.tools.DefineClassCapability; | |||
import static javassist.tools.Callback.*; | |||
@@ -34,7 +35,7 @@ public class CallbackTest extends TestCase { | |||
}); | |||
// Change class and invoke method; | |||
classToChange.toClass(); | |||
classToChange.toClass(DefineClassCapability.class); | |||
new DummyClass().dummyMethod(); | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
package test.javassist; | |||
/* | |||
* This is used as a capability for running CtClass#toClass(). | |||
*/ | |||
public class DefineClassCapability { | |||
} |
@@ -1,7 +1,5 @@ | |||
package test.javassist.convert; | |||
import java.net.URL; | |||
import java.net.URLClassLoader; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
@@ -11,7 +9,7 @@ import javassist.CtClass; | |||
import junit.framework.TestCase; | |||
public class ArrayAccessReplaceTest extends TestCase { | |||
private static SimpleInterface simple; | |||
private static SimpleInterface simple = null; | |||
public void setUp() throws Exception { | |||
ClassPool pool = new ClassPool(true); | |||
@@ -21,7 +19,9 @@ public class ArrayAccessReplaceTest extends TestCase { | |||
converter.replaceArrayAccess(echoClass, new CodeConverter.DefaultArrayAccessReplacementMethodNames()); | |||
simpleClass.instrument(converter); | |||
//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 { | |||
@@ -31,7 +31,9 @@ public class ArrayAccessReplaceTest extends TestCase { | |||
CodeConverter converter = new CodeConverter(); | |||
converter.replaceArrayAccess(clazz, new CodeConverter.DefaultArrayAccessReplacementMethodNames()); | |||
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)); | |||
} | |||
@@ -90,9 +90,9 @@ public class ProxyCacheGCTest extends TestCase | |||
// now create a proxyfactory and use it to create a proxy | |||
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(); | |||
MethodFilter filter = (MethodFilter)javaFilterClass.getConstructor().newInstance(); |
@@ -330,5 +330,36 @@ public class ProxySimpleTest extends TestCase { | |||
public static class Extended267 extends Base267 { | |||
public String base(Integer i) { return "extended" + i + i; } | |||
} | |||
String value267b; | |||
public void testJIRA267b() throws Exception { | |||
Extended267b extended267 = new Extended267b(); | |||
ProxyFactory factory = new ProxyFactory(); | |||
factory.setSuperclass(Extended267b.class); | |||
Extended267b e = (Extended267b)factory.create(null, null, new MethodHandler() { | |||
@Override | |||
public Object invoke(Object self, Method thisMethod, | |||
Method proceed, Object[] args) throws Throwable { | |||
value267b += thisMethod.getDeclaringClass().getName(); | |||
value267b += ";" + thisMethod.getReturnType().getName(); | |||
return proceed.invoke(self, args); | |||
} | |||
}); | |||
value267b = ""; | |||
assertEquals("extended", e.base()); | |||
System.out.println(value267b); | |||
assertEquals(Extended267b.class.getName() + ";" + String.class.getName(), value267b); | |||
} | |||
public static class Base267b { | |||
public Object base() { return "base"; } | |||
} | |||
// Extended267b has a bridge method for base():Object, | |||
// since Extended267b#base() is covariant. | |||
public static class Extended267b extends Base267b { | |||
public String base() { return "extended"; } | |||
} | |||
} |
@@ -1,281 +0,0 @@ | |||
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();} | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
/* | |||
* This is used as a capability for running CtClass#toClass(). | |||
*/ | |||
package test.javassist.tools; | |||
public class DefineClassCapability { | |||
} |
@@ -0,0 +1,7 @@ | |||
/* | |||
* This is used as a capability for running CtClass#toClass(). | |||
*/ | |||
package test1; | |||
public class DefineClassCapability { | |||
} |
@@ -0,0 +1,7 @@ | |||
/* | |||
* This is used as a capability for running CtClass#toClass(). | |||
*/ | |||
package test2; | |||
public class DefineClassCapability { | |||
} |
@@ -0,0 +1,7 @@ | |||
/* | |||
* This is used as a capability for running CtClass#toClass(). | |||
*/ | |||
package test3; | |||
public class DefineClassCapability { | |||
} |
@@ -0,0 +1,7 @@ | |||
/* | |||
* This is used as a capability for running CtClass#toClass(). | |||
*/ | |||
package test4; | |||
public class DefineClassCapability { | |||
} |
@@ -0,0 +1,7 @@ | |||
/* | |||
* This is used as a capability for running CtClass#toClass(). | |||
*/ | |||
package test5; | |||
public class DefineClassCapability { | |||
} |
@@ -0,0 +1,11 @@ | |||
package test5; | |||
public class NestHost { | |||
private int value; | |||
public class Foo { | |||
int foo() { return value++; } | |||
} | |||
public class Bar { | |||
int bar() { return value++; } | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
package test5; | |||
public class NestHost2 { | |||
private int value; | |||
public class Foo { | |||
int foo() { return value++; } | |||
} | |||
public class Bar { | |||
int bar() { return value++; } | |||
} | |||
} |