diff options
author | Ning Zhang <ning.n.zhang@ericsson.com> | 2018-11-02 10:11:18 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-02 10:11:18 +0800 |
commit | 10f4eaf582721005fefc77c6d20c91d2c4981629 (patch) | |
tree | 3591f42475c4adb55b05f0c581fcde9be59e17d0 | |
parent | bca5c7aad8551aebda6039fb74b41c20de3d23d9 (diff) | |
parent | fa3dd56b836208944683de8a50bb36e96b5e001e (diff) | |
download | javassist-10f4eaf582721005fefc77c6d20c91d2c4981629.tar.gz javassist-10f4eaf582721005fefc77c6d20c91d2c4981629.zip |
Merge pull request #1 from jboss-javassist/master
Sync with jboss-javassist/javassist.
46 files changed, 1171 insertions, 740 deletions
@@ -12,4 +12,4 @@ tmp/ .project .settings TestLog.xml - +*~ diff --git a/Readme.html b/Readme.html index c8649848..b89b5b95 100644 --- a/Readme.html +++ b/Readme.html @@ -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>
\ No newline at end of file +</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"/> diff --git a/javassist.jar b/javassist.jar Binary files differindex edf45a97..73e3f0fd 100644 --- a/javassist.jar +++ b/javassist.jar @@ -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> diff --git a/src/main/META-INF/MANIFEST.MF b/src/main/META-INF/MANIFEST.MF index 448c6fca..09bb026c 100644 --- a/src/main/META-INF/MANIFEST.MF +++ b/src/main/META-INF/MANIFEST.MF @@ -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 diff --git a/src/main/javassist/ClassPool.java b/src/main/javassist/ClassPool.java index 60055992..37f2acbd 100644 --- a/src/main/javassist/ClassPool.java +++ b/src/main/javassist/ClassPool.java @@ -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); diff --git a/src/main/javassist/ClassPoolTail.java b/src/main/javassist/ClassPoolTail.java index 2ab8ee46..965d72fb 100644 --- a/src/main/javassist/ClassPoolTail.java +++ b/src/main/javassist/ClassPoolTail.java @@ -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?: " diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index 375c7ba5..847d9c7b 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -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) diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index 3791b5fb..ae196c4a 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -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); } diff --git a/src/main/javassist/Loader.java b/src/main/javassist/Loader.java index 8eb067c4..37f83d68 100644 --- a/src/main/javassist/Loader.java +++ b/src/main/javassist/Loader.java @@ -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; diff --git a/src/main/javassist/Modifier.java b/src/main/javassist/Modifier.java index fd73c3b3..733cc67b 100644 --- a/src/main/javassist/Modifier.java +++ b/src/main/javassist/Modifier.java @@ -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) { diff --git a/src/main/javassist/bytecode/AttributeInfo.java b/src/main/javassist/bytecode/AttributeInfo.java index 4bfd0dbb..be6e2a21 100644 --- a/src/main/javassist/bytecode/AttributeInfo.java +++ b/src/main/javassist/bytecode/AttributeInfo.java @@ -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 diff --git a/src/main/javassist/bytecode/NestHostAttribute.java b/src/main/javassist/bytecode/NestHostAttribute.java new file mode 100644 index 00000000..28466d56 --- /dev/null +++ b/src/main/javassist/bytecode/NestHostAttribute.java @@ -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); + } +} diff --git a/src/main/javassist/bytecode/NestMembersAttribute.java b/src/main/javassist/bytecode/NestMembersAttribute.java new file mode 100644 index 00000000..a402e26e --- /dev/null +++ b/src/main/javassist/bytecode/NestMembersAttribute.java @@ -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); + } +} diff --git a/src/main/javassist/expr/ExprEditor.java b/src/main/javassist/expr/ExprEditor.java index 0b3f934e..0f9bedce 100644 --- a/src/main/javassist/expr/ExprEditor.java +++ b/src/main/javassist/expr/ExprEditor.java @@ -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 {} diff --git a/src/main/javassist/expr/NewArray.java b/src/main/javassist/expr/NewArray.java index 70d74afd..df30e26f 100644 --- a/src/main/javassist/expr/NewArray.java +++ b/src/main/javassist/expr/NewArray.java @@ -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) { diff --git a/src/main/javassist/expr/NewExpr.java b/src/main/javassist/expr/NewExpr.java index 6b28475b..3171fc3f 100644 --- a/src/main/javassist/expr/NewExpr.java +++ b/src/main/javassist/expr/NewExpr.java @@ -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. diff --git a/src/main/javassist/scopedpool/SoftValueHashMap.java b/src/main/javassist/scopedpool/SoftValueHashMap.java index 1154e62e..01d8498a 100644 --- a/src/main/javassist/scopedpool/SoftValueHashMap.java +++ b/src/main/javassist/scopedpool/SoftValueHashMap.java @@ -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. */ diff --git a/src/main/javassist/util/proxy/DefineClassHelper.java b/src/main/javassist/util/proxy/DefineClassHelper.java index b367931a..96ade4aa 100644 --- a/src/main/javassist/util/proxy/DefineClassHelper.java +++ b/src/main/javassist/util/proxy/DefineClassHelper.java @@ -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}. * diff --git a/src/main/javassist/util/proxy/DefinePackageHelper.java b/src/main/javassist/util/proxy/DefinePackageHelper.java index 2124eebe..8a91eb28 100644 --- a/src/main/javassist/util/proxy/DefinePackageHelper.java +++ b/src/main/javassist/util/proxy/DefinePackageHelper.java @@ -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 diff --git a/src/main/javassist/util/proxy/FactoryHelper.java b/src/main/javassist/util/proxy/FactoryHelper.java index 1fb63b65..1928fdd5 100644 --- a/src/main/javassist/util/proxy/FactoryHelper.java +++ b/src/main/javassist/util/proxy/FactoryHelper.java @@ -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); diff --git a/src/main/javassist/util/proxy/ProxyFactory.java b/src/main/javassist/util/proxy/ProxyFactory.java index 646bbcc4..0c39a508 100644 --- a/src/main/javassist/util/proxy/ProxyFactory.java +++ b/src/main/javassist/util/proxy/ProxyFactory.java @@ -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)) diff --git a/src/main/javassist/util/proxy/SecurityActions.java b/src/main/javassist/util/proxy/SecurityActions.java index acb89c8d..c940561b 100755 --- a/src/main/javassist/util/proxy/SecurityActions.java +++ b/src/main/javassist/util/proxy/SecurityActions.java @@ -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) diff --git a/src/test/DefineClassCapability.java b/src/test/DefineClassCapability.java new file mode 100644 index 00000000..c6dec200 --- /dev/null +++ b/src/test/DefineClassCapability.java @@ -0,0 +1,5 @@ +/* + * This is used as a capability for running CtClass#toClass(). + */ +public class DefineClassCapability { +} diff --git a/src/test/javassist/JvstTest.java b/src/test/javassist/JvstTest.java index e2f9211f..aab0b907 100644 --- a/src/test/javassist/JvstTest.java +++ b/src/test/javassist/JvstTest.java @@ -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); diff --git a/src/test/javassist/JvstTest2.java b/src/test/javassist/JvstTest2.java index 117560c0..0ea4571b 100644 --- a/src/test/javassist/JvstTest2.java +++ b/src/test/javassist/JvstTest2.java @@ -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()); } diff --git a/src/test/javassist/JvstTest3.java b/src/test/javassist/JvstTest3.java index 47131a93..46f06b16 100644 --- a/src/test/javassist/JvstTest3.java +++ b/src/test/javassist/JvstTest3.java @@ -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 diff --git a/src/test/javassist/JvstTest4.java b/src/test/javassist/JvstTest4.java index 187abb3d..7c8c7457 100644 --- a/src/test/javassist/JvstTest4.java +++ b/src/test/javassist/JvstTest4.java @@ -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()); } diff --git a/src/test/javassist/JvstTest5.java b/src/test/javassist/JvstTest5.java index 6f6eb075..c5eff4d1 100644 --- a/src/test/javassist/JvstTest5.java +++ b/src/test/javassist/JvstTest5.java @@ -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); + } } diff --git a/src/test/javassist/SetterTest.java b/src/test/javassist/SetterTest.java index 44eec140..a76b2a30 100644 --- a/src/test/javassist/SetterTest.java +++ b/src/test/javassist/SetterTest.java @@ -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)}); + } +} diff --git a/src/test/javassist/bytecode/InsertGap0.java b/src/test/javassist/bytecode/InsertGap0.java index 824b9305..425f12d7 100644 --- a/src/test/javassist/bytecode/InsertGap0.java +++ b/src/test/javassist/bytecode/InsertGap0.java @@ -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(); diff --git a/src/test/javassist/tools/CallbackTest.java b/src/test/javassist/tools/CallbackTest.java index 96632bda..f5861ceb 100644 --- a/src/test/javassist/tools/CallbackTest.java +++ b/src/test/javassist/tools/CallbackTest.java @@ -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(); } } diff --git a/src/test/test/javassist/DefineClassCapability.java b/src/test/test/javassist/DefineClassCapability.java new file mode 100644 index 00000000..e6a0fd51 --- /dev/null +++ b/src/test/test/javassist/DefineClassCapability.java @@ -0,0 +1,7 @@ +package test.javassist; + +/* + * This is used as a capability for running CtClass#toClass(). + */ +public class DefineClassCapability { +} diff --git a/src/test/test/javassist/convert/ArrayAccessReplaceTest.java b/src/test/test/javassist/convert/ArrayAccessReplaceTest.java index d81f220d..cebd1853 100644 --- a/src/test/test/javassist/convert/ArrayAccessReplaceTest.java +++ b/src/test/test/javassist/convert/ArrayAccessReplaceTest.java @@ -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)); } diff --git a/src/test/test/javassist/proxy/ProxyCacheGCTest.java b/src/test/test/javassist/proxy/ProxyCacheGCTest.java index 97b7281d..e47c4383 100644 --- a/src/test/test/javassist/proxy/ProxyCacheGCTest.java +++ b/src/test/test/javassist/proxy/ProxyCacheGCTest.java @@ -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(); diff --git a/src/test/test/javassist/proxy/ProxySimpleTest.java b/src/test/test/javassist/proxy/ProxySimpleTest.java index 76178a7e..63298501 100644 --- a/src/test/test/javassist/proxy/ProxySimpleTest.java +++ b/src/test/test/javassist/proxy/ProxySimpleTest.java @@ -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"; } + } } diff --git a/src/test/test/javassist/proxy/TestSecuredPrivileged.java b/src/test/test/javassist/proxy/TestSecuredPrivileged.java deleted file mode 100644 index c51555f5..00000000 --- a/src/test/test/javassist/proxy/TestSecuredPrivileged.java +++ /dev/null @@ -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();} - - } -} diff --git a/src/test/test/javassist/tools/DefineClassCapability.java b/src/test/test/javassist/tools/DefineClassCapability.java new file mode 100644 index 00000000..fc3dc4e4 --- /dev/null +++ b/src/test/test/javassist/tools/DefineClassCapability.java @@ -0,0 +1,7 @@ +/* + * This is used as a capability for running CtClass#toClass(). + */ +package test.javassist.tools; + +public class DefineClassCapability { +} diff --git a/src/test/test1/DefineClassCapability.java b/src/test/test1/DefineClassCapability.java new file mode 100644 index 00000000..fe386f06 --- /dev/null +++ b/src/test/test1/DefineClassCapability.java @@ -0,0 +1,7 @@ +/* + * This is used as a capability for running CtClass#toClass(). + */ +package test1; + +public class DefineClassCapability { +} diff --git a/src/test/test2/DefineClassCapability.java b/src/test/test2/DefineClassCapability.java new file mode 100644 index 00000000..303ffcc7 --- /dev/null +++ b/src/test/test2/DefineClassCapability.java @@ -0,0 +1,7 @@ +/* + * This is used as a capability for running CtClass#toClass(). + */ +package test2; + +public class DefineClassCapability { +} diff --git a/src/test/test3/DefineClassCapability.java b/src/test/test3/DefineClassCapability.java new file mode 100644 index 00000000..60b91835 --- /dev/null +++ b/src/test/test3/DefineClassCapability.java @@ -0,0 +1,7 @@ +/* + * This is used as a capability for running CtClass#toClass(). + */ +package test3; + +public class DefineClassCapability { +} diff --git a/src/test/test4/DefineClassCapability.java b/src/test/test4/DefineClassCapability.java new file mode 100644 index 00000000..d0bef62e --- /dev/null +++ b/src/test/test4/DefineClassCapability.java @@ -0,0 +1,7 @@ +/* + * This is used as a capability for running CtClass#toClass(). + */ +package test4; + +public class DefineClassCapability { +} diff --git a/src/test/test5/DefineClassCapability.java b/src/test/test5/DefineClassCapability.java new file mode 100644 index 00000000..1bb573c8 --- /dev/null +++ b/src/test/test5/DefineClassCapability.java @@ -0,0 +1,7 @@ +/* + * This is used as a capability for running CtClass#toClass(). + */ +package test5; + +public class DefineClassCapability { +} diff --git a/src/test/test5/NestHost.java b/src/test/test5/NestHost.java new file mode 100644 index 00000000..045d9d30 --- /dev/null +++ b/src/test/test5/NestHost.java @@ -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++; } + } +} diff --git a/src/test/test5/NestHost2.java b/src/test/test5/NestHost2.java new file mode 100644 index 00000000..571059be --- /dev/null +++ b/src/test/test5/NestHost2.java @@ -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++; } + } +} |